Book Image

Mastering Windows Presentation Foundation

By : Sheridan Yuen
Book Image

Mastering Windows Presentation Foundation

By: Sheridan Yuen

Overview of 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.
Table of Contents (19 chapters)
Mastering Windows Presentation Foundation
Credits
About the Author
About the Reviewer
www.PacktPub.com
Customer Feedback
Preface

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.