Simple MVVM in WPF

The WPF scene has exploded as the now-dominant desktop application scene for Windows desktop applications. Couple that with the new Windows 8-style store apps and XAML, it looks like it has a very rosy future indeed.

The problem is, WPF is hard, very hard. You have Prism, MVVM Light, MVVM Cross, Catel, and dozens of other frameworks that all claim to be the best way to do MVVM in a WPF application. If you're still relatively wet behind the ears with WPF, and still much prefer the simplicity of sticking with Windows forms, then like me you may have or may be finding that all this choice just seems to make things way too complicated.

There Has to Be an Easier Way

The good news is, there is, but before we come to that, you need to understand why all these frameworks are needed in the first place. The MVVM model that WPF employs is not all that straightforward, especially when you compare it to things like KnockoutJS, Angular, and many others in the HTML world.

For a starters, before your data objects will even begin to start telling their parent application about what's going on, you need to add something called property notifications to them. This generally means that you need to build a base class, and then derive all your models from that base class. Your base class would typically have the stubs in to allow you implement these notifications so that the parent app and its XAML can see the changes to data in your objects.

Secondly, you need to use different variable types to those you may be more used to as a Win-Forms developer. For example, many of you might be used to using 'List<T>' for many lists of objects. In WPF, you nearly always have to use an 'ObservableCollection<T>', which you wouldn't ever know until someone pointed it out to you. Why? A 'List<T>' will work quite happily with no obvious errors other than the fact you'll see no list changes when you add/remove data from your list, and adding all the Property Notifications in the world won't help.

Once you get your head around all the object changes, you then have the binding syntax to deal with in the XAML itself, and there are about 100 different ways here to do the same thing with the same bit of data. The net result is that all these frameworks have sprung up to 'Make it Easier' to deal with.

This new ease comes at a cost generally, though. You have the learning curve that the framework itself exposes, and each framework tends to implement MVVM in what it believes to be a correct use of the pattern. This 'Correct Use' does not always align with how many developers understand, or might even implement, the pattern themselves.

If you're coming from Win-Forms and are used to how that works, the gap between the stepping stones is even larger, because many will already have a good set of pre-conceived notions as to exactly what desktop application development entails, and I can pretty much grantee that mentality goes something like:

  • Load Data
  • Manipulate Data
  • Push Data into a Data Context
  • Let the Component draw it how it needs to

In contrast, the WPF version is a lot longer, and involves way more fluff to get the data displayed in the form. Now you have a firm Idea of where I'm heading with this, I'm going to introduce you to "Fody"

"Fody"? Isn't That a Small Bird of Some Description?

Yes it is, but it's also the name of a rather nifty .NET toolkit designed not just for WPF, but for .NET in general. Fody consists of a very simple transparent kernel (that you typically don't even have to touch or do anything with) and a number of 'Plugins', which in most cases you don't have to do anything with either. Fody, as described at its github page, is

"An Extensible tool for weaving .net assemblies"

In a nutshell, Fody's exact purpose for existence is to inject 'Stuff' automatically into your code, that you may need but without you having to do the injecting. It handles the link between your project and MSBuild, it makes sure all the dependencies are met, and various other things that you simply just don't need to deal with. The net result is you get value added goodness without the overheads.

Okay, So Fody's Great, but What's That Got to Do with WPF?

The easiest way to answer that question is to build a simple WPF application. Fire up Visual Studio, head to New Project, and create a new WPF Application project. Name it however you wish; I'll be calling mine 'EasyWpfWithFody'.

Once your application is up and running, the first thing we're going to do is design the UI. This won't be anything particularly pretty. I'm a developer by trade and I left my box of crayons behind when I left pre-school. My sense of design is not a particularly good one, so instead of describing the UI step by step, I'm just going to give you a screen shot and the XAML that produces it.

Simple1
Figure 1: Product of the following XAML code

The XAML code to produce this screen is as follows: (Warning; there's a lot of it!!)

<Window x:Class="EasyWpfWithFody.MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/
         xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="clr-namespace:EasyWpfWithFody.Models"
      Title="Simple Fody MVVM Example" Height="600" Width="800"
      DataContext="{Binding CurrentJobSheet,
         RelativeSource={RelativeSource Mode=Self}}">

   <Window.Resources>
      <Style TargetType="{x:Type ListBox}">
         <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
         <Setter Property="ItemTemplate">
            <Setter.Value>
               <DataTemplate>
                  <Border BorderBrush="Silver" BorderThickness="1"
                     CornerRadius="0" Margin="3">
                     <Grid>
                        <Grid.ColumnDefinitions>
                           <ColumnDefinition Width="0.5*" />
                           <ColumnDefinition Width="0.5*" />
                           <ColumnDefinition Width="0.5*" />
                           <ColumnDefinition Width="0.5*" />
                           <ColumnDefinition Width="0.5*" />
                        </Grid.ColumnDefinitions>

                        <TextBlock Grid.Column="0"
                           Text="{Binding JobTitle}"/>
                        <TextBlock Grid.Column="1"
                           Text="{Binding ClientName}"/>
                        <TextBlock Grid.Column="2"
                           Text="{Binding CostPerHour}"/>
                        <TextBlock Grid.Column="3"
                           Text="{Binding NumberOfHours}"/>
                        <TextBlock Grid.Column="4"
                           Text="{Binding TotalCost}"/>

                     </Grid>
                  </Border>
               </DataTemplate>
            </Setter.Value>
         </Setter>

         <Setter Property="Template">
            <Setter.Value>
               <ControlTemplate TargetType="ListBox">
                  <Grid Background="{TemplateBinding Background}">

                     <Grid.RowDefinitions>
                        <RowDefinition Height="Auto"/>
                        <RowDefinition Height="*"/>
                     </Grid.RowDefinitions>

                     <Grid Grid.Row="0"
                            HorizontalAlignment="Left"
                            Width="{Binding ActualWidth,
                               ElementName=itemsPresenter}"
                            Height="25">

                        <Grid.ColumnDefinitions>
                           <ColumnDefinition Width="0.5*" />
                           <ColumnDefinition Width="0.5*" />
                           <ColumnDefinition Width="0.5*" />
                           <ColumnDefinition Width="0.5*" />
                           <ColumnDefinition Width="0.5*" />
                        </Grid.ColumnDefinitions>

                        <Grid.Resources>
                           <Style TargetType="TextBlock">
                              <Setter Property="FontSize"
                                 Value="15"/>
                           </Style>
                        </Grid.Resources>

                        <TextBlock Grid.Column="0"
                           Text="Job Title" />
                        <TextBlock Grid.Column="1"
                           Text="Client Name" />
                        <TextBlock Grid.Column="2"
                           Text="Cost per Hour" />
                        <TextBlock Grid.Column="3"
                           Text="Number of Hours" />
                        <TextBlock Grid.Column="4"
                           Text="TotalCost" />

                     </Grid>

                     <ScrollViewer Grid.Row="1"
                        HorizontalAlignment="{TemplateBinding
                        HorizontalContentAlignment}">
                        <ItemsPresenter x:Name="itemsPresenter"/>
                     </ScrollViewer>

                  </Grid>
               </ControlTemplate>
            </Setter.Value>
         </Setter>

         <Setter Property="ItemContainerStyle">
            <Setter.Value>
               <Style TargetType="ListBoxItem">
                  <Setter Property="Margin" Value="0"/>
                  <Setter Property="Padding" Value="0"/>
               </Style>
            </Setter.Value>
         </Setter>

      </Style>
   </Window.Resources>

   <Grid>
      <Grid.RowDefinitions>
         <RowDefinition Height="*" />
         <RowDefinition Height="50" />
         <RowDefinition Height="100" />
      </Grid.RowDefinitions>

      <ListBox Grid.Row="0" Background="AntiqueWhite"
         ItemsSource="{Binding Jobs}" />

      <Grid Grid.Row="1">
         <Grid.ColumnDefinitions>
            <ColumnDefinition Width="0.6*" />
            <ColumnDefinition Width="0.4*" />
            <ColumnDefinition Width="0.8*" />
            <ColumnDefinition Width="0.4*" />
            <ColumnDefinition Width="0.8*" />
            <ColumnDefinition Width="0.6*" />
         </Grid.ColumnDefinitions>

         <TextBlock Grid.Column="0"
            Text="Tax Rate" HorizontalAlignment="Right"
            VerticalAlignment="Center" Margin="5"
            FontSize="10pt" FontWeight="Bold"/>
         <TextBlock Grid.Column="2" Text="Total Cost (Pre Tax)"
            HorizontalAlignment="Right" VerticalAlignment="Center"
            Margin="5" FontSize="10pt" FontWeight="Bold"/>
         <TextBlock Grid.Column="4"
            Text="Total Cost (Post Tax) : "
            HorizontalAlignment="Right" VerticalAlignment="Center"
            Margin="5" FontSize="10pt" FontWeight="Bold" />

         <TextBlock Grid.Column="1" Text="{Binding TaxRatePercentage,
            StringFormat=: {0}%}" HorizontalAlignment="Left"
            VerticalAlignment="Center" Margin="10"
            FontSize="10pt" FontWeight="Bold"/>
         <TextBlock Grid.Column="3" Text="{Binding TotalCostBeforeTax,
            StringFormat=: {0:C}}" HorizontalAlignment="Left"
            VerticalAlignment="Center" Margin="10"
            FontSize="10pt" FontWeight="Bold"/>
         <TextBlock Grid.Column="5" Text="{Binding TotalCostAfterTax,
            StringFormat=: {0:C}}" HorizontalAlignment="Left"
            VerticalAlignment="Center" Margin="10"
            FontSize="10pt" FontWeight="Bold"/>

      </Grid>

      <Grid Grid.Row="2" Background="Aquamarine">
         <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="*" />
         </Grid.RowDefinitions>
         <Grid.ColumnDefinitions>
            <ColumnDefinition Width="0.5*" />
            <ColumnDefinition Width="0.5*" />
            <ColumnDefinition Width="0.5*" />
            <ColumnDefinition Width="0.5*" />
            <ColumnDefinition Width="0.5*" />
         </Grid.ColumnDefinitions>

         <TextBlock Grid.Row="0" Grid.Column="0" Text="Job Title :"
            VerticalAlignment="Center" HorizontalAlignment="Right"
            Margin="0,0,10,0" FontSize="15pt" />
         <TextBlock Grid.Row="0" Grid.Column="2" Text="Client Name :"
            VerticalAlignment="Center" HorizontalAlignment="Right"
            Margin="0,0,10,0" FontSize="15pt" />
         <TextBlock Grid.Row="1" Grid.Column="0" Text="Cost per Hour :"
            VerticalAlignment="Center" HorizontalAlignment="Right"
            Margin="0,0,10,0" FontSize="12pt" />
         <TextBlock Grid.Row="1" Grid.Column="2" Text="Number of Hours :"
            VerticalAlignment="Center" HorizontalAlignment="Right"
            Margin="0,0,10,0" FontSize="12pt" />

         <TextBox Grid.Row="0" Grid.Column="1" x:Name="txtJobTitle"
            Margin="10,10,10,10" Text="{Binding JobEntryToAdd.JobTitle}" />
         <TextBox Grid.Row="0" Grid.Column="3" x:Name="txtClientName"
            Margin="10,10,10,10" Text="{Binding JobEntryToAdd.ClientName}" />
         <TextBox Grid.Row="1" Grid.Column="1" x:Name="txtCostPerHour"
            Margin="10,10,10,10" Text="{Binding JobEntryToAdd.CostPerHour}" />
         <TextBox Grid.Row="1" Grid.Column="3" x:Name="txtNumberOfHours"
            Margin="10,10,10,10" Text="{Binding JobEntryToAdd.NumberOfHours}" />

         <Button x:Name="btnAddNew" Grid.Row="0"
            Grid.Column="4" Grid.RowSpan="2" Margin="20,20,20,20"
            Content="Add New" FontSize="15pt" Click="BtnAddNewClick" />

      </Grid>

   </Grid>

</Window>

If you look through the various 'Text', 'Content', and other properties in the XAML, you'll see many of the '{binding xxxx}' entries that bind the data values between our UI and the code we'll write in just a moment. The Jobs list, for example, has its collection bound to 'Jobs', which, as you'll see in just a moment, in an 'ObservableCollection<JobEntry>' bound to a local object in the windows code behind. Most of the bindings used in the UI are purposely done the most simple way possible, so that a developer coming over from WPF can see the relationship to how this may have been done using Win-Forms.

Once we have the UI added, it's then time to turn our attention to the code behind the UI, and here's where it gets strange. Many developers used to Win-Forms will be used to having a LOT of code in the code behind. In WPF, the situation is drastically reversed. As you can see above, the code to draw the UI is not by any means short; the code for the code behind is, well... see for yourself:

using System.Windows;
using EasyWpfWithFody.Models;

namespace EasyWpfWithFody
{
   public partial class MainWindow : Window
   {
      public JobSheet CurrentJobSheet
         { get; private set; }

      public MainWindow()
      {
         CurrentJobSheet = new JobSheet();
         InitializeComponent();
      }

      private void BtnAddNewClick(object sender,
         RoutedEventArgs e)
      {
         CurrentJobSheet.Jobs.Add(CurrentJobSheet.
            JobEntryToAdd);
      }


   }
}

Yes, you're reading that correctly; that's ALL there is. It may take some believing; you could even remove that button handler if you wanted, but doing things the button click way feels comfortable (at least to me, anyway). One important thing to point out is the order of initialising the data. You'll see that I create my local data object BEFORE I let .NET initialize the various components and so forth on the UI. If you get this the wrong way around, the bindings simply won't work even if you have all the various property notification stuff correctly wired in.

You can see that my local variable 'CurrentJobSheet' is of type 'JobSheet', the UI is connected to this via the following line in the XAML:

DataContext="{Binding CurrentJobSheet,
   RelativeSource={RelativeSource Mode=Self}}"

You can see right away, at the very top in the 'Window' definition. From this point on, any bindings that simply point to a variable or property name are expected by XAML to be available inside this object.

The class definition for a 'JobSheet' looks like this:

using System;
using System.Collections.ObjectModel;
using System.Linq;
using PropertyChanged;

namespace EasyWpfWithFody.Models
{
   public class JobSheet
   {
      public Double TaxRatePercentage { get; set; }
      public ObservableCollection<JobEntry>
         Jobs { get; private set; }

      public Double TotalCostBeforeTax
      {
         get { return Jobs.Sum(x => x.TotalCost); }
      }

      public Double TotalCostAfterTax
      {
         get
         {
            var temp = TotalCostBeforeTax/100;
            return TotalCostBeforeTax +
               (TaxRatePercentage*temp);
         }
      }

      public JobEntry JobEntryToAdd { get; set; }

      public JobSheet()
      {
         Jobs = new ObservableCollection<JobEntry>();
         TaxRatePercentage = 20;   // Default for UK is 20%
         SetUpDemoData();
      }

      private void SetUpDemoData()
      {
         Jobs.Add(new JobEntry
         {
            JobTitle = "Fix Roof",
            ClientName = "Joe Shmoe",
            CostPerHour = 30,
            NumberOfHours = 2
         });

         Jobs.Add(new JobEntry
         {
            JobTitle = "Unblock Sink",
            ClientName = "Fred Flintstone",
            CostPerHour = 20,
            NumberOfHours = 4
         });

         Jobs.Add(new JobEntry
         {
            JobTitle = "Mow Lawns",
            ClientName = "Mr Mansion Owner",
            CostPerHour = 40,
            NumberOfHours = 6
         });

         Jobs.Add(new JobEntry
         {
            JobTitle = "Sweep Path",
            ClientName = "Jane Doe",
            CostPerHour = 10,
            NumberOfHours = 1
         });

         JobEntryToAdd = new JobEntry();
      }

   }
}

There's not a great deal to it, and most of its bulk consists of setting up the initial dummy data. The other class you'll need to create is the actual job entry itself; it looks like this:

using System;
using PropertyChanged;

namespace EasyWpfWithFody.Models
{
   public class JobEntry
   {
      public string JobTitle { get; set; }
      public string ClientName { get; set; }
      public Double CostPerHour { get; set; }
      public int NumberOfHours { get; set; }

      public Double TotalCost
      {
         get { return CostPerHour*NumberOfHours; }
      }

   }
}

Once you get to this point with the two models—the code behind and the UI—you should actually be able to hit F5 and get something up and running. You'll notice, however, that adding new entries to the job sheet doesn't update the display, and if anything is entered into to any of the text boxes on the form, the respective elements in the code behind won't get populated as expected. This is exactly the problem that trips up most devs coming from Win-Forms to WPF. They go ahaed and build up a UI in a not too dissimilar way to how they might do it in a Win-Forms application.

Then, they inevitably hit a brick wall and don't understand why the updates don't work. This is where you need to start adding your property notifications and sub classing things so you don't have to repeat your code; it's also at this point where Fody comes into play.

Jump into your NuGet package manager. You can use the console if you want, but I tend to use the GUI becauses it's easier to do partial name searches. Pop 'fody property' into the search box, and you should see the following:

Simple2
Figure 2: The search results

As you can see, I've already selected and clicked install on 'Fody' and 'PropertyChanged.Fody' because these are the only two we need for this example. There are, however, many others, for all sorts of different purposes.

I mentioned previously that we could remove the Button Handling from the code behind. Well, you would use 'Commander.Fody' to help you with that one. Feel free to explore; there's some great tools in the kit. For the rest of this article, we'll be looking only at 'PropertyChanged'.

At this point you might be thinking, "Okay, great, we have the code, we have the UI, but now I have to learn this Fody thing, so I'm still not skipping the learning curve step."

Well, yes, you can dig in and learn all about Fody and everything it can do.

OR

You can take a quick peek in the object browser and note that all you really need to know is that it implements an 'Aspect' or, as some may know, an 'Attribute'

We've not got space here for a full discussion on AOP (Aspect Orientated Programming), but those of you who've used tools such as 'Postsharp' or are very familiar with ASP.NET MVC will have used this model very often.

To add the Fody property notification stuff to our data classes, we simply have to make ONE change. Let's take the 'JobEntry' class as an example.

Before adding Fody:

using System;
using PropertyChanged;

namespace EasyWpfWithFody.Models
{
   public class JobEntry
   {
      public string JobTitle { get; set; }
      public string ClientName { get; set; }
      public Double CostPerHour { get; set; }
      public int NumberOfHours { get; set; }

      public Double TotalCost
      {
         get { return CostPerHour*NumberOfHours; }
      }

   }
}

After adding Fody:

using System;
using PropertyChanged;

namespace EasyWpfWithFody.Models
{
   [ImplementPropertyChanged]
   public class JobEntry
   {
      public string JobTitle { get; set; }
      public string ClientName { get; set; }
      public Double CostPerHour { get; set; }
      public int NumberOfHours { get; set; }

      public Double TotalCost
      {
         get { return CostPerHour*NumberOfHours; }
      }

   }
}

Spot the difference? The addition is that line just before the class definition that looks like an array declaration. That's the aspect that Fody makes available, and the one that at compile time will ensure any additional code required to make your objects implement 'IPropertyNotification' is injected into them.

Go ahead; add the same line just before the class definition on your 'JobSheet' class, then run and test the app. You should see that when you now add a new entry, everything updates correctly. Now, wasn't that easy?

If you've been introduced to desktop programming under Windows using WPF and are one of the newer breed of developers out there, much of this article will be pretty meaningless to you. If, like me, however, you've been used to Win-Forms, and possibly even further back than that to the original WIn32 GDI model, this will hopefully make a lot more sense.

Old dev or new Dev, however, Fody in my mind shows just how simple this CAN BE. I Like Fody simply because I don't for one second believe that we have to have all the complexity that all these large frameworks have, which in many cases is simply complexity for the sake of complexity. A developer's job is to find the best, quickest, and most stable solution to a problem, and at this moment in time Fody ticks all those boxes.

I'll put a copy of the project from this article on my github page at:

http://github.com/shawty

for those who want to clone it, and develop WPF in a far simpler way than is the norm.

If you have a particular subject that you'd like to see covered, or an issue that's been chewing away at you, please do come and find me lurking around the interwebs. My Twitter handle is @shawty_ds and I can usually be pinged quite easily using that. Let me know your thoughts and ideas either there, or in the comments below this article. If I can accommodate your suggestions, I most certainly will.



Related Articles

Comments

  • How to use with vb.net

    Posted by Leon B on 05/04/2016 06:05am

    This is very useful, I've tried to use it with vb.net but didn't manage to get the ball rolling. Always telling me that the [ImplementPropertyChanged] is invalid.

    Reply
  • question

    Posted by mahmoud on 04/20/2016 03:52am

    how to implement PropertyChanged the above code give me a fault in PropertyChanged

    Reply
  • Good Article

    Posted by Vandana on 05/16/2015 02:32pm

    Very helpful ....

    Reply
  • Total fields?

    Posted by fred on 12/29/2014 09:06am

    hello, thanks for this. What does [ImplementPropertyChanged] is useful for in JobSheet class? I've tried by many way to update the TotalCostBeforeTax and TotalCostAfterTax values but no way. Am I missing something? Thanks for your answers.

    Reply
  • Nice Article

    Posted by Nagarajan on 12/04/2014 01:01am

    Thanks for the article, Peter. It has thrown up a new avenue for WPF developers especially since Aspect Oriented Programming has been used in a way that can definitely reduce coding effort and minimize errors. I would definitely dig further into Fody and check it out. I could not find the code for the sample project in the link specified by you in github.

    • RE: Nice Article

      Posted by Peter Shaw on 12/23/2014 07:52am

      oops my bad. i honestly thought i'd uploaded it. it's there now though, you can find it at https://github.com/shawty/EasyWpfWithFody shawty

      Reply
    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date