pátek 28. října 2011

Using Xaml serialization to generate Design Time Data

Recently I have been working on a almost finished Silverlight project which was needed a big change in graphical interfaces, in other words I needed a designer to be able to change all the Pages and components in Blend.

The issue - well not real issues is that all the data was bounded to properties behind, and without the data, the desiner was not able to work change the UI.

Blend can help you in this case, thanks to it's ability to generate XAML data from an existing class. So you can just generate data from existing ViewModel. That is great, but the problem is that, the data generated by Blend is not always usable. When you have an account and Blend will generate the name of the account: "Rhoncus vulputate" and the string specifing currency "Ipsum hac phasellus", you are thinking, maybe I will have to do some manual changes. Well with one account it is ok, but if you have a list of accounts (like 10) and list of operations (another 10). Editing all the things manulally might not be the right approach.

Well it happens so that in our project, we are using AutoPoco for the data generation. If you do not know it, it is a great library for generating simple POCO's - it seems like the development has been stoped last year, but hopefully this project will live on.

Well basically this means that I have already a lot of meaningful data generated in the form of POCO's, later stored in the DB. I have also working application which connects to Web Services and generates the ViewModels on the client side, so I asked myself:
Why should'nt I just take these ViewModels, serialize them into XAML and givem them to Blend as Design Time Data Sources?

So my goal was to have the same data which I was using in execution time during design time. And as you can see at the two following pictures I got it to work.
Run-time

Design-time

So I set myself to try to do so:

First idea - use the XamlSerializer. The problem - this class is only presented in WPF - not in Silverlight. OK - I thought I will just use my ViewModels in WPF application.

First Problem: The ViewModels were not ready to be used in WPF. Concretely I had two issues:
  • The INotifyDataErrorInfo interface does not exists (http://connect.microsoft.com/VisualStudio/feedback/details/568212/inotifydataerrorinfo-for-wpf)
  • The Web Service proxies generated using Silverlight are not the sames as the proxies generated using WPF
Possible solulution to these issues:
  • Try to make your ViewModel completely platform independent. Reuse a DTO's (Data Transfer Objects) on the server and client side (do not let VS to generate proxies for your DTO projects).
  • Use #if SILVERLIGHT directive to specify which parts should be build for WPF and which for Silverlight.
Well this turned out to be too complicated. Maybe it is possible on a project which you start from the very beginning. But I already had several ViewModels and changing all of these would take too much time.

Second idea - I will stay in Silverlight and see if I can get the XamlSerializer working in Silverlight

The build-int XamlSerializer is available only for WPF but there is an open-source implementation done by David Poll, which is part of his Silverlight and Beyond Library.
The use of the serializer is described in the following blog post.

This saved my life, thought I had to do some changes.
  • To determine which properties to serialize, the Serializer uses the description provided on this blog. So it will leave out all generic properties which are not read-only. In my case I had a lot of ObservableCollection properties which I wanted to serialize. Actually this was the reason to try this approach, because I did not want to edit these collections for Blend by hand. To solve this issue I had to modify the VisitProperty method of the Serializer in order to force him to serialize even the writable generic properties.
  • Disable serialization of certain properties. Blend designer has problems with some special cases of XAML. Concretely I had problem with the serialized WCF proxies. When you generate a proxy, there are at least two classes generated. Service interface (ends with "Service") and Service implementation (ends with "Client"). Blend was unable to process the xaml when he was expecting property of type "Service" and I have given him "Client" it seems that it has problems regarding services and implementations. To overcome this issue I have introduced XamlSerializationVisibility attribute which can have two values (Visible and Hidden). This parameter allows me to specify to the XamlSerializer whether to seralize or not the property. Again a slight edit of the VisitProperty method was neede to check this attribute before serialization.
  • And at last, the original WPF XamlSerializer does not serialize all basic types. In my case I had several Decimal values which I wanted to serialize and they were skipped out. This was solved quite easy. XamlSerializer contains a class BuiltInTypeConverter. This class contains a list of types which can be serialized by simple conversion to String (SupportedTypes. I have added to this collection the Decimal and DateTime and it just worked.
So thats it, grab the source code, try to serialize your ViewModels or whatever you need. Really a great thanks to David for the implementation of this class.

Žádné komentáře:

Okomentovat