Suppose we have a Model (class Model
) with the following property.
public string InputFileName
{
get { return m_InputFileName; }
set
{
m_InputFileName = value;
RaiseNotifyPropertyChanged("InputFileName");
}
}
The above model implements the INotifyPropertyChanged
interface, so we have also the following method and the following event. The RaiseNotifyPropertyChanged
method below is used to update the ViewModel.
#region INotifyPropertyChanged Implementation
private void RaiseNotifyPropertyChanged(string property)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(property));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
The following are the main sections of the class that implements the ViewModel.
public class ViewModel : INotifyPropertyChanged
{
#region Members
private Model m_Model;
private string m_InputFileStr;
private readonly ICommand m_SubmitCommand;
#endregion
#region Constructors
public ViewModel()
{
m_Model = new Model();
m_Model.PropertyChanged += new PropertyChangedEventHandler(this.Model_PropertyChanged);
m_InputFileStr = string.Empty;
// ...
// initialize m_SubmitCommand
}
#endregion
// ...
#region Properties
public string InputFileStr
{
get { return m_InputFileStr; }
set
{
if (value == m_InputFileStr) return;
m_InputFileStr = value;
OnPropertyChanged("InputFileStr");
m_SubmitCommand.RaiseCanExecuteChanged();
}
}
#endregion
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
// This method is called when the model changes, so the Model notified the ViewModel.
private void Model_PropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == "InputFileName")
{
InputFileStr = m_Model.InputFileName;
}
else if (args.PropertyName == "OutputFileName")
{
OutputFileStr = m_Model.OutputFileName;
}
else if (args.PropertyName == "ReportText")
{
ReportTextStr = m_Model.ReportText;
}
}
}
The following are the main sections of the class that implements the View:
MainWindow.xaml
<TextBox Name="inputfileTextBox"
Text="{Binding Path=InputFileStr, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<Button Name="submitButton"
Content="Submit"
Command="{Binding SubmitCommand}"/>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
The above implementation works correctly:
With the aim of enabling the ViewModel to update the Model, I thought I would add the following call inside the set property InputFileStr
of ViewModel:
m_Model.InputFileName = value;
However, this solution of updating the Model causes an obvious unintended effect:
m_Model.InputFileName = value;
).Is the above behavior a correct behavior? I expect that if the ViewModel updates the Model, then the Model does not have to re-notify the ViewModel about the same change... As an alternative solution I thought I'd add an Update
method to the Model: this method should update the Model without using the Model Properties.
public void Update(string inputFileName) // this method does not notifies the ViewModel
{
m_InputFileName = inputFileName;
}
Is this alternative solution a correct solution or are there better solutions?
If you want your Models to alert the ViewModels of changes, they should implement INotifyPropertyChanged, and the ViewModels should subscribe to receive PropertyChange notifications. But typically this is only needed if more than one object will be making changes to the Model's data, which is not usually the case.
You should always have the Model implement INotifyPropertyChanged and this is just a mistake which would be corrected if this were developed from a code example to an application.
Depending on what your model is, you will usually just invoke a "Save" method or similar. Most models (say, a database) don't need/want to have every change given to them in real-time.
So in general, the flow would be:
If your DTO objects are shared between the model and view model, you don't even need to worry about synchronization. Otherwise, this is a good time to sync them.
On a similar note, using PropertyChanged
in a model class is usually a bad idea. For starters, its no fun at all to listen to. Instead, if the model receives new data, raise a more semantically clear event to the VM with the new data.
tldr; Basically, don't worry so much about keeping your model and view model in sync. Very often, the model won't be keeping a copy of the current state at all! Even when it is, just update it when the view model is ready to "commit" changes, and notify the View Model of external changes to the model via normal events.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With