Saturday, October 15, 2011

MVVM Step By Step, Part III


In the previous posts, which you can find here and here, we created a first, simple MVVM app. We already set up some basic databinding between our view, MainView, and viewmodel, MainViewModel, to show something in the ContentPresenter of the view. What we still need to do, however, is react to the button clicks of the buttons present in the MainView. In a normal, non-MVVM application, you would use eventhandlers for the button clicks. This is, however, something we will not do in an MVVM application, mostly because we want our viewmodels to be testable. Instead off an eventhandler with a sender object and eventargs, in MVVM, we create properties of the ICommand interface.

ICommand properties are properties which return a type that implements the ICommand interface. They are bindable to commands in a view, like the Command property of a Button. And second, they consist of an Execute and CanExecute method. The combination of these two gives a nice way of indicating which code needs to be executed and whether or not it can be executed. On the downside of ICommand properties is the fact that they can only be bound to commands in a view and not to events. This is why a lot of MVVM frameworks add an EventToCommand kind of type which makes it possible to bind ICommand properties to events. More on that in a later post.

For now, let's add a first ICommand property to the MainViewModel and bind it to the Command property of a button. Let's start with the ICommand for a new customer, the NewCustomerCommand.

public class MainViewModel
{
    private ICommand _newCustomerCommand;

    public ICommand NewCustomerCommand
    {
        get
        {
            if (_newCustomerCommand == null)
                _newCustomerCommand = new NewCustomerCommand();
            return _newCustomerCommand;
        }
    }
}

The NewCustomerCommand is a class we still need to write, so lets add it to our solution. I prefer adding an extra Commands folder for these kinds of classes (not that we will be creating much of them, you will soon see why). Since the NewCustomerCommand needs to implement the ICommand interface it will initially look like this.

public class NewCustomerCommand : ICommand
{
    public bool CanExecute(object parameter)
    {
        throw new NotImplementedException();
    }

    public void Execute(object parameter)
    {
        throw new NotImplementedException();
    }

    public event EventHandler CanExecuteChanged;
}

Since there are no restrictions on executing this command, we will have the CanExecute method always return true. The Execute method needs to open the NewCustomerView within the ContentPresenter of the MainView. We will again use the same technique of initializing a view and viewmodel and then binding the two together using the DataContext property of the view (didn't we do this three times allready? As any good (lazy) programmer, might we not think about making this something more generic??? Yes, we will, but not yet).

public class NewCustomerCommand : ICommand
{
    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        var view = new NewCustomerView();
        var viewmodel = new NewCustomerViewModel();
        view.DataContext = viewmodel;
    }

    public event EventHandler CanExecuteChanged;
}

Now that we have this view, we need to assign it to the ViewToShow property of our MainViewModel. This is kind of hard, since the NewCustomerCommand and the MainViewModel don't know each other. We need to add a constructor to the NewCustomerCommand that takes the MainViewModel as a parameter and then uses it as a private datamember. Now we can assign the ViewToShow property.


public class NewCustomerCommand : ICommand
{
    private MainViewModel _mainViewModel;

    public NewCustomerCommand(MainViewModel mainViewModel)
    {
        _mainViewModel = mainViewModel;
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        var view = new NewCustomerView();
        var viewmodel = new NewCustomerViewModel();
        view.DataContext = viewmodel;
        _mainViewModel.ViewToShow = view;
    }

    public event EventHandler CanExecuteChanged;
}

The call to this constructor in the MainViewModel needs to pass itself (this) now to the NewCustomerCommand.


public ICommand NewCustomerCommand
{
    get
    {
        if (_newCustomerCommand == null)
            _newCustomerCommand = new NewCustomerCommand(this);
        return _newCustomerCommand;
    }
}

The only thing left now, is add a binding to the MainView so the Command property of the NewCustomer button is bound to the NewCustomerCommand property.


<Button Content="New Customer" Margin="10" Command="{Binding NewCustomerCommand}" />

Again, add a TextBlock with some dummy text to your NewCustomerView to test this out. If you run the application you will find, however, the NewCustomer button does not seem to work. Clicking it, will not result in the other view being shown. If you place breakpoints in your code however, you will see that the Execute method of your NewCustomerCommand does get called.

What is actually wrong is the fact that the ViewToShow property gets a new value, but it does not announce this fact. For this you need the INotifyPropertyChanged interface. Implement it in your MainViewModel and change the ViewToShow property from an autoproperty to a property with a backing field and call the PropertyChanged event.

public class MainViewModel : INotifyPropertyChanged
{
    private ICommand _newCustomerCommand;
    private FrameworkElement _viewToShow;

    public MainViewModel()
    {
        var view = new AllCustomersView();
        var viewmodel = new AllCustomersViewModel();
        view.DataContext = viewmodel;
        ViewToShow = view;
    }

    public FrameworkElement ViewToShow
    {
        get { return _viewToShow; }
        set
        {
            _viewToShow = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("ViewToShow"));
        }
    }

    public ICommand NewCustomerCommand
    {
        get
        {
            if (_newCustomerCommand == null)
                _newCustomerCommand = new NewCustomerCommand(this);
            return _newCustomerCommand;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}


If you now run the application, you can use the NewCustomer button to show the other view.

In a next post I will show you how to add some more framework-like capabilities. As in, how can you provide certain parts, so you don't need to rewrite masses of code every time you need to add something new (like a new command).

Monday, October 10, 2011

MVVM Step By Step, part II

In the previous post, we created the initial setup of an MVVM application. We already created a first view, MainView, and a first viewmodel, MainViewModel. Both were hooked up using the DataContext property of the view. What we didn't do yet, was show something in the ContentPresenter on the MainView.

Initially, we want to show all of the customers. In our MVVM app, this will be another view, AllCustomersView, and another viewmodel, AllCustomersViewModel. It will be the responsibility of the MainViewModel to initialize these two. The basics of hooking the view and viewmodel together will be similar to what we did for the MainView and MainViewModel. Lets initialize all this in the constructor of the MainViewModel.

public MainViewModel()
{
    var view = new AllCustomersView();
    var viewmodel = new AllCustomersViewModel();
    view.DataContext = viewmodel;
}

For the AllCustomersView just create a new UserControl in your views folder. For the AllCustomersViewModel, create a new class in your ViewModels folder.

Now that we have this view and viewmodel, we need to be able to show them in the ContentPresenter of the MainView. For this, you will need a property in your MainViewModel to which the MainView ContentPresenter can bind to. We will add this property to the MainViewModel class.

public FrameworkElement ViewToShow { get; set; }

Make sure you assign the view you created in the constructor to this property.

public MainViewModel()
{
    var view = new AllCustomersView();
    var viewmodel = new AllCustomersViewModel();
    view.DataContext = viewmodel;
    ViewToShow = view;
}

Last thing you need to add is the binding in your view.

<ContentPresenter Grid.Column="1" Content="{Binding ViewToShow}" />

Just for testing purposes, you can add a TextBlock with some dummy text in your AllCustomersView and run the application. You should see the content you placed in your AllCustomersView in your application.

That's it, as simple as that. A viewmodel provides properties for your view to bind to. In the next post we will see how we can react to the clicks of the buttons in the MainView.