Thursday, September 11, 2014

WPF and MVVM - Fundamentals

Model-View-ViewModel (MVVM) is a popular pattern when programming in WPF (Windows Presentation Foundation). It is used to separate the GUI, the business logic and the data. While Model and ViewModel are class files, the View consists of a XAML file and a class file (code-behind) as a User Control.
The patern consists of several components. Sometimes not all components are needed to achieve the goal (and also sometimes MVVM is not needed..). But always the ViewModel needs to be set to the DataContext of the View. This can be done in several ways (here you can found how: Setting the DataContext of a View to a ViewModel in MVVM).
By setting the DataContext of a View, you can bind components of a View to properties of a ViewModel. For example, when you enter in a View some text into a TextBox, the value is set to the binded property in the ViewModel.
<TextBox x:Name="UserName"
         Text="{Binding UserName}" />  
public string UserName
{
    get;
    set;
}
If you are using a Model (maybe a POCO), the Model is very likely a member of your ViewModel. Then your code could look something like that.
public string UserName
{
    get
    {
        return _Model.UserName;
    }
    set
    {
        _Model.UserName = value;
    }
}

You can also bind collections in a ViewModel to a View that act as source for controls like ComboBox, ListBox, DataGrid, and so on.
<ListBox ItemsSource="{Binding Users}"
         DisplayMemberPath="UserName"/> 
public ICollection<User> Users
{
    get
    {
        return _Model.Users;
    }
    set
    {
        _Model.Users = value;
    }
}
public class User
{
    public string UserName
    {
        get;
        set;
    }
 
    // ...
}
To dynamically add elements from a View to a collection in a Model, we need to encapsulate the collection by a ObservableCollection.
Users = new ObservableCollection<User>(_Model.Users);
public ObservableCollection<User> Users
{
    get;
    set;
}
When a new element is added to the ObservableCollection, we also need to add a element to the collection in the Model. Therfor we add an event handler to the CollectionChanged event of the ObservableCollection.
Users.CollectionChanged += Users_CollectionChanged;
void Users_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.Action == NotifyCollectionChangedAction.Add)
    {
        foreach (User item in e.NewItems)
        {
            _Model.Users.Add(item);
        }
    }
}
The ViewModel consequently have all properties of the Model. But not only this, it have also properties that affect the View. So maybe you want to disable a Button until a TextBox is not empty.
<Button x:Name="Login"
        Content="Login"
        IsEnabled="{Binding CanLogin}" /> 
public string UserName
{
    get
    {
        return _Model.UserName;
    }
    set
    {
        _Model.UserName = value;
        CanLogin = !string.IsNullOrEmpty(value);
    }
} 
public bool CanLogin
{
    get;
    set;
}
But this will not work until now. The View doesn't realize that the property CanLogin has changed. To let the View realize that, we need to implement the INotifyPropertyChanged interface. If we move the implementation of the interface in an own class, we can use this base class for all ViewModels. Here is an example of the implementation of the INotifyPropertyChanged:
public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
 
    public void OnPropertyChanged(string name)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(thisnew PropertyChangedEventArgs(name));
        }
    }
}
public class LoginViewModel : ViewModelBase
Now we can call the OnPropertyChanged method to let the View know that some property has changed. The method name of the changed property is used as parameter.
public bool CanLogin
{
    get
    {
        return _canLogin;
    }
    set
    {
        _canLogin = value;
        OnPropertyChanged("CanLogin");
    }
}
With the ViewModelBase class we have our first component of our own MVVM-Framework.

Further Posts

  1. WPF and MVVM - Fundamentals
  2. WPF and MVVM - Commands
  3. WPF and MVVM - Events

No comments:

Post a Comment