WPF Bind Window Title to ViewModel Property - c#

I am trying to bind a Window Title to the ViewModel which has a Title property. Below is the MainWindow XAML:
<Window x:Class="MyProject.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:MyProject.ViewModel;assembly=MyProject.ViewModel"
Title="{Binding Path=Title}" Height="350" Width="525" DataContext="{Binding Source={StaticResource mainWindowViewModel}}">
<Window.Resources>
<vm:MainWindow x:Key="mainWindowViewModel"/>
</Window.Resources>
...
</Window>
When I try to run this, I get the following exception "Provide value on 'System.Windows.StaticResourceExtension' threw an exception. The line number and position point to the DataContext property, and the inner exception states "Cannot find resource named mainWindowViewModel.
Below is the code for the View Model:
namespace MyProject.ViewModel
{
public class MainWindow : INotifyPropertyChanged
{
#region Fields
private const string TitlebarPrefixString = "My Project";
private string title = TitlebarPrefixString;
public string Title {
get
{
return this.title;
} // End getter
set
{
this.title = value;
OnPropertyChanged("Title");
} // End setter
} // End property
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
} // End if
} // End method
public event PropertyChangedEventHandler PropertyChanged;
} // End class
} // End namespace
My theory is that the resources are loaded after the attempt to bind the title to the property. When the exception is thrown, the Resources property for the Window is empty.
Is the only solution to set the DataContext in the Code Behind? I can get this to work, but I would prefer to keep it in XAML.

You can try to set the DataContext using property element syntax:
<Window x:Class="MyProject.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:MyProject.ViewModel;assembly=MyProject.ViewModel"
Title="{Binding Path=Title}" Height="350" Width="525">
<Window.Resources>
<vm:MainWindow x:Key="mainWindowViewModel"/>
</Window.Resources>
<Window.DataContext>
<StaticResourceExtension ResourceKey="mainWindowViewModel"/>
</Window.DataContext>
That should work as the xaml parser will execute StaticResourceExtension after the resources dictionary is set.
But i think maybe even better would be to set the DataContext directly, without declaring it as resource:
<Window x:Class="MyProject.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:MyProject.ViewModel;assembly=MyProject.ViewModel"
Title="{Binding Path=Title}" Height="350" Width="525">
<Window.DataContext>
<vm:MainWindow x:Key="mainWindowViewModel"/>
</Window.DataContext>

A little late but a simple and perfect solution that I am using just in case people are still searching for possibilities:
<Window x:Class="Project.MainWindow"
Title="{Binding DataContext.ApplicationTitle, ElementName=TheMainView}">
<views:MainView x:Name="TheMainView"/>
</Window>
Than simply enough, just add a Property to your MainViewModel that is the DataContext of the MainView like so:
public string ApplicationTitle
{
get
{
var text = "Application Name";
if (!string.IsNullOrEmpty(_currentFileLoaded))
{
text += $" ({_currentFileLoaded})";
}
return text;
}
}
Hope it helps..

Related

MVVM - UserControl Loaded only first time

I'm using ViewModel-first aprroach. In MainWindow.xaml I've set ContentControl where I display my UserControls (Views), by a click on a MenuItem. When I click to display UserControl first time, everything works fine.
But when I click same MenuItem to open It once again, my UserControl displays again but doesn't get loaded anymore, resulting in not having refreshed bindings. Setting my ContentControl's Content to null doesn't resolve issue.
My whole setup is like this:
1.) App.xaml resource
<!--DataContext for MainWindow.xaml-->
<ViewModels:MainWindowViewModel x:Key="Main_VM"/>
<!--DataTemplate for UserControl-->
<DataTemplate DataType="{x:Type ViewModels:MyViewModel}">
<Views:MyView />
</DataTemplate>
2.) MainWindow.xaml, where my ContenControl is located
<Window x:Class="My.Views.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
DataContext="{StaticResource Main_VM}">
<Grid>
<!--Menu which opens view on command-->
<Menu VerticalAlignment="Top" IsMainMenu="True" >
<MenuItem Header="My View" Command="{Binding Show_View}" CommandParameter="1"/>
</Menu>
<ContentControl Content="{Binding Display_View}" />
<!--And all other controls, like Menu for opening views on click...-->
</Grid>
</Window>
3.) ViewModel for Mainwindow.xaml (inherited from BaseViewModel)
public class MainWindowViewModel : BaseViewModel
{
public MainWindowViewModel()
{
//Command for displaying Views
Show_View = new Relay_Command(Open_view, null);
}
public ICommand Show_View { get; set; }
private BaseViewModel _display_view;
public BaseViewModel Display_View
{
get { return _display_view; }
set { _display_view = value; OnPropertyChanged(); }
}
private void Open_view(object parameter)
{
Display_View = null; //This doesn't help at all!!!
switch (parameter)
{
case "1":
Display_View= new MyViewModel();
break;
}
}
}
4.) And my UserControl.xaml
<UserControl x:Class="MyProject.Views.MyView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
mc:Ignorable="d"
d:DesignHeight="450"
d:DesignWidth="800">
<!--Event-->
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<!--Calling a method on Load (firing only first tme !!)-->
<ei:CallMethodAction MethodName="MethodForRetrievingData" TargetObject="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
<!--Controls in UserControl for binding etc...-->
</Grid>
</UserControl>
I've tried debugging, but as told, Loaded event of UserControl happens only once. I'm out of ideas on this one, looks like my design has a flaw.
What could be a problem here, maybe I'm missing something like NotifyProperty on UserControl itself?
You need to actually unload the view for it to be loaded again. Setting the source property of the ContentControl's Content property to null just before setting it to another MyViewModel won't unload the view. The DataTemplate is "cached".
Why don't you call the MethodForRetrievingData from the view model itself instead of relying on the view raising a Loaded event? You may for example initialize it asynchronously.
Instead of displaying a MyViewModel object content using template, try display a MyView content instead.
So you would have
private MyView _display_view;
public MyView Display_View
{
get { return _display_view; }
set { _display_view = value; OnPropertyChanged(); }
}
private void Open_view(object parameter)
{
Display_View = null; //This doesn't help at all!!!
switch (parameter)
{
case "1":
Display_View= new MyView(); // Or assign a view model here: {DataContext=new MyViewModel()}
break;
}
}

Can't bind View to ViewModel in WPF MVVM

I am working on desktop app in WPF and I want to follow the MVVM pattern. I have my view ready and it was time to do a viewmodel. But for some reason i can't bind viewmodel to the view.
I have tried this in XAML of the view:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d"
Title="" Height="626" Width="1200" Background="#FFDEDF1A"
DataContext="ViewModels/MainViewModel">
Didn't work so i tried this in the class of View:
public MainWindow()
{
this.DataContext = new MainViewModel();
InitializeComponent();
}
But it doesn't work either... I tried to look it up on the internet but everyone is doing the same thing.
ViewModel:
class MainViewModel : INotifyPropertyChanged
{
public string BindingTest { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public MainViewModel()
{
BindingTest = "test";
}
}
And how I binded the property:
<TextBlock Text="{Binding Path= BindingTest}" Padding="10"/>
This is how my files look:
If you want to set the DataContext in XAML, you should do something like this:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:viewModels="clr-namespace:AssemblyName.ViewModels"
mc:Ignorable="d"
Title="" Height="626" Width="1200" Background="#FFDEDF1A">
<Window.DataContext>
<viewModels:MainViewModel />
</Window.DataContext>
<!-- Your Code Here... -->
</Window>
Change the AssemblyName to your project name.

WPF - MVVM - UserControl binding

I'm trying to realize a simple example of a UserControl, showing in a TextBox the current DateTime, updated four times each second.
I create a simple user control:
<UserControl x:Class="UC.TestUC"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:UC"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="100">
<d:UserControl.DataContext>
<local:TestUC_VM/>
</d:UserControl.DataContext>
<Grid Background="Azure">
<TextBox Text="{Binding TestString}"/>
</Grid>
</UserControl>
Where its ViewModel is:
namespace UC
{
public class TestUC_VM : INotifyPropertyChanged
{
private string _testString;
public string TestString
{
get => _testString;
set
{
if (value == _testString) return;
_testString = value;
OnPropertyChanged();
}
}
public TestUC_VM()
{
TestString = "Test string.";
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MainWindow XAML:
<Window x:Class="UC.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:UC"
mc:Ignorable="d"
Title="MainWindow" Height="100" Width="200">
<Window.DataContext>
<local:MainWindow_VM/>
</Window.DataContext>
<Window.Resources>
<local:TestUC_VM x:Key="TestUC_VM"/>
</Window.Resources>
<Grid>
<local:TestUC DataContext="{StaticResource TestUC_VM}"/>
</Grid>
</Window>
And its ViewModel:
namespace UC
{
public class MainWindow_VM
{
public TestUC_VM _uc_VM;
public MainWindow_VM()
{
_uc_VM = new TestUC_VM();
Task.Run(() => ChangeString());
}
public async Task ChangeString()
{
while (true)
{
_uc_VM.TestString = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
await Task.Delay(250);
}
}
}
}
Even though I see with debugger that I'm passing through the TestString setter, the MainWindow is not updated.
I'm quite sure I'm missing something trivial in setting DataContext of UC in MainWindow, but I've not been able to find what after several hours of browsing and thinking.
Any help appreciated.
The expression
<local:TestUC DataContext="{StaticResource TestUC_VM}"/>
assigns the value of the TestUC_VM resource to the UserControl's DataContext. This is a different object than the _uc_VM member of the main view model, which you are later updating.
Turn the member into a public property
public TestUC_VM UcVm { get; } = new TestUC_VM();
and write
<local:TestUC DataContext="{Binding UcVm}"/>
Update the view model like this:
UcVm.TestString = ...

C# WPF OxyPlot error plot does not exist in namespace

Hi I have a problem configuring oxyplot in WPF. I have found a few solutions but nothing works. I have found out that this is the best http://blog.bartdemeyer.be/2013/03/creating-graphs-in-wpf-using-oxyplot/
but something still wrong and I get this error in XAML file:
the name plot does not exist in the namespace codeplex
MainWindow.xaml FILE:
<Window x:Class="OxyPlotDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:OxyPlotDemo"
xmlns:oxy="http://oxyplot.codeplex.com"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<oxy:Plot x:Name="Plot1" Title="A Graph" Model="{Binding PlotModel}" Margin="10" Grid.Row="1">
</oxy:Plot>
</Grid>
MainWindow.xaml.cs
using System.Windows;
namespace OxyPlotDemo
{
public partial class MainWindow : Window
{
private ViewModels.MainWindowModel viewModel;
public MainWindow()
{
viewModel = new ViewModels.MainWindowModel();
DataContext = viewModel;
InitializeComponent();
}
}
}
MainWindowModel.cs
using System.ComponentModel;
using OxyPlot;
namespace OxyPlotDemo.ViewModels
{
public class MainWindowModel : INotifyPropertyChanged
{
private PlotModel plotModel;
public PlotModel PlotModel
{
get { return plotModel; }
set { plotModel = value; OnPropertyChanged("PlotModel"); }
}
public MainWindowModel()
{
PlotModel = new PlotModel();
}
public event PropertyChangedEventHandler PropertyChanged;
//[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Maybe someone has better solution. I just need to display a few charts, that contains a lot of data from sensors, not in real time.
I've had a same issue with OxyPlot lib from NuGet, when migrating from lib version 1.x to 2.1.
SO i found, that in version 2.1 Plot class is moved to another library, see releases history at their github page.
That worked for me:
xmlns:oxy="http://oxyplot.org/wpf"
xmlns:oxycontrols="http://oxyplot.org/wpf/contrib"
<oxycontrols:Plot .../>
<Window x:Class="Universal_trc_csvParser.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Universal_trc_csvParser"
xmlns:oxy="http://oxyplot.org/wpf"
mc:Ignorable="d"
Title="Window1" Height="450" Width="800">
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<oxy:PlotView Model="{Binding MyModel}"/>
</Grid>
</Window>
public class MainViewModel
{
public MainViewModel()
{
this.MyModel = new PlotModel { Title = "Example 1" };
this.MyModel.Series.Add(new FunctionSeries(Math.Cos, 0, 10, 0.1, "cos(x)"));
}
public PlotModel MyModel { get; private set; }
}
This worked for me.
I had the same issue.
So what i did was downgrade my "OxyPlot.Wpf" to version 1.0.0. and the example you speak of works fine, without the error "the name plot does not exist in the namespace".
<oxy:Plot x:Name="Plot1" Title="A Graph" Model="{Binding PlotModel}" Margin="10" Grid.Row="1">
</oxy:Plot>

Cannot get internal XAML binding to work against a Dependency Property

I have a user control "CtrlComments", this control has the following XAML (It's super basic).
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:wpftoolkit="http://schemas.microsoft.com/wpf/2008/toolkit"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="ucRoot">
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock Text="ID: " />
<TextBlock Text="{Binding Path=Deployment.Id}" />
</StackPanel>
</Grid>
The code behind is as follows, it's the bare basics to get the control to function. The key is the DependencyObject typeof(DeploymentDto) which has an int property called Id that we are interested in showing on our window as per XAML binding above.
public partial class CtrlComments : UserControl, INotifyPropertyChanged
{
public static readonly DependencyProperty DeploymentProperty =
DependencyProperty.Register("Deployment", typeof(DeploymentDto),
typeof(CtrlComments), new PropertyMetadata(new DeploymentDto()));
public DeploymentDto Deployment
{
get
{
return (DeploymentDto)GetValue(DeploymentProperty);
}
set
{
SetValue(DeploymentProperty, value);
OnPropertyChanged(new PropertyChangedEventArgs("Deployment"));
}
}
public CtrlComments()
{
InitializeComponent();
this.DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
PropertyChanged(this, e);
}
}
Our problem is, despite the fact that the binding between the parent control and my user control via the dependency property is working (verified) and the OnPropertyChanged method firing, the TextBlock in my XAML isn't updating.
I have noticed that when the OnPropertyChanged method is run, the eventhandler is null meaning no one is notified that there was a property change.
I don't understand why this is the case though. If you could help explain where we are going wrong it would be enormously appreciated.
Thanks!
I have tried to replicate your problem and while doing so, I figured that the problem for me was in the following line in CtrlComments:
this.DataContext = this;
Dropping this line just made it work for me. Also note (as #Aron wrote in the comments) that the OnPropertyChanged of INotifyPropertyChanged shouldn't be called while in the setter of the DependencyProperty. At least for me it isn't necessary to implement INPC at all.
In the XAML file where you are using the UserControl you are most likely going to have another DataContext set (on a higher level, perhaps in the Window), and thus I guess it isn't inherited to the user control if already set in there (or overwritten). Below is my working code, but perhaps I misunderstood exactly what you're doing. If that is the case, please extend your question to include how you are using the UserControl, as that is a key to answering the question if this doesn't work :)
CtrlComments.xaml:
<UserControl x:Class="WpfApplication1.CtrlComments"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock Text="ID: "/>
<TextBlock Text="{Binding Path=Deployment.Id}"/>
</StackPanel>
</Grid>
</UserControl>
CtrlComments.xaml.cs:
namespace WpfApplication1
{
public partial class CtrlComments : UserControl
{
public static readonly DependencyProperty DeploymentProperty =
DependencyProperty.Register("Deployment", typeof(DeploymentDto), typeof(CtrlComments), new PropertyMetadata(new DeploymentDto { Id = 5 }));
public DeploymentDto Deployment
{
get { return (DeploymentDto)GetValue(DeploymentProperty); }
set
{
SetValue(DeploymentProperty, value);
}
}
public CtrlComments()
{
InitializeComponent();
}
}
}
MainWindow.xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:local="clr-namespace:WpfApplication1"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<StackPanel>
<local:CtrlComments x:Name="testUC" Height="100" Deployment="{Binding Deployment}"/>
<Button Click="Button_Click" Height="50" Width="100"/>
</StackPanel>
</Window>
MainWindow.xaml.cs:
namespace WpfApplication1
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
}
private DeploymentDto deployment = new DeploymentDto { Id = 2 };
public DeploymentDto Deployment
{
get { return deployment; }
set { deployment = value; OnPropertyChanged("Deployment"); }
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Deployment = new DeploymentDto { Id = new Random().Next(100) };
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
}
DeploymentDto:
public class DeploymentDto
{
public int Id { get; set; }
}
It's quite ugly to bind MainWindow.DataContext to its code-behind, but since it's just used for example purposes I hope it's okay :)

Categories