Archive

Archive for the ‘MVVM’ Category

Using Reflection with WPF and the INotifyPropertyChanged interface

February 8, 2011 3 comments

WPF has a powerful feature in data binding.  The problem with it though is it requires you to fire a Property Changed event on the property that had the value changed.  This forces the UI to refresh all controls bound to the property.  This task is tedious and error prone having to type in the name of the property as a string literal.

Default Implementation of INotifyPropertyChanged.  The only required item is the PropertyChanged Event handler.

using System.ComponentModel;

namespace WpfApplication2
{
    public class DefaultClass : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    }
}

 

Here a property was added with a call to property changed.  The first potential point of failure is mistyping the string value “MyProperty” – notice in the example – it is misspelled and these are case sensitive.  This will never work not to mention it doesn’t perform any null checking on the event handler.  This can be added to each class separately – which is painful for large projects.

public string MyProperty { get; set; }

        public DefaultClass()
        {
            PropertyChanged(this, new PropertyChangedEventArgs("MyPropery"));
        }

 

Welcome to the Observable base class.  This class allows you to call a notify without typing the name of the property in a string literal and it performs the null checking for you.

using System;
using System.ComponentModel;
using System.Linq.Expressions;

namespace WpfApplication2
{
    public abstract class ObservableBase : INotifyPropertyChanged
    {
        /// <summary>
        /// Notify Property Changed
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression"></param>
        protected virtual void NotifyPropertyChanged<T>(Expression<Func<T>> expression)
        {
            string propertyName = GetPropertyName(expression);
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }

        /// <summary>
        /// Notify Property Changed (Shorted method name)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression"></param>
        protected virtual void Notify<T>(Expression<Func<T>> expression)
        {
            string propertyName = GetPropertyName(expression);
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }

        /// <summary>
        /// Get the string name for the property
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expression"></param>
        /// <returns></returns>
        protected string GetPropertyName<T>(Expression<Func<T>> expression)
        {
            MemberExpression memberExpression = (MemberExpression)expression.Body;
            return memberExpression.Member.Name;
        }

        /// <summary>
        /// Property Changed Event
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;
    }
}

Below is the new implementation of the class with the observable base.  Reflection is used to get the property name to pass into the notify handler on the observable base class.


namespace WpfApplication2
{
    public class DefaultClass : ObservableBase
    {
        public string MyProperty { get; set; }

        public DefaultClass()
        {
            NotifyPropertyChanged(() => MyProperty);
        }
    }
}

 

Note on Reflection

For the counter argument that reflection is slow – a test was performed with calling these property change events 10,000 times for each method.  The difference was less than a 1/4 of a second.  If your UI is calling property changed events that fast – you may want to rethink your design.   Smile

 

Some of the credit for this post belongs to Colin Borrowman who helped develop this.

Simple WPF UI Performance Improvement Tips

December 20, 2010 Leave a comment

Slowness in the WPF UI can occur when loading data intensive business objects or regular objects over a slow connection (WAN / VPN / etc).  This can also occur if your business objects require a significant amount of iterative processing prior to loading the UI.  Some simple ways to make the UI more responsive is to load this data asynchronously.

The demo below makes use of the System.ComponentModel BackgroundWorker class.

The source code can be downloaded from WPFAsynchronousDataLoadingDemo.zip.

The project is made up of a few parts:

  • MainWindow.xaml (View)
  • MyViewModel (ViewModel)
  • MyDataGetterClass (more of a data services class versus model)
    The UI contains the following:
  • Button for Synchronous data loading
  • Button for Asynchronous data loading
  • Slider to demonstrate UI resources
  • Listbox to store results of data retrieval

image

For simplicity the Buttons on UI have event handlers that call methods on the view model and handle clearing the collections.  Typically these functions would not be found in the view model.

Synchronous Loading

The synchronous load passed the Collection object to the MyDataGetterClass and loads it completely prior to returning.  As you can see there is a Thread.Sleep call that pauses the thread for 1/4 of a second to simulate large data operations.

MyViewModel.cs

public void LoadSync()
        {
             dataGetter.BuildDataSynchronously(OCollection, _numItems);
        }

MyDataGetterClass.cs

public void BuildDataSynchronously(ObservableCollection<string> collection, int numItems)
        {
            for (int index = 0; index < numItems; index++)
            {
                collection.Add(GetString());
                Thread.Sleep(250);
            }
        }

When using this option the UI thread becomes unresponsive while the data is loaded.  Even though the observable collection automatically updates the UI when the collection changes (items added / removed /etc) it is not able to update the UI because the thread is busy building the objects.  The slider / scroll bars / etc become frozen until the operation is complete.

Asynchronous Loading

Now we will take a look at loading the data asynchronously.  This time the view calls a different method on the View Model.

MyViewModel.cs

 public void LoadAsync()
        {
            BackgroundWorker bw = new BackgroundWorker();
            bw.WorkerReportsProgress = true;
            bw.DoWork += new DoWorkEventHandler(bw_DoWork);
            bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
            bw.RunWorkerAsync();
        }

When the RunWorkerAsync() method is called – the following two methods come into play.

MyViewModel.cs

void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            dataGetter.BuildDataAsynchronously(sender as BackgroundWorker, _numItems);
        }

MyDataGetterClass.cs

        public void BuildDataAsynchronously(BackgroundWorker worker, int numItems)
        {
            for (int index = 0; index < numItems; index++)
            {
                worker.ReportProgress(0, GetString());
                Thread.Sleep(250);
            }
        }

As the work reports the progress – a method on the view model is handling the messages.

MyViewModel.cs

void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            string s = e.UserState as string;
            if (!string.IsNullOrEmpty(s))
                OCollection.Add(s);
        }

Each event is cast as a string type and then added to the collection.  This all is done on a separate thread so the UI remains responsive.  While the list box is loading using this method – you can adjust the scroll bar – play with the Slider Control, etc to demonstrate the UI is still functional.

This is one of the more simple ways to speed up the UI responsiveness of your application.  You can use other techniques such as call back event handlers to handle items once data loading is complete as well.

The source code can be downloaded from WPFAsynchronousDataLoadingDemo.zip.

Command Binding with WPF and Silverlight (.net 4.0) with M-V-VM

June 23, 2010 8 comments

This is the second part in a demo series concerning using the M-V-VM pattern.  This section will deal with command binding.  Command binding allows you to bind a command event such as a button click in a view to an action on a view model.  This eliminates the need to create an event handler in the view to pass on to the view model.  It requires less code and allows you to test command events on the view model without the need for the UI.

Command binding wasn’t available with Silverlight until Silverlight 4 was released.  The information below was gathered using the following resources.

http://www.dotnetfunda.com/articles/article859-command-binding-in-silverlight-4-stepbystep-.aspx

http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

The common method being used for WPF is the RelayCommand class courtesy of Josh Smith.  This is nice way to implement command binding in WPF.  However it cannot be used with Silverlight as the Command Manager does not exist in Silverlight’s version of the ICommand Interface.

The source code for the following demos can be found here.

Silverlight Demo

WPF Demo

Silverlight Implementation

For Silverlight the DelegateCommand class will be used.  This class can be found in here.

Create a new Silverlight Application (Blank)

Create a class and copy the DelegateCommand code from the link above into it an save.

Create a View Model Class

Add code to the View (MainPage.xaml.cs) to initialize the view model and set the data context of the view to the view model

public MainPage()
        {
            DataContext = new MainPageViewModel();
            InitializeComponent();
        }

On the view model add the following code

using System;
using System.Windows;
using System.Windows.Input;

namespace SLCommandDemo
{
    public class MainPageViewModel
    {
        /// <summary>
        /// Command Property to Bind to From View (done in xaml)
        /// </summary>
        public ICommand ButtonClickCommand {get; private set; }

        public MainPageViewModel()
        {
            // Setup Command Binding
            // First Parameter - Method with Action to perform
            // Second Parameter - Method to determine if button is enabled
            ButtonClickCommand = new DelegateCommand(PerformClickAction, CanClickButtonandPerformAction);
        }

        public bool CanClickButtonandPerformAction(object parameter)
        {
            // Add any logic here to enable / disable the button
            return true;
        }

        public void PerformClickAction(object parameter)
        {
            // Here you would put the code to execute the desired command.  We are
            // going to show a message box
            MessageBox.Show("You are using Command Binding with Silverlight!!!!");
        }
    }
}

Add a button to the view (MainPage.xaml) and add the following binding statement to bind the command property of the button to the view model.

<Button
            Content="Button"
            Height="23"
            Name="button1"
            VerticalAlignment="Center"
            Width="75"
            Command="{Binding ButtonClickCommand}"
            />

Build and Run the Solution and then click the button.  Congratulations.  You are now using command binding in Silverlight.

image

WPF Implementation

The RelayCommand class will be used for WPF.  This class can be found here.

Create a new WPF Application

Create a class and copy the RelayCommand code from the link above into it an save.

Create a View Model Class

Add code to the View (MainWindow.xaml.cs) to initialize the view model and set the data context of the view to the view model

public MainWindow()
        {
            DataContext = new MainWindowViewModel();
            InitializeComponent();
        }

On the view model add the following code

using System;
using System.Windows.Input;

namespace WPFCommandBindingDemo
{
    public class MainWindowViewModel
    {
        /// <summary>
        /// Command Property to Bind to from View (done in xaml)
        /// </summary>
        public ICommand ButtonClickCommand { get; private set; }

        public MainWindowViewModel()
        {
            // Setup Command Binding
            ButtonClickCommand = new RelayCommand(PerformClickAction, CanClickButtonandPerformAction);
        }

        public bool CanClickButtonandPerformAction(object parameter)
        {
            // Add any logic here to enable / disable the button
            return true;
        }

        public void PerformClickAction(object parameter)
        {
            // Here you would put code to execute the desired command.  We are
            // going to show a message box.
            System.Windows.MessageBox.Show("You are using Command Binding with WPF!!!");
        }
    }
}

Add a button to the view (MainWindow.xaml) and add the following binding statement to bind the command property of the button to the view model.

<Button
            Content="Button"
            Height="23"
            Name="button1"
            VerticalAlignment="Center"
            Width="75"
            Command="{Binding ButtonClickCommand}"
            />

Build and Run the Solution and then click the button.  Congratulations.  You are now using command binding in WPF.

image

Summary

By using command binding with the MVVM pattern you allow each command action to be tested without the need for creating the UI (View).

If you have any questions or comments please post them.

WPF Model-View-ViewModel (M-V-VM) Example

June 2, 2010 4 comments

When starting out with WPF and the M-V-VM pattern I was looking for a simple example of this pattern.  Most of the things I found were full of items that complimented the pattern but didn’t show just the pattern itself.  I decided to walk through a simple example and will detail it below.

You can download the source code from here.

Basic M-V-VM OverView

Layer Items
Model Business Objects, Business Logic

View Model

UI Logic, Data / Services Interaction

View

UI elements (Styling, Data Binding, etc)

The Model

The model in the MVVM pattern will be the business object. For this example my model is a picture object.  This object contains some basic properties a picture would have such as:

  • Name
  • File Path
  • Height
  • Width

It also contains a validation method to validate the Picture object.  This was placed here for example only (notice there is no logic in the method).  Any business logic for the model should be contained within this class or a model helper class.

namespace MVVMDemoApplication.Models
{
    public class Picture : BaseModel
    {
        private string _pictureName;
        private string _picturePath;
        private int _height;
        private int _width;

        public Picture()
        {
            Width = 300;
            Height = 200;
        }
        /// <summary>
        /// Name of Picture
        /// </summary>
        public string PictureName
        {
            get { return _pictureName; }
            set
            {
                if (_pictureName != value)
                {
                    _pictureName = value;
                    NotifyPropertChanged("PictureName");
                }
            }
        }
        /// <summary>
        /// Picture path
        /// </summary>
        public string PicturePath
        {
            get { return _picturePath; }
            set
            {
                if (_picturePath != value)
                {
                    _picturePath = value;
                    NotifyPropertChanged("PicturePath");
                }
            }
        }
        /// <summary>
        /// Height of Picture
        /// </summary>
        public int Height
        {
            get { return _height; }
            set
            {
                if (_height != value)
                {
                    _height = value;
                    NotifyPropertChanged("Height");
                }
            }
        }
        /// <summary>
        /// Width of Picture
        /// </summary>
        public int Width
        {
            get { return _width; }
            set
            {
                if (_width != value)
                {
                    _width = value;
                    NotifyPropertChanged("Width");
                }
            }
        }
        /// <summary>
        /// Determine if Picture is Valid
        /// This is a model based logic validation and does not belong in the viewmodel
        /// </summary>
        /// <returns></returns>
        public bool IsPictureValid()
        {
            bool isValid = true;
            // Add Code here to validate picture - whatever you want
            return isValid;
        }
    }
}

Base Model Class

This class is used to provide functionality to each model class without the need to replicate code.  This works perfectly when implementing the INotifyPropertyChanged interface for Data Bound Properties in WPF and Silverlight.

namespace MVVMDemoApplication.Models
{
    public class BaseModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Notify of Property Changed event
        /// </summary>
        /// <param name="propertyName"></param>
        public void NotifyPropertChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

The View Model

The view model contains the UI logic for the view.  Any logic, method calls, parameters, etc should always be placed in the View Model.  This logic should not be placed in the view.

The view model contains a collection of picture objects.  These objects have to be populated from a data source.  After many hours of research I have to agree with some of the experts (a.k.a Josh Smith) and say that while the data operations belong in the View Model layer they should be extracted from the view model in a separate set of classes.  This will simplify the view models and allow the reuse of the data access logic across multiple view models.  This will be more apparent and useful when using Silverlight with WCF / RIA Services.

Properties

  • Error Message – displayed when error occurs
  • Pictures – observable collection of Picture Objects (Model).  It is better to use an observable collection when items maybe added / removed from the collection so the data binding recognizes the changes automatically.  This does not occur with list objects.

Methods

  • MakePictureBigger – enlarge picture if valid (notice the call to the IsPictureValid object on the model itself)
  • MakePictureSmaller – make picture smaller (notice the validation logic – since this is UI logic and not business logic it belongs in this class)
  • CheckIfPictureSelected – ensure that an item is selected from the list box – if not modify the error message on the view model.
  • LoadPictures – call to the services class to retrieve the data.  In this case it is a list of image files.  This can be anything from a WCF services call to a call out to a custom Data Access Layer (DAL).
  • BuildPictureCollection – build the collection of picture objects – this is here versus the data services layer to reduce coupling.
  • BuildPictureObject – load values on the object based on the file path given
namespace MVVMDemoApplication.ViewModels
{
    public class PictureListBoxViewModel : BaseViewModel
    {

        ObservableCollection<Picture> _pictures;
        PictureDataServices _services;
        string _pictureDirectory;
        string _errorMessage;

        /// <summary>
        /// Selected Picture in ListBox
        /// </summary>
        public Picture SelectedPicture { get; set; }

        /// <summary>
        /// Error Message
        /// </summary>
        public string ErrorMessage
        {
            get { return _errorMessage; }
            set
            {
                if (_errorMessage != value)
                {
                    _errorMessage = value;
                    NotifyPropertChanged("ErrorMessage");
                }
            }

        }

        /// <summary>
        /// Observable Collection of Pictures
        /// Uses INotifyPropertyChange when list changes
        /// </summary>
        public ObservableCollection<Picture> Pictures
        {
            get { return _pictures; }
            set
            {
                if (_pictures != value)
                {
                    _pictures = value;
                    NotifyPropertChanged("Pictures");
                }
            }

        }

        /// <summary>
        /// Constructor
        /// </summary>        
        public PictureListBoxViewModel()
        {
            // Initialize data services class for later use
            // It is best to keep this isolated from the view model
            _services = new PictureDataServices();
            // Set Picture Directory
            _pictureDirectory = Directory.GetCurrentDirectory() + "\\Images";
            // Initialize here so we don't forget to later and end up with a null reference exception
            Pictures = new ObservableCollection<Picture>();
            LoadPictures();
        }

        /// <summary>
        /// Overload to allow different images directory
        /// Maybe one view has an option to select a directory
        /// </summary>
        /// <param name="imagespath"></param>
        public PictureListBoxViewModel(string imagespath)
        {
            // Initialize data services class for later use
            // It is best to keep this isolated from the view model
            _services = new PictureDataServices();
            // Set Picture Directory
            _pictureDirectory = imagespath;
            // Initialize here so we don't forget to later and end up with a null reference exception
            Pictures = new ObservableCollection<Picture>();
            LoadPictures();
        }

        /// <summary>
        /// Make the selected picture bigger
        /// </summary>
        public void MakePictureBigger()
        {
            //Perform Error Check
            if (!CheckifPictureIsSelected())
                return;
            Picture picture = SelectedPicture;
            // Here is where we call business logic on the picture object itself
            // The validation method on the object determines if object is valid
            // This logic has nothing to do with the view or the view model so it
            // Is placed on the model
            if (picture.IsPictureValid())
            {
                picture.Height = picture.Height + 50;
                picture.Width = picture.Width + 50;
            }
        }

        /// <summary>
        /// Make the selected picture Smaller
        /// </summary>
        public void MakePictureSmaller()
        {
            //Perform Error Check
            if (!CheckifPictureIsSelected())
                return;
            Picture picture = SelectedPicture;
            // Verify we don't make the picture to small.  Since this is a UI
            // check we place in the View Model versus the model.  The model
            // does not care how big the picture is displayed
            if (picture.Height > 60)
            {
                picture.Height = picture.Height - 50;
                picture.Width = picture.Width - 50;
            }
        }

        /// <summary>
        /// Check if user selected item
        /// </summary>
        /// <returns></returns>
        private bool CheckifPictureIsSelected()
        {
            if (SelectedPicture == null)
            {
                ErrorMessage = "Please select a picture";
                return false;
            }
            else
            {
                ErrorMessage = string.Empty;
                return true;
            }
        }

        /// <summary>
        /// Load Pictures from FileSystem into Collection
        /// </summary>
        private void LoadPictures()
        {
            // Load picture collection from services (could be WCF or other Data Access Logic)
            List<string> pictureFilePaths = _services.GetPicturesFromDirectory(_pictureDirectory);
            // Build the Collection of Models here - not done in DataSevices to reduce coupling
            BuildPictureCollection(pictureFilePaths);
        }

        /// <summary>
        /// Build / Load Collection of Pictures
        /// </summary>
        /// <param name="pictureFilePaths"></param>
        private void BuildPictureCollection(List<string> pictureFilePaths)
        {
            foreach (string file in pictureFilePaths)
            {
                // Build Picture Object
                Picture pic = BuildPictureObject(file);
                // Add object to collection if not null
                if (pic != null)
                    Pictures.Add(pic);
            }
        }

        /// <summary>
        /// Load Picture object
        /// </summary>
        /// <param name="filepath"></param>
        /// <returns></returns>
        private Picture BuildPictureObject(string filepath)
        {
            Picture pic = new Picture();
            FileInfo fi = new FileInfo(filepath);
            pic.PictureName = fi.Name;
            pic.PicturePath = filepath;
            return pic;
        }
    }
}

Base View Model Class

This class is used to provide functionality to each view model class without the need to replicate code.  This works perfectly when implementing the INotifyPropertyChanged interface for Data Bound Properties in WPF and Silverlight.

namespace MVVMDemoApplication.ViewModels
{
    public class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Notify of Property Changed event
        /// </summary>
        /// <param name="propertyName"></param>
        public void NotifyPropertChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

The View

The view should contain the bindings to the view model only.  This may include some event handling.  In this example I used some event triggers for buttons to call methods on the view model directly.  Command binding reduces the need for this extra code.  For this example I did not want to introduce command binding as it does add an extra level of complexity that is best held for future example / post.

This solution has 2 views.  The first one shows the initialization of the View and View Model via XAML.  The second one performs the exact same functions in the code behind page.

View 1

namespace MVVMDemoApplication.Views
{
    /// <summary>
    /// Interaction logic for PictureView.xaml
    ///
    /// NO Business / Application logic should be in this class
    /// This class is for UI Logic ONLY!!!!
    ///
    /// Also note that the events you see in this class can be removed completely when using
    /// Command Binding.
    ///
    /// Added to interface to enable easy UI switching
    /// </summary>
    public partial class PictureView : Page, PictureViewInterface
    {
        // Reference for events. This can be elimnated when using command binding.  The
        // ViewModel in this case was created via XAML through the Data Context method
        PictureListBoxViewModel _plvm; 

        public PictureView()
        {
            InitializeComponent();
            _plvm = (PictureListBoxViewModel)this.gdRoot.DataContext;
        }
        private void btnMakeSmaller_Click(object sender, RoutedEventArgs e)
        {
            _plvm.MakePictureSmaller();
        }
        private void btnMakeBigger_Click(object sender, RoutedEventArgs e)
        {
            _plvm.MakePictureBigger();
        }
        private void btnClose_Click(object sender, RoutedEventArgs e)
        {
            // Grabbed parent window to call the close event on the parent window.  This
            // can be wired up different but was done this way for simplicity
            Window win = (Window)this.Parent;
            win.Close();
        }
        /// <summary>
        /// Added just to show the difference when using different views
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSwitchViews_Click(object sender, RoutedEventArgs e)
        {
            Window1 win = (Window1)this.Parent;
            win.SwitchViews(this);
        }
    }
}

View 2 – Constructor only

    public partial class PictureView2 : Page
    {
        PictureListBoxViewModel _plvm; 

        public PictureView2()
        {
            InitializeComponent();
            // Initialize ViewModel in constructor
            _plvm = new PictureListBoxViewModel();
            // Set Data Context for Grid
            gdRoot.DataContext = _plvm;
        }

View 1 – XAML

Notice the data context and PictureViewModel initialization is done at the Grid.DataContext section instead of in the code behind page.

<Page x:Class="MVVMDemoApplication.Views.PictureView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:ViewModels="clr-namespace:MVVMDemoApplication.ViewModels"
    Title="PictureView">
    <Grid x:Name="gdRoot">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="40" />
        </Grid.RowDefinitions>
        <Grid.Resources>
            <Style TargetType="Button">
                <Setter Property="Margin" Value="5" />
            </Style>
        </Grid.Resources>
        <Grid.DataContext>
            <!--Set the Grid Data Context to the PictureListViewModel which initializes the class-->
            <ViewModels:PictureListBoxViewModel/>
        </Grid.DataContext>
        <ListBox x:Name="lbPictures"
                 ItemsSource="{Binding Path=Pictures}"
                 HorizontalAlignment="Left"
                 BorderBrush="AliceBlue"
                 BorderThickness="2"
                 Margin="10"
                 SelectedItem="{Binding Path=SelectedPicture}">
            <!--The list box template can be removed from this file and placed in a resource dictionary-->
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock
                            Margin="5"
                            HorizontalAlignment="Left"
                            VerticalAlignment="Center"
                            Text="{Binding Path=PictureName}"
                            />
                        <Image
                            Margin="5"
                            Height="{Binding Path=Height}"
                            Width="{Binding Path=Width}"
                            Source="{Binding Path=PicturePath}"
                            />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <StackPanel Grid.Row="1" HorizontalAlignment="Center" Orientation="Horizontal">
            <Label>PictureView</Label>
            <Button Name="btnMakeSmaller" Click="btnMakeSmaller_Click"  ToolTip="Make selected item smaller" >Shrink</Button>
            <Button Name="btnMakeBigger"  Click="btnMakeBigger_Click"  ToolTip="Make selected item bigger" >Grow</Button>
            <Button Name="btnSwitchViews" Click="btnSwitchViews_Click" ToolTip="Click to switch to other PictureView">Switch Views</Button>
            <Button Name="btnClose" Click="btnClose_Click" ToolTip="Close the application" >Close</Button>
            <TextBlock Name="tbErrorMessage" Text="{Binding Path=ErrorMessage}" Foreground="Red" />
        </StackPanel>
    </Grid>
</Page>

Testing

Another major benefit of M-V-VM when implemented correctly is the ability to test the view model.  These can be in the form of Unit or Integration tests.  I added a test project to this solution utilizing NUnit (DLL provided in solution) so it will build.  You will need a test runner to be able to execute the tests.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using MVVMDemoApplication.ViewModels;
using System.IO;

namespace MVVMTestProject
{
    [TestFixture]
    public class TestClass
    {
        [Test]
        public void Verify_PictureModel_HasPictures()
        {
            string imagesdirectory = Directory.GetCurrentDirectory() + "\\Images";
            PictureListBoxViewModel vm = new PictureListBoxViewModel(imagesdirectory);
            Assert.IsTrue(vm.Pictures.Count() > 0);
        }
    }
}

Summary

Using the M-V-VM pattern is a different mindset from traditional WinForms application development.  It is similar to the ASP.NET MVC / Rails patterns but for WPF and Silverlight.  This pattern makes it easy to test the application as the UI presentation layer has been extrapolated from the UI and business logic.  It also helps reduce coupling and make changing the UI layer (Views) much easier.

You can download the source code from here.

Categories: Agile, C#, MVVM, WPF Tags: , , ,
Follow

Get every new post delivered to your Inbox.