Binding View and ViewModel in WPF
In this topic, I’m trying to list different ways to connect your View and ViewModel inside your MVVM application.
As you know, the MVVM architecture is used to separate presentation and business logic of your application.
MVVM architecture consists of three parts :
- M : Refers to Model or data to be manipulated by the application
- V : Refers to View or appearance of data on the screen.
- VM : Refers to ViewModel which is responsible to allow View to interact with Model (data). But how to bind View and ViewModel in WPF in order to allow interration with data ?
There are two ways to bind these two components :
- By using View Constructor
- By using Locator pattern
Use View constructor
Use DataContext property to set the ViewModel as shown in the following code.
1- With XAML :
<Window x:Class=”Mvvm.MyView” …
…
<Window.DataContext>
<vm:MyViewModel/>
</Window.DataContext>
…
</Window>
2- Or in Code-behind
...
public MyView()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
...
In the real world, applications can have a master UI (MainWindow) with some UserControls to retrieve data, each UserControl (View) has its own ViewModel. In this case, you can declare and bind ViewModels for all UserControls Views in the MainWindow as shown in the MainWindow.xaml file below.
<!-- MainWindow.xaml -->
<Window x:Class=”Mvvm.MainWindow” …
…
<Window.DataContext>
<vm:MainWindowViewModel/>
</Window.DataContext>
…
<Grid>
<!-- bind UserControl MyView1 with MyViewModel1-->
<vm:MyView1 DataContext="{Binding MyViewModel1}/>
<!-- bind UserControl MyView2 with MyViewModel2-->
<vm:MyView2 DataContext="{Binding MyViewModel2}/>
</Grid></Window>
The master ViewModel declares slaves ViewModels like properties as shown in the following code.
/* MainWindowViewModel.cs */
public class MainWindowViewModel
{
private MyViewModel1 myViewModel1 = new MyViewModel1();
private MyViewModel2 myViewModel2 = new MyViewModel2();
public MyViewModel1 MyViewModel1
{
get { return myViewModel1; }
set { myViewModel1 = value; }
}
public MyViewModel2 MyViewModel2
{
get { return myViewModel2; }
set { myViewModel2 = value; }
}
}
Finally, each slave ViewModel has its own implementation as shown in the following code.
/* MyViewModel1.cs */
public class MyViewModel1
{
...
public MyViewModel1()
{
//load data
//init Properties
}
...
}
/* MyViewModel2.cs */
public class MyViewModel2
{
...
public MyViewModel2()
{
//load data
//init Properties
}
...
}
Use Locator pattern
In MVVM architecture, each View needs to be connected with its ViewModel.
As I explain in the first section, to do that we need to set the DataContext property for each View.
Locator pattern comes to centralize the code responsible to connect Views with View Models in order to simplify binding and to decouple the Views from the code.
So, we create a new Class named, for example “ViewModelLocator”, this class has attached property named, for example “AutoConnectedViewModelProperty” and a callback method to perform actions to do whenever the value of the property has been changed.
When attached property AutoConnectedViewModel has been created, it can be consumed from the View. AutoConnectedViewModel property can be set within the View.
Visual Studio provides a code snippet for an attached property. Create a class named ViewModelLocator and type “propa” and “Tab twice” the following code appears :
public class ViewModelLocator
{
public static int GetMyProperty(DependencyObject obj)
{
return (int)obj.GetValue(MyPropertyProperty);
}
public static void SetMyProperty(DependencyObject obj, int value)
{
obj.SetValue(MyPropertyProperty, value);
}
// Using a DependencyProperty as the backing store for MyProperty. // This enables animation, styling, binding, etc...
public static readonly DependencyProperty MyPropertyProperty =
DependencyProperty.RegisterAttached("MyProperty",
typeof(int),
typeof(ownerclass),
new PropertyMetadata(0));
}
In our case, we want to name attached property as AutoConnectedViewModel, and the callback method to perform changes as AutoConnectedViewModelChanged. The following code shows our example of attached property:
public static readonly DependencyProperty AutoConnectedViewModelProperty =
DependencyProperty.RegisterAttached(
"AutoConnectedViewModel",
typeof(bool),
typeof(ViewModelLocator),
new PropertyMetadata(false, AutoConnectedViewModelChanged));
}
This code creates attached property AutoConnectedViewModelProperty, of type bool. The property is owned by the ViewModelLocator class, and has a default value of false and a reference to the callback implementation named AutoConnectedViewModelChanged. The following code shows how to implement the callback method in order to get ViewModel name and set the DataContext property of the View, which is the view that caused the change of the Attached property.
private static void AutoConnectedViewModelChanged
(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var viewType = d.GetType();
string str = viewType.FullName;
var viewTypeName = str;
var viewModelTypeName = viewTypeName + "Model";
var viewModelType = Type.GetType(viewModelTypeName);
var viewModel = Activator.CreateInstance(viewModelType);((FrameworkElement)d).DataContext = viewModel;
}
Following is the complete implementation of ViewModelLocator class.
public class ViewModelLocator
{
public static bool GetAutoConnectedViewModelProperty
(DependencyObject obj)
{
return (bool)obj.GetValue(AutoConnectedViewModelProperty);
}
public static void SetAutoConnectedViewModelProperty
(DependencyObject obj, bool value)
{
obj.SetValue(AutoConnectedViewModelProperty, value);
}
// Using a DependencyProperty as the backing store for MyProperty.
// This enables animation, styling, binding, etc...public static readonly DependencyProperty AutoConnectedViewModelProperty =
DependencyProperty.RegisterAttached(
"AutoConnectedViewModel",
typeof(bool),
typeof(ViewModelLocator),
new PropertyMetadata(false, AutoConnectedViewModelChanged));
}private static void AutoConnectedViewModelChanged
(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var viewType = d.GetType();
string str = viewType.FullName;
var viewTypeName = str;
var viewModelTypeName = viewTypeName + "Model";
var viewModelType = Type.GetType(viewModelTypeName);
var viewModel = Activator.CreateInstance(viewModelType);((FrameworkElement)d).DataContext = viewModel;
}
Finally, the following code showd how the Attached property can be consumed from the View.
<UserControl
x:Class="Mvvm.MyView"
...
vm:ViewModelLocator.AutoConnectedViewModelProperty = "True"
...
>
<Grid>
...
</Grid>
</UserControl>
In this article, we saw how to bind View and ViewModel using your own code. You can also use MVVM frameworks to do that for you. Maybe in my next article, I will discuss about one of the most popular MVVM frameworks named MVVM Light.
Don’t forget to give me claps :).