Runtime XAML generation

I know what you are thinking: Woah! A new blog post this soon? Its only years since the last one; where do you find the time?
Writer’s block? Lazy? Just useless? Anyway! I’ve been wanting to do this post for a while now; hopefully it’s of use to someone.

Background

While working on a client project, we came to a point where we needed to generate UI at runtime to support templating of forms.

The scenario is this:
We have a wizard component that guides a user through data input and analysis which is a set of sections and pages with input fields, workflow statuses, data visualizations, etc.
However, a customer of the software may need to customize this process to their company requirements or even create a new process.

Now, we don’t want to upload XAML markup to the backend which would allow too much editing freedom (this is something the customer should be able to do themselves without involving expensive template-designing consultant fees) nor do we want XAML in the backend because the client app could be replaced/supplemented with another app on a different platform.
So, the template is a JSON format which does not deal with the specific client-side implementation. Which gives us even more benefits such as validation of uploaded templates (kind of hard with a XAML dump) and the payload is small and fast to deal with.

That’s the quick and generic overview of this.

Solution

First off: The code for this is my client’s property so I cannot share it here without permission.

I got the idea after a friend was talking enthusiastically about Monoids.
My thought was this: XAML elements are DependencyObjects which can only be created on the UI-thread and it’s a bit of a hassle to deal with the structure as you initialize it all.
I didn’t want to create all the elements, add the elements to their parent StackPanels, Grids, etc and it all becomes very sequential and annoying to deal with.
Furthermore, if some part of the UI generation required some async work it all becomes an issue with these DependencyObjects.

So I played around with creating this XAML describing class which behaved as a monoid. The reason for that is that the XAML element (such as a textbox) can be dealt with on its own, in a parallel thread or however you want it.
When all of the elements are done parsing, they can be combined in any sequence they feel like because each element knows who it is and who their desired parent is.
This information comes from the template – basically, a group of fields is given id 1 (and in the app is considered to be a container such as a StackPanel) and the fields that belong to that group get their own ids with a parent id of 1.
Having this information means that each element can return from wherever they come from in any order and for final combination it will all fall into place.

In short, the code could look like this for declaring some XAML controls:


var field = XamlElementDescriptor<TextBlock>.Create(groupId: null, parentGroupId: 1);

field.AddProperty(“Text”, “asdf”);

 


var container = XamlElementDescriptor<StackPanel>.Create(groupId: 1);

container.AddProperty(“HorizontalAlignment”, “Stretch”);

 

To combine them it would be a matter of just adding them together:


var combinedElements = field + container;

 

And render it: var xaml = XamlWriter.Render(combinedElements.First());

 

combinedElements.First() will be the StackPanel with the TextBlock inside it.
And the output of the parsing results in a simple:


This also supports custom controls and attached properties, of course, and will add the necessary namespaces to the XAML markup to make use of them.

A slightly more complex example to demonstrate this could be something like this:

 

var field = XamlElementDescriptor<TextBlock>.Create(groupId: null, parentGroupId: 1);

field.AddProperty(“Text”, “asdf”);

 


var container = XamlElementDescriptor<StackPanel>.Create(groupId: 1);

container.AddProperty(“HorizontalAlignment”, “Stretch”);

 


var grid = XamlElementDescriptor<Grid>.Create(groupId: 2, parentGroupId: 1);

grid.AddExpandedProperty(“Grid.ColumnDefinitions”, new
XamlElementDescriptorCollection {


XamlElementDescriptor<ColumnDefinition>.Create(),


XamlElementDescriptor<ColumnDefinition>.Create()

});

 


var image = XamlElementDescriptor<Image>.Create(parentGroupId: 2);

image.AddProperty(“Source”, “/Assets/blabla/etc.jpg”);

image.AddProperty(“Stretch”, “UniformToFill”);

image.AddProperty(“Grid.Column”, “0”);


var combinedElements = container + field + grid + image;

 

 

 


var xaml = XamlWriter.Render(combinedElements.First());

Using XamlReader now to instantiate the controls is now just a walk in the park.

That is the quick run-down of my solution for this – and it may not be the best (any input would be appreciated), but it works really well and its really fast.

Oh, you may be thinking “Well, this is error prone as adding properties using strings can cause runtime exceptions if typos are introduced and the like”.
The sexy lady Roslyn helps us there! I wrote an extension that validates this and provides errors and simple code fixes.

Or even custom attached properties (with a code fix):

 

I might do a follow-up blog post how the Roslyn analyzer was written; maybe quite fast if there’s any interest in it.

Advertisements

One Response to Runtime XAML generation

  1. noocyte says:

    I would love to see a followup with Roslyn Analyzer details!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: