It’s strange how common application architecture concerns are often ignored when it comes to supporting design-time data. By “design-time data” I am referring to pre-canned/fake data that is shown in Views while they are being displayed and edited in Expression Blend, or the Visual Studio visual design surface (a.k.a. Cider). There are two commonly seen approaches to supporting design-time data, each of which has severe drawbacks.
The most commonly seen approach is to use the d:DataContext and d:DesignInstance settings, which assign a designer-only data context for a View. The d:DataContext setting is ignored at run-time. This approach might seem great because it appears to keep design-time concerns consolidated in the View layer, which is the application layer in which “design-time” applies most directly. Nothing could be further from the truth. While having support for design-time data settings in the View layer might be great for early UI prototyping, it quickly becomes problematic as the rest of the application layers fall into place. The main problem with d:DataContext is that you now have to maintain two separate data contexts: the real one and the design one. Often people create entirely separate classes (or the designer tools generate dynamic classes) that mimic the real data objects and ViewModel objects that are used at run-time. This introduces a grotesque amount of duplication in the code base, just for the sake of showing some fake data in Blend. Having duplicated classes and extra settings in XAML just for the sake of design-time data strikes me as patently absurd. Have fun maintaining that application over the years…
Another common approach to creating design-time data is to keep the Views unaware of being in design-time, and to have their ViewModel objects handle it for them. This can easily lead to ViewModel classes that are littered with conditional logic that does one thing at design-time and something different at run-time. For example, a ViewModel that exposes a collection of Foo objects might issue a network call to get Foos at runtime, but just new up a bunch of Foos at design-time. Complicating ViewModels with repetitive conditional logic has a distinctly rotten code smell. If a ViewModel is a Model of a View, and a View should not be smart enough to know about “design-time” then, by extension, neither should its Model (i.e. the ViewModel). There has to be a better way!
Let’s take a step back and return to basics. In a layered application architecture, you separate the responsibilities and concerns into various layers. The View layer is responsible for showing things. In MVVM, the ViewModel layer is responsible for maintaining the logical state of Views, and processing user interactions. So far, we have not mentioned anything about data access. That’s the job of a layer below the ViewModel, perhaps called the Model layer or the DataAccess layer. If the application needs data, it goes to the DataAccess layer to get it. Design-time data is still data. It, too, should come from the DataAccess layer. Everything above the DataAccess layer, such as ViewModels and Views, should not be responsible for making decisions about how data is accessed: including design-time data.
In an application that I’m working on these days, I created an interface that the ViewModel objects use to access data and save data. It’s a modest sized application, so only one interface was needed so far. In a larger system you could have as many interfaces as you need to meet your data access requirements. The data access interface is implemented by three classes: one for run-time data access, one for design-time data access, and another for unit test-time data access. I use dependency inversion (specifically, the Service Locator pattern) to supply an implementation of the data access interface to the ViewModels. When the Views and ViewModels are created and start living their life, they are blissfully unaware of the run-time context in which they exist. When running my unit tests, I provide the data access implementation with whatever data I want it to return to the ViewModel that invokes it. The design-time implementation loads up some fake data from disk and passes it back into the ViewModels, which then bubbles it up to the Views on the design surface. Of course, at run-time the “real” data access implementation is used to retrieve the actual data processed by the application.
To me, the primary benefit of this approach is that almost the entire system has no concept of “run-time” vs. “design-time” vs. “test-time.” The only place in the code base that is aware of the run-time context is the tiny bit of code that determines which implementation of the data access interface to make available to the ViewModels.