Back in 2010, Josh Smith proposed that design-time data could, and probably should, participate in the entire Service/Model-ViewModel-View pipeline. That, at least, is how I read it. The theory goes that the “entire system” does not need knowledge of “run-time” vs. “test-time” vs. “design-time”. That may be true in the intermediate layers, but it strikes me as very wrong to involve Model-to-ViewModel processing when all you’re looking for is sample data for design-time.
To me, a proper separation of concerns would say that if the designer (Blend or the Cider designer in Visual Studio) needs to invoke any application code in order to show the UI being designed, something is wrong. What if the data access layer or Model-to-ViewModel code is being worked on by another developer? What if there’s a bug? Does that mean that the UI work should be completely blocked? Clearly, that’s an untenable proposition. Separation of concerns applies to the development process as well as it does to code itself. Working on the UI design should not act like running a unit test does. We aren’t looking to get code coverage every time we open the designer. And, in practice, invoking code in order to populate the designer can result in problems; using this technique sometimes (but not every time) makes the designer hang VS for 15-or-so seconds when I close the designer window.
With that in mind, the d:DataContext/DesignData technique feels better. The DesignData files themselves should be marked with a BuildAction of “None”, so they will not be present as artifacts in your build output. Further, since the standard is to make the “d:” prefix ignored, the XAML-to-BAML compilation ought to exclude the “d:DataContext” attribute from the output as well. (I’m not 100% sure this is, in fact, true… I’ll need to break apart an assembly to verify.) Even if it’s not excluded from the assembly, it’s simply a short and lightweight bit of text that says “you can find design data elsewhere”. There’s no additional code that might end up in your application whose only purpose is to populate data for your design-time experience.
The problem is that the actual implementation of the DesignData tooling is, in my mind, broken. Using DesignData requires that the designer actually create instances of your ViewModel classes. This causes errors if you have any read-only properties and attempt to provide design-time values for them, because the DesignData tool will fail with a “????” message. That DesignData fails in this case is the actual problem, one that leads to horrible work-arounds like manually creating design-time-only classes. And creating design-time-only classes is one of the things that Josh calls a “grotesque amount of duplication”. I’ll absolutely agree that it’s groteque when it’s hand-coded duplication, especially if it sneaks in as an artifact in your build output. Where I disagree is when it’s a class generated entirely dynamically and automatically by the design tools.
What the DesignData tool should be doing is creating dynamic objects based entirely on the XAML in the DesignData document. There shouldn’t even technically be a need to have an existing ViewModel at all! Why? Because there is a very loose coupling between a ViewModel class and the View; the View typically does not know the ViewModel’s actual type, it only knows that there will be a handful of properties with particular names. It should not be necessary to create instances of the actual run-time class in order to satisfy the designer’s needs. The only value in letting the DesignData tool know which ViewModel you’re faking is so that it can warn you if your design data doesn’t actually “look like” the ViewModel… but that should only be a warning, it shouldn’t break the designer the way that it actually does.
[[Now, with all that said, Josh does say that d:DataContext “might be great for early UI prototyping”, but that it becomes “problematic as the rest of the application layers fall into place”. To me, this is a non-issue, because the “rest of the application layers” shouldn’t even come into play when I’m working on the view.]]