I am working on an application and trying to follow MVVM as much as possible and thus have many Views with corresponding ViewModes. I am deserializing a ViewModel which is instantiated in the View using XAML. For example, if the View is called "ExampleView" and the ViewModel is called "ExampleViewModel". The ViewModel is instantiated in the ExampleView by doing this...
<UserControl.Resources>
<local:ExampleViewModel x:Key="ViewModel" />
</UserControl.Resources>
With the following code behind to get/set the ViewModel from the View (normally this is only a get, but I tried the set to set the ViewModel after deserialization).
public ExampleViewModel ViewModel
{
get { return (ExampleViewModel)this.Resources["ViewModel"]; }
set
{
if (this.Resources["ViewModel"]!=value)
{
this.Resources["ViewModel"] = value;
}
}
}
This didn't work, but I figured the reason is that PropertyChanged wasn't being fired. So in ExampleViewModel I put in a method to refresh each of the Properties. For example ...
public void RefreshAllProperties()
{
NotifyPropertyChanged("Property1");
NotifyPropertyChanged("Property2");
...
}
where NotifyPropertyChanged is ...
private void NotifyPropertyChanged([CallerMemberName] string PropertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
While this doesn't pass the code smell test, I was trying to understand on my way to find something more elegant. However, I was surprised to find it didn't work.
I would prefer to keep the ViewModel instantiated in the XAML. Is there a best practice to re-instantiate the ViewModel after deserialization?
Modified follow on question
Any comments on moving the ViewModel creating into the constructor of the View class? Is this a better design pattern?
ExampleViewModel exampleViewModel;
public ExampleView()
{
InitializeComponent();
ExampleViewModel = new ExampleViewModel();
this.DataContext = ExampleViewModel;
}
public ExampleViewModel ViewModel
{
get { return exampleViewModel; }
set
{
if (exampleViewModel!=value)
{
exampleViewModel = value;
NotifyPropertyChanged();
}
}
}
CodePudding user response:
I haven't seen a ViewModel defined in a ResourceDictionary before. I tend to initialize my ViewModels in the code-behind (I know you mentioned you wanted to keep it in XAML) since I can more directly control the page DataContext and when it ultimately updates (like after deserialization in your case). Modifying the ResourceDictionary at runtime seems like a dangerous approach and the ResourceDictionary in WPF does not implement INotifyPropertyChanged or any other change interface like INotifyCollectionChanged
, meaning that it will not notify anything that one of its key-value pairs has changed in some way.
In summary: My answer is to not define your VM in a ResourceDictionary, but to manage it in your page code-behind where you can ensure the DataContext is appropriately updated when the VM or its state changes.