Saturday, 24 February 2007

We’ve been working on a little research project recently with WPF and created a small (and hopefully handy) application where you can list your contacts synchronized from Microsoft CRM. We store the offline data in a SQL 2005 Compact Edition database and we access the data using Genome.

To make the experiment more exciting we involved a designer in the project, not only to design a cool UI using the features of WPF, but mainly to see how the collaboration between designer and developer works in reality.

Why do we need sample data?

There are a few designer tasks (when creating “pure design” assets like icons, images or nice color schemes) that are somehow less bound to the implementation process. I mean it does not make a big difference in the application’s implementation what the 64x64 icon looks like, but the designer still has to spend some time to draw it nicely. In these cases there must be a kind of “compatibility” so that the produced result can be seamlessly integrated into the application, but there is no real cooperation yet.

The challenge begins when we start working together on the software-related assets like the windows and controls. Our designer used Blend to open the same WPF solution to see/edit the forms of the application from the “designer’s perspective”. Browsing the WPF samples, I realized that all those XAML samples were designed carefully to make sure that you can see the data in design time as well. This is very important for the designer. Without the data behind the controls you don’t really see what your UI will look like. Hence, it is quite important to set up the application in a way that the designer can see some sample data in Blend in design time.

Here are our goals for involving the designer in the app development process:

  • The designer should work on the same application as the developer. We didn’t want to branch a “designer friendly” version of the source code and maintain it separately. The “same” application should display real data when executed and sample data during design (of course the configuration can be different, though).
  • The design time support should cause either no or insignificant overhead during runtime.
  • The sample data should be easily changeable by the designer.
  • The sample data retrieval should be cheap and easily maintainable (the data should not come from the database and the external dependencies should be minimized as much as possible).

WPF binding surprise: XML vs CLR object data source

The majority of the XAML samples use XML as data source. This is a simple way of providing sample data and the designer can easily change the actual data if necessary. The key problem with these samples was that they display the same sample data also when you execute the application. ;) I first thought it will be just fine to bind the sample XML in XAML and simply override (set) the ItemsSource of my list when the persistent objects are retrieved during runtime.

Unfortunately I had to realize that the binding syntax of WPF depends on the type of the underlying object. If I use my CLR objects during runtime I have to use the Path property in the Binding, e.g.:

<ObjectDataProvider x:Key="ContactsStaticDataProviderDS" d:IsDataSource="True" ObjectType="{x:Type Contacts:ContactsStaticDataProvider}"/>

...
<ListBox ItemsSource="{Binding Path=Contacts, Mode=Default, Source={StaticResource ContactsStaticDataProviderDS}}"/>

...
<TextBlock Text="{Binding Path=FirstName}" />

However, the Path property is ignored in case of an XML data source and the XPath has to be used instead:

<XmlDataProvider d:IsDataSource="True" Source="testdata.xml" x:Key="ContactsStaticDataProviderDS"/>

...
<ListBox ItemsSource="{Binding XPath=/Root/Contacts/Contact, Mode=Default, Source={StaticResource ContactsStaticDataProviderDS}}"/>

...
<TextBlock Text="{Binding XPath=FirstName}" />

Hence, assuming the XAML file is the same, I could use XML as sample data only if I use an XML data source during runtime as well. The ones who want to do this, please raise your hands! Nobody? Let’s see what we can do then instead.

(To the ones who raised their hands: it might still be worth reading the solution below, as I will use a brand new Genome extension that can be useful in other scenarios as well.)

Creating sample data for persistent types

As the XML data representation was not feasible for us we decided to create a CLR object graph that we can bind as sample data. However, during design time we don’t want to fetch data from the database, so we cannot return our persistent objects with Genome as is normally done during runtime.

On the other hand the sample objects should “look like” the persistent objects populated during runtime, so that the design time data binding and other designer features can work seamlessly.

We decided to use the brand new GenMock library (Beta version introduced in v3.1) to generate mock types for the persistent classes. The main purpose of this library is to support unit testing (mocking) but we can just as well use it to create our sample data. The process is very simple; in two steps you can have your transient mock classes generated.

1. You add a “Genome mock definition file” in Visual Studio to the project where you want your mock classes to be generated. (Add New Item -> select Genome mock definition file)

Adding a Genome mock definition file to the project

1a. Ignore the error message that pops up in the beta version, telling you that it cannot add the reference of the GenMock runtime library to the project. Please add the reference manually to your project by browsing the dll from the Visual Studio directory of the Genome installation (eg. C:\Program Files\TechTalk\Genome 3.1 XXX Edition for .NET 2.0\VisualStudio\bin). We will fix this misbehavior in the next release.

2. You select the source assembly and the types you want to include in the mock generation:

Modifying the Genome mock definition

That’s it. You can check the generated source code if you open the generated .cs sub-node in the Solution Explorer:

Solution explorer showing the GenMock items in the project

The generated code looks like this:

using TechTalk.GenMock.Runtime.InvocationStrategies;

namespace Contacts.Business
{
  public partial class mock_Contact : Business.Contact
  {
    #region ContactId
    public IInvocationStrategy mock_invocationStrategy_ContactId = 
      new global::TechTalk.GenMock.Runtime.InvocationStrategies.FieldInvocationStrategy();
		
      [global::TechTalk.GenMock.Runtime.InvocationStrategies.InvocationStrategyField("mock_invocationStrategy_ContactId")]
      public override System.Guid ContactId
      {
        get 
        { 
          InvocationResult result = mock_invocationStrategy_ContactId.GetValue(this);
          if (result.IsHandled)
          {
            return result.ReturnValue;
          }
          else
          {
            throw new 
              global::System.InvalidOperationException("Invokation strategy specified for abstract property cannot delegate the call to the base class.");
          }
        }
        set 
        { 
          mock_invocationStrategy_ContactId.SetValue(this, value); 
          InvocationResult result = mock_invocationStrategy_ContactId.SetValue(this, value);
          if (!result.IsHandled)
          {
            throw new 
              global::System.InvalidOperationException("Invokation strategy specified for abstract property cannot delegate the call to the base class.");
	  }
        }
      }		
    #endregion		
  }
}

2a. I have found another little glitch in the beta version, namely that the generated namespace is sometimes wrong, and hence the compiler complains about not finding the persistent base class. The namespace of the mock class should be exactly the same as the namespace of the source persistent class. Please fix the namespace for now if necessary. It will be fixed in the next version.

If you have a look at the generated code you will notice the following:

  • The generated classes are derived from the abstract persistent classes hence they can be used in the same way as proxies when populating “real” data from the database.
  • The generated classes are partial classes,hence you can add your own extensions to the generated mock classes easily (you can easily regenerate the classes as follows: Solution Explorer -> right click on the GenMock item -> run Custom Tool).
  • The property values are stored in transient private fields. Well, this might be not obvious at first sight, because each property has a corresponding IInvocationStrategy (a settable strategy) where you can override how the property actually behaves. This is especially interesting in mocking (unit testing) scenarios. However, by default the FieldInvocationStrategy is used, which simply stores the value in a private field. ;)

As a result these mock classes can be easily used to create our sample data programmatically, e.g.:

            mock_Account a = new mock_Account();
            a.AccountId = Guid.NewGuid();
            a.Name = "TechTalk Ltd";

            mock_Contact c = new mock_Contact();
            c.ContactId = Guid.NewGuid();
            c.LastName = "Foo";
            c.FirstName = "Jack";
            c.EmailAddress1 = "hisemail@dummy.at";
            c.Account = a;

            mock_Contact c2 = new mock_Contact();
            c2.ContactId = Guid.NewGuid();
            c2.LastName = "Foo";
            c2.FirstName = "Jane";
            c2.EmailAddress1 = "heremail@dummy.at";
            c2.Account = a;

In my next post I will take about how to provide the sample data in Blend for the designer and discuss some workarounds we came up for some glitches we found there.

Posted by TZ.

Genome | WPF
Comments are closed.