Mastering Windows Presentation Foundation

4 (4 reviews total)
By Sheridan Yuen
    Advance your knowledge in tech with a Packt subscription

  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. A Smarter Way of Working with WPF

About this book

Windows Presentation Foundation is rich in possibilities when it comes to delivering an excellent user experience. This book will show you how to build professional-grade applications that look great and work smoothly.

We start by providing you with a foundation of knowledge to improve your workflow – this includes teaching you how to build the base layer of the application, which will support all that comes after it. We’ll also cover the useful details of data binding. Next, we cover the user interface and show you how to get the most out of the built-in and custom WPF controls.

The final section of the book demonstrates ways to polish your applications, from adding practical animations and data validation to improving application performance. The book ends with a tutorial on how to deploy your applications and outlines potential ways to apply your new-found knowledge so you can put it to use right away.

Publication date:
February 2017
Publisher
Packt
Pages
568
ISBN
9781785883002

 

Chapter 1. A Smarter Way of Working with WPF

When Windows Presentation Foundation (WPF) was first released as part of the .NET Framework version 3.0 in 2006, it was billed as the future of desktop application Graphical User Interface (GUI) languages and supporters claimed that it would put an end to the previous GUI technology, Windows Forms. However, as time passed, it has fallen far short of this claim.

There are three main reasons that WPF has not taken off as widely as previously expected. The first reason has nothing to do with WPF and stems from the recent push to host everything in the cloud and having web interfaces rather than desktop applications. The second reason relates to the very steep learning curve and the very different way of working that is required to master WPF.

The last reason is that it is not a very efficient language and if a WPF application has lots of 'bells and whistles' in, then either the client computers will need to have additional RAM and/or graphics cards installed, or they could face a slow and stuttering user experience.

This explains why many companies that make use of WPF today are in the finance industry, where they can afford to upgrade all users' computers to be able to run their applications optimally. This book will aim to make WPF more accessible to the rest of us by providing practical tips and tricks to help build our real-world applications more easily and more efficiently.

One of the simplest changes with the biggest workflow improvements that we can make to improve the way we work with WPF is to follow the MVVM software architectural pattern. It describes how we can organize our classes to make our applications more maintainable, testable, and generally simpler to understand. In this chapter, we will take a closer look at this pattern and discover how it can help us to improve our applications.

After discovering what MVVM is and what its benefits are, we'll learn several new ways to communicate between the various components in this new environment. We'll then focus on the physical structure of the code base in a typical MVVM application and investigate a variety of alternative arrangements.

 

What is MVVM and how does it help?


Model-View-View Model (MVVM) is a software architectural pattern that was famously introduced by John Gossman on his blog back in 2005 and is now commonly used when developing WPF applications. Its main purpose is to provide a Separation of Concerns between the business model, the User Interface (UI), and the business logic. It does this by dividing them into three distinct types of core components: Models, Views, and View Models. Let's take a look at how they are arranged and what each of these components represent.

As we can see here, the View Models component sits between the Models and the Views and provides two-way access to each of them. It should be noted at this point that there should be no direct relationship between the Views and Models components and only loose connections between the other components. Let's now take a closer look at what each of these components represent.

Models

Unlike the other MVVM components, the Model constituent comprises of a number of elements. It encompasses the business data model along with its related validation logic and also the Data Access Layer (DAL), or data repositories, that provide the application with data access and persistence.

The data model represents the classes that hold the data in the application. They typically mirror the columns in the database more or less, although it is common that they are hierarchical in form, and so may require joins to be performed in the data source in order to fully populate them. One alternative would be to design the data model classes to fit the requirements in the UI, but either way, the business logic or validation rules will typically reside in the same project as the data model.

The code that is used to interface with whatever data persistence technology is used in our application is also included within the Models component of the pattern. Care should be taken when it comes to organizing this component in the code base, as there are a number of issues to take into consideration. We'll investigate this further in a while, but for now, let's continue to find out more about the components in this pattern.

View Models

The View Models can be explained easily; each View Model provides its associated View with all of the data and functionality that it requires. In some ways, they can be considered to be similar to the old Windows Forms code behind files, except that they have no direct relationship with the View that they are serving. A better analogy, if you're familiar with MVC, would be that they are similar to the Controllers in the Model-View-Controller (MVC) software architectural pattern. In fact, in his blog, John describes the MVVM pattern as being a variation of the MVC pattern.

They have two-way connections with the Model component in order to access and update the data that the Views require, and often, they transform that data in some way to make it easier to display and interact with in the UI. They also have two-way connections with the Views through data binding and property change notification. In short, View Models form the bridge between the Model and the View, which otherwise have no connection to each other.

However, it should be noted that the View Models are only loosely connected to the Views and Model components through their data binding and interfaces. The beauty of this pattern enables each element to be able to function independently from each other.

To maintain the separation between the View Models and the View, we avoid declaring any properties of UI-related types in the View Model. We don't want any references to UI-related DLLs in our View Models project, and so we make use of custom IValueConverter implementations to convert them to primitive types. For example, we might convert Visibility objects from the UI to plain bool values or convert the selection of some colored Brush objects to an Enum instance that is safe to use in the View Model.

Views

The Views define the appearance and layout of the UI. They typically connect with a View Model through the use of their DataContext property and display the data that it supplies. They expose the functionality provided by the View Model by connecting its commands to the UI controls that the users interact with.

In general, the basic rule of thumb is that each View has one associated View Model. This does not mean that a View cannot data bind to more than one data source or that we cannot reuse View Models. It simply means that, in general, if we have a class called SecurityView, it is more than likely that we'll also have an instance of a class named SecurityViewModel that will be set as the value of that View's DataContext property.

Data binding

One often overlooked aspect of the MVVM pattern is its requirement for data binding. We could not have the full Separation of Concerns without it, as there would be no easy way of communicating between the Views and View Models. The XAML markup, data binding classes, and ICommand and INotifyPropertyChanged interfaces are the main tools in WPF that provide this functionality.

The ICommand interface is how commanding is implemented in the .NET Framework. It provides behavior that implements and even extends the ever useful Command pattern, in which an object encapsulates everything needed to perform an action. Most of the UI controls in WPF have Command properties that we can use to connect them to the functionality that the commands provide.

The INotifyPropertyChanged interface is used to notify binding clients that property values have been changed. For example, if we had a User object and it had a Name property, then our User class would be responsible for raising the PropertyChanged event of the INotifyPropertyChanged interface, specifying the name of the property each time its value was changed. We'll look much deeper into all of this later, but now let's see how the arrangement of these components help us.

So how does MVVM help?

One major benefit of adopting MVVM is that it provides the crucial Separation of Concerns between the business model, the UI, and the business logic. This enables us to do several things. It frees the View Models from the Models, both the business model and the data persistence technology. This in turn enables us to reuse the business model in other applications and swap out the DAL and replace it with a mock data layer so that we can effectively test the functionality in our view models without requiring any kind of real data connection.

It also disconnects the Views from the View logic that they require, as this is provided by the View Models. This allows us to run each component independently, which has the advantage of enabling one team to work on designing the Views, while another team works on the View Models. Having parallel work streams enables companies to benefit from vastly reduced production times.

Furthermore, this separation also makes it easier for us to swap the Views for a different technology without needing to change our Model code. We may well need to change some aspects of the View Models, for example, the new technology used for the Views may not support the ICommand interface, but in principal, the amount of code that we would need to change would be fairly minimal.

The simplicity of the MVVM pattern also makes WPF easier to comprehend. Knowing that each View has a View Model that provides it with all the data and functionality that it requires means that we always know where to look when we want to find where our data bound properties have been declared.

Is there a downside?

There are, however, a few drawbacks to using MVVM, and it will not help us in every situation. The main downside to implementing MVVM is that it adds a certain level of complexity to our applications. First, there's the data binding, which can take some time to master. Also, depending on your version of Visual Studio, data binding errors may only appear at runtime and can be very tricky to track down.

Then, there are the different ways to communicate between the Views and View Models that we need to understand. Commanding and handling events in an unusual way takes a while to get used to. Having to discover the optimal arrangement of all the required components in the code base also takes time. So, there is a steep learning curve to climb before we can become competent at implementing MVVM for sure. This book will cover all of these areas in detail and attempt to lessen the gradient of that learning curve.

However, even when we are well practiced at the pattern, there are still occasional situations when it wouldn't make sense to implement MVVM. One example would be if our application was going to be very small, it would be unlikely that we would want to have unit tests for it or swap out any of its components. It would, therefore, be impractical to go through the added complexity of implementing the pattern when the benefits of the Separation of Concerns that it provides were not required.

 

Debunking the myth about code behind


One of the great misconceptions about MVVM is that we should avoid putting any code into the code behind files of our Views. While there is some truth to this, it is certainly not true in all situations. If we think logically for a moment, we already know that the main reason to use MVVM is to take advantage of the Separation of Concerns that its architecture provides. Part of this separates the business functionality in the View Model from the user interface-related code in the Views. Therefore, the rule should really be we should avoid putting any business logic into the code behind files of our Views.

Keeping this in mind, let's look at what code we might want to put into the code behind file of a View. The most likely suspects would be some UI-related code, maybe handling a particular event, or launching a child window of some kind. In these cases, using the code behind file would be absolutely fine. We have no business-related code here, and so we have no need to separate it from the other UI-related code.

On the other hand, if we had written some business-related code in a View's code behind file, then how could we test it? In this case, we would have no way to separate this from the View, no longer have our Separation of Concerns and, therefore, would have broken our implementation of MVVM. So in cases like this, the myth is no longer a myth... it is good advice.

However, even in cases like this where we want to call some business-related code from a View, it is possible to achieve without breaking any rules. As long as our business code resides in a View Model, it can be tested through that View Model, so it's not so important where it is called from during runtime. Understanding that we can always access the View Model that is data bound to a View's DataContext property, let's look at this simple example:

private void Button_Click(object sender, RoutedEventArgs e) 
{ 
  UserViewModel viewModel = (UserViewModel)DataContext; 
  viewModel.PerformSomeAction(); 
} 

Now, there are some who would balk at this code example, as they correctly believe that Views should not know anything about their related View Models. This code effectively ties this View Model to this View. If we wanted to change the UI layer in our application at some point or have designers work on the View, then this code would cause us a problem. However, we need to be realistic... what is the likelihood that we will really need do that?

If it is likely, then we really shouldn't put this code into the code behind file and instead handle the event by wrapping it in an Attached Property, and we'll see an example of this in the next section. However, if it is not at all likely, then there is really no problem with putting it there. Let's follow rules when they make sense for us to follow them rather than blindly sticking to them because somebody in a different scenario said they were a good idea.

One other situation when we can ignore this 'No code behind' rule is when writing self-contained controls based on the UserControl class. In these cases, the code behind files are often used for defining Dependency Properties and/or handling UI events and for implementing general UI functionality. Remember though, if these controls are implementing some business-related functionality, we should write that into a View Model and call it from the control so that it can still be tested.

There is definitely perfect sense in the general idea of avoiding writing business-related code in the code behind files of our Views and we should always try to do so. However, we now hopefully understand the reasoning behind this idea and can use our logic to determine whether it is ok to do it or not in each particular case that may arise.

 

Learning how to communicate again


As we tend not to handle UI events directly, when using MVVM, we need alternative ways to implement the same functionality that they provide. Different methods are required to reproduce the functionality of different events. For example, the functionality of the SelectionChanged event of a collection control is typically reproduced by data binding a View Model property to the SelectedItem property of the collection control.

<ListBox ItemsSource="{Binding Items}" 
  SelectedItem="{Binding CurrentItem}" /> 

In this example, the setter of the CurrentItem property will get called by the WPF Framework each time a new item is selected from the ListBox. Therefore, instead of handling the SelectionChanged event in the code behind, we can call any method directly from the property setter in the View Model:

public TypeOfObject CurrentItem 
{ 
  get { return currentItem; } 
  set 
  { 
    currentItem = value; 
    DoSomethingWithTheNewlySelectedItem(currentItem); 
  } 
} 

Note that we need to keep any methods that we call from data bound property setters from doing too much, as the time that it takes to execute them could negatively affect the performance when entering data. However, in this example, we would typically use this method to start an asynchronous data access function using a value from the current item or alter the value of another property in the View Model.

Many other UI events can be replaced with some form of Trigger in the XAML markup directly. For example, imagine that we had an Image element that was set as the Content property value of a Button control and that we wanted the Image element to be semi-transparent when the Button was disabled. Instead of handling the UIElement.IsEnabledChanged event in the code behind file, we could write a DataTrigger in a Style that we could apply to the Image element:

<Style x:Key="ImageInButtonStyle" TargetType="{x:Type Image}"> 
  <Setter Property="Opacity" Value="1.0" /> 
  <Style.Triggers> 
    <DataTrigger Binding="{Binding IsEnabled,       
      RelativeSource={RelativeSource FindAncestor, 
      AncestorType={x:Type Button}}, FallbackValue=False}" 
      Value="False"> 
      <Setter Property="Opacity" Value="0.5" /> 
    </DataTrigger> 
  </Style.Triggers> 
</Style> 

Binding syntax will be covered in detail in Chapter 4, Becoming Proficient With Data Binding, but in short, the binding in this DataTrigger is specifying the target as the IsEnabled property of the ancestor (or parent) of the Image element with a type of Button. When this binding target has a value of False, the Opacity property of the Image will be set to 0.5 and set back to its original value when the target property value is True. Therefore, the Image element in our Button will become semi-transparent when the Button is disabled.

Introducing the ICommand interface

When it comes to button clicks in WPF and MVVM, instead of handling the well-known Click event, we typically use some form of command that implements the ICommand interface. Let's take a look at an example of a basic standard command:

using System; 
using System.Windows.Forms; 
using System.Windows.Input; 
 
public class TestCommand : ICommand 
{ 
  public event EventHandler CanExecuteChanged; 
 
  public void Execute(object parameter) 
  { 
    MessageBox.Show("You executed a command"); 
  } 
 
  public bool CanExecute(object parameter) 
  { 
    return true; 
  } 
}

Tip

Please note that in this book, we will display code with two-space tabs, instead of the more commonly used four-space tabs, in order to enable more characters of each code snippet to fit onto each line.

We can see that it has an Execute method, where the functionality that the command provides is performed. The CanExecute method is called by the CommandManager at various points over time, when it believes that the output value may have changed. We'll cover this in more detail later, but basically, raising the CanExecuteChanged event is one of the ways to trigger the CommandManager to do this. The output of the CanExecute method specifies whether the Execute method can be called or not.

You can imagine how cumbersome it would be if we had to create one of these classes for every action that we needed to implement. Furthermore, there is no context of where the command was called from other than the single command parameter. This means that if we wanted the command to add an item into a collection, we would have to put both the collection and the item to add into another object so that they could both be passed through the single input parameter.

When using MVVM, rather than implementing the commands in the standard way, we tend to use a single, reusable implementation that allows us to handle actions with standard methods directly in the View Model. This enables us to use commands without having to create a separate class for each one. There are a number of variations of this command, but its simplest form is shown here:

using System; 
using System.Windows.Input; 
 
public class ActionCommand : ICommand 
{ 
  private readonly Action<object> action; 
  private readonly Predicate<object> canExecute; 
 
  public ActionCommand(Action<object> action) : this(action, null) { } 
 
  public ActionCommand(Action<object> action, 
    Predicate<object> canExecute) 
  { 
    this.action = action; 
    this.canExecute = canExecute; 
  } 
 
  public event EventHandler CanExecuteChanged 
  { 
    add { CommandManager.RequerySuggested += value; } 
    remove { CommandManager.RequerySuggested -= value; } 
  } 
 
  public bool CanExecute(object parameter) 
  { 
    return canExecute == null ? true : canExecute(parameter); 
  } 
 
  public void Execute(object parameter) 
  { 
    action(parameter); 
  } 
} 

This is typically used in the View Model classes, as shown in the following example, where the command functionality comes from the Save method and the bool return value of the CanSave method determines whether the command can execute or not:

public ICommand SaveCommand 
{ 
  get { return new ActionCommand(action => Save(), 
    canExecute => CanSave()); } 
} 

A safer way to ensure that the command is never called by code when the CanExecute condition is not satisfied would be to make this alteration; however, please note that the CommandManager will always perform this check before calling any commands anyway:

public void Execute(object parameter) 
{ 
  if (CanExecute(parameter)) action(parameter); 
} 

Full credit for this custom command should go to Josh Smith, as his RelayCommand class was the first implementation like this that I came across, although there are several variations to be found online. The beauty of this particular implementation should not be underestimated. Not only is it simple, elegant, and saves us from writing large amounts of code, but it also makes testing our functionality much easier, as our command code can now be defined right in our View Models.

The action parameter of type Action<object> will hold the reference to the method that will be called when the command is executed and the object generic parameter relates to the optionally used command parameter. The canExecute parameter of type Predicate<object> will hold the reference to the method that will be called to verify whether the command can be executed or not and its object generic parameter relates to the optionally used command parameter again.

The CanExecuteChanged event should be raised whenever the canExecute parameter value changes. It is typically handled by command sources, such as the Button control, to set their IsEnabled property value appropriately. When a command source receives a notification that this event has been raised, it will call the ICommand.CanExecute method to check the new value. Therefore, when a command can execute, its data bound control will be enabled and when it can't, its data bound control will be disabled.

The CommandManager.RequerySuggested event will be raised when the CommandManager detects a change in the UI that could reflect on whether a command could execute or not. For example, this could be due to a user interaction, such as the selection of an item in a collection or some other change in focus. Therefore, connecting one to the other seems to be a logical thing to do. In fact, an example of this is actually found in the source code of the .NET RoutedCommand class. We'll look at this ActionCommand again and in more detail in Chapter 3, Writing Custom Application Frameworks, but for now, let's move on to the next method of communication.

Handling events in Attached Properties

There is one way to handle events in WPF without having to resort to writing code in the code behind file of a View. Using Attached Properties, we can encapsulate the handling of events and effectively expose their behavior using properties that we can data bind to in our Views. Let's take a look at a simple example using the PreviewKeyDown event:

public static DependencyProperty OnEnterKeyDownProperty =  
  DependencyProperty.RegisterAttached("OnEnterKeyDown", 
  typeof(ICommand), typeof(TextBoxProperties), 
  new PropertyMetadata(OnOnEnterKeyDownChanged)); 
 
public static ICommand GetOnEnterKeyDown(DependencyObject dependencyObject) 
{ 
  return (ICommand)dependencyObject.GetValue(OnEnterKeyDownProperty); 
} 
 
public static void SetOnEnterKeyDown(DependencyObject dependencyObject, 
  ICommand value) 
{ 
  dependencyObject.SetValue(OnEnterKeyDownProperty, value); 
} 
 
public static void OnOnEnterKeyDownChanged( 
  DependencyObject dependencyObject, 
  DependencyPropertyChangedEventArgs e) 
{ 
  TextBox textBox = (TextBox)dependencyObject; 
  if (e.OldValue == null && e.NewValue != null)  
    textBox.PreviewKeyDown += TextBox_OnEnterKeyDown; 
  else if (e.OldValue != null && e.NewValue == null)  
    textBox.PreviewKeyDown -= TextBox_OnEnterKeyDown; 
} 
 
private static void TextBox_OnEnterKeyDown(object sender, KeyEventArgs e) 
{ 
  if (e.Key == Key.Enter || e.Key == Key.Return) 
  { 
    TextBox textBox = sender as TextBox; 
    ICommand command = GetOnEnterKeyDown(textBox); 
    if (command != null && command.CanExecute(textBox))  
      command.Execute(textBox); 
  } 
} 

As can be seen in this example, the event is handled by attaching an event handler in the normal way, except that all relating code is encapsulated within the class that declares the Attached Property. Let's take a closer look. First, we declare an Attached Property named OnEnterKeyDown of type ICommand in a class named TextBoxProperties, and we pass a reference of our handling method to the PropertyChangedCallback delegate parameter of the PropertyMetadata constructor.

The GetOnEnterKeyDown and SetOnEnterKeyDown methods represent the normal way to get and set Attached Property values. In the unfortunately named OnOnEnterKeyDownChanged method, which will be called when the property value changes, we look at the NewValue and OldValue values of the DependencyPropertyChangedEventArgs input parameter in order to decide whether we need to attach or detach the event handler to the 'PreviewKeyDown event of the relevant TextBox.

If the OldValue value is null and the NewValue value is not null, it means that there was no previous value, and so the property is being set for the first time. In this case, we want to attach the event handler. Conversely, when the OldValue value is not null and the NewValue value is null, it means that there previously was a value, which has been removed, so we should detach the event handler.

Finally, the TextBox_OnEnterKeyDown event handling method first detects whether either the Enter key or the Return key were pressed. If one was pressed, the data bound ICommand instance is checked for null and if the command can execute, it is then executed. Therefore, we have effectively wrapped a PreviewKeyDown event in an Attached Property and can now execute any command that has been data bound to it when the user presses Enter on their keyboard.

In order to use this Attached Property, we must first add an XML namespace prefix for our Attached folder in the XAML file of the View that this functionality is required in. Note that the TextBoxProperties class will be declared in the Attached folder of the Views project and so, its namespace will be as follows:

xmlns:Attached="clr-namespace:CompanyName.ApplicationName.Views.Attached;  
  assembly=CompanyName.ApplicationName.Views" 

Microsoft's convention for naming these prefixes is for the first character to be a lowercase letter, but it has always made more sense to me to simply use the last segment of the declared namespace, which will start with a capital letter. Once you have defined the prefix, you can use the Attached Property, as shown in the following example:

<TextBox Attached:TextBoxProperties.OnEnterKeyDown="{Binding Command}" /> 

Any UI events that we might need to handle in our applications can be encapsulated in Attached Properties in this same way. At first, this might seem to be a complicated way to handle events, compared with having a simple handler in a code behind file, but once we have a collection of these properties declared, we will find ourselves having to create fewer and fewer new ones. Think of them as simply being a reusable way of converting events into properties.

Making use of delegates

Delegates are very similar to events and, in fact, events can be thought of as a particular kind of delegate. They are a very simple tool to use to pass a signal or message from one place to another in the program. Unlike when creating custom events, we are not forced to use particular input parameters, for example, some form of the EventArgs class. We are totally unconstrained when creating custom delegates and are able to define our own method signatures, including both input and output parameter types.

As most of you will already be familiar with events and event handling, you'll already inadvertently know how to use delegates too. Let's look at a simple example. Imagine that we have a parent View Model that spawns child View Models and that one of these child View Models is paired with a View that enables administrative users to select security permissions. Now, let's imagine that the parent View that relates to the parent View Model has a menu that needs to be updated depending on the user's selection in the child View. How do we notify the parent View Model upon selection?

This is where delegates save the day. Keeping this example simple initially, let's say that we just need to notify the parent View Model that a particular change has been made so that it can refresh the current user's security permissions from a database. In this case, we only need to pass a signal, so we can create a delegate with no input or output parameters. We can declare it in the View Model that will be sending the signal, in this case, the child View Model.

public delegate void Signal(); 

Note that we define it in the same way that we define an abstract method, except that the abstract keyword is replaced with the delegate keyword after the access modifier. In short, a delegate defines a type that references a method with a particular signature. Now that we have defined our signaling delegate, we need to create a way for elements outside the View Model to use it. For this, we can simply create a property of the type of our new delegate in the same View Model:

public Signal OnSecurityPermissionChanged { get; set; }  

As we don't need any property change notifications for this property, we can save ourselves some typing and take advantage of the .NET Auto-Implemented Property syntax. Bear in mind that delegates work in a multicast way like events, meaning that we can attach more than one handler to each one. In order to do this, we need to use the += operator to add handlers for the delegate, and in this example, we would want to do that in the parent View Model when the child View is instantiated:

ChildViewModel viewModel = new ChildViewModel(); 
viewModel.OnSecurityPermissionChanged += RefreshSecurityPermissions; 

Here, we have assigned the RefreshSecurityPermissions method in the parent View Model to be the handler for this delegate. Note that we omit the parenthesis and the input parameters if there were any when attaching the handler. Now, you may be wondering, "What does the method signature of this handler look like?", but you already have the answer to this. If you remember, we declared the delegate with the signature of the method that we want to handle it. Therefore, any method that shares this signature can be a handler for this type of delegate:

private void RefreshSecurityPermissions() 
{ 
  // Refresh user's security permissions when alerted by the signal 
} 

Note that the name used is irrelevant and all that matters when matching the delegate signature are the input and output parameters. So, we now have our delegate declared and hooked up to a handler in the parent View Model, but it's still not going to do anything because we haven't actually called it yet. In our example, it's the child View Model that is going to call the delegate because that's the object that needs to send out the information, or signal in this case.

When calling delegates, we must always remember to check for null before trying to use them because there may be no handlers attached. In our example, we'd call our Signal delegate via the OnSecurityPermissionChanged property at the point that we need to send the signal from the child View Model, let's say in reaction to a user changing their own security permissions:

if (OnSecurityPermissionChanged != null) OnSecurityPermissionChanged(); 

Alternatively, we could do so using the more concise null conditional operator in C# version 6.0, which calls the delegate's Invoke method if it is not null:

OnSecurityPermissionChanged?.Invoke(); 

Note that we do need to include the parenthesis when calling the delegate in the first example even though OnSecurityPermissionChanged is a property. This is because the delegate type of the property relates to a method and it is that method that we are calling. Please bear in mind that the first of these methods is not thread safe, so if thread safety is important for your application, then you will need to use the latter way.

We now have the complete picture, but while it is common to have a signal-sending delegate such as this, it is not overly useful because it only passes a signal with no other information. In many real-world scenarios, we would typically want to have some sort of input parameter so that we could pass some information, rather than just a signal.

For example, if we wanted to be notified with details each time a user selected a different item from a collection control in the UI, we could add a CurrentItem property into a generic BaseCollection class in our application and data bind it to the data bound collection control's SelectedItem property. This CurrentItem property would then be called by the WPF Framework each time a user makes a new selection, and so we can call our new delegate from its property setter:

protected T currentItem; 
 
public virtual CurrentItemChange CurrentItemChanged { get; set; }  
 
public virtual T CurrentItem 
{ 
  get { return currentItem; } 
  set 
  { 
    T oldCurrentItem = currentItem; 
    currentItem = value; 
    CurrentItemChanged?.Invoke(oldCurrentItem, currentItem); 
    NotifyPropertyChanged(); 
  } 
} 

Delegates can be used to communicate between any related classes as long as they have access to the class that exposes the delegate so that they can attach a handler. They are commonly used to send information between child Views or View Models and their parents, or even between Views and View Models, but they can also be used to pass data between any two connected parts of the application.

 

Structuring the application code base


Now that we have a better understanding of the MVVM pattern, let's look at how we might implement it in a WPF application. What should the folder structure of our application be like? Clearly, we'll need somewhere to put our Models, Views, and View Models; however, how we arrange them will somewhat depend on the overall size of our application.

As we have heard, very small projects do not really suit MVVM because implementing it can involve a lot of preparation and often, the benefits do not apply. For small WPF applications, we would typically have just one project in our WPF application. In these cases, our classes would be separated into different folders within the single project.

With larger scale applications, we arrange our classes in the same basic structure, but as there are more classes and more chance that we want to reuse some of this code, it makes sense to use separate projects instead of folders. Either way, our classes should end up with the same CLR namespaces, as they tend to follow the structure of the application, regardless of whether those classes were separated using folders or projects.

While the CLR namespace in our startup project might be something along the lines of CompanyName.ApplicationName, the namespace of the classes in the Models component would be, or start with, CompanyName.ApplicationName.Models. For the purpose of the remainder of this book, we will assume that we are dealing with a large-scale WPF application and using projects for the separation of our classes.

There is nothing in the MVVM pattern that dictates what structure our code base should have, although there are clues. We will clearly need Views and ViewModels projects, but the Models project is less clearly defined. There are several elements within the Models component of MVVM, but we don't necessarily want to group them all into a single project in our code base. There are other projects that will be required too.

Let's visualize some possible structures so that we can get started with building our application.

These examples offer an insight into what the project structure of an MVVM-based WPF application might look like. However, nothing is set in stone and we are free to rename and to reorganize our application projects as we see fit. The important thing is how the components are connected together rather than the arrangement of the application files.

After we have developed a number of WPF applications, we get a feel for which project names and which structure we prefer, so I'd suggest trying a few variations and seeing which you feel more comfortable working with. Of course, some of us may not have the luxury of being able to create or alter the structure of the application that we work on. Let's first focus on the projects common to both example structures.

We see that the Images and Resources folders reside in the startup project. While this is customary, they can technically reside in any project or even in their own project. However, I prefer to keep them in this project because it provides a marginal performance benefit. Typically, when using MVVM, the only other files in the startup project will be the MainWindow.xaml, App.xaml (and their constituent code behind files), and app.config files.

The Images folder contains the images and icons that are displayed in the UI controls, whereas the Resources folder normally contains any resource files, such as XML schemas or text or data files that are used by the application.

The next project is named Converters and is fairly self-explanatory. It only contains classes that have implemented the IValueConverter interface and are used for converting data bound values in the Views. These classes are all reusable and the DLL from this project should be kept up to date and shared amongst our other applications.

Both examples show an Extensions project, but this is entirely optional and not a requirement of the MVVM pattern. I just happen to find Extension methods to be an essential part of .NET development, having built up a large collection of invaluable helper methods. After getting used to being able to call Add on an IEnumerable instance or ToObservableCollection on a query result for example, I now reuse them in every application. We'll see some examples of these in Chapter 3 , Writing Custom Application Frameworks, Chapter 8, Implementing Responsive Data Validation, and Chapter 9, Completing That Great User Experience.

The next common project that we can see is a project called Managers. Others may prefer to call this Engines, Services, or something similar, but that is just a personal preference, and either way, the content will be the same. In this project, we typically find a number of classes that together provide a wide variety of functionality to the View Models. For example, in this project, we might find classes named ExportManager, FeedbackManager, HardDriveManager, WindowManager, and so on.

It is important to have a project like this, where we have one common place to provide all of the required specialized functionality for our application, rather than having to repeat the code in each View Model that requires that certain functionality. These classes are totally reusable between applications and this arrangement also promotes behavioral consistency throughout the application.

For example, without consolidating all of our functionality in this project, we might be tempted to copy and paste certain bits of code from one View Model to another. If the code then requires a change in the future, we may not remember that it has been copied and only update it in one View Model, thereby breaking the consistency of the application.

Another benefit to utilizing a project like this is that it reduces the number of references that the other projects need. The Managers project will typically require many references to be added, whereas the View Model and other classes that make use of its functionality will only need to add a single reference to this project.

Some or all of the functionality from these classes can be exposed through a BaseViewModel class and can therefore be made available to every View Model. We'll see more about this in Chapter 3, Writing Custom Application Frameworks, but for now, let's start to look at the differences between the two structures.

In the first structure example, the Business folder within the Models project simply represents the business data models of the application. There's no real need to have these classes in a separate Business folder other than the fact that it highlights that they are connected with the ViewModels.Business View Models and the Views.Business Views.

Technically, the data model classes in our application should represent our business objects and not contain any properties that bear no relevance to the business model, such as properties named CurrentItem or IsSelected. If this were the case and they were defined in their own project, as shown in the first example, then we could reuse their DLL in our other business applications. Alternatively, perhaps we already have a DLL representing the business model from another application that we will be reusing in the next application.

In either of these cases, we would need to add other folders into the ViewModels project in which we would implement an additional View Model class for each business model class to be displayed. This arrangement is shown in the ViewModels.Business folder of the first example and demonstrates the separation of the data model from the Views.

In these classes, we would encapsulate each public business model property in a new property that raised change notification and add any further properties required by the UI. It would look similar to the following example, where the BaseBusinessViewModel class simply implements the INotifyPropertyChanged interface:

using System;

namespace CompanyName.ApplicationName.Models.Business
{
  public class User
  {
    public User(Guid id, string name, int age)
    {
      Id = id;
      Name = name;
      Age = age;
    }

    public Guid Id { get; set; }

    public string Name { get; set; }

    public int Age { get; set; }
  }
}

using System;
using CompanyName.ApplicationName.Models.Business;

namespace CompanyName.ApplicationName.ViewModels.Business
{
  public class UserViewModel : BaseBusinessViewModel
  {
    private User model;
    private bool isSelected = false;

    public UserViewModel(User model)
    {
      Model = model;
    }

    public User Model
    {
      get { return model; }
      set { model = value; NotifyPropertyChanged(); }
    }

    public Guid Id
    {
      get { return Model.Id; }
      set { Model.Id = value; NotifyPropertyChanged(); }
    }

    public string Name
    {
      get { return Model.Name; }
      set { Model.Name = value; NotifyPropertyChanged(); }
    }

    public int Age
    {
      get { return Model.Age; }
      set { Model.Age = value; NotifyPropertyChanged(); }
    }

    public bool IsSelected
    {
      get { return isSelected; }
      set { isSelected = value; NotifyPropertyChanged(); }
    }
  }  
} 

When implementing this pattern, after each data object was loaded from the data source, it would need to be wrapped in one of these View Model classes before being displayed in the UI:

User user = new User(Guid.NewGuid(), "John Smith", 25);
UserViewModel userViewModel = new UserViewModel(user); 

Following the pattern in the first example structure through to the Views project, we see that it also contains a Business folder. Typically, we could find a small, individual object-sized View there for each of these business model-related View Models. However, in the vast majority of applications, this additional level of separation between the business model and the UI is simply unrequired. Also, following this pattern adds a small overhead to all implementation and data access times.

For some, a viable alternative would be to simply add the properties and property change notification required by the UI straight into the data model classes. If we don't need this separation, then there is little point in writing all of the extra code. I am a great fan of Agile practices and one of the twelve principles from the Manifesto for Agile Software Development summarizes this point perfectly:

"Simplicity--the art of maximizing the amount of work not done--is essential"

This simpler, alternative implementation is shown in the DataModels project of the second example, where the business model classes are combined with the UI-related properties along with the business rules or validation logic.

In other types of applications, you may find a separate validation layer that sits between the DAL and the code behind the UI layer. As we'll see in Chapter 8, Implementing Responsive Data Validation, with WPF, we can build validation right into the business classes, along with the properties that they are validating.

This DataModels project contains a number of sub-folders, grouping similar types of classes together. The Collections folder typically contains an extension of the ObservableCollection<T> class for each data model class in the application. The Enums folder is also often well used in most WPF applications, as enumerations are great to use when data bound to either radio buttons or checkboxes.

The interfaces found in the Interfaces folder are essential to enable the functionality of the base classes, as we'll see in Chapter 3, Writing Custom Application Frameworks. If we're likely to use a large number of delegates in our application, then it also makes sense to organize them into a separate Delegates folder as well. Otherwise, if a delegate is strongly tied to a particular class, they can just be declared locally in the classes that will be raising them.

One other alternative would be to have a single class in the Models project that encapsulates all of the application delegates, although this would require prefixing the name of this class to the delegate names when using them, for example, Delegates.CloseRequest. Declaring each delegate in the class that uses them enables us to reference them directly, for example, CloseRequest.

The data model classes in this project could be thought of as View Models too, although View Models that only serve the display of individual objects, as opposed to those that serve the main application Views. They would have a base class that implements the INotifyPropertyChanged interface like the main View Models, but then it would also typically implement a validation error interface too.

They also differ from the main application View Models because they generally provide no functionality other than validation to their associated Views. We can think of these classes as mere data containers with a few extra properties to enable effective communication with the UI.

When following this structure, we can render these individual object-sized View Models in the UI using data templates, so we generally don't need to declare a separate View for each of them. Furthermore, we may want to display the same objects differently in different parts of the application, or even switch their display in response to some user action and that is also easier to accomplish with data templates.

This explains why these objects do not reside in the View Models project along with the main application View Models. If you remember, each View Model should only have one associated View. For the purpose of this book, this simpler, alternative implementation is the pattern that we will be following. Now, let's continue by investigating the DAL of the application.

The DataProviders project from the first example is responsible for providing access to the persisted data source of the application. Another commonly used name is Repositories, but again, you can call it what you like. The important thing is that it has an essential Interfaces folder that contains one or more interfaces that form the connection between the data source(s) and the rest of the application.

The DataProviders and Interfaces folders in the second example appear within the Models project, but they have the same responsibilities. Either way, it is through the use of these interfaces that we are able to disconnect the data source and replace it with a mock source of some kind when testing. We will look at an example of this in Chapter 3, Writing Custom Application Frameworks, but for now, let's continue.

The ViewModels project is fairly easy to understand, as it just contains View Models. You may be wondering why there is a Commands folder inside it. If we were using commands in the old fashioned way, writing a separate class for each command, then we could end up with a great many classes and that would probably warrant putting them into their own project.

However, if you remember, we will be using only one single command, the ActionCommand. As this will be used by the View Model classes alone, it makes sense to include it in their project. We've already covered the differences in the View Models and Views projects between the two example structures, so let's finish off looking at the remaining common parts.

We often find an Attached folder in the Views project that contains the Attached Properties that are used in the application. As these classes contain View-related code and are only used by the Views, it is logical that they should reside here. Alongside that, we see the Controls folder where we find reusable user controls and/or custom controls, such as a custom textbox that spawns a child window to help with editing when clicked or a custom clock face that can be used to enter a time.

At the bottom of both example structures, we see the test projects that contain the code that tests our application. If your application needs testing, this is a good pattern to follow. By prefixing the name of the projects that we will be testing with a Test domain, they will all appear in the Visual Studio Solution Explorer in one group, either above or below the other projects, and in the same order as the projects being tested.

The Mocks project typically hosts the application objects to be used while testing the application. This would normally include any mock data generation or provider classes and mock Manager classes. We may need to create these mock Manager classes if we don't want to use expensive resources while testing, or in case they access any UI elements that we also want to avoid when testing. Let's take a look at an example of one possible method of a UiThreadManager class:

public Task RunAsynchronously(Action method) 
{ 
  return Task.Run(method); 
} 

This method is fairly straightforward and enables us to pass a reference to any method that we want to run asynchronously. It simply passes the method reference to the Task.Run method and lets it do its thing. It can be called like this:

UiThreadManager.RunAsynchronously(() => GenerateReports()); 

However, running code asynchronously in unit tests can have unpredictable results that may make them fail. Therefore, when testing, we need to use a MockUiThreadManager class and implement its RunAsynchronously method, as follows:

public Task RunAsynchronously(Action method) 
{ 
  Task task = new Task(method); 
  task.RunSynchronously(); 
  return task; 
} 

In this method, we can see that we use the RunSynchronously method of the Task class to run the referenced method synchronously, or in other words, immediately and on the same thread. In effect, this simply bypasses the functionality of the original method. Using these mock objects enable us to run different code while testing than we do at runtime. We'll see more examples of these mock objects in Chapter 3, Writing Custom Application Frameworks, but let's first take a look back at what we have covered so far.

 

Summary


In this chapter, we have discovered what the MVVM architectural pattern is and the benefits of using it when developing WPF applications. We're now in a better position to decide which applications to use it with and which not to. We started looking into the various new ways of communicating between the various components of this pattern and also investigated the most common ways of organizing our source code. We are now ready to start setting out our own application structures.

In the next chapter, before we properly get started building our application, we'll look at several methods of the sometimes tricky task of debugging our data bound values. We'll discover other useful tips and tricks that we can use to help us to iron out any problems that may occur in our applications so that once we start building, we'll be able to avoid wasting time with problems that may arise.

About the Author

  • Sheridan Yuen

    Sheridan Yuen is a Microsoft .NET MCTS and Oracle Java SCJP certified software developer, living in London, England. His passion for coding made him stand out from the crowd right from the start. From his second year onward at university, he was employed to be a teaching assistant for the first year student coding workshops and has since returned as a guest lecturer.

    Among other prestigious positions, he was the primary software developer for the Ministry of Sound group for four years, working on their main music business application, responsible for creating their multi award winning albums. This application managed to increase its users’ productivity by up to 80% in some cases.

    In addition to this, he architected a unique ticket scanning application for their award winning nightclub, making it the first club in the world to introduce scanned ticket entry across all streams for their clients. Coming from a musical background and being a qualified audio engineer, with experience of record production and digital audio, this post was a perfect union.

    He soon became a popular figure in the C# and WPF sections of the Stack Overflow, “question and answer” website, being awarded enough reputation by the community members to raise him to well within the top half percent of all users. While authoring this book and other projects have kept him away for some time, he is keen to return to continue to help new users to get to grips with WPF.

    Browse publications by this author

Latest Reviews

(4 reviews total)
I’ve rated this a 5 because it was easy to purchase online, I got a great price, and the content looks like it covers a wide range of topics that will be helpful. I haven’t actually started using the book yet, though.
Fedup of being nagged by feefo EVERYTIME I purchase something from Packt - Love the products BUT STOP SPAMMING ME for FEEDBACK
For advanced readers only! Goes deep into the consideration of various topics and describes them in a not too technological language. Helped me understand the subject a lot.

Recommended For You

Book Title
Unlock this book and the full library for FREE
Start free trial