Saturday, July 24, 2010

Synching View Model with Data Model

In MVVM, property notification mechanisms allow to notify the view for changes in the property values in the View Model and otherwise. These mechanisms include implementing INotifyPropertyChanged or using Dependency properties. But how to synchronize between View Model and DataModel?

With INotifyPropertyChanged implementation, this can be achieved by assigning the same property values to the DataModel properties as being set for the View Model properties. So we can use the values of all proxy properties in the View model to their corresponding properties in the DataModel. We can also set the values of other properties based on these proxy properties in the same set block of these properties. e.g. we have a property StudentName in the ViewModel. We have a property with same name in dataModel (student). We can copy the values in the ViewModel as follows:
string _StudentName;
public String StudentName
{
get {...}
set
{
_StudentName = value;
student.StudentName = _StudentName;
PropertyChanged("StudentName");
}
}

With Dependency Properties usage, we inherit our view model with DependencyObject. this gives access to the methods like GetValue() and SetValue(). The problem in when these properties are registered with WPF property system. The WPF runtime does not use the set block to set the property value. It directly calls SetValue() on this property if this property is registered with WPF property system. So how should we pass on these values to the Data model if model needs real time updates based on user interaction with the view.

Many a times people use different techniques to resolve this. The easiest is using the DependencyPropertyDescriptor. DependencyPropertyDescriptor allows to provide the description of intended dependency property. One of the description is getting the Value change notification for this property. It enables other objects to be notified when there is a change in the property.

Let's consider an example. We create a window Window1 with a text box Window1.


The above window is defined as follows:
<Window x:Class="DPDescriptor.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DPDescriptor"
DataContext="{DynamicResource ViewModel}"
Title="Window1" Height="302" Width="397">
<Window.Resources>
<local:Student x:Key="ViewModel" x:Name="_vm" />
</Window.Resources>
<Grid>
<Label Height="22" HorizontalAlignment="Left" Margin="12,12,0,0" Name="label1" VerticalAlignment="Top" Width="43">Name</Label>
<TextBox Height="22" Margin="0,12,12,0" Name="txtName" VerticalAlignment="Top"
HorizontalAlignment="Right" Width="302" >
<TextBox.Text>
<Binding Path="StudentName" UpdateSourceTrigger="PropertyChanged" />
</TextBox.Text>
</TextBox>
</Grid>
</Window>

As you can see above, we have added a text box and a label to the window. We have also instantiated Window1ViewModel with ViewModel key. We have specified this as the data context of this window. The text box is data bound with its StudentName property. The UpdateSourceTrigger is set as PropertyChanged. This would cause copying the value to be copied to the DataContext for every change by the user.

The code behind of the this window is as follows:

public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}

It is the default code behind. We are not doing anything special here.

Now we provide the definition of the Window1ViewModel as expected by the above Window. In the View Model we are using student object as our data model, instantiated in the constructor. The view model is registering StudentName property with WPF property system.
class Window1ViewModel : DependencyObject
{
Student student;

#region Constructors
public Window1ViewModel()
{
student = new Student();
StudentNameDescriptor.AddValueChanged(this,
(o, e) => student.StudentName = this.StudentName );
}
#endregion

#region StudentName Property
public static DependencyProperty StudentNameProperty =
DependencyProperty.Register("StudentName", typeof(string), typeof(Window1ViewModel));

public string StudentName
{
get { return (string)GetValue(StudentNameProperty); }
set { SetValue(StudentNameProperty, value); }
}

public DependencyPropertyDescriptor StudentNameDescriptor =
DependencyPropertyDescriptor.FromProperty(StudentNameProperty, typeof(Window1ViewModel));
#endregion
}

The most important thing in the above code is StudentNameDescriptor. This descriptor is defining a ValueChange event handler for StudentName property. Whenever the value of StudentName is updated, the below lambda statement is executed (which copies the view model's property to the data model's property):
(o, e) => student.StudentName = this.StudentName

The Student class is defined as follows for the above example:
class Student
{
public string StudentName { get; set;}
}

No comments: