Page 1 of 1

[WPF] Exploring MVVM (Model View ViewModel) I

#1 andrewsw  Icon User is online

  • It's just been revoked!
  • member icon

Reputation: 3834
  • View blog
  • Posts: 13,582
  • Joined: 12-December 12

Posted 28 September 2014 - 05:55 AM

The MVVM design pattern is favoured, and encouraged, in WPF. I have created a small application to demonstrate and explore this pattern.

Part 2 (there is also a zip file of the application in the second post below)

Many of the examples that I found were too brief. A lot of them have just a TextBox and a Button, and work with a single property. That's fine as a short introduction, but I couldn't find a good example that goes beyond this first stage. In particular, I wanted a fuller example that works with a collection of items, and with different views. Here it is.

Disclaimer: I am still studying this subject myself; I do not (yet) claim to be an expert. You should supplement this tutorial with further study (see the links that follow). Besides, it is a subjective topic, and there are different approaches. Please also read any comments that might be added below the tutorial.

Nevertheless, I believe there is value in this tutorial. It goes beyond the many simple ones you will find, and provides a template (a complete application) that you might use to pursue this subject further.

Here is a preview of our application:

Attached Image

Attached Image

Attached Image



This could be considered as a continuation of my tutorial sequence WPF Build An Application. That works with many WPF features towards building a complete application, but without following the MVVM pattern.

This current tutorial doesn't teach WPF, concentrating on the pattern. (I do discuss some WPF features though, and provide links.) If you encounter a feature that you are not familiar with you will probably find it discussed in my earlier tutorial sequence, or in my sequence WPF Build A Window, which introduces many WPF features.

WPF Build An Application is not a requirement though. I build a similar ToDo Application, but completely from scratch.

Why from scratch?
Spoiler




Some Links:

Model View ViewModel :wikipedia

A Simple MVVM Example
MVVM Made Simple
Understanding the basics of MVVM design pattern

ListView Overview

Implementing the MVVM Pattern Using the Prism Library
(Uses the Prism framework, but still useful.)

The second of these links provided me with a starting point for my application. It only took me so far though, so, in the end, I used many different resources.

Introducing the Pattern

wikipedia said:

Model: as in the classic MVC pattern, the model refers to either (a) a domain model which represents the real state content (an object-oriented approach), or ( b ) the data access layer that represents that content (a data-centric approach).

View: as in the classic MVC pattern, the view refers to all elements displayed by the GUI such as buttons, labels, and other controls.

View model: the view model is a "model of the view" meaning it is an abstraction of the view that also serves in mediating between the view and the model which is the target of the view data bindings. It could be seen as a specialized aspect of what would be a controller (in the MVC pattern) that acts as a converter that changes model information into view information and passes commands from the view into the model. The view model exposes public properties, commands, and abstractions. The view model has been likened to a conceptual state of the data as opposed to the real state of the data in the model.

Attached Image

My application doesn't use a database, or other persistent storage, simply a (temporary) collection of ToDo items. (Persisting these ToDo items is demonstrated in my Build An Application tutorial.)

[Small Note: We can use the term Model or Models. Model is perhaps the more formal term but it is quite possible for a large application to be considered to have a number of models.]

Views can be Windows, User Controls or, as you shall see, DataTemplates. In fact, any block of UI elements could potentially be considered as a View.

Consider that a main aim of the pattern is that Views are created as independent as possible. They are totally unaware of the Model, and only reference ViewModels through their DataContext. It is this DataContext that provides the Data Bindings, and access to Commands and Notifications, through which Views and ViewModels can communicate.

Another main aim is that ViewModels do all the hard work. They provide the bridge between Views and the Model. (Because of this I suspect that the Models tend to be more lightweight than in other patterns, because a lot of the business logic can be pushed into the ViewModels.)

With any pattern, two important benefits (as a consequence of code separation) are reusability and ease of unit testing. We can swap out WPF views and relatively easily swap in WinForms, web-forms, or even a Console. For unit testing we can create faux-Views, or switch to a temporary (non-persistent) Model. Well, that's the theory. In practice, it will take a little work to be able to do this.



Folder Structure

Create a new WPF Application named ToDoMVVM. Right-click in the Solution Explorer window to create three new folders named Models, ViewModels and Views. (As suggested above, the first folder could be named Model, but I prefer to name these folders consistently.)

Attached Image

The Views folder is actually empty. That's because I'm using DataTemplates as Views, rather than Windows or User Controls.

App Styles

There isn't a particular order in which the application-files should be created, and the order that I present here isn't exactly the same order that I used. Personally, I always aim to be able to press the Start button as early as possible, and to continue to build the application in such a way that I can press Start at (almost) any time.

Modify App.xaml to provide some basic styling for Labels, Buttons, etc.:
<Application x:Class="ToDoMVVM.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="Mainwindow.xaml">
    <Application.Resources>
        <Style x:Key="styWindow" TargetType="{x:Type Window}">
            <Setter Property="FontSize" Value="12" />
            <Setter Property="FontFamily" Value="Verdana" />
        </Style>
        <Style TargetType="{x:Type Button}">
            <Setter Property="Padding" Value="5" />
            <Setter Property="Margin" Value="5" />
        </Style>
        <Style TargetType="{x:Type Label}">
            <Setter Property="Margin" Value="5" />
            <Setter Property="HorizontalAlignment" Value="Center" />
            <Setter Property="VerticalAlignment" Value="Center" />
        </Style>
        <Style TargetType="{x:Type DatePicker}">
            <Setter Property="Margin" Value="5" />
            <Setter Property="HorizontalAlignment" Value="Center" />
            <Setter Property="VerticalAlignment" Value="Center" />
        </Style>
    </Application.Resources>
</Application>


I have created a Window-Style named "styWindow" that we will apply to our Mainwindow. This sets a font and font-size that should cascade to affect all of the controls of the window. (WPF prefers the term elements rather than controls.)

The other styles don't have names, so they will affect every Label, Button and DatePicker of the window.

Our application will only have one Window so you could, if you prefer, put these styles in the Window's Resources, and not use App.xaml at all. I prefer to use App.xaml, partly because I might decide at a future date to add other Windows, but mainly because it reduces the size of the Mainwindow.xaml file.

Window Outlining

I use DataTemplates as our Views. These will only be visible when the Window is wired-up to a corresponding DataContext. So what I did was to use a ContentControl and, in turn, create, test, and comment-out three different element-structures within the ContentControl, using dummy text/data. Once I was happy that they looked okay I then moved them and converted them to DataTemplates.

Modify Mainwindow.xaml to the following, but build, test and then comment-out, the TextBlock, Grid and ListView separately. (You have to comment the others out anyway, as a ContentControl can only have one child.)
<Window x:Class="ToDoMVVM.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ToDoMVVM" 
        xmlns:viewmodels="clr-namespace:ToDoMVVM.ViewModels"
        Title="Main Window" Style="{StaticResource styWindow}" SizeToContent="WidthAndHeight"
        MinWidth="500" MinHeight="350">
    <DockPanel>
        <Border DockPanel.Dock="Left" BorderBrush="Blue" BorderThickness="0,0,1,0">
            <StackPanel DockPanel.Dock="Left">
                <Button Content="ToDo Item" />
                <Button Content="All Items" />
                <Button Content="Home" />
                <Button Content="Exit" />
        </StackPanel>
        </Border>
        <ContentControl>
            <!--<TextBlock Text="Welcome to the ToDo Application!" FontSize="20" 
                       HorizontalAlignment="Center" VerticalAlignment="Center" />-->
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition />
                    <RowDefinition />
                    <RowDefinition />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition />
                    <ColumnDefinition />
                    <ColumnDefinition />
                    <ColumnDefinition />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <Label Grid.Row="0" Grid.Column="0" Content="Title" Margin="5" />
                <TextBox Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="3" 
                         HorizontalAlignment="Center" VerticalAlignment="Center" 
                         MinWidth="200"
                         Text="Enter Title Here" Padding="10,5,10,5" Margin="5" />
                <Label Grid.Row="1" Grid.Column="0" Content="Start Date" />
                <DatePicker Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" />
                <Label Grid.Row="2" Grid.Column="0" Content="Due Date" />
                <DatePicker Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" />
                <Label Grid.Row="2" Grid.Column="3" Content="x Days" />
                <Label Grid.Row="3" Grid.Column="0" Content="Completed Date" />
                <DatePicker Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" />
                <StackPanel Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="4" MaxHeight="40"
                            Orientation="Horizontal">
                    <Button Content="First" />
                    <Button Content="Previous" />
                    <Button Content="Next" />
                    <Button Content="Last" />
                    <Button Content="Add" />
                    <Button Content="Delete" />
                </StackPanel>
            </Grid>
            <!--<ListView>
                <ListView.View>
                    <GridView>
                        <GridViewColumn Header="Title" Width="200" />
                        <GridViewColumn Header="Start Date" Width="120" />
                        <GridViewColumn Header="Due Date" Width="120" />
                        <GridViewColumn Header="Completed Date" Width="120" />
                    </GridView>
                </ListView.View>
            </ListView>-->
        </ContentControl>
    </DockPanel>
</Window>


        xmlns:local="clr-namespace:ToDoMVVM" 
        xmlns:viewmodels="clr-namespace:ToDoMVVM.ViewModels"
        Title="Main Window" Style="{StaticResource styWindow}" SizeToContent="WidthAndHeight"


These first two lines provide references (namespaces) for the application and for the ViewModels folder. (They aren't used in this code yet.) I would have liked to do the same for the Views folder (even though it's empty) but we can't do this for an empty folder. Note: You'll need to comment-out or omit this second line (for viewmodels) until this folder contains at least one file.

The third line shows applying the Style "styWindow" from the App.xaml file.

Side Note: I avoid setting Widths and Heights as much as possible in WPF, but use MinWidth and MinHeight (and MaxWidth/Height) strategically to set sensible restrictions. In my Build An Application tutorial I demonstrated a different approach, creating a regular grid to control the layout. Even there, it was the Grid that used fixed column/row widths and heights, not the elements that I placed in the Grid. (If an element required more space I simply spanned it across rows or columns.)

You might tell that I wasn't too concerned about the styling/appearance. It is the MVVM pattern that we are interested in. Nonetheless, try resizing the form and changing the screen resolution - I think it behaves quite well.

The Model (Helpers)

We will use the INotifyPropertyChanged interface to enable the Model to notify the data-bindings of changes to (model) properties. Without this you would have to navigate away, and back to, an item before you would see the changes, or possibly even move to a different View and back.

To help with this process create an abstract class named ObservableObject in the Models folder:
using System;
using System.ComponentModel;

namespace ToDoMVVM.Models {
    public abstract class ObservableObject : INotifyPropertyChanged {

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void onpropertychanged(string propertyName) {
            if (TypeDescriptor.GetProperties(this)[propertyName] == null) {
                throw new Exception("Invalid property name: " + propertyName);
            }
            if (this.PropertyChanged != null) {
                var e = new PropertyChangedEventArgs(propertyName);
                this.PropertyChanged(this, e);
            }
        }
    }
}


(I modified this from the second link listed towards the beginning of this tutorial.)
This is equivalent to the ViewModelBase class here.

Any class in our model that needs to implement INotifyPropertyChanged will, instead, derive from this abstract class. This isn't essential, ObservableObject is just a helper-class. But, without it, we would have to repeat the above code in every one of these classes.

I just noticed this page: An elegant way to implement INotifyPropertyChanged. I haven't studied it though, so don't know if it is anymore elegant than the code we have.

Another common helper that we will use is a Relay Command. Does this help: A Relay Command is "a command whose sole purpose is to relay its functionality to other objects by invoking delegates"? Read on..

The preferred method for Button clicks, menu buttons, keyboard-shortcuts etc. to cause something to happen in a ViewModel (and thence the Model) is to implement the ICommand interface. This way, there is a single Command that can be triggered by different UI actions. This is an important aspect of both WPF and MVVM. With WinForms it was/is a barrier to proper code separation that event-code exists within the Form-class itself. With WPF (and MVVM) the Commands can exist in the ViewModels, and are accessible to the Views via the DataContext.

Commanding Overview :MSDN

Without the help of a RelayCommand class our Model-code would be significantly larger (repeating some of the code that you will see shortly), possibly even necessitating a separate file for each Command. (In my Build An Application tutorial I avoided the need for separate files for each Command by putting similar Commands in a single class-file and using enums to determine which Command was invoked.)

Create class RelayCommand.cs in the Models folder:
using System;
using System.Windows.Input;

namespace ToDoMVVM.Models {
    public class RelayCommand : ICommand {

        readonly Action<object> _execute;
        readonly Predicate<object> _canExecute;

        public RelayCommand(Action<object> execute, Predicate<object> canExecute) {
            if (execute == null)
                throw new ArgumentNullException("execute");

            _execute = execute;
            _canExecute = canExecute;
        }
        public RelayCommand(Action<object> execute)
            : this(execute, null) {
        }

        public bool CanExecute(object parameters) {
            return _canExecute == null ? true : _canExecute(parameters);
        }

        public event EventHandler CanExecuteChanged {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        public void Execute(object parameters) {
            _execute(parameters);
        }
    }
}


You should still be able to build, and possibly run, the application now, and at any stage as you progress through the tutorial.

The Model

Create the ToDo.cs file in the Models folder:
using System;

namespace ToDoMVVM.Models {
    public class ToDo : ObservableObject {
        private string _title;
        private DateTime? _startDate;
        private DateTime? _dueDate;
        private DateTime? _completedDate;
        private string _note;

        public string Title {
            get { return _title; }
            set {
                if (value != _title) {
                    _title = value;
                    onpropertychanged("Title");
                }
            }
        }
        public DateTime? StartDate {
            get { return _startDate; }
            set {
                if (value != _startDate) {
                    _startDate = value;
                    onpropertychanged("StartDate");
                }
            }
        }
        public DateTime? DueDate {
            get { return _dueDate; }
            set {
                if (value != _dueDate) {
                    _dueDate = value;
                    onpropertychanged("DueDate");
                }
            }
        }
        public DateTime? CompletedDate {
            get { return _completedDate; }
            set {
                if (value != _completedDate) {
                    _completedDate = value;
                    onpropertychanged("CompletedDate");
                }
            }
        }
        public string Note {
            get { return _note; }
            set {
                if (value != _note) {
                    _note = value;
                    onpropertychanged("Note");
                }
            }
        }
    }
}


This is nice and simple. Notice that it implements ObservableObject that we created earlier to handle change notification.

Finally (for the Models folder) create MyObservableCollection.cs:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using cview = System.Windows.Data.CollectionViewSource;

namespace ToDoMVVM.Models {
    public class MyObservableCollection<T> : ObservableCollection<T> {

        public MyObservableCollection()
            : base() {
        }
        public MyObservableCollection(List<T> list)
            : base(list) {
        }
        public MyObservableCollection(IEnumerable<T> collection)
            : base(collection) {
        }

        public void MoveFirst() {
            cview.GetDefaultView(this).MoveCurrentToFirst();
        }
        public void MovePrevious() {
            cview.GetDefaultView(this).MoveCurrentToPrevious();
        }
        public void MoveNext() {
            cview.GetDefaultView(this).MoveCurrentToNext();
        }
        public void MoveLast() {
            cview.GetDefaultView(this).MoveCurrentToLast();
        }

        public bool CanMoveBack() {
            return cview.GetDefaultView(this).CurrentPosition > 0;
        }
        public bool CanMoveForward() {
            return (this.Count > 0) &&
                (cview.GetDefaultView(this).CurrentPosition < this.Count - 1);
        }
    }
}


This is effectively a wrapper for an ObservableCollection and was introduced in my Build An Application tutorial.

There are discussions (debates?) over whether such a wrapper is necessary. ObservableCollection is already a very capable class. One option is to just create an ObservableCollection<ToDo> (or <ToDoVM>). Allowing this though, begins the process (in my opinion) of dismantling the MVVM pattern. It creates too big a hole for the pattern to cover-over; the usefulness of the pattern is diminished.

Many examples that I have seen simply use a ViewModel to store the ObservableCollection, with a couple of methods (available via Commands) thrown in for good measure. This is certainly feasible, but what methods would you need? What do you need to do with a list? Move up and down it, add and remove items? The OC already has these capabilities.

Creating the wrapper allows much more control. For example, I created an isValid property in my other tutorial that prevents moving away from the current item if it is not valid. In the code above the user is prevented from deleting the last item in the collection. Besides, MyObservableCollection is generic, so we probably only require this single class per application.

The application is completed in Part 2

This post has been edited by andrewsw: 30 September 2014 - 04:08 PM


Is This A Good Question/Topic? 3
  • +

Replies To: [WPF] Exploring MVVM (Model View ViewModel) I

#2 andrewsw  Icon User is online

  • It's just been revoked!
  • member icon

Reputation: 3834
  • View blog
  • Posts: 13,582
  • Joined: 12-December 12

Posted 28 September 2014 - 06:54 AM

Here is the complete solution zipped:

Attached File  ToDoMVVM.zip (14.89K)
Number of downloads: 32

targeted for .NET Framework 4.5 and any CPU.

This post has been edited by andrewsw: 28 September 2014 - 06:56 AM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1