The Curious Case of the DesignData msbuild target

By Rob on Friday, December 18, 2009

2 Comments

Filed Under: .Net, Blendability, WPF

Thanks to Karl Shifflet and Unni Ravindranathan, I’ve finally gotten my head around a superior method of populating Blend (and VS 2010’s WPF designer) with sample data. This method, which I am going to call the “DesignData build target”, has the following advantages:

  • Blend will render your control as though it’s running with real data, as you design.
  • You won’t have to look up and type magic strings when you’re creating your bindings in Blend, Blend will be able to list the possible properties for you. Intellisensational!
  • Once compiled, your output dll will have NO baggage associated with your designery frivolities.
  • You don’t have to write lots of dummy / mock / stub code
  • You don’t even have to finish writing your viewmodel: however you DO have to write all your properties. This is a Good Thing, because it means your bindings will have the right magic strings in them.

How does it work?

First, you need a xaml file that builds up a sample instance of your viewmodel. Then all you have to do is set your Blend-specific DataContext to read from that file.

How do I tell Blend where to get this design data?

I’ve already blogged about the d:DataContext attribute, letting you set up a DataContext but only inside Blend. Another secret of the “d” xml namespace* is the {d:DesignData Source=someDesignDataFile.xaml} markup extension. So all you need in your UserControl or Window’s xaml is (usually on the first element):

d:DataContext=”{d:DesignData Source=someDesignDataFile.xaml}”

Karl’s blog post explains this in a little more depth.

What should be in this design data xaml file?

Just like you usually declare and populate a Window or UserControl, you can actually declare and populate your own view-models as the root element of a xaml file.

I got myself quite confused when I started working with this, because i didn’t realise that the root element of the xaml file should be a declaration of your viewmodel (I was actually placing my viewmodel within a ResourceDictionary >< ):

image

Here you can see I’ve defined a new ScrumBoardDesignDataDemo.ViewModel.Board, and started assigning some StageNames to it. I’ve used the clr namespace of my viewmodel classes (”ScrumBoardDesignDataDemo.ViewModel”) as the default xml namespace, instead of using what xaml files usually give you: the WPF control namespace. This just cuts down on the amount of typing required.

Normally, if you’re trying to instantiate a class without a public, parameterless constructor in xaml, you’ll get a compiler error. You also won’t be able to set properties that don’t have public setters. This technique allows Blend to do a bit of magic (reflectively creating a doppelganger of your class) that means you can bend the rules a little. More on that in my next post. Let’s just concentrate on getting some sample data into Blend.

By now, if you go into Blend and you try to set up a binding through the binding dialog (you get this by clicking on the peg next to a property, say ItemsSource, and choosing Binding), you’ll get  this helpful dialog:

image

See how the “Fields” tree is populated? That’s Blend’s version of intellisense, which means you no longer have to rely on knowing what the right property name is and hand-typing it.

Don’t go away! We are only half finished. Blend might be helping you choose property names, but we’re left with two more problems:

  • Blend isn’t displaying the sample data on screen.
  • This xaml file full of things that were only useful during development is being shipped to our customers inside our DLL.

We can fix this with a simple, but quirky little feature. By setting our design data file’s build action to “DesignData” we will have both of our problems fixed. Blend will realise that it’s ok to try and instantiate the object in the xaml file, and MSBuild will leave that xaml file out of our dll.

Setting DesignData build action for the first time

Normally, if you have a look at the properties of a xaml file in visual studio you’ll see that the Build Action is set to “Page”, or in the case of App.xaml it’s “ApplicationDefinition”. You’d think that it’s just a simple matter of typing the word “DesignData” into that combobox, but if you try this, you’ll get an exception message. Karl’s blog post is about a template he wrote that gets over this problem (by creating the file with all the right properties for you), but I had to figure out how the magic worked. It turns out that it’s relatively easy to work around this, you just have to edit the csproj file manually. I’ll walk you through doing this with visual studio – although you could just as easily use notepad.

Save all your files, right click your WPF project and choose “Unload”. This puts your project into a disabled state where you can’t do anything but hand edit the xml – which is exactly what we want to do.

  1. Right click the (now greyed out) project file and choose “Edit”.
  2. Locate the design data xaml file: image
  3. Edit the element to be a “DesignData” element with no children: image
  4. Save your csproj file, right click it and choose “Reload”

Now if you compile your project, you’ll find that all traces of this design data have been erased. Even better, if you go back to blend, you’ll see that your sample data is now present (you may have to rebuild or even reload the project):

image

So hopefully you’ve got another tool in your toolbox to help you (or your graphic designer) be effective in Blend. This is my favourite method so far, however there’s a couple of situations where it might be unsuitable. My next post will be all about why, but just so you know, this doesn’t work well when:

  • You want custom logic to occur in your viewmodel at design time (ie a Color property on your viewmodel changes whenever another integer property goes below zero)
  • You are using DataTemplates with no Key, just a DataType, and are relying on WPF to find the Template by your ViewModel class. (I understand that Silverlight doesn’t support this anyway, so no problem there!)

As always, I’m keen to know if I got anything wrong. There isn’t much information about the whole “d:” namespace on the internet! Also, my third and final post in this series (about the magic that makes this work and how said magic causes the above problems) will come with a demo project.

* This namespace is actually http://schemas.microsoft.com/expression/blend/2008, but Blend always imports this as “d”

Design-time Data in Expression Blend 3: Revisited

By Rob on Wednesday, December 16, 2009

0 Comments

Filed Under: .Net, Blendability, WPF

How do you make sure that what you (or your designer) sees in Expression Blend is exactly what the finished product will look like? A while ago I posted about the d:DataContext attribute in Blend, and how you could leverage that to populate Blend with realistic sample data. What was awesome about that?

  • You could see exactly how the finished product would look WHILE you were designing. True WYSIWYG means efficient designers!
  • Blend would provide typesafe data binding (I don’t think I called this out, but bringing up the Binding dialog box shows a chooser for correct property names. Bye bye magic strings!)
  • Having sample data in your ItemsPresenters/ListBoxes/TreeViews means that blend will let you edit the template in place, providing for an even more useful WYSIWYG experience.

If you haven’t tried it yet, but you are a XAML developer, I really suggest you try developing a screen in Blend when it’s populated with sample data. It’s just so easy. With unit tests on your viewmodel and sample data in Blend, you can finish a screen without ever having to press F5. Okay, maybe once, but you’ll be super happy because everything will work the way it was intended.

But at what cost?

Here are the problems that I’ve found with this approach in my own projects:

I often had to write alot of code, either dummy services to be passed into the viewmodel, or lots and lots of code to simply add things into the derived viewmodel’s collections. This code (often hacked together) got deployed, bloating my DLL file. This sucks extra for Silverlight where the DLL has to be downloaded every time) and, well, looking messy.

Depending on how your viewmodel is coded, you can’t always provide a simple design-time subclass that will populate everything you want. While Dependency Injection is awesome, we aren’t all the architects on our projects, and you might get handed a viewmodel that relies on hard coded dependencies that will do yucky things like call web services, read files or connect to a database. Other times, the ways in which the services are used are so watertight that it’s impossible (or at least it would take hundreds of lines of well thought out code) to make sure you had the right sample data for your designer.

Whenever the viewmodel or the services evolved, the design-time viewmodel needed to keep up. Often (on my project), the person adding features to the viewmodel wasn’t the person who cared about the design time viewmodel. Sometimes they took the effort to make sure the design-time viewmodel compiled, other times they deleted it. Which is fair enough, they didn’t write the code, they don’t see the point of it, all its doing is helping me design the XAML. So I’d end up restoring the file from source control, adding it back into the project, and maintaining it, which was often a lot of effort!

The solution

In the meantime, I’ve been reading around the blogosphere and found a better way to do design data in blend. It doesn’t rely on Dependency Injection (although DI is lovely), it’s easy to maintain and best of all it has NO effect on your compiled DLL. Stay tuned, having gone over (in tedious detail) the why of this new approach, I’m going write a new post about how.

Design-time Data in Expression Blend 3

By Rob on Sunday, August 2, 2009

6 Comments

Filed Under: .Net, Blendability, WPF

Update: I’ve detailed another technique for implementing design time data in blend in this newer post (using something called DesignData)

How can you develop a complex GUI without ever needing to build and run it?

WYSIWYG tools have come a long way in recent years, and if you’re building a WPF app you can’t go past Microsoft’s newly-released Expression Blend 3. I’d always preferred Blend 2 over Visual Studio 2008 for building my XAML, not least because of the Silverlight support. But Blend 3 has some features that quite frankly put me in GUI developer nirvana. Sketchflow shows an awful lot of promise, but what’s really floating my boat is the almost undocumented design time only datacontext support. What does that even mean? Let me use a simple WPF game, Connect 4, to show you…

Lets say I’ve already built the game. Just pretend I’d even written most of the XAML by hand. Here’s what the game looks like when it’s just been launched:

running_startgame

and here’s a game that’s partway through:

running_midgame

Perfect! The game is finished. Let me tell you about how this was built:

First, I coded my ViewModel, a bunch of classes that the XAML binds to. I’ve implemented INotifyPropertyChanged where necessary, and am using ObservableCollections instead of Lists. Note that this is quite a simple game, and I don’t really have a Model (in the M-V-VM sense) to speak of:

viewmodel

The Game is the root of everything, it tracks the Players, whos turn it is, and the Columns. Columns have a collection of Cells, and Cells track whether or not  a Token has been placed within. I’ve exposed a Command on the Column class, which picks up mouse clicks through a tall, invisible button in my XAML.

The XAML for this game is quite straightforward. The window contains an ItemsControl for the columns, the column template contains an ItemsControl for the cells, and the cell template renders a blue rectangle with a hole in it, and an optional token. Grab the code and check it out if you like - you can download all this source at the bottom of the post.

Lets say that I had made this Viewmodel the datacontext of my main Window in the Window.Loaded event:

public void Window_Loaded(...){LayoutRoot.DataContext=new Game(7,5);}

When I come to edit the XAML in Blend (let’s say I was planning to make the UI look a bit foxier, with animations and gradients and whatnot), this is what I get:

No data in blend

Which is fair enough, because blend is effectively running my program with no data. The Window_Loaded event won’t be fired by Blend, Blend only works with the XAML. The Visual Studio WPF designer has the same problem:

Visual Studio with no data

A feature of WPF that has always existed, and is completely cool is that you can shift this datacontext declaration into your XAML (Note that the use of an ObjectDataProvider is optional. If we were using silverlight we’d turn these constructor arguments into properties):

xaml_datacontext

Even before Blend 3 came along, doing this would mean that Blend (and Visual Studio) would be able to render your XAML as though a game had just started:

blend_realdata

This is nice. Now we get a good idea of how the GUI will really look when it’s running. We can even edit the XAML Templates in place (to see what I mean, download the source, right click an ItemsControl and edit its ItemsTemplate). We’d be in big trouble, however, if our ViewModel tried to call anything external on construction. For example, if we were building a twitter client, we might start downloading tweets through a web service call. This may or may not work when your code is running inside blend, but you’d be working with variable results, and as soon as you have an exception your designer would die. Jonas Follesoe has a great post that talks about how you can leverage Dependency Injection to get design time data services and runtime data services going. Go read it – that’s the post that made me realise how cool and important it is to get realistic data into your design time experience.

Now that Blend 3 is out, however, we’ve got a new tool in our belt. I don’t know if you’ve ever tried to change the DesignWidth or DesignHeight of a UserControl or Window from Blend. This post explains it pretty well, if you haven’t… Well, Blend will go and add a bunch of namespace declarations to your XAML, the most important alias of which is “d”. You’ll notice that you can put d:DesignWidth in any element you like, and nothing will happen at runtime. If you’re lucky (or you put it in the right place), blend will pick up this design-time data, and will render your control at that width, but only from within blend. The fact that your XAML can contain directives that will only be executed / honoured / considered by Blend is quite a powerful one.  There aren’t very many of these attributes available, however,  Blend 3 introduced a big one: “d:DataContext”.

I stumbled across d:Datacontext when I was playing with Blend 3 and tried adding a “live datasource” to my window. I thought it was going to behave just like the old way of setting a DataContext, but when I ran my application, there was no data! I had a look at the XAML and saw that a Datacontext was being set on my root element, just like I expected. But why wasn’t this being applied at runtime? I had a really, really close look at the XAML and spotted the “d:” before the word datacontext. Sweet. I deleted that, and the app ran as expected. My next thought was to try setting both a design time (d:) and a run time (no d:) Datacontext. It worked!

So the upshot is, now we can write code like this:

Setting a runtime AND a designtime datacontext

Or we can even set the real DataContext at runtime through the Window_Loaded (or whatever) event. Why is this exciting? Now, it’s easyto have blend show a truly representative form of our gui at design time:

blend_designdata

This, by the way, was acheived with the following design time subclass of Game:

code_designgame

Simple, no? By using an ObjectDataProvider, we can even prevent the DesignTimeGame from even being instantiated at runtime. If you’re coding in silverlight, you’d use an alternative: “d:DataContext={d:DesignInstance vm:DesignTimeGame}”, which would only work with an empty constructor – which is fine, because even if the runtime game requires constructor parameters, we can just write an empty designtimegame constructor that passes some dummy ones in.

Now that we’ve got this design time data, it’s much easier to spruce things up in the XAML (note that I’m editing a template in-place, and that I deliberately coded the design time model to have a token in the top left cell):

Editing a template in place with DesignTime data

I can’t really express how much easier this design time data can make your coding. So please, download my example project, open it in blend 3, and try (for example) adding a gradient overlay to the tokens, or even just resizing them. I have to admit, I didn’t hand code the XAML, I blended it in about ten minutes once I had the design time data available to me. And I didn’t have to run it till I’d finished!

Download the Connect4 Source here