Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
MVVM Survival Guide for Enterprise Architectures in Silverlight and WPF
MVVM Survival Guide for Enterprise Architectures in Silverlight and WPF

MVVM Survival Guide for Enterprise Architectures in Silverlight and WPF: If you're using Silverlight and WPF, then employing the MVVM pattern can make a powerful difference to your projects, reducing code and bugs in one. This book is an invaluable resource for serious developers.

eBook
$9.99 $32.99
Paperback
$54.99
Subscription
Free Trial
Renews at $19.99p/m

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Table of content icon View table of contents Preview book icon Preview Book

MVVM Survival Guide for Enterprise Architectures in Silverlight and WPF

Chapter 1. Presentation Patterns

By Ryan Vice

Separation of Concerns or SoC is a core principle of enterprise software development which provides many benefits and has been a key driving force behind many presentation (or UI) design patterns that have emerged over the last 30 years. In the arena of Silverlight and WPF development, Model View View Model or MVVM has quickly become the de-facto pattern for achieving SoC in UIs. However, this pattern often leaves developers and architects frustrated and at the time of this writing, can be difficult to implement in an effective way that provides more benefits than some of the older, more familiar presentation patterns (MVC, MVP, and so on).

In this chapter we will cover the evolution of presentational patterns along with the problems that are solved by each pattern along the evolutionary path. We will also dive into the shortcomings of each pattern which led to the next pattern in the evolution and will finish this chapter ready to look at MVVM.

We will begin this chapter by reviewing the functionality of the Project Billing sample application that we will use throughout this book. We will follow this by briefly talking about the various types of state that must be managed in UI applications. Then we dive into the history of presentational patterns and as we go through the history we will implement Project Billing using each pattern to show you explicitly the benefits and the shortcomings of each pattern that lead to the next pattern in the evolution. This will help you understand why you'd want to use MVVM through examples and make the benefits of MVVM easier to appreciate when we dive into that topic in the next chapter. This would also help you evangelize the pattern on your projects if needed and be able to explain what benefits MVVM would offer over other presentation patterns.

If you are already familiar with (or not interested in) the history of presentation patterns, you should still at a minimum review the following sections:

  • The Project Billing sample application: This section will review the functionality of the sample application that will be used in the first two chapters

  • Types of state: This section defines and discusses the various types of state that need to be managed in a UI application

  • Monolithic design: The introduction of this section discusses the coupling that results from not using some kind of presentational design pattern

    • The problems with Monolithic design: This section discusses the many problems that result from not using presentational design patterns

  • Data service stub: This section covers creating the data service stub that will be used by the Project Billing application throughout this book

  • Memory leaks: This section covers how .NET events can cause memory leaks

However, I'd recommend that unless you are intimately familiar with patterns such as Model 2 and Passive View that you take the time to go through this chapter as this knowledge will be very useful in driving home some of the fundamentals of presentation patterns which will help you adapt these notoriously flexible patterns to your needs

The Project Billing sample application


Let's start off by walking through the functionality of the Project Billing application. Project Billing is a contrived application that—as the name suggests—allows for simple project billing. The application's UI is shown in the following screenshot:

The application consists of a simple master/details form for the main window. At the top of the application is a list of projects that when selected make up the master of the master/detail relationship. Following the projects come the details which include the following:

  • Estimated Cost

  • Actual Cost

Notice how all the details are disabled along with the Update button. Whenever a user selects a project from the list, the UI is updated so that all of the details controls are enabled as shown in the following screenshot:

Now a user can update any of the details they like. If the user sets a value for Actual Cost that is lower than the Estimated Cost for the selected project and clicks the Update button, the Estimated Cost will be displayed in green.

Note

The following screenshot shows Project Billing with an Actual Cost that is lower than the Estimated Cost; however, this book is not in color and so you will have to run any of the sample implementations of Project Billing in this book to see the color of estimated cost change.

Note

This is a contrived example and doesn't have validations or robust error handling, so entering invalid values for actual cost can cause problems for the application. However, we will explore validations later in this book.

Putting in a value that is above the estimated value will cause the Estimated Cost to be displayed in red. You can also:

  • Change the Estimated Cost.

  • Click on the Update button, then change your selection and when you reselect the updated project you will see that your new values have been maintained in the view state.

  • After updating a project, you can also open a second Projects view and see that the data is synchronized (session state). This is not supported in all versions of Project Billing but only in those versions whose architecture supports easily sharing session state.

It's a very simple example but complex enough to demonstrate the various types of state and logic that need to be managed by a UI application and to show how well the various patterns handle each type of state and logic.

Types of state

The Project Billing application demonstrates all three types of state that must be managed in all UI applications.

  • View state: UI state or view state is the state of the UI which includes the data being displayed that was provided by the model but could also include things like what buttons are disabled and the color changes that may have been applied to text. The disabling of the details controls and changing the color of Estimated Cost in Project Billing are examples of types of view state.

    Note

    You may be familiar with the concept of view state from working in ASP.NET where the view state is stored in a hidden field in the HTML and accessible server-side via the ViewState collection.

  • Session state: It is the state of the data that has been retrieved from the persistence store and is being held in memory. This data could be accessed by multiple components in the application and remains in memory only until the user terminates their session or until it is persisted. In Project Billing, any changes that are made to project details become session state once you click on the Update button.

  • Persisted state: It is the state of the applications data that has been retrieved from or is persisted to some sort of repository such as a database, service or XML file. In Project Billing, the data that is mocked in the DataService is an example of persisted state.

    Note

    Project Billing uses a data service stub that returns fake data and doesn't demonstrate real persistence. Persistence will be covered in Chapter 3, Northwind—Foundations.

History of presentational patterns


In this section we will cover the history of presentational (or GUI) patterns. Presentational patterns have been around for over 30 years and a full coverage of all the various patterns is outside of the scope of this book. We will instead focus on two of the major trends that have emerged over the last 30 years and look at how those two trends eventually evolved to MVVM for Silverlight and WPF.

Note

If you are interested in learning more about the history of presentational patterns than what is covered here, then see Martin Fowler's article GUI Architectures (http://martinfowler.com/eaaDev/uiArchs.html).

Monolithic design

Enterprise applications deal with displaying, manipulating, and saving data. If we build enterprise applications with no design so that each GUI component is coupled all the way down to the data access code, then there are a lot of problems that can emerge.

This style of design is called monolithic and the following diagram shows the coupling that exists under monolithic designs:

The problems with monolithic design

In this section we will review the problems caused by the tight coupling and low cohesion found in monolithic designs.

Code maintenance

Looking at the previous screenshot if you assume that UI Widget1 and UI Widgetn are using the same business logic, then using a monolithic design will cause code duplication. Every time a change needs to be made to the business logic, it would need to be made in both places. This is the type of issue that is solved by SoC and one of the motivators for design paradigms like 3-tier which we will look at in the Layered design section later in this chapter.

Code structure

Not having the code structured into reusable components and well-organized layers makes things like sharing session state difficult under monolithic design. As you will see in the examples that follow, once we move to MVC and MVP, there are many benefits including:

  • The session state becomes much easier to manage and share

  • Code is easier to reuse

  • Code is well-organized and easier to understand and maintain

  • Code scales easier as you can build components into separate DLLs for distributed deployment

  • Code is more extensible as you can replace components to provide different behaviors

Code testability

Creating code that can be effectively tested with unit tests requires designing for testability. The monolithic approach poses several problems for code testability including:

  • Poor isolation of tests: One of the core principles of unit testing is isolation of the tests. You want your unit tests to test one scenario of one method of one class and not to test the dependencies. Following this principle makes your tests more valuable because when a test fails it's more likely that developers who didn't write the test but introduced the change that broke the test will fix the issue. This is because it will be very easy for the developer to determine what the problem was that broke the test because it's so isolated and clear in its purpose. A big part of getting return on investment from unit tests comes from making them easy for developers to use and avoid making your unit tests high maintenance. With high-maintenance unit tests the developers might just delete, disable, or comment out the test instead of fixing the problem, which makes the expense that was put into creating the test a waste.

  • Testing the UI is difficult: Using automated testing to test the UI is notoriously difficult. Monolithic design makes this problem worse as there is no separation between the UI and the rest of the layers of logic. One of the major contributors to the need of separated UI patterns is the desire to move as much logic as possible out of the UI and into separate testable components.

  • Poor code coverage: Code coverage refers to how much of your code is covered by unit tests. Generally speaking, the more code you have covered by tests, the more stability you will create in your development process, and the more benefits you will reap from your tests. High code coverage provides fewer bugs and quicker refactoring times. When you create a monolithic application, it affects your ability to achieve high code coverage levels, because you can't test the UI logic and the coupling between the various layers as it makes mocking dependencies difficult, prohibiting creation of unit tests.

    Note

    100 percent test coverage is not always the best level of coverage as too much coverage can make the code brittle to change and make the code high maintenance. My general rule of thumb is that I want to test the functionality that is defined by the public interface of the class under test. Testing internal details that could change can provide more inconvenience than benefit. However, this rule of thumb assumes that you have a good separation of concerns and have applied the Single Responsibility Principle to the design of your application. Single Responsibility Principle is part of the SOLID design principles and more details about SOLID are easily found online if needed.

Data service stub

We will be using a data service stub as part of our data layer to take the place of a real data service in our sample applications so that we can focus on presentation patterns and not on data access patterns and techniques.

Note

Data layer will be explained in the Layered design section later in this chapter.

Let's start by creating a new Class Library project called ProjectBilling.DataAccess in a solution called MVVM Survival Guide as shown in following screenshot:

Now delete the Class1.cs file that is created by default by the project template and add a new class called Project and add the following code to Project.cs:

namespace ProjectBilling.DataAccess
{
    public interface IProject
    {
        int ID { get; set; }
        string Name { get; set; }
        double Estimate { get; set; }
        double Actual { get; set; }
        void Update(IProject project);
    }
 
    public class Project : IProject
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public double Estimate { get; set; }
        public double Actual { get; set; }
        
        public void Update(IProject project)
        {
            Name = project.Name;
            Estimate = project.Estimate;
            Actual = project.Actual;
        }
    }
}

Note

There are certainly better options than using an interface with an update method to allow for updating data objects but this approach will allow us to keep the code in this chapter and the next concise and allow keep our focus on the topic at hand.

Project is a simple domain object (or business object) that stores the project name, estimated cost, and actual cost. It's implemented off an interface to provide more flexibility and better testability and it provides an update method to make it easy to update an instance's values.

Now we will create the data service stub that will return fake data for our various clients to consume so that we don't have to be concerned with data access patterns and techniques and can instead focus on presentation patterns. Add a class to the project called DataService and add the code that follows to DataService.cs.

This class exposes one method called GetProjects(), which creates three projects and then returns them as a IList<Project>. We have implemented our data service stub based on an interface to support dependency injection.

Note

Dependency injection is a pattern where a dependency is allowed to be specified by an external component instead of being created internally. This pattern will be covered in more detail in Chapter 6, Northwind—Hierarchical View Model and IoC.

using System.Collections.Generic;

namespace ProjectBilling.DataAccess
{
    public interface IDataService
    {
        IList<Project> GetProjects();
    }

    public class DataServiceStub : IDataService
    {
        public IList<Project> GetProjects()
        {
            List<Project> projects = new List<Project>()
                {
                    new Project()
                    {
                        ID = 0,
                        Name = "Halloway",
                        Estimate = 500
                    },
                    new Project()
                    {
                        ID = 1,
                        Name = "Jones",
                        Estimate = 1500
                    },
                    new Project()
                    {
                        ID = 2,
                        Name = "Smith",
                        Estimate = 2000
                    }
                };

            return projects;
        }
    }
}

Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.PacktPub.com . If you purchased this book elsewhere, you can visit http://www.PacktPub.com/support and register to have the files e-mailed directly to you.

This will allow us the flexibility to provide different implementations depending on the context. In a unit test we can provide a testing fake (stub or mock), in blend we can return a stub that returns design-time data and at runtime we can provide a real data service that returns real data. We will look into all of these techniques and also the use of inversion of control frameworks that make this process easier later in this book.

Monolithic Project Billing sample


Let's go ahead and walk through a simple implementation in WPF of the Project Billing application that was introduced at the beginning of this chapter. We will create the UI using a monolithic style.

Note

This will be a WPF application but we are not using RAD (Rapid Application Development) support available in Visual Studio, XAML or WPF project templates as it better demonstrates the monolithic style. If you are not familiar with writing code only WPF applications in this style and want to learn more then see Applications = Code + Markup: A Guide to the Microsoft Windows Presentation Foundation, by Charles Petzold.

Start by creating a solution and then adding a new Console Application project named ProjectBilling.Monolithic to your solution, as shown in the following screenshot:

Note

We will convert this console application to a Windows application later in this section but it's not necessary to do so as you can run a WPF application from a console application. Full details are coming later in this section.

Now add a reference to the PresentationFramework, PresentationCore, System.Xaml, and WindowsBase assemblies, as shown in the following screenshot:

Note

The previous screenshot only shows adding a reference to PresentationFramework. Repeat this process for PresentationCore, System.Xaml, and WindowsBase as well.

Now add a project reference to ProjectBilling.DataAccess, as shown in the following screenshot:

Next, delete Program.cs and add a new class named ProjectsView and add the following code to that file.

Note

Using data service means that technically we are not implementing a monolith as we are introducing a data access layer. This is done to keep the code as short as possible. Keep in mind that a purely monolithic application would not have a separate data access layer. The variation of monolithic design that we are implementing here is commonly referred to as autonomous view.

ProjectsView

The heart of this application is the ProjectsView class. Let's start by making this class a window and bringing in the namespaces we need.

using System;
using System.Windows;
using System.Windows.Controls;
using ProjectBilling.DataAccess;
using System.Windows.Media;

namespace ProjectBilling.UI.Monolithic
{
    sealed class ProjectsView : Window
    {
 
    }
}

This class now derives from System.Windows.Window, which is what allows it to be displayed as a WPF application. Add a main function to ProjectsView as follows:

[STAThread]
static void Main(string[] args)
{
    ProjectsView mainWindow
        = new ProjectsView();
    new Application().Run(mainWindow);
}

The main function is given the STAThread attribute—which makes it run in a single threaded apartment—which is a requirement of WPF and for interoperability with COM (Component Object Model). The main function simply creates a ProjectsView and then passes it to System.Windows.Application.Run(), which initializes WPF, starts a message loop, and then displays ProjectsView as the application's main window.

Initialization

Most of the work of the application will be done by the ProjectsView constructor and field initializers. Add the following fields to the class:

private static readonly Thickness _margin 
    = new Thickness(5);
private readonly ComboBox _projectsComboBox 
    = new ComboBox() { Margin = _margin };
private readonly TextBox _estimateTextBox 
    = new TextBox() 
        { IsEnabled = false, Margin = _margin };
private readonly TextBox _actualTextBox 
    = new TextBox()
        { IsEnabled = false, Margin = _margin };
private readonly Button _updateButton = new Button()
    { 
        IsEnabled = false, 
        Content = "Update",
        Margin = _margin
    };

Here we've created the Project combobox, Estimated Cost and Actual Cost textboxes in addition to the Update button.

Next let's add a constructor with the following code. We'll start by setting the Title and size of the MonolithicProjectBillingWindow instance. We will then call two helper methods that will be covered shortly and also add an event handler for the updateButton.Click event.

Note

This event handler will allow the code to be notified of user input via .NET's built-in support for the Observer pattern that is implemented by .NET events.

public ProjectsView()
{
    Title = "Project";
    Width = 250;
    MinWidth = 250;
    Height = 180;
    MinHeight = 180;
 
    LoadProjects();
 
    AddControlsToWindow();
 
    _updateButton.Click += updateButton_Click;
}

Note

See the Helpers section for methods that are called but not yet defined such as LoadProjects() and AddControlsToWindow().

Event handlers

Most of the rest of the functionality of the application is contained within the event handlers:

  • The following code will create projectsComboBox_SelectionChanged(), which is an event handler for the projectsComboBox.SelectionChanged event that we will wire up in the LoadProjects()method that was called from the constructor. This code first determines if an item is selected by casting the sender to a comboBox, making sure it isn't null and also that an item is selected.

    private void projectsListBox_SelectionChanged(
        object sender, SelectionChangedEventArgs e)
    {
        ComboBox comboBox = sender as ComboBox;
     
        // If there is a selected item
        if (comboBox != null && comboBox.SelectedIndex > -1)
        {
            UpdateDetails();
        }
        else
        {
            DisableDetails();
        }
    }
  • If there is an item selected in projectsComboBox then the UpdateDetails() helper method is called; if no item is selected then the DisableDetails() helper method is called.

  • updateButton.Click() is shown in the following code:

    private void updateButton_Click(object sender,
        RoutedEventArgs e)
    {
        Project selectedProject
            = _projectsComboBox.SelectedItem
            as Project;
        if (selectedProject != null)
        {
            selectedProject.Estimate =
                double.Parse(_estimateTextBox.Text);
            if (!string.IsNullOrEmpty(
                _actualTextBox.Text))
            {
                selectedProject.Actual
                    = double.Parse(
                        _actualTextBox.Text);
            }
            SetEstimateColor(selectedProject);
        }
    }

    updateButton.Click() will fire when the user clicks on the Update button and determine if an item is selected. If an item is selected, it will update the details controls with the details of the selected item. The values to populate the details controls will be fetched from the properties of the details controls which we are currently using for view state. Next updateButton.Click() will call the SetEstimateColor() helper function to update the color of the estimateTextBox (view state) based on whether the estimated cost is higher or lower than the actual cost (view logic).

    Note

    _actualTextBox is checked for null or empty as it starts out in an empty state and could be empty that state if the user updates only the Estimated Cost but not actual. This validation was provided to keep the application running down the happy path while all other validation have been left out to keep the code short.

Helpers

These private helper methods will add the remaining functionality:

  • Add the LoadProjects() method, as shown in the following code:

    private void LoadProjects()
    {
        foreach (Project project
            in new DataServiceStub().GetProjects())
        {
            _projectsComboBox.Items.Add(project);
        }
        _projectsComboBox.DisplayMemberPath = "Name";
        _projectsComboBox.SelectionChanged
            += new SelectionChangedEventHandler(
                projectsListBox_SelectionChanged);
    }
  • The LoadProjects() method will do the following:

    • Fetch the projects to populate the projectsComboBox with data retrieved from persisted state by instantiating a new DataService and then calling GetProjects()

    • The results of GetProjects() are iterated over and added to _projectsComboBox for display

    • Set the DisplayMemeberPath to "Name" to use the Project.Name property for the displayed text for each project in the _projectsComboBox.Items collection

    • Wire up an event handler for the projectsComboBox.SelectionChanged event allowing us to update the details view when the user changes the selected project

  • Add the AddControlsToWindow() method with the following code:

    private void AddControlsToWindow()
    {
        UniformGrid grid = new UniformGrid()
            { Columns = 2 };
        grid.Children.Add(new Label() 
            { Content = "Project:" });
        grid.Children.Add(_projectsComboBox);
        Label label = new Label() 
            { Content = "Estimated Cost:" };
        grid.Children.Add(label);
        grid.Children.Add(_estimateTextBox);
        label = new Label() 
            { Content = "Actual Cost:"};
        grid.Children.Add(label);
        grid.Children.Add(_actualTextBox);
        grid.Children.Add(_updateButton);
        Content = grid;
    }
  • The previous code will do the following:

    • Create a new UniformGrid

    • Configure the controls we will be using and then add the controls to the grid

    • Set the grid as the content of the window for display

  • Add the GetGrid() method to ProjectsView as follows:

    private Grid GetGrid()
    {
         Grid grid = new Grid();
         grid.ColumnDefinitions
            .Add(new ColumnDefinition());
         grid.ColumnDefinitions
            .Add(new ColumnDefinition());
         grid.RowDefinitions
            .Add(new RowDefinition());
         grid.RowDefinitions
            .Add(new RowDefinition());
         grid.RowDefinitions
            .Add(new RowDefinition());
         grid.RowDefinitions
            .Add(new RowDefinition());
        return grid;
    }
  • This code creates a 2x3 Grid that is used to create a basic form layout.

    Note

    We are not trying to make this form pretty but are instead trying to focus on the presentation patterns. One of the big benefits of MVVM is that it will allows us to give our view XAML to a designer and have them make it look nice without having the need to involve the developer. We will look at this approach in detail later in this book in Chapter 7, Dialogs and MVVM.

  • Add the UpdateDetails() method as follows:

    private void UpdateDetails()
    {
        Project selectedProject
            = _projectsComboBox.SelectedItem
            as Project;
     
        _estimateTextBox.IsEnabled = true;
        _estimateTextBox.Text
            = selectedProject.Estimate.ToString();
        _actualTextBox.IsEnabled = true;
        _actualTextBox.Text
            = (selectedProject.Actual == 0)
                    ? ""
                    : selectedProject.Actual.ToString();
        SetEstimateColor(selectedProject);
        _updateButton.IsEnabled = true;
    }
  • The UpdateDetails() method simply transfers data from the projectsComboBox.SelectedItem (or master) to the details controls and then updates the estimateTextBox by calling SetEstimateColor().

  • Add a DisableDetails() method as follows:

    private void DisableDetails()
    {
      _estimateTextBox.IsEnabled = false;
      _actualTextBox.IsEnabled = false;
      _updateButton.IsEnabled = false;
    }
  • The DisableDetails() method sets the details controls IsEnabled to false along with the update button.

  • Add SetEstimateColor() as follows:

    private void SetEstimateColor(Project selectedProject)
    {
        if (selectedProject.Actual == 0)
        {
            this.estimateTextBox.Foreground 
                = _actualTextBox.Foreground;
        }
        else if (selectedProject.Actual 
            <= selectedProject.Estimate)
        {
            this.estimateTextBox.Foreground 
                = Brushes.Green;
        }
        else
        {
            this.estimateTextBox.Foreground 
                = Brushes.Red;
        }
    }
  • The SetEstimateColor() method will be called by both event handlers to update the color of Estimated Cost (view state) by examining the Actual Cost and Estimated Cost.

Running the sample

Right-click on the ProjectBilling.Monolithic project and select Properties. Next, set the Output type to Windows Application as shown in the following screenshot:

Tip

If you leave the Project type as Console Application then a Console Window will be displayed while your WPF application runs. This can be useful for debugging as you can write debug messages to the console and easily kill the application using Ctrl + C when debugging.

Finally set ProjectBilling.Monolithic as the startup project by right-clicking on it and selecting Set as StartUp project. Now run the application by hitting F5.

You should now an application as shown in The Project Billing sample application section at the beginning of this chapter.

Takeaways

This code gets the job done, so what's the problem and why is there the need to restructure it?

Poor testability

This code has poor testability as the entire code is tightly coupled to the view and requires the view to fire the events that drive the logic of application. You could change the access modifiers of the methods of ProjectsView to public the help alleviate the situation but then you weaken the design from the encapsulation and design by contract perspectives.

Note

Encapsulation and design by contract are basic principles of Object-oriented design that are covered extensively on the Web. Please look up for them if you are already not familiar with them.

Poor extensibility and code reuse

If the users wanted a command line or web-interface, all of the code would need to be rewritten. Also, supporting multiple synchronized ProjectView is not possible under this design and would require at a minimum refactoring out a model.

Note

We will demonstrate how adding SoC allows for creating multiple synchronized vs of the model when we get to the MVC section.

Rapid application development

Microsoft puts a lot of development effort into creating Rapid Application Development (or RAD) tools that allow developers to simply drag-and-drop controls onto the IDE's design surface and then allow for configuring the controls' data needs mostly through the IDE's designer. The designer then creates monolithic code to get the job done. These tools make the problems of monolithic design worse by encouraging that style of design and by making it easier to do.

RAD Project Billing sample

This section will walk through rewriting the Project Billing application using RAD tools in Visual Studio.

Start by adding a new WPF Application project to your solution called ProjectBilling.RAD. This project template creates two files for you, App.xaml and MainWindow.xaml.

Next add a project reference to ProjectBilling.DataAccess.

Open MainWindow.xaml in Cider (the WPF designer) by double-clicking on MainWindow.xaml in the Solution Explorer. If they're not already expanded, expand the Toolbox window and the Data Sources window. You should have Visual Studio set up as shown in the following screenshot:

The first step is to add an Object Data Source to connect to DataService.GetProjects(). To do this start by clicking on Add New Data Source in the Data Sources window, as shown in the following screenshot:

You will now be presented with a dialog that will allow you to specify an Object Data Source, as shown in the following screenshot:

You will now be given the option to select the object that will be your data source. Select the Project class as shown in the following screenshot:

Next, select ComboBox from the Name drop-down menu, as shown in the following screenshot. This will change the type of generated control to be a combobox for the Name property.

Now drag the Name column onto the designer surface so that Visual Studio can generate some code to create a ComboBox which will be ready to be bound by an IList<Product>.

Change the width of the window to 250 by clicking on the MainWindow and setting the width value in the properties. You should now see something similar to what is shown in the following screenshot:

Looking at the XAML in the previous screenshot you will see that some code was generated for you. The important parts are highlighted as follows.

Note

It is assumed that you are familiar with the basics of WPF's data binding as full details fall outside of the scope of this book. However, see Appendix B, Binding at a glance, and/or see Data Binding (WPF) on MSDN (http://msdn.microsoft.com/en-us/library/ms750612.aspx).

<Window x:Class="RadProjectBilling.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="250" 
        mc:Ignorable="d" 
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        xmlns:my="clr-namespace: ProjectBilling.DataAccess;assembly=ProjectBilling.DataAccess" 
        Loaded="Window_Loaded">
    <Window.Resources>
        <CollectionViewSource x:Key="projectViewSource" d:DesignSource="{d:DesignInstance my:Project, CreateList=True}" />
    </Window.Resources>
    <Grid>
        <Grid DataContext="{StaticResource projectViewSource}"
              HorizontalAlignment="Left"
              Margin="12,12,0,0" Name="grid1"
              VerticalAlignment="Top">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Label Content="Name:" Grid.Column="0" 
                   Grid.Row="0" HorizontalAlignment="Left" 
                   Margin="3" VerticalAlignment="Center" />
            <ComboBox DisplayMemberPath="Name" Grid.Column="1" 
                      Grid.Row="0" Height="23" 
                      HorizontalAlignment="Left" 
                      ItemsSource="{Binding}" Margin="3" 
                      Name="nameComboBox" 
                      VerticalAlignment="Center" Width="120">
                <ComboBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <VirtualizingStackPanel />
                    </ItemsPanelTemplate>
                </ComboBox.ItemsPanel>
            </ComboBox>
        </Grid>
    </Grid>
</Window>

At the top of the file, there is an event handler added for the Window.Loaded event which is set to Window_Loaded.As you will see soon, Window_Loaded was created in the code behind. Next, a new CollectionViewSource named projectViewSource was added and set to reference to the DataLayer.Project class.

Note

A CollectionViewSource class wraps a data source and allows you to navigate and display the collection based on sort, filter, and group quires.

The grid, grid1, then had its DataContext set to projectViewSource and a ComboBox called nameComboBox was added with its ItemsSource bound to its DataContext with the following code.

ItemsSource="{Binding}"

Specifying Binding with no path in a binding expression will cause the binding target to be bound to the combobox's DataContext property.

Note

We will be covering bindings and DataContext in more depth later in this book.

DataContext is an inherited DependencyProperty and inherited DependencyProperties will have their values propagated from parents to children in the Visual Tree and in this case will result in the DataContext that was set on grid1 being propagated to all of its children including nameComboBox.

Note

For more information on DependencyProperties see Dependency Properties Overview on MSDN (http://msdn.microsoft.com/en-us/library/ms752914.aspx) and for more information on the Visual Tree see Trees in WPF on MSDN (http://msdn.microsoft.com/en-us/library/ms753391.aspx).

If we look in the code behind, MainWindow.xaml.cs, we'll see that projectViewSource has been initialized in Window_Loaded().

private void Window_Loaded(object sender, RoutedEventArgs e)
{

    System.Windows.Data.CollectionViewSource projectViewSource 
        = ((System.Windows.Data.CollectionViewSource)
        (this.FindResource("projectViewSource"));
    // Load data by setting the 
    // CollectionViewSource.Source property:
    // projectViewSource.Source = [generic data source]
}

There is some commented out code created for you.

// projectViewSource.Source = [generic data source]

By uncommenting the previous line of code, you can easily set the data source to the collection returned by DataServiceStub.GetProjects, as shown in the following code.

Note

You will need to add a using statement for ProjectBilling.DataAccess to the top of the file.

private void Window_Loaded(object sender, RoutedEventArgs e)
{

    System.Windows.Data.CollectionViewSource projectViewSource 
        = ((System.Windows.Data.CollectionViewSource)
        (this.FindResource("projectViewSource"));
    // Load data by setting the 
    // CollectionViewSource.Source property:
    projectViewSource.Source=new DataServiceStub().GetProjects(); 

}

Now if we run the application, we will see that the Name combobox is populated with data shown in the following screenshot:

Next we need to add the details controls. The first step is to click on the drop-down menu next to the Project data source in the DataSources window and change its type to Details, as shown in the following screenshot:

Now we will generate the details controls as shown in the following screenshot:

Perform the following steps:

  1. Drag the Project data source to the MainWindow on the cider design surface as shown in the previous screenshot. This will create a mini form with the controls for displaying the details.

  2. Drag the form that was created by clicking on the drag handles for the grid and move it below the name, as shown in the previous screenshot.

  3. Next, clean up the form by removing the labels, textboxes, and rows that are associated with the Id and Name labels.

Now run ProjectBilling.RAD and you should have a working master/details view, as shown in the following screenshot:

As you can see, it's easy to set up a working master/details form using these tools. It'd need some tweaking to be exactly the same as the monolithic one but I'm sure you get the idea of how this works compared to the monolithic style.

To finish off the application, add a button and change its Content to Update, Name to UpdateButton, IsEnabled to false, and then double-click on the button to create an event handler called UpdateButton_Click().

Add the following code to updateButton_Click():

private void UpdateButton_Click(object sender, 
    RoutedEventArgs e)
{
    Project selectedProject
        = this.nameComboBox.SelectedItem
        as Project;
    if (selectedProject != null)
    {		        selectedProject.Estimate =
            double.Parse(this.estimateTextBox.Text);
        if (!string.IsNullOrEmpty(
            this.actualTextBox.Text))
        {
            selectedProject.Actual
                = double.Parse(
                    this.actualTextBox.Text);
        }
        SetEstimateColor(selectedProject);
    }
}

This is almost exactly the same code we saw in the previous monolithic example and works exactly the same way.

Next, double-click on the Project combobox to add a SelectionChanged event handler. This will take you from the designer to the newly created event handler. Add the following code to this event handler along with the related SetEstimateColor() method.

Note

You will need to include the System.Windows.Controls and System.Windows.Media namespaces.

private void nameComboBox_SelectionChanged(object sender, 
    SelectionChangedEventArgs e)
{
    ComboBox comboBox = sender as ComboBox;
 
    // If there is a selected item
    if (comboBox != null && comboBox.SelectedIndex > -1)
    {
         Project selectedProject
            = comboBox.SelectedItem as Project;
 
        SetEstimateColor(selectedProject);
        this.UpdateButton.IsEnabled = true;
    }
    else
    {
        this.estimateTextBox.IsEnabled = false;
        this.actualTextBox.IsEnabled = false;
        this.UpdateButton.IsEnabled = false;
    }
}
 
private void SetEstimateColor(Project selectedProject)
{
    if (selectedProject.Actual == 0)
    {
        this.estimateTextBox.Foreground
            = Brushes.Black;
    }
    else if (selectedProject.Actual
        <= selectedProject.Estimate)
    {
        this.estimateTextBox.Foreground
            = Brushes.Green;
    }
    else
    {
        this.estimateTextBox.Foreground
            = Brushes.Red;
    }
 }

The previous code is similar to the monolithic code, except shorter. A lot of the code that was used to update the UI before is now not necessary and has been specified as a part of the XAML.

<Window.Resources>
    <CollectionViewSource x:Key="projectViewSource"
            d:DesignSource="{d:DesignInstance my:Project, 
                                CreateList=True}" />
</Window.Resources>

The projectViewSource is now doing the work we were manually doing before to move data in and out of our details controls, and that is accomplished through the bindings that have been created for us on the details controls.

<TextBox Grid.Column="1" Grid.Row="0" Height="23"
        HorizontalAlignment="Left" Margin="3"
        Name="actualTextBox"
        Text="{Binding Path=Actual, Mode=TwoWay, 
                ValidatesOnExceptions=true, 
                NotifyOnValidationError=true}"
        VerticalAlignment="Center" Width="120" />

Each details control will have a binding configured, as shown previously, to allow for two-way communication with the binding source, which in this case is the Project.Actual that is exposed from the projectViewSource CollectionViewSource class.

Takeaways

Looking at the code we just created, we see a situation that is slightly better than with pure monolithic design. The use of a CollectionViewSource reduced the amount of code that was created and that would need to be maintained and tested. However, the ease with which these controls allow for creating monolithic designs makes them an overall negative for those who care about design. We still have all the problems of monolithic code here that result from tight coupling and poor separation of concerns. However, we now have the additional problem of Visual Studio encouraging that type of design and we still can't easily support multiple dynamic views of our session state.

MVC

As a result of the problems caused by monolithic design, there has been a movement that started in the 70s towards presentational patterns or "Model View" patterns that provide better SoC and better testability. All this began in 1979 when MVC (Model View Controller) was described by Trygve Reenskaug while he was working on Smalltalk at Xerox PARC. Presentation patterns are notoriously flexible and this flexibility is part of what makes them difficult to master. Because of this there have been numerous versions of the MVC pattern; it's out of the scope of this book to cover all the various types of MVCs. What is important to understand is what these MVC patterns generally looked like and what problems they had which led to MVP. The basic structure of MVC is shown in the following diagram:

Note

Over the years MVC has taken many forms and it has evolved to where it is now common for the controllers to have a larger scope than just one widget, and under this newer style you'd more likely have one controller per form or user control instead of per widget. The sample used in this book makes use of the more modern style with one controller per window.

We will now cover the responsibilities of each of the components mentioned earlier and as part of that discussion you will see where the components introduced in the Monolithic section of this chapter fit into the MVC paradigm.

View

The view is responsible for displaying data and collecting user input. The view gets its data from the model including notifications that data has been updated and needs to be refreshed. These notifications are implemented using an observer pattern.

Note

In .NET, events are an implementation of the observer pattern.

When the user interacts with the view through gestures, the view is responsible for collecting those gestures and forwarding them along to the controller for processing.

Controller

The controller is responsible for taking user input and communicating it to the model for processing.

Note

The controller doesn't have to pass user input directly to the model and in many cases will instead communicate input gestures to a service layer or business logic layer for processing, which will then update the existing model or return a new model depending on the architecture. These details will be covered more extensively in the section titled Layered Design later in this chapter.

The main benefit that the controller provides is the ability to remove as much logic as possible into an external component that can be tested using automated tests.

Note

There are many variations of MVC that we will not be covering where the controller is responsible for collecting input from the user including Model 2, which is the pattern that ASP.NET MVC is based on.

Model

In MVC, the model is the in-memory representation of the data that was retrieved from the persistence store (session state). The model is also responsible for notifying the view of changes in state which is generally done with an observer pattern. Abstracting the model in this way allows for easily sharing session state among views, as we will discover shortly in the MVC Project Billing sample section.

Layered design

The design shown in the previous screenshot is an over-simplification of what is generally done in enterprise applications. Enterprise applications are generally separated into three logical layers as shown in the following diagram:

Layering an application in this way provides many benefits including the ability to scale more easily by deploying different layers to different servers and the ability to swap out layers with alternate implementations making the design extensible to change. A full discussion of layered design is outside of the scope of this book. If you'd like to learn more about layered enterprise design then see Chapter 5, Northwind—Commands and User Inputs, and Layered Application Guidelines from Microsoft Application Architecture Guide, 2nd Edition which is freely available online as part of MSDN (http://msdn.microsoft.com/en-us/library/ff650706.aspx).

The layers

The common three-layer design shown in the previous screenshot consists of the following layers:

Presentation layer

The presentation layer is responsible for

  • Displaying data

  • Providing feedback to the user

  • Collecting user input which is passed along to the business logic layer for processing

Separating the presentation in this way provides the benefits of being able to change the UI or provide a second UI without having to duplicate the code in the lower layers if for example you need to provide a thick client, thin client and a command-line version of your application.

Business layer

The business layer or application layer is where the core functionality of the system lives. This logic is called the business logic or domain logic and is applied to the raw data that is fetched from the data access layer for processing. Having the business logic in its own layer allows for scalability as the business logic can be hosted separately from the other layers and allows for extensibility as it provides the flexibility to support multiple types of UIs and multiple types of data stores if needed.

Data layer

The data layer is responsible for pulling data from and pushing data to a data store like a database, service or XML file. Having the data access layer provides the benefit of allowing for change in the data store without having to change code in higher layers.

MVC with layered design

Layered design may seem like a similar idea to MVC and it does have some similar ideas but they are not the same. However, they are generally used together in enterprise architecture, as shown in the following diagram:

As you can see from the previous screenshot, using MVC with layered design results in having the view and controller as part of the presentation layer and the model as part of the business logic layer.

There are various approaches to how the model and business logic can be structured. Martin Fowler describes the most common approaches on his blog and in his book Patterns of Enterprise Application Architecture. The patterns Martin describes include the following:

  • Transaction script: This approach organizes business logic in procedures where each procedure handles a single request from the presentation. Under this design you have one large facade that exposes your business logic through its methods.

  • Domain model: This approach organizes domain logic into an object model of the domain that incorporates both behavior and data. Under this design you have an object graph that mirrors your domain objects and each of these domain or business objects could provide methods for fetching or manipulating data.

  • Table module: Under this design you'd have a model that mirrors the database tables instead of the domain objects.

We have barely scratched the surface here because there are so many ways of organizing business logic and the model. Covering all of the options available for the business layer and model is outside of the scope of this book. If you are interested in learning more see Patterns of Enterprise Application Architecture by Martin Fowler.

MVC Project Billing sample

The increased SoC that comes from implementing MVC will allow us to implement a slightly better version of Project Billing which will support multiple views of the same data with dynamic updates, as shown in the following screenshot:

The classes involved in our MVC design are shown in the following screenshot:

As you can see in the previous screenshot we will have:

  • ProjectsView that keeps a reference to an IProjectsController interface via the ProjectsView.controller field and will keep a reference to an IProjectsModel interface via the ProjectsView.model field

  • ProjectsController class that implements IProjectsController

  • ProjectsModel that implements IProjectsModel

ProjectsView will use its reference to IProjectController to communicate user gestures to the controller by calling ProjectsView.controller.Update(). Internally this will call ProjectsController.model.UpdateProject().

Note

ProjectsView could call ProjectsView.model.UpdateProject() directly, but then the view logic would not be easily testable.

ProjectsView uses its reference to IProjectModel so that it can observe the IProjectsModel.ProjectUpdated event that will be raised after a call to ProjectModel.UpdateProject() finishes updating the model to provide dynamic synchronization of view state with session state (or model data) across all the ProjectsView instances. This design will make it so that when a user clicks on the Update button, all views that are currently open and viewing the same project will get the update and display the new data.

Let's start by creating a new WPF Application project called MvcProjectBilling. Add a project reference to the ProjectBilling.DataAccess.

Model

Add a new class to ProjectBilling.MVC called ProjectsModel andput the following code in it:

using System;
using System.Collections.Generic;
using System.Linq;
using ProjectBilling.DataAccess;

namespace ProjectBilling.Business.MVC
{
    public interface IProjectsModel
    {
        IEnumerable<Project> Projects { get; set; }
        event EventHandler<ProjectEventArgs> ProjectUpdated;
        void UpdateProject(Project project);
    }

    public class ProjectsModel : IProjectsModel
    {
        public IEnumerable<Project> Projects { get; set; }

        public event EventHandler<ProjectEventArgs>
            ProjectUpdated = delegate { };

        public ProjectsModel()
        {
            Projects = new DataServiceStub().GetProjects();
        }

        private void RaiseProjectUpdated(Project project)
        {
            ProjectUpdated(this, 
                new ProjectEventArgs(project));
        }

        public void UpdateProject(Project project)
        {
            Project selectedProject
                = Projects.Where(p => p.ID == project.ID)
                      .FirstOrDefault() as Project;
            selectedProject.Name = project.Name;
            selectedProject.Estimate = project.Estimate;
            selectedProject.Actual = project.Actual;
            RaiseProjectUpdated(selectedProject);
        }
    }

    public class ProjectEventArgs : EventArgs
    {

        public Project Project { get; set; }

        public ProjectEventArgs(Project project)
        {
            Project = project;
        }
    }
}

This code creates a model for consumption by our view. The view uses the observer pattern to get its updates from the model and the controller passes along user input from the view to the model using an IProjectsModel reference.

We have implemented ProjectsModel based on the IProjectModel so that our design is more extensible and allows using dependency injection. Dependency Injection will allow for increased testability as a fake object (mock or stub) can now be provided during unit tests.

The IProjectsModel interface is shown as follows:

    public interface IProjectsModel
    {
        IEnumerable<Project> Projects { get; set; }
        event EventHandler<ProjectEventArgs> ProjectUpdated;
        void UpdateProject(Project project);
    }

IProjectsModel defines the following contract:

  • Projects: It is a collection of projects that was loaded from persisted state

  • ProjectUpdated: It is an event for notifying when a project has its data updated

  • UpdateProject(): It is a method for submitting a project to be updated

The ProjectModel class implements the IProjectModelInterface and uses IDataServices.GetProjects() to fetch the data from our persistence service stub.

Note

The following code is the preferred way of defining events and it allows you to avoid having to check for a null event before raising the event. The code is more concise than the more common null checking pattern and also thread safe.

public event EventHandler<ProjectEventArgs>

ProjectUpdated = delegate { };

Controller

Add a class to the ProjectsBilling.MVC project called ProjectsController and add the code as follows:

using System;
using ProjectBilling.Business.MVC;
using ProjectBilling.DataAccess;
using System.Windows;

namespace ProjectBilling.UI.MVC
{
    public interface IProjectsController
    {
        void ShowProjectsView(Window owner);
        void Update(Project project);
    }

    public class ProjectsController : IProjectsController
    {
        private readonly IProjectsModel _model;

        public ProjectsController(IProjectsModel projectModel)
        {
            if (projectModel == null)
                throw new ArgumentNullException(
                    "projectModel");
            _model = projectModel;
        }

        public void ShowProjectsView(Window owner)
        {
            ProjectsView view 
                = new ProjectsView(this, _model);
            view.Owner = owner;
            view.Show();
        }

        public void Update(Project project)
        {
            _model.UpdateProject(project);
        }
    }
}

We've implemented the controller based on an interface to again take advantage of the benefits of dependency injection. The interface defines the following contract:

  • ShowProjectsView(): It is a method that allows for displaying a ProjectsView to the user

  • Update(): It is a method that allows for updating a project that delegates the updating of the project to the model

    Note

    It was common in older versions of MVC to have the controller responsible for determining the next view and then to display the view. We demonstrated that in the previous code with ShowProjectsView(). This is not a responsibility that always is taken on by the controller in MVC.

View

Add a new window to ProjectBilling.MVC called ProjectsView and add the following code to ProjectView.xaml:

<Window x:Class="ProjectBilling.UI.MVC.ProjectsView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Projects" MinHeight="180" Height="180"
        MinWidth="250" Width="250" Padding="5"
        FocusManager.FocusedElement
            ="{Binding ElementName=ProjectsComboBox}">
    <UniformGrid Columns="2">
        <Label Content="Project:" />
        <ComboBox Name="ProjectsComboBox" Margin="5"
                SelectionChanged
                    ="ProjectsComboBox_SelectionChanged" />
        <Label Content="Estimated Cost:" />
        <TextBox Name="EstimatedTextBox" Margin="5" 
                 IsEnabled="False" />
        <Label Content="Actual Cost:" />
        <TextBox Name="ActualTextBox" Margin="5" 
                 IsEnabled="False" />
        <Button Name="UpdateButton" Content="Update"
                Margin="5" IsEnabled="False" 
                Click="UpdateButton_Click" />
    </UniformGrid>
</Window>

This XAML creates a simple master/details form, like the one shown in the screenshot at the beginning of the MVC Project Billing sample section.

Note

Coverage of the basics of XAML is outside the scope of this book.

Next, add the following code to ProjectsView.xaml.cs. Start by adding the fields that will hold a reference to the model and controller.

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using ProjectBilling.Business.MVC;
using ProjectBilling.DataAccess;

namespace ProjectBilling.UI.MVC
{
    public partial class ProjectsView : Window
    {
        private readonly IProjectsModel _model;
        private readonly IProjectsController _controller 
            = null;
        private const int NONE_SELECTED = -1;
    }
}

Initialization

Add the constructor as llows:

public ProjectsView(
    IProjectsController projectsController,
    IProjectsModel projectsModel)
{
    InitializeComponent();
    _controller
        = projectsController;
    _model = projectsModel;
    _model.ProjectUpdated
        += model_ProjectUpdated;
    ProjectsComboBox.ItemsSource 
        = _model.Projects;
    ProjectsComboBox.DisplayMemberPath 
        = "Name";
    ProjectsComboBox.SelectedValuePath 
        = "ID";
}

This constructor allows for dependency injection by taking an interface for the model and controller as parameters. As previously mentioned, this allows for more isolated unit tests as fake objects (mocks or stubs) can be passed in for testing. The constructor:

  1. Wires up the model and controller.

  2. Subscribes to the _model.ProjectUpdated event.

  3. Sets the projectsComboBox.ItemSource to this.Model.Projects and sets the DisplayMemberPath and SelectedValuePath so that they resolve to Project.Name and Project.ID respectively.

Event handlers

Now we will add some event handlers:

  • The following model_ProjectUpdated code will execute when the ProjectsModel.ProjectUpdated event fires:

    void model_ProjectUpdated(object sender,
        ProjectEventArgs e)
    {
        int selectedProjectId = GetSelectedProjectId();
    
        if (selectedProjectId > NONE_SELECTED)
        {
            if (selectedProjectId == e.Project.ID)
            {
                UpdateDetails(e.Project);
            }
        }
    }
  • If the project that was updated is currently displayed in the details of this view, then this code will update the details with the project's new data. This allows for multiple synchronized views of the same data.

  • The ProjectsComboBox_SelectionChanged event handler fires when the user changes the selected project in the ProjectsComboBox. This event gets the selected project and then updates the details controls with the newly selected project's data and then calls UpdateEstimateColor() to set estimateTextBox.Foreground to the appropriate color based on the view logic.

    private void ProjectsComboBox_SelectionChanged(
        object sender, SelectionChangedEventArgs e)
    {
        Project project = GetSelectedProject();
        if (project != null)
        {
            EstimatedTextBox.Text
                = project.Estimate.ToString();
            EstimatedTextBox.IsEnabled = true;
            ActualTextBox.Text
                = project.Actual.ToString();
            ActualTextBox.IsEnabled = true;
            UpdateButton.IsEnabled = true;
            UpdateEstimatedColor();
        }
    }
  • The UpdateButton_Click event handler fires when a user clicks on the UpdateButton. This event handler simply creates a new Project populated with the details data and then passes that project to the controller for processing.

    private void UpdateButton_Click(object sender, 
        RoutedEventArgs e)
    {
        Project project = new Project()
        {
            ID = (int)ProjectsComboBox.SelectedValue,
            Name = ProjectsComboBox.Text,
            Estimate = GetDouble(
                EstimatedTextBox.Text),
            Actual = GetDouble(ActualTextBox.Text)
        };
        _controller.Udate(project); 
    }
Helpers

Add the code that follows as the private helper methods that are called from the event handlers:

  • The UpdateEstimateColor function will look at the values of the details controls and update the EstimateTextBox.Foreground to the appropriate color based on the view logic.

    private void UpdateEstimatedColor()
    {
        double actual
            = GetDouble(ActualTextBox.Text);
        double estimated
            = GetDouble(EstimatedTextBox.Text);
        if (actual == 0)
        {
            EstimatedTextBox.Foreground
                = ActualTextBox.Foreground;
        }
        else if (actual > estimated)
        {
            EstimatedTextBox.Foreground
                = Brushes.Red;
        }
        else
        {
            EstimatedTextBox.Foreground
                = Brushes.Green;
        }
    }
  • The UpdateDetails function takes a project and updates the details controls including calling UpdateEstimateColor() to update the color of estimateTextBox.Foreground.

    private void UpdateDetails(Project project)
    {
        EstimatedTextBox.Text
            = project.Estimate.ToString();
        ActualTextBox.Text
            = project.Actual.ToString();
        UpdateEstimatedColor();
    }
  • Next add the following methods which are self explanatory.

    private double GetDouble(string text)
    {
        return string.IsNullOrEmpty(text) ?
            0 : double.Parse(text);
    }
    
    private Project GetSelectedProject()
    {
        return ProjectsComboBox.SelectedItem
            as Project;
    }
    
    private int GetSelectedProjectId()
    {
        Project project = GetSelectedProject();
        return (project == null)
            ? NONE_SELECTED : project.ID;
    }
MainWindow
  • Update MainWindow.xaml as shown in the following code:

    <Window x:Class="ProjectBilling.UI.MVC.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="Shell" Height="150" Width="150"
            MinHeight="200" MinWidth="200"
            FocusManager.FocusedElement
                ="{Binding ElementName=ShowProjectsButton}">
        <StackPanel>
            <Button Content="Update Projects" 
                    Name="ShowProjectsButton" Margin="5"
                    Click="ShowProjectsButton_Click" />
        </StackPanel>
    </Window>
  • This will create a window with one button that says ShowProjects. Double-click on ShowProjects in cider to create an event handler and then add the following code to MainWindow.xaml.cs.

    using System.Windows;
    using ProjectBilling.Business.MVC;
    
    namespace ProjectBilling.UI.MVC
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            private IProjectsController _controller;
    
            public MainWindow()
            {
                InitializeComponent();
                _controller 
                    = new ProjectsController(new ProjectsModel());
            }
    
            private void ShowProjectsButton_Click(object sender, 
                RoutedEventArgs e)
            {
                _controller.ShowProjectsView(this);
            }
        }
    }
  • This code will serve as the main window of the application and will show a new ProjectsView each time the Show Projects button is clicked by calling IProjectsController.ShowProjectsView().

How it works

Run the application now. You will see a window like the one shown in the following screenshot:

Each time the Show Projects button is clicked, a ProjectsView will be displayed as shown in the following screenshot:

What's interesting about this architecture is that it's now easy to have updates propagate across views as you can see by following these steps:

  1. Select the Jones account in each view.

  2. Change the Actual Cost to be 1700 in one of the windows.

  3. Click on the Update button in the same window that you changed the Actual Cost in.

You will now see that both open ProjectsView windows update and this is because the model has been abstracted away and the views observes the model. When an update occurs, the windows get their updates in the form of events (the observer pattern). You can also set each window with a different project and then try updating a project in one window. Next, verify that the changes display in the second window when you select the updated project.

Takeaways

MVC makes several improvements over the monolithic approach:

  • The increased SoC created by abstracting out a model allowed for easily supporting multiple synchronized views of the same data.

  • The controller abstraction allows for increased testability of view interactions (gestures).

However, this design also has the following issues:

  • The view logic and the view state are both still tightly coupled in the view leaving them difficult to test or share.

  • If we wanted to do a thin client for the Web in Silverlight, we'd only be able to reuse the model and not the controller and we'd have to duplicate the view logic and view state.

  • The final less obvious issue with this design deals with memory leaks. Details of the memory leaks follow. You can add code to fix the memory leak situation but I've found on the projects that I've worked on that this is often not done and requires higher maintenance than designs that don't rely on events. Having to go through this extra effort required by .NET events makes MVC less desirable than a pattern like MVVM that doesn't require the use of .NET events.

Memory leaks

In the MVC Project Billing Sample the view observes the Model using .NET events, which are an implementation of the observer pattern. One thing to watch out for when using .NET events is memory leaks. This is because unfortunately in .NET, events only support using strong references and not weak references. The issue here is that when an observer object (view in our example) subscribes to an event on a subject object (model in our example), the subject keeps a reference in the form of a delegate (or function pointer) to the observer. In .NET, memory management is handled by the garbage collector and the garbage collector will not collect any object as long as another object has a strong reference to it. This means that our view subscribing to our model's events will cause those models to hold strong references to the views. These strong references will prevent the garbage collector from collecting the views causing the views to leak.

To see this for yourself, update the previous example as follows. Let's start by adding a button to MainWindow.xaml:

<Button Content="GC Collect"
        Name="GCCollectButton" Margin="5"
        Click="GCCollectButton_Click" />

Next add gcCollectButton_Click to MainWindow.xaml.cs as follows:

private void GCCollectButton_Click(object sender, 
    RoutedEventArgs e)
{
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
}

Note

You will need to add the System namespace to use GC.

This code will call GC.Collect() twice and GC.WaitForPendingFinalizers() once. The .NET garbage collector is non-deterministic so there are no guarantees about when it will collect but I find this combination works pretty well at getting it to collect.

Now let's add a finalizer to ProjectsView.xaml.cs as follows.

Note

Finalizers are called when an object is collected by the garbage collector. Full coverage of .NET memory management is out of the scope of this book. See C# Via CLR by Jeffery Richter for more details.

~ProjectsView()
{
    MessageBox.Show("ProjectsView collected");
}

Now when a ProjectsView instance is collected by the garbage collector, a message box will pop up and we will know that it was collected.

Go ahead, run the application and follow these steps:

  1. Click on the GC Collect button as shown in the previous screenshot.

  2. Open a few ProjectsViews by clicking on the Show Projects button and then close them.

  3. Click on the GC Collect button, in fact click it a few times. Try all you want, you will not be able to get the finalizers to execute from the views that you created because the ProjectsModel instance is holding a reference to them.

You will never see the ProjectsView collected message box displayed under this design. If you used a memory profiler, you'd see that after each window is closed the memory used by the application doesn't decrease.

To fix this situation add the following code to ProjectsView.xaml.cs and then re-run the application repeating the steps listed previously.

protected override void OnClosed(EventArgs e)
{
    base.OnClosed(e);
    _model.ProjectUpdated -= model_ProjectUpdated;
}

Now you will see that when you click on GC Collect the finalizers will execute. This isn't a lot of code to correct this situation but developers do tend to get this wrong from time to time and it can make the code higher maintenance than a design that doesn't require .NET events.

Note

Microsoft recommends using the weak event pattern to deal with this situation (http://msdn.microsoft.com/en-us/library/aa970850.aspx). However, I prefer the WeakEvent class found in CLR via C# by Jeffery Richter because it's a much lower maintenance approach than the weak event pattern and Richter's WeakEvent classes are used almost exactly like regular CLR events, so they require very little training. Please check Richter's blog for the latest version of this code which contains bug fixes to the published version.. These topics will not be covered in this book but feel free to dig deeper on your own.

MVP

MVP or Model View Presenter is a pattern that first appeared at IBM and then emerged more prominently at Taligent in the 1990's. MVP was a derivative of MVC that took a slightly different approach. Under MVP, the view is no longer required to observe the model.

Note

Martin Fowler officially retired the MVP pattern on his blog and replaced it with two variations, Passive View and Supervising Controller. Passive view is what is shown in the following screenshot. Under Supervising Controller, the view still observes the model via an observer but with a much more limited scope than under MVC. For full details see Martin's blog (http://martinfowler.com/eaaDev/ModelViewPresenter.html).

The following diagram shows the basic structure of MVP:

Note

It's more common for presenters to have a larger scope than a single UI widget and to instead have one presenter per form or user control.

As you can see in the previous diagram, the presenter has taken the place of the controller in the triad and is responsible for moving user input from the view to the model as well as being responsible for updating the view about changes that occur in the model. The presenter communicates with the view through an interface which allows for increased testability as the model can be replaced by a fake object (mock or stub) for unit tests. The following diagram shows MVP in a layered architecture:

MVP Project Billing sample

We will create an application with the classes shown in the following screenshot:

As you can see our view now consists of a class, ProjectsView that implements an interface, IProjectsView. We will be implementing the Passive View version of MVP and so IProjectsView contains everything needed to update the view and to communicate user gestures into the presenter. This allows for maximum test coverage under the MVP paradigm.

The presenter, ProjectsPresenter, takes an IProjectsView and an IProjectsModel as constructor arguments and keeps references to them in ProjectsPresenter.view and ProjectsPresenter.model respectively as shown previously.

This design requires that communications go through the presenter. For example, when the user clicks on the updateButton, this will cause the IProjectView.ProjectUpdated event to be raised, which will call the ProjectsPresenter.view_ProjectUpdated() event handler. ProjectsPresenter.view_ProjectUpdated will in turn call ProjectsPresenter.model.UpdateProject() which will update the model data and then raise the IProjectsModel.ProjectUpdated event to notify presenters that the model has been updated. The Presenters will then call IView.UpdateProject(), which will take care of updating the view state and applying view logic to set the color of estimateTextBox if necessary. This will allow for dynamic updates to session data across multiple views just like in MVC. However, this design allows for better test coverage than under MVC because the view is passive and we've moved the view logic and view state out of the view and into the presenter. We've also removed direct communication between the view and model and all of the communication is handled through the presenter.

Note

Having no interaction between the model and view is a detail that is specific to the passive view version of MVP. In supervising controller the model and view can still communicate directly.

Let's start by adding a new WPF Application project called ProjectBilling.MVP to the solution and then add a project reference to the ProjectBilling.DataAccess project.

Model

Add a class called ProjectsModel and add the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using ProjectBilling.DataAccess;

namespace ProjectBilling.Business
{
    public class ProjectEventArgs : EventArgs
    {
        public Project Project { get; set; }
        public ProjectEventArgs(Project project)
        {
            Project = project;
        }
    }

    public interface IProjectsModel
    {
        void UpdateProject(Project project);
        IEnumerable<Project> GetProjects();
        Project GetProject(int Id);
        event EventHandler<ProjectEventArgs> ProjectUpdated;
    }

    public class ProjectsModel : IProjectsModel
    {

        private IEnumerable<Project> projects = null;

        public event EventHandler<ProjectEventArgs> 
            ProjectUpdated = delegate { };

        public ProjectsModel()
        {
            projects = new DataServiceStub().GetProjects();
        }

        public void UpdateProject(Project project)
        {
            ProjectUpdated(this, 
                new ProjectEventArgs(project));
        }

        public IEnumerable<Project> GetProjects()
        {
            return projects;
        }


        public Project GetProject(int Id)
        {
            return projects.Where(p => p.ID == Id)
                .First() as Project;
        }
    }
}

Our ProjectsModel implements the IProjectsModel interface for better testability via dependency injection and implements the following contrct.

  • UpdateProject(): This method allows for updating a project across the session state. This design could be extended to support updates across persisted state, but covering that is outside the scope of this book.

  • GetProjects(): This method will return all projects in the session state.

  • GetProject(): This method is given a project ID and will return a project from session state.

  • ProjectUpdated: This event will fire when a project has been updated in the session state.

The ProjectEventArgs class is for our events to use to communicate which project has changed.

View

Add a window called ProjectsView and add the following code to ProjectsView.xaml:

<Window x:Class="ProjectBilling.UI.MVP.ProjectsView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Projects" MinHeight="180" Height="180"
        MinWidth="250" Width="250" Padding="5"
        FocusManager.FocusedElement
            ="{Binding ElementName=ProjectsComboBox}">
    <UniformGrid Columns="2">
        <Label Content="Project:" />
        <ComboBox Name="ProjectsComboBox" Margin="5"
                SelectionChanged
                    ="ProjectsComboBox_SelectionChanged" />
        <Label Content="Estimated Cost:" />
        <TextBox Name="EstimatedTextBox" Margin="5" 
                 IsEnabled="False" />
        <Label Content="Actual Cost:" />
        <TextBox Name="ActualTextBox" Margin="5" 
                 IsEnabled="False" />
        <Button Name="UpdateButton" Content="Update"
                Margin="5" IsEnabled="False" 
                Click="UpdateButton_Click" />
    </UniformGrid>
</Window>

With the exception of the namespace declaration, this XAML file is exactly the same as that found in the MVC ProjectsView.xaml.

Next, add the following code to the ProjectsView.xaml.cs file.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using ProjectBilling.Business;
using ProjectBilling.DataAccess;

namespace ProjectBilling.UI.MVP
{
    public interface IProjectsView
    {
        int NONE_SELECTED { get; }
        int SelectedProjectId { get; }
        void UpdateProject(Project project);
        void LoadProjects(IEnumerable<Project> projects);
        void UpdateDetails(Project project);
        void EnableControls(bool isEnabled);
        void SetEstimatedColor(Color? color);
        event EventHandler<ProjectEventArgs> ProjectUpdated;
        event EventHandler<ProjectEventArgs> DetailsUpdated;
        event EventHandler SelectionChanged;
    }

    /// <summary>
    /// Interaction logic for ProjectsView.xaml
    /// </summary>
    public partial class ProjectsView : Window, IProjectsView
    {

        public int NONE_SELECTED { get { return -1; } }
            public event EventHandler<ProjectEventArgs> 
            ProjectUpdated = delegate { };
        public int SelectedProjectId { get; private set; }

        public event EventHandler SelectionChanged
            = delegate { };
        public event EventHandler<ProjectEventArgs>
            DetailsUpdated = delegate { };

        public ProjectsView()
        {
            InitializeComponent();
            SelectedProjectId = NONE_SELECTED;
        }

    }
}

This code creates the IProjectsView interface, the fields and the events that are needed by ProjectsView. ProjectsView will implement the IProjectView and the contract described as follows:

  • NONE_SELECTED: This read-only property returns a constant that is used to determine if SelectedProjectID currently has a selection

  • SelectedProjectId: This read-only property returns the currently selected project ID

  • UpdateProject(): This function allows to update a project in the view state and will take care of updating the details view if needed

  • LoadProjects(): This function allows for populating the projectsComboBox for the first time

  • UpdateDetails(): This function allows for updating the details view

  • EnableControls(): This function allows for setting the details controls and the IsEnabled property of updateButton to enable and disable these controls

  • SetEstimatedColor(): This function allows for setting the text color of estimatedTextBox

  • ProjectUpdated: This event will notify the presenter that the user clicked on the updateButton

  • DetailsUpdated: This event will notify the presenter that the details have changed so that the presenter can update the text color of estimatedColor

  • SelectionChanged: This event will notify the presenter that the current selection has changed in the projectsComboBox

Event handlers

Now let's add the event handls that we will need:

  • The UpdateButton_Click function will fire when a user clicks on UpdateButton and will create a new project, populate it with the details control data, and then raise the IProjectsView.ProjectUpdated event passing the new project to the constructor of the new ProjectEventArgs that is being passed with the event.

    private void UpdateButton_Click(object sender,
        RoutedEventArgs e)
    {
        Project project = new Project();
        project.Estimate =
            GetDouble(EstimatedTextBox.Text);
        project.Actual =
            GetDouble(ActualTextBox.Text);
        project.ID =
            int.Parse(
                ProjectsComboBox.SelectedValue.
                    ToString());
        ProjectUpdated(this, 
            new ProjectEventArgs(project));
    }
  • The ProjectsComboBox_SelectionChanged() function will fire when the selection changes in the ProjectsComboBox and it simply raises the IProjectsView.SelectionChanged event to notify the presenter so that it can update the view as needed.

    private void ProjectsComboBox_SelectionChanged(
        object sender, SelectionChangedEventArgs e)
    {
        SelectedProjectId
            = (ProjectsComboBox.SelectedValue == null)
                    ? NONE_SELECTED
                    : int.Parse(
                        ProjectsComboBox.SelectedValue.
                            ToString());
        SelectionChanged(this,
            new EventArgs());
    }
Public methods

Add te following public methods:

  • The UpdateProject function allows for updating a project in the view state by first finding the project in the ProjectsComboBox.ItemsSource using a little Linq. It then updates the project and if it's the project that is currently selected, it calls UpdateDetails() to update the details controls.

    public void UpdateProject(Project project)
    {
        // Null checks excluded
        IEnumerable<Project> projects =
            ProjectsComboBox.ItemsSource as
                IEnumerable<Project>;
        Project projectToUpdate =
            projects.Where(p => p.ID == project.ID)
                .First();
        projectToUpdate.Estimate = project.Estimate;
        projectToUpdate.Actual = project.Actual;
        if (project.ID == SelectedProjectId)
            UpdateDetails(project);
    }
  • The LoadProjects function allows for loading a collection of Projects as the ItemsSource for projectsComboBox.

    public void LoadProjects(IEnumerable<Project> projects)
    {
        ProjectsComboBox.ItemsSource = projects;
        ProjectsComboBox.DisplayMemberPath = "Name";
        ProjectsComboBox.SelectedValuePath = "ID";
    }
    
  • The EnableControls function allows for setting the IsEnabled state of the details controls and updateButton.

    public void EnableControls(bool isEnabled)
    {
        EstimatedTextBox.IsEnabled = isEnabled;
        ActualTextBox.IsEnabled = isEnabled;
        UpdateButton.IsEnabled = isEnabled;
    }
    
  • The SetEstimatedColor function takes a color and will update the estimateTextBox.Foreground color to be the passed in color.

    public void SetEstimatedColor(Color? color)
    {
        EstimatedTextBox.Foreground
            = (color == null)
                    ? ActualTextBox.Foreground
                    : new SolidColorBrush((Color)color);
    }

    Note

    Note that this function doesn't contain view logic and that it's the presenter's responsibility to calculate the correct color.

  • The UpdateDetails function will update the details controls with the data contained in the project that is passed in.

    public void UpdateDetails(Project project)
    {
        EstimatedTextBox.Text
            = project.Estimate.ToString();
        ActualTextBox.Text
            = project.Actual.ToString();
        DetailsUpdated(this,
            new ProjectEventArgs(project));
    }
Helpers

The model_ProjectUpdated function will get a double from textpassed in taking care of null/empty checks.

private double GetDouble(string text)
{
    return string.IsNullOrEmpty(text)
        ? 0 : double.Parse(text);
}

Presenter

Add a class called ProjetsPresenter and add the following code to it:

using System;
using System.Windows.Media;
using ProjectBilling.Business;
using ProjectBilling.DataAccess;

namespace ProjectBilling.UI.MVP
{
    public class ProjectsPresenter
    {
        private readonly IProjectsView _view = null;
        private readonly IProjectsModel _model = null;

        public ProjectsPresenter(IProjectsView projectsView, 
            IProjectsModel projectsModel)
        {
            _view = projectsView;
            _view.ProjectUpdated += view_ProjectUpdated;
            _view.SelectionChanged 
                += view_SelectionChanged;
            _view.DetailsUpdated += view_DetailsUpdated;
            _model = projectsModel;
            _model.ProjectUpdated += model_ProjectUpdated;
            _view.LoadProjects(
                _model.GetProjects());
        }
 
    }
}

As you can see the presenter takes IProjectsView and IProjectsModel as constructor arguments and then subscribes to various events and the loads projects into the view from the model with the following code:

this.view.LoadProjects(
    this.model.GetProjects());
Event handlers
  • The view_DetailsUpdated function is called in response to the IProjectsView.DetailsUpdated event and simply calls SetEstimateColor() to update the color of the estimateTextBox.Foreground. This allows the view logic to be easily tested.

    private void view_DetailsUpdated(object sender, 
        ProjectEventArgs e)
    {
        SetEstimatedColor(e.Project);
    }
  • view_SelectionChanged will be called in response to the IProjectsView.SelectionChanged event firing performs the view logic of updating the details controls after a the user changes the selected project. Again, this design allows this view logic to be easily tested.

    private void view_SelectionChanged(object sender, 
        EventArgs e)
    {
        int selectedId = _view.SelectedProjectId;
        if (selectedId > _view.NONE_SELECTED)
        {
            Project project =
                _model.GetProject(selectedId);
            _view.EnableControls(true);
            _view.UpdateDetails(project);
            SetEstimatedColor(project);
        }
        else
        {
            _view.EnableControls(false);
        }
    }
  • model_ProjectUpdated will be called in response to the IProjectsModel.ProjectUpdated event firing and will allow for propagating the changes to session state made in one view to the other views.

    private void model_ProjectUpdated(object sender,
        ProjectEventArgs e)
    {
        _view.UpdateProject(e.Project);
    }
  • The view_ProjectUpdated function will fire in response to the IProjectsView.ProjectsUpdated event and will notify the model so that it can update the project in the session state and also calls SetEstimatedColor() to perform the view logic for updating the color of estimateTextBox.Foreground if needed.

    private void view_ProjectUpdated(object sender, 
        ProjectEventArgs e)
    {
        _model.UpdateProject(e.Project);
        SetEstimatedColor(e.Project);
    }
Helpers

Now add the following helper method:

  • The SetEstimateColor performs the view logic needed to set estimateColor.Foreground to the appropriate color and then calls IProjectsView.SetEstimatedColor() to apply the needed color again allowing for the view logic to be easily tested.

    private void SetEstimatedColor(Project project)
    {
        if (project.ID == _view.SelectedProjectId)
        {
            if (project.Actual <= 0)
            {
                _view.SetEstimatedColor(null);
            }
            else if (project.Actual 
                        > project.Estimate)
            {
                _view.SetEstimatedColor(Colors.Red);
            }
            else
            {
                _view.SetEstimatedColor(Colors.Green);
            }
        }
    }
    

Main window

Add the following code to MainWindow.xaml:

<Window x:Class="ProjectBilling.UI.MVP.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Shell" Height="150" Width="200"
        MinHeight="150" MinWidth="200"
        FocusManager.FocusedElement
            ="{Binding ElementName=ShowProjectsButton}">
    <StackPanel>
        <Button Content="Show Projects"
                Name="ShowProjectsButton" Margin="5"
                Click="ShowProjectsButton_Click" />
    </StackPanel>
</Window>

With the exception of the namespace declaration, this XAML code is exactly the same as the one found in the MVC MainWindow.xaml.

Next, add the following code to MainWindow.xaml.cs:

using System.Windows;
using ProjectBilling.Business;

namespace ProjectBilling.UI.MVP
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private IProjectsModel _model = null;

        public MainWindow()
        {
            InitializeComponent();
            _model = new ProjectsModel();
        }

        private void ShowProjectsButton_Click(object sender,
            RoutedEventArgs e)
        {
            ProjectsView view = new ProjectsView();
            ProjectsPresenter presenter 
                = new ProjectsPresenter(view, _model);
            view.Owner = this;
            view.Show();
        }
    }
}

The constructor creates a model that will be shared across all views. The model is used to initialize and update session state.

ShowProjectsButton_Click() will be called when the ShowProjectsButton is clicked and it will instantiate a new view and pass the new view instance as an argument to the constructor for a new ProjectsPresenter instance along with a reference to MainWindow.model. ShowProjectsButton_Click will then show the view by calling view.Show().

How it works

Running the application you will see that it works the same as our previous MVC application.

Takeaways

MVP represents a big improvement over MVC in a few ways:

  • It provides testable view state and view logic by moving them into the presenter allowing the view logic to be easily tested.

  • It decouples the view from the model by requiring communication to go through the presenter. Unlike MVC, MVP allows for reuse of the view logic and this is achieved by moving the logic into a presenter and having the presenter communicate with the view through an interface. Now if you wanted to implement a Silverlight version of this application, you would only need to create a view in Silverlight that implements IProjectsView and could reuse IProjectsPresenter and IProjectsModel.

However there are still a few issues as follows:

  • We still use a lot of events, and as shown in the Memory Leaks section previously, events can cause memory leaks and end up causing code to be higher maintenance than a design that doesn't require events.

  • There is still a lot of untested code in the view.

These short comings are all motivators for MVVM or presentation model and we will look at how MVVM addresses each of these issues in the next chapter.

Summary


In this chapter we reviewed the long history of presentation patterns with examples. We started by looking at the state of affairs before applications started having their architectures organized into presentation patterns and were instead written as monoliths. We reviewed the many issues with this approach and looked at how Microsoft is making the situation worse with its RAD toolkit that encourages this kind of monolithic design.

We then looked at how things were improved under MVC and how dynamically sharing the session state across views was made easier by MVC. We also reviewed the shortcomings of MVC including covering issues with .NET events and memory leaks before moving on to discussing how MVP addresses some of the MVC short comings. We finished the chapter by looking at an example of the passive view version of MVP covering all the improvements that it offers over MVC in the area of testing and code reuse while pointing out MVP's shortcomings.

In the next chapter will dive into MVVM and demonstrate how it helps address the shortcomings of all the presentational patterns that came before it by taking advantage of features in Silverlight and WPF.

Left arrow icon Right arrow icon

Key benefits

  • Build an enterprise application using Silverlight and WPF, taking advantage of the powerful MVVM pattern, with this book and e-book
  • Discover the evolution of presentation patternsóby exampleóand see the benefits of MVVM in the context of the larger picture of presentation patterns
  • Customize the MVVM pattern for your projects' needs by comparing the various implementation styles

Description

MVVM (Model View View Model) is a Microsoft best practices pattern for working in WPF and Silverlight that is highly recommended by both Microsoft and industry experts alike. This book will look at the reasons for the pattern still being slow to become an industry standard, addressing the pain points of MVVM. It will help Silverlight and WPF programmers get up and running quickly with this useful pattern.MVVM Survival Guide for Enterprise Architectures in Silverlight and WPF will help you to choose the best MVVM approach for your project while giving you the tools, techniques, and confidence that you will need to succeed. Implementing MVVM can be a challenge, and this book will walk you through the main issues you will come across when using the pattern in real world enterprise applications.This book will help you to improve your WPF and Silverlight application design, allowing you to tackle the many challenges in creating presentation architectures for enterprise applications. You will be given examples that show the strengths and weaknesses of each of the major patterns. The book then dives into a full 3 tier enterprise implementation of MVVM and takes you through the various options available and trade-offs for each approach. During your journey you will see how to satisfy all the demands of modern WPF and Silverlight enterprise applications including scalability, testability, extensibility, and blendability.Complete your transition from ASP.NET and WinForms to Silverlight and WPF by embracing the new tools of these platforms, and the new design style that they allow for. MVVM Survival Guide for Enterprise Architectures in Silverlight and WPF will get you up to speed and ready to take advantage of this powerful new presentation platform.

Who is this book for?

This book will be a valuable resource for Silverlight and WPF developers who want to fully maximize the tools with recommended best practices for enterprise development. This is an advanced book and you will need to be familiar with C#, the .Net framework, and Silverlight or WPF.

What you will learn

  • Maximize separation of concerns by taking advantage of WPF and Silverlight s rich binding system, templates, and commanding infrastructure
  • Discover the built-in support for MVVM in Entity Framework and WCF
  • Create unit testable user interfaces the MVVM way
  • Work in parallel with minimal dependencies by creating blendable architectures
  • Solve common MVVM problems both with and without frameworks depending on your preference
  • Extend your architecture and test it by using inversion of control frameworks
  • Tackle complex designs by using hierarchical view model design and mediators
  • Reduce the amount of code in your user interface by letting the WPF and Silverlights binding system eliminate your need to do things like casting controls and dispatching
  • Best practices for dealing with collections
  • Create designs that allow for dramatically changing your user interface without having to change code outside the view using data templates
Estimated delivery fee Deliver to United States

Economy delivery 10 - 13 business days

Free $6.95

Premium delivery 6 - 9 business days

$21.95
(Includes tracking information)

Product Details

Country selected
Publication date, Length, Edition, Language, ISBN-13
Publication date : Aug 03, 2012
Length: 490 pages
Edition : 1st
Language : English
ISBN-13 : 9781849683425
Vendor :
Microsoft
Category :
Languages :

What do you get with Print?

Product feature icon Instant access to your digital eBook copy whilst your Print order is Shipped
Product feature icon Paperback book shipped to your preferred address
Product feature icon Download this book in EPUB and PDF formats
Product feature icon Access this title in our online reader with advanced features
Product feature icon DRM FREE - Read whenever, wherever and however you want
OR
Modal Close icon
Payment Processing...
tick Completed

Shipping Address

Billing Address

Shipping Methods
Estimated delivery fee Deliver to United States

Economy delivery 10 - 13 business days

Free $6.95

Premium delivery 6 - 9 business days

$21.95
(Includes tracking information)

Product Details

Publication date : Aug 03, 2012
Length: 490 pages
Edition : 1st
Language : English
ISBN-13 : 9781849683425
Vendor :
Microsoft
Category :
Languages :

Packt Subscriptions

See our plans and pricing
Modal Close icon
$19.99 billed monthly
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Simple pricing, no contract
$199.99 billed annually
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts
$279.99 billed in 18 months
Feature tick icon Unlimited access to Packt's library of 7,000+ practical books and videos
Feature tick icon Constantly refreshed with 50+ new titles a month
Feature tick icon Exclusive Early access to books as they're written
Feature tick icon Solve problems while you work with advanced search and reference features
Feature tick icon Offline reading on the mobile app
Feature tick icon Choose a DRM-free eBook or Video every month to keep
Feature tick icon PLUS own as many other DRM-free eBooks or Videos as you like for just $5 each
Feature tick icon Exclusive print discounts

Frequently bought together


Stars icon
Total $ 120.98
Windows Presentation Foundation 4.5 Cookbook
$65.99
MVVM Survival Guide for Enterprise Architectures in Silverlight and WPF
$54.99
Total $ 120.98 Stars icon
Banner background image

Table of Contents

11 Chapters
Presentation Patterns Chevron down icon Chevron up icon
Introduction to MVVM Chevron down icon Chevron up icon
Northwind – Foundations Chevron down icon Chevron up icon
Northwind—Services and Persistence Ignorance Chevron down icon Chevron up icon
Northwind—Commands and User Inputs Chevron down icon Chevron up icon
Northwind—Hierarchical View Model and IoC Chevron down icon Chevron up icon
Dialogs and MVVM Chevron down icon Chevron up icon
Workflow-based MVVM Applications Chevron down icon Chevron up icon
Validation Chevron down icon Chevron up icon
Using Non-MVVM Third-party Controls Chevron down icon Chevron up icon
MVVM Application Performance Chevron down icon Chevron up icon

Customer reviews

Top Reviews
Rating distribution
Full star icon Full star icon Full star icon Half star icon Empty star icon 3.6
(14 Ratings)
5 star 35.7%
4 star 7.1%
3 star 35.7%
2 star 21.4%
1 star 0%
Filter icon Filter
Top Reviews

Filter reviews by




David Mar 05, 2016
Full star icon Full star icon Full star icon Full star icon Full star icon 5
The book acknowledges the plethora of contradictory, even misleading, generally available information on using MVVM with WPF (I don't use Silverlight). Against this background, its objective approach helped clarify my thoughts, so that I am delighted with what I have been able to put into practice.A few other reviews have mentioned the number of external references to topics that are out of scope. I bought the book specifically to address MVVM/WPF integration, so I have not minded them at all. For me, it has a specific focus for which it caters admirably.
Amazon Verified review Amazon
AmazonCustomer Aug 17, 2019
Full star icon Full star icon Full star icon Full star icon Full star icon 5
I've owned this book for several years now and have implemented MVVM in real world WPF projects.The first few chapters explain why you'd actually want to use MVVM by contrasting the pattern with predecessor UI patterns using several code based example. This provides a solid and convincing justification for the pattern.The author then then goes on to build a more substantial application conforming to industry best practices and explaining his choices throughout.I was initially skeptical of the use of the MVVM lite library but when it came to the reality of implementing an MVVM project without MVVM lite you'l be forced to either write your own version of the libraries functionality so you might as well use it.Its a shame the author mentions Silverlight first in the title because that dead tech will probably put people off but there is very little Silverlight this is a solid WPF book.
Amazon Verified review Amazon
B. Henry Dec 21, 2015
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Good stuff
Amazon Verified review Amazon
Evil J Oct 25, 2012
Full star icon Full star icon Full star icon Full star icon Full star icon 5
Lots of words have been written about MVC, MVP, and MVVM among other programming patterns and how they are all designed to help the developer create applications quicker that are more maintainable. If you are working in XAML with WPF or Silverlight, then MVVM is the defacto standard for application development. What I appreciated in this book was that this is the assumption that is made and it isn't an apologetics title on why you should pick MVVM as your design pattern. I hate when a book spends the first half trying to sell me on the pattern... I'm already committed to it that is why I buy the title! The authors here recognize that and don't waste my time trying to convince me why I should be using the pattern.The book starts out with a bit of history by talking about monolithic application design and walks the reader down memory lane touching on RAD, MVC, MVP, and finally bringing us to MVVM. I thought that was really cool. For someone not familiar with this heritage, it is good to learn it as it gives some context about MVVM and how we got here. What I also thought was good is that the same example application is used to describe each model with a discussion of how the application would be implemented under that model. That really highlights the differences and helps the reader understand the drive towards MVVM.But let's face it; this book is about MVVM and how to successfully use it in 3-tier applications at an enterprise-ready level. And that is important... because it means the details are there to help the developer identify the dark corners of application development that take an effort from average to enterprise. (The book covers WF and workflows... In an MVVM book! That is how detailed it is!)At over 450 pages, the book is crammed full of useful information. The reader is literally walked-through the design and development of a 3-tier enterprise application as each chapter introduces a new concept building on the previous one to add layer after layer of functionality. Along the way, all the challenges of complex application development are covered as well as how to structure for maintainability. The pain points in MVVM are talked about where applicable as well as ways to mitigate that pain. I've been writing apps in the MVVM pattern for years and I was still able to get a lot from this book.Is this for absolute beginners? Maybe not... things move fast and tackling a big app right out of the gate is probably a setup for failure. But for someone that has some familiarity with C#, WPF, and/or Silverlight (and MVVM or even MVC/MVP) this book will be great. I found that I was relying on some of my knowledge from outside the book to help understand and grasps the concepts inside the book but I chalk that up to just the fact that MVVM and enterprise application development are complex topics. It is impossible to present every facet of knowledge in one place. Plus, even though the pattern is well defined, there is a lot of wiggle room and space for interpretation.Overall, I think that for people working in WPF or Silverlight and that know some MVVM, even at a beginner level; this book will give them tons of useful information. Even if, like me, you've been doing it for a while, I think there is still a lot of information to gain. I have gone back a few times to refer to a section while working on projects. I've had this book for just over 2 months now and it has proven to be a great guide to getting deep with MVVM and then again as a reference to help refresh my memory as I tackle projects.
Amazon Verified review Amazon
Alfredo Sep 13, 2012
Full star icon Full star icon Full star icon Full star icon Full star icon 5
The job of the reader is to understand what the presentation design patterns define with respect to purpose and principles and how technologies and real world environments can enhance or limit the purpose without violating the principles. While the name of the game is facilitation of development via SoC, performance improvements can be gained with the use of the right design and complementary technologies.This book really covers all the relevant topics, most are discussed in detail and some are just alluded to for further investigation. The authors show you one way of doing something, and then improve upon it later. This is a book designed to teach students (very heavy on the sample code), not to tell experienced developers what they already know. It would actually serve as a great syllabus in a course.Chapter 1 tackles the monolithic approach but it also criticizes the RAD approach. If one didn't know better you'd think RAD was always the way to go, so I'm happy to see it brought down a notch. MVC and MVP are historically examined and differentiated but not bashed; the benefits and limitations are simply put out there. They don't muddy the waters with too much MVVM up front. Takeaway sections can be referenced if you begin to second guess your design pattern of choice.Chapter 2 makes it clear why MVC's strong presence is due primarily to the lack of web client technologies with strong declarative binding support (with the presence of frameworks such as KnockoutJS and AngularJS, the MVVM pattern is gaining wider support). Supporting patterns such as Property Object and Command are introduced along with their technical implementations and from what need they arose.Chapter 3 can be quickly skimmed if you don't intend to implement the ongoing sample project.While in Chapter 4 I was surprised to not find the phrase "RIA Services" in the entire book. This was such a good decision, it is much better left to another book entirely; any brief samples would only be confusing. This chapter has a lot of value in terms of putting you in the right frame of mind regarding persistence ignorance; it just tends to force you into a UoW pattern, which is good. But I don't see too much value in DDD over Table Module for your data object model since for the most part your data object model will mimic the database tables. I thought DDD was for your logical entities... yet nevertheless the authors' advice is applicable in principle.In Chapter 5, the use of attached behaviors to map multiple control events to one or more commands is impressive.Chapter 6 left me with a bitter taste in that I consider the View Model to be the context of a View, AKA "Region" of the page, synonymous with an ASP.NET user control. I don't consider the View Model to be a context for Data Templates. That's equivalent to saying that each item in a listbox is a View, that's why it's called a DATA Template, and listboxes have ITEM Templates, not VIEW Templates. I don't like the great gap in scope between a classic View and a View for each item in a collection. I am accustomed to exposing data objects directly to Data Templates, as collection properties on the View Model. View Models contain and represent much more than just database records, a View Model:* contains cross data entity business rules related to the View* communicates with other View Models (Event Aggregation) to enforce cross View business rules* defines commands to object context logic* is not a database record (which for the most part are only objects because that's how .NET works, they are more relationally defined than object oriented)* maps to one or more logical entities that provide its functionality* pulls together multiple data objects into a well prepared context for a View* is not a data objectThe authors do not contrast the difference between a data model and a logical object model. Take a Product Manager class as opposed to a Product class, their purpose couldn't be more different and only one gets persisted. How often do you have a data object that serves as the entire context of a classic View? It's typically a collection for a list control or two with surrounding menu context, so the argument that the View Model would require the data model's properties to be redundantly defined on it, isn't considering that it's usually only exposing a couple of collections.If you wouldn't think to navigate to a View or establish it as a named region, then it doesn't need a View Model.If you understand my point of view as well as the authors', then you're on the right path, regardless of which approach you choose to implement.Chapter 7 will save you lots of time on the learning curve, as any WPF or Silverlight purest will tell you.Chapter 8 was very interesting since I've spent a lot of time trying to figure out how WF fit with a MVVM style application. The more you play with it the more you realize that your application probably doesn't even need it (it fits best in an automated distributed system), although it's good to know it's there if you do. The absolute lack of guidance from Microsoft on this subject is depressing.Chapter 9 is excellent. Validation is validation but with so many options you want to know how to pick the appropriate approach.Chapters 10, and 11 are pretty much what they say they are, but you're getting into the fringe.It's too bad custom controls weren't discussed in any detail, since the MVVM pattern applies just as well, and offers the chance to highlight the theoretical aspects of MVVM in a much more compact scenario.If you're relatively new to WPF and Silverlight using the MVVM pattern, please realize there are supporting patterns, principles, and frameworks that make it practically possible. Chapters 4, 5 and 6 are the theoretical meat of this book, re-read them if necessary. You don't have to memorize patterns and principles, just develop a list of questions to ask yourself in the design phase.Writing testable code often automatically has you asking all the right questions, which BTW this book does a superb job of doing.
Amazon Verified review Amazon
Get free access to Packt library with over 7500+ books and video courses for 7 days!
Start Free Trial

FAQs

What is the delivery time and cost of print book? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela
What is custom duty/charge? Chevron down icon Chevron up icon

Customs duty are charges levied on goods when they cross international borders. It is a tax that is imposed on imported goods. These duties are charged by special authorities and bodies created by local governments and are meant to protect local industries, economies, and businesses.

Do I have to pay customs charges for the print book order? Chevron down icon Chevron up icon

The orders shipped to the countries that are listed under EU27 will not bear custom charges. They are paid by Packt as part of the order.

List of EU27 countries: www.gov.uk/eu-eea:

A custom duty or localized taxes may be applicable on the shipment and would be charged by the recipient country outside of the EU27 which should be paid by the customer and these duties are not included in the shipping charges been charged on the order.

How do I know my custom duty charges? Chevron down icon Chevron up icon

The amount of duty payable varies greatly depending on the imported goods, the country of origin and several other factors like the total invoice amount or dimensions like weight, and other such criteria applicable in your country.

For example:

  • If you live in Mexico, and the declared value of your ordered items is over $ 50, for you to receive a package, you will have to pay additional import tax of 19% which will be $ 9.50 to the courier service.
  • Whereas if you live in Turkey, and the declared value of your ordered items is over € 22, for you to receive a package, you will have to pay additional import tax of 18% which will be € 3.96 to the courier service.
How can I cancel my order? Chevron down icon Chevron up icon

Cancellation Policy for Published Printed Books:

You can cancel any order within 1 hour of placing the order. Simply contact customercare@packt.com with your order details or payment transaction id. If your order has already started the shipment process, we will do our best to stop it. However, if it is already on the way to you then when you receive it, you can contact us at customercare@packt.com using the returns and refund process.

Please understand that Packt Publishing cannot provide refunds or cancel any order except for the cases described in our Return Policy (i.e. Packt Publishing agrees to replace your printed book because it arrives damaged or material defect in book), Packt Publishing will not accept returns.

What is your returns and refunds policy? Chevron down icon Chevron up icon

Return Policy:

We want you to be happy with your purchase from Packtpub.com. We will not hassle you with returning print books to us. If the print book you receive from us is incorrect, damaged, doesn't work or is unacceptably late, please contact Customer Relations Team on customercare@packt.com with the order number and issue details as explained below:

  1. If you ordered (eBook, Video or Print Book) incorrectly or accidentally, please contact Customer Relations Team on customercare@packt.com within one hour of placing the order and we will replace/refund you the item cost.
  2. Sadly, if your eBook or Video file is faulty or a fault occurs during the eBook or Video being made available to you, i.e. during download then you should contact Customer Relations Team within 14 days of purchase on customercare@packt.com who will be able to resolve this issue for you.
  3. You will have a choice of replacement or refund of the problem items.(damaged, defective or incorrect)
  4. Once Customer Care Team confirms that you will be refunded, you should receive the refund within 10 to 12 working days.
  5. If you are only requesting a refund of one book from a multiple order, then we will refund you the appropriate single item.
  6. Where the items were shipped under a free shipping offer, there will be no shipping costs to refund.

On the off chance your printed book arrives damaged, with book material defect, contact our Customer Relation Team on customercare@packt.com within 14 days of receipt of the book with appropriate evidence of damage and we will work with you to secure a replacement copy, if necessary. Please note that each printed book you order from us is individually made by Packt's professional book-printing partner which is on a print-on-demand basis.

What tax is charged? Chevron down icon Chevron up icon

Currently, no tax is charged on the purchase of any print book (subject to change based on the laws and regulations). A localized VAT fee is charged only to our European and UK customers on eBooks, Video and subscriptions that they buy. GST is charged to Indian customers for eBooks and video purchases.

What payment methods can I use? Chevron down icon Chevron up icon

You can pay with the following card types:

  1. Visa Debit
  2. Visa Credit
  3. MasterCard
  4. PayPal
What is the delivery time and cost of print books? Chevron down icon Chevron up icon

Shipping Details

USA:

'

Economy: Delivery to most addresses in the US within 10-15 business days

Premium: Trackable Delivery to most addresses in the US within 3-8 business days

UK:

Economy: Delivery to most addresses in the U.K. within 7-9 business days.
Shipments are not trackable

Premium: Trackable delivery to most addresses in the U.K. within 3-4 business days!
Add one extra business day for deliveries to Northern Ireland and Scottish Highlands and islands

EU:

Premium: Trackable delivery to most EU destinations within 4-9 business days.

Australia:

Economy: Can deliver to P. O. Boxes and private residences.
Trackable service with delivery to addresses in Australia only.
Delivery time ranges from 7-9 business days for VIC and 8-10 business days for Interstate metro
Delivery time is up to 15 business days for remote areas of WA, NT & QLD.

Premium: Delivery to addresses in Australia only
Trackable delivery to most P. O. Boxes and private residences in Australia within 4-5 days based on the distance to a destination following dispatch.

India:

Premium: Delivery to most Indian addresses within 5-6 business days

Rest of the World:

Premium: Countries in the American continent: Trackable delivery to most countries within 4-7 business days

Asia:

Premium: Delivery to most Asian addresses within 5-9 business days

Disclaimer:
All orders received before 5 PM U.K time would start printing from the next business day. So the estimated delivery times start from the next day as well. Orders received after 5 PM U.K time (in our internal systems) on a business day or anytime on the weekend will begin printing the second to next business day. For example, an order placed at 11 AM today will begin printing tomorrow, whereas an order placed at 9 PM tonight will begin printing the day after tomorrow.


Unfortunately, due to several restrictions, we are unable to ship to the following countries:

  1. Afghanistan
  2. American Samoa
  3. Belarus
  4. Brunei Darussalam
  5. Central African Republic
  6. The Democratic Republic of Congo
  7. Eritrea
  8. Guinea-bissau
  9. Iran
  10. Lebanon
  11. Libiya Arab Jamahriya
  12. Somalia
  13. Sudan
  14. Russian Federation
  15. Syrian Arab Republic
  16. Ukraine
  17. Venezuela