- UI Design Patterns
- The Model-View-ViewModel Pattern
- Binding the View Model to the View
- Lists and Data Elements
- Summary
The Model-View-ViewModel Pattern
In 2005, a developer named John Gossman working on WPF—which at the time was code-named Avalon—published a blog post that would ultimately introduce the MVVM pattern to the world (http://blogs.msdn.com/b/johngossman/archive/2005/10/08/478683.aspx). In his post he described “a variation of Model/View/Controller (MVC) that is tailored for modern UI development platforms where the View is the responsibility of a designer rather than a classic developer.”
The introduction to his post provides valuable insight into one of the original motivations for the pattern: the designer/developer workflow. His post further explains that the view is defined declaratively (a reference to Xaml) and is responsible for inputs, keyboard shortcuts, visual elements, and more. The view model is responsible for tasks that are too specific for the general model to handle (such as complex UI operations), for maintaining the view state, and for projecting the model to the view, especially when the model contains data types that won’t map directly to controls.
Although MVVM is most often compared to MVC, it actually makes more sense to think of it as a specialized flavor of MVP that uses data-binding and the Visual State Manager (VSM). Instead of raising events, the view drives the view model through data-binding—whether it is by updating a value that in turn synchronizes to a property on the view model, or by mapping an event to a command that fires on the view model. The input aspects of the controller have been absorbed into the view, while the presentation logic is distributed between the view model and the view. Presentation logic in the view takes the form of behaviors, triggers, visual states, and value converters.
Like other patterns, MVVM is a solution to common problems. When implemented correctly, it should make the job of building a Silverlight application easier. Unfortunately, the pattern can be abused and end up slowing down projects and making them more complex than necessary. I’ve built dozens of large enterprise Silverlight applications. Not all applications followed the MVVM pattern, and in many cases my company was called in to rescue a failing project by refactoring the application to use MVVM. Although the pattern is now over six years old, there are many lingering misconceptions. Table 7.1 lists some of these misconceptions and the truth that addresses them.
Table 7.1. Common MVVM Misconceptions
Misconception |
Truth |
MVVM is extremely complex. |
MVVM can be incredibly simple when implemented correctly. |
Code-behind isn’t allowed in MVVM. |
Code-behind is simply an extension of the declarative Xaml for the view. The view is responsible for managing the user interface, including user inputs, and there is no reason the code-behind cannot deal with events and interactions. |
MVVM is hard to your implement. |
Many frameworks exist that can enable you to have project up and running in minutes. I recorded a video to demonstrate building a feed reader from scratch using MVVM in just 30 minutes (http://vimeo.com/17926625). |
MVVM eliminates the need for value converters. |
Value converters are reusable, testable pieces of code that can map data from the model to the view, and there is no reason to eliminate them when using the MVVM pattern. |
MVVM reduces the performance of the application. |
The improper implementation of any pattern can create performance issues. Proper use of MVVM facilitates unit tests that can help tweak and improve performance. |
MVVM is only good for very large projects. |
A good MVVM framework coupled with solid understanding is just as suitable for small projects as it is big ones. |
MVVM is about commands and messaging systems. |
MVVM simply specifies the responsibilities of various modules within the code. Commands, messaging frameworks, and other constructs are just helpers and building blocks. |
MVVM is hard to understand. |
MVVM is no more difficult to understand than data-binding and the Visual State Manager, because it really is just a pattern that describes how best to use these features. |
In the rest of this chapter you’ll learn about the various parts of MVVM and how to apply it. If you’ve read the previous chapters in this book and followed the examples, you already have an understanding of MVVM because you’ve created specific classes that implement property-change notification to facilitate data-binding. Those classes can actually be thought of as your view models.
Contrary to the misconceptions about MVVM, there are many advantages the pattern provides above and beyond the separation of design from development. In my experience, these are the top ten benefits you may receive by using MVVM in your applications:
- A clean separation of concerns (decoupling)—MVVM follows best practices for software architecture.
- Designer/developer workflow—MVVM enables parallel development and design by multiple team members working on the same project.
- Unit testing—You will learn more about testing in Chapter 9, “Testing.”
- Use of data-binding—MVVM takes direct advantage of the rich and powerful data-binding system in Silverlight.
- Improved code reuse—View models can be used to power multiple views, and various helpers and scaffolding can be reused throughout your project and across various products in your organization.
- Modularity—MVVM encourages a modular design that makes it easy to modify parts of the application independently of each other.
- Refactoring containment—Through the clean separation of concerns, MVVM minimizes the impact to other areas of the application from refactoring.
- Extensibility—A well-designed MVVM framework makes it easy to extend the application by adding new screens, modules, and plug-ins.
- Tools support—Various tools, such as Expression Blend and the designer, are built in to Visual Studio that can take direct advantage of MVVM.
- Pattern vocabulary
The final item, pattern vocabulary, requires some additional explanation. When you are learning how to read, there is a strong correlation between the size of your vocabulary and your ability to comprehend what you are reading. This should not be surprising because vocabulary provides the building blocks for the text you are trying to comprehend, and not understanding those blocks can lead to confusing conclusions and misinterpretations of the text. Although there is a strong correlation, vocabulary certainly doesn’t guarantee comprehension because you must be able to piece the words together and derive their meaning as a whole.
Developing software is an exercise that also involves a vocabulary. You start with the vocabulary of the language you are developing in. Programs have their own syntax and grammar, and comprehension relies on your ability to interpret the keywords correctly and understand them in context. Patterns provide a higher level vocabulary that can describe entire subroutines and components within the system. As with vocabulary, knowing a pattern isn’t the same thing as comprehending how it best fits into a software application (or whether it belongs at all).
The more you are able to understand and integrate patterns, the more you will be able to build your vocabulary and better comprehend complex software systems. Although I am not aware of any scientific studies that definitively support this conclusion, it is based on years of working in the software industry with individuals of varying degrees of skill and experience. I’ve found the developers who are involved in the most successful projects and who have tackled the most complex systems also tend to have a strong pattern vocabulary. They are not only aware of many patterns that exist in software development, but also understand when and where they make sense.
I believe MVVM is popular because it has been so successful at providing the benefits listed earlier when implemented correctly. MVVM is an important pattern to learn and understand for Silverlight LOB applications—even if only to articulate why it doesn’t make sense for a particular project. Like all patterns, it is a tool and must be used for the right job. In the next few sections I’ll cover MVVM in more detail to help you learn the pattern and determine when it makes sense to use it in your applications. I’ll start by examining the components that make up MVVM.
The Model
The model is often confused with a “data model,” which is far too specific. A better definition is the application’s model of the world. It is the model that encompasses everything that must happen in order to solve the business problem without defining a specific user interface or presentation of data. Some like to call this the domain model, but a domain model is a conceptual representation, whereas the model in MVVM is an actual implementation.
To provide a simple example, a banking system might contain customers and accounts. The representations of customers and accounts are part of the model. The model describes how they are related: A customer has one or many accounts. It describes state (an account is open or closed) and provides behaviors (an account accrues interest). To make the model work requires implementations of classes with properties, a database to store the information, and loads of APIs to fetch data, transfer it, and apply various algorithms.
When built correctly, the model should expose only the parts needed by the application. For example, the presentation layer shouldn’t have to worry about how the data is stored (is it in a database or in an XML file?) or how the data is retrieved (was it parsed and passed as a binary object over a TCP/IP socket or sent over a REST service?). A model that is too open will create unnecessary dependencies and overcomplicate the code.
Regardless of whether you are a junior developer learning how to build applications or a seasoned architect who has worked on massive enterprise software projects, I believe it is important to know some key fundamentals of software design. As I stated before, I can’t point to a scientific study that states you must follow these principles to build quality software, but I can certainly point to my experience that developers who follow these principles tend to write code that ships with fewer defects, is easier to understand, can be readily maintained, and is produced more quickly than code written by developers who ignore them. These principles will assist you with building the model portion of your MVVM application correctly.
Don’t Repeat Yourself
The first principle to follow is called D.R.Y., which stands for Don’t Repeat Yourself. This is an easy principle to follow. As you are writing your software, you will find there are certain blocks of code and algorithms that repeat themselves. An experienced developer is able to quickly identify those patterns and refactor them into a single class or method. Not only does this save time by not repeating the pattern over and over, it also places the pattern in a single location to make it easier to change the algorithm when it becomes necessary. Finally, it protects other developers by giving them less opportunity to write bad code—it’s a lot easier to introduce bugs in a ten-line block of code than it is calling a simple API.
I recently came across an example of D.R.Y. for a customer project. I was writing a web service interface and quickly noticed a common pattern. Consider the following method that represents a completed web service call:
private static void ClientGetWidgetCompleted(object sender, GetWidgetCompletedEventArgs e) { if (e.Error != null) { throw e.Error; } var callback = e.UserState as Action<Widget>; if (callback != null) { callback(e.Result); } }
Now look at a similar method that returns a list of related widget items:
private static void ClientGetWidgetItemsCompleted(object sender, GetWidgetItemsCompletedEventArgs e) { if (e.Error != null) { throw e.Error; } var callback = e.UserState as Action<IEnumerable<WidgetItem>>; if (callback != null) { callback(e.Result); } }
The second method introduced what is referred to as code smell or the symptom of a deeper problem. In this case, the problem wasn’t a bug or defect with how the application runs, but instead a level of complexity that just isn’t necessary. Each time the method is implemented, the developer must remember to check for an error and throw it, then cast the state to get the callback, and finally invoke the callback. Forgetting just one simple step, such as invoking the callback, could result in application defects—in this case, one that would be hard to find because the code will simply wait for a result that never comes.
By looking at the methods and deciding that I did not want to repeat myself, I was able to create an extension method to simplify things. The method uses generics to abstract the part of the formula that changes—in this case the return type—while implementing the rest of the pattern in a single place.
public static class ServiceExtensions { public static Action<T> GetCallback<T>( this AsyncCompletedEventArgs args) { if (args.Error != null) { throw args.Error; } var callback = args.UserState as Action<T> ?? (obj => { throw new Exception(); }); return callback; } }
Now the previous service calls can be simplified to the following—notice how the code does not repeat the pattern, but instead focuses on the parts that are unique to each method:
private static void ClientGetWidgetCompleted(object sender, GetWidgetCompletedEventArgs e) { e.GetCallback<Widget>()(e.Result); } private static void ClientGetWidgetItemsCompleted(object sender, GetWidgetItemsCompletedEventArgs e) { e.GetCallback<IEnumerable<WidgetItem>>()(e.Result); }
The pattern here worked for the specific application, but I wouldn’t necessarily build it upfront in every project. There is always the possibility that another project will handle service calls completely differently and not use the same type of mechanism to abstract calls. This brings me to the next principle.
You Aren’t Going to Need It
You Aren’t Going to Need It—or Y.A.G.N.I—is one of the most difficult principles to follow. Some software architects tend to dislike this principle because it keeps them from working on really fun, complex systems by introducing all sorts of frameworks and patterns. Instead, it forces them to design simple, straightforward solutions that are written so cleverly that adding additional features when they are actually needed is not a problem. I’ve watched many systems collapse under the burden of features that were piled on “just in case” and then never used. The understanding of what may be needed often fails to meet the future requirement, and results in having to rip out the initial “guess” and extra refactoring to supply the more appropriate solution.
One example of this is the Enterprise Library, a library of implementations that is released by Microsoft’s own Patterns and Practices Team. I was asked by someone how excited I was when a build of this was released for Silverlight, and this person was surprised when I said I may not use it.
Don’t get me wrong—there are a lot of great features in the library and I’ve used it in many projects. The key, however, is to identify the parts you’ll need and use rather than plugging in the entire system and using only a fraction of it. A really common exercise is to load the Exception Handling Application Block because it allows dynamic configuration of your exception policies. Want to change where they are logged? Great—it handles that for you. Want to write certain exceptions to the database, swallow other exceptions, and write yet a third class to a rolling trace file? Not a problem! It’s a very flexible system.
What often ends up happening is the system is put in place, the configuration files are tinkered with until they are working, and then the application goes to production... and that’s it. The policies are never tweaked, there is no swapping of targets or sources, and often there is not a segregation of exception types. Essentially, a big piece of plumbing is put into place when a simple logging mechanism would have sufficed. It gets worse because then when someone does want to log or trace a certain set of events, they have to wade through reams of configuration to finally get the output they are looking for.
It is a much more straightforward approach to assume you aren’t going to need it, but build the code so that it is easy to add later on. In the case of exceptions, you can always provide a consistent pattern of exception blocks that are processed by a common interface, and start with an implementation that sends them to the event log. If you find a real need to enforce a specific set of policies, you can implement the Exception Handling Application Block in your single class and introduce the policies without having to touch the rest of the application.
Another example of Y.A.G.N.I. is caches. Despite there being no metrics to support it, many developers feel that a cache is needed. I’ve often heard, “We’re moving data, we need a cache.” But usually the overhead of synchronizing the cache for thread safety and applying various cache-expiration policies can make it slower than fetching the data directly from the database! There is no rule of thumb, and you must be prepared to analyze performance and obtain metrics in order to determine if there truly is a benefit.
Following this principle simplifies projects and makes them faster and easier to deliver. The fear many people have is that it will be too complex to rip out the code and replace it when a need is identified down the road, but you can cover those bases as well. As long as you build your code on a “solid” foundation, you’ll find it’s easy to introduce the features you do need when you are certain you really need them. That foundation is the next principle.
The S.O.L.I.D. Principle
I saved this principle for last because it’s the more complex one to learn and really builds on the first two. I also cheated because S.O.L.I.D. is not a single principle, but really a set of five principles that I believe are keys to building quality code. I include them here because I know firsthand the impact of applying them can have on the quality of your code. For MVVM to work well, the model must be done right, and S.O.L.I.D. is the foundation to make that happen.
An entire book could be written (and probably has been) about these principles, so I’ll only briefly introduce them here and hope you will explore them further online. S.O.L.I.D. is an acronym for the following principles:
- Single Responsibility—A class should be responsible for exactly one thing. A great example is a class that reads and parses a comma-separated text file. This class has more than one responsibility because it both reads the file and parses it. A better strategy would be to provide a class that reads files and a class that parses them. Now you can easily add a class that retrieves the file from a web service and passes it along to the parser, or a class that uses the reader to retrieve configuration information.
- Open/Closed Principle—A class should be open for extension but closed for modification. A great example is a class that has the responsibility for logging. It is common to provide a simple API that takes a message to log. This is fine until you need to specify different places for messages to go. Now the class has to be opened up to modify it and the API changed to provide routing information. A more robust implementation would be a logger that provides a delegate for the call and allows registration of logging implementations. The class can now be extended by providing different logging mechanisms, and does not have to be opened to modify the behavior.
- Liskov Substitution Principle—This principle states that you should be able to substitute any class for its base class and have it behave the same way. The classic example of this is a square and a rectangle. If you derive the square from the rectangle and override the properties so that setting the width always sets the height, you can no longer substitute the class for its base class. Casting the square to a rectangle will provide unexpected behavior because with a rectangle, you expect to be able to set different widths and heights. A better design is to create an abstract base rectangle that provides getters and private setters. The rectangle implementation derives from this and makes the setters public, whereas the square derives from the same base class and provides a “length of side” property that sets the width and height at the same time.
- Interface Segregation Principle—This principle goes hand in hand with single responsibility and states that interfaces should be fine-grained and focused on a specific set of related tasks. An example of this is in the .NET Framework. Most classes have a concept of equality (what makes two classes the same) and comparability (what order the classes are in relation to each other in a list). These concepts are used in different ways at different times. Instead of a single interface for both concepts, the .NET Framework provides the IComparable interface and the IEquatable interface. If you are only concerned with equality, you can cast to IEquatable and deal directly with that simple interface. There is no need to involve any other interfaces that aren’t being used.
- Dependency Injection/Inversion of Control—This principle goes hand in hand with the single responsibility. When the class is required to create an instance of the logger, it is taking control of that dependency and going beyond its single responsibility. This can lead to issues if you decide to swap to a different logging implementation and have to track down all of the places the old logger was created. Instead, you can simply provide a logging interface that is passed into the class. Now the class no longer has the responsibility of finding the logger; it simply consumes the interface. The dependency for the logger was injected and the control inverted to something else. We’ll discuss this concept in detail in the next chapter, which covers the Managed Extensibility Framework (MEF).
These principles work together to provide a set of guidelines for writing flexible, extensible, testable, and maintainable code. When the model of your application follows these principles, the MVVM pattern can easily connect to the interfaces and classes that are needed without creating dependencies on parts of the system that have nothing to do with presentation logic. The model is the “application model” of the real world, but at some point that model must be presented to the end user. This is done through output, which in the case of Silverlight is a very rich and powerful user interface (UI). The screen that the user is presented with is referred to as the view.
The View
The view in Silverlight is the easiest part to describe—it is what interacts with the user. The view itself is the user interface. The user interface is almost always represented using the declarative Xaml markup. The Xaml participates in the dependency property system, and the view is able to present information to the user as well as respond to user inputs. Table 7.2 shows common parts of the views and their function.
Table 7.2. The View in MVVM
Component |
Description |
Xaml |
Declarative markup to provide layout, controls, and other components that make up a screen |
Value converters |
Special classes used to transform data to a user element type and back |
Data templates |
Templates that map data elements to controls |
Visual state groups |
Named states that impact the properties of various elements to provide a physical state based on the logical states of controls |
Storyboards |
Animations and transitions |
Behaviors |
Reusable algorithms that can be applied to various controls |
Triggers |
Algorithms that can be applied to controls and invoked based on configured events |
Code-behind |
Extensions of the Xaml markup to perform additional UI-specific tasks |
It should be obvious from Table 7.2 that the view is not completely ignorant of presentation logic. Commands map controls to actions on the controller, and data-binding declarations require knowledge of the structure of the underlying data to bind to. Animations, visual states, templates, behaviors, and triggers all represent various components of business logic that relate to the view.
What may not be as obvious is that all of these components are stateless with regard to the model of the application. Storyboards maintain a state (started, stopped, playing, and so on) and visual state groups maintain a state, but all of these states are related to the UI. Behaviors and triggers also operate based on events or act on generic controls and should not be designed with dependencies on the underlying data and business logic. Even code-behind is typically written to facilitate certain aspects of the UI. More complex code should go somewhere else—not because there is a rule that code-behind is not allowed, but because more complicated algorithms need to be tested, and having a separate and decoupled class makes it easier to test without having to wire up a full UI.
So where does the bulk of presentation logic go, and what is responsible for maintaining the business state of the application? This state includes the data that is being presented as well as the status of various commands and processes that both drive the UI and respond to user inputs. The answer is the essence of the MVVM pattern and the one element that makes it unique: the view model.
The View Model
The view model is what makes MVVM unique. It is simply a class that holds the responsibility of coordinating the interaction between the view and the model. The view model is where the bulk of the presentation logic should reside. In my opinion, a well-written view model can be tested without creating any views and has three main methods for communication with the view:
- Data-binding
- Visual states
- Commands and/or method calls
With this definition in mind, you’ve already created a view model. In the previous example using countries and states, the class that held the lists of states and countries was the view model. View models typically implement the property-change notification interface and one of the validation interfaces, and they also have some type of connection to the Visual State Manager. (I’ve seen some fairly elaborate hacks try to launch storyboards from view models when a simple visual state transition was all that was needed.)
In the previous examples, you used one of two methods to instantiate the class that functions as the view model. The first was to declare an instance in the resources and then use a static reference to bind to the class. The second was to instantiate the class directly in the data context definition for the root panel of the control, most often the grid called LayoutRoot. This approach creates a direct dependency between the view and the view model. Oftentimes you’ll want to share the same view model between different views (for example, when each view is a different representation of the same data) or you’ll want to dynamically change the view or view model based on a condition. It is also popular to use a design-time view model to represent design data, so the view model may be different during design and runtime. What is the best way to resolve this binding?