XAML pure

This article was originally published in VSJ, which is now part of Developer Fusion.
What is XAML? Is it a graphics language that you can use to create WPF/Silverlight based user interfaces? Is it a 3D graphics language? The answer is a very definite no. XAML is a, mostly declarative, object instantiation language – pure and simple. Its use in conjunction with WPF is just one of its many possible applications, and indeed it has started to appear in other places – Windows Workflow for example. To explain exactly what XAML is, this article works with custom classes that have nothing to do with WPF. Yet after reading it you may well understand how XAML and WPF fit together a little better.

MyClass becomes MyObject

To get started you will need either Visual Studio 2008 or, if you just want to experiment, C# Express 2008. We could start with a non-WPF project to prove how “pure” we are, but this would waste a lot of time adding references and “usings”. So let’s start with a simple WPF Application. You don’t need to modify any of the generated code, but you do need to add a simple custom class with which to try out XAML:
public class MyClass
{
	public MyClass()
	{ }
}
All that is necessary for a class to be instantiable by XAML is that it has a parameterless constructor and it can’t be nested. It can have other constructors but these play no part in its working with XAML.

Now that we have our minimal class we can write some XAML to create an instance of it. However, first there has to be some way of making the link between the XAML document and the class definition. This is achieved by importing the class’s namespace into XAML. You can import the namespace of any assembly and use namespaces to indicate exactly which class you are referring to. In this case we need to import the namespace of the assembly that the XAML file is part of, i.e. the current project. So moving to the XAML editor we need to add a single line to the <Window> tag:

<Window x:Class=”WpfApplication1.Window1”
	xmlns=”http://schemas.microsoft.com/
		winfx/2006/xaml/presentation”
	xmlns:x=”http://schemas.microsoft.com/
		winfx/2006/xaml”
xmlns:m=”clr-namespace:WpfApplication1”   
	Title=”Window1” Height=”300”
	Width=”300” Loaded=”Window_Loaded”>
The “clr-namespace” is a special token which is interpreted to mean “get the namespace from the named CLR runtime”. In general you might also need an “assembly=” token to supply the location of the assembly, but in this case it’s assumed to be the current project. Following this any name prefixed by m: is taken from the namespace of the current project.

The next thing we have to do is to get rid of the <Grid> tags as we cannot nest a general class within a grid – it needs a class that can be displayed. To create the instance of our class all we have to enter is:

	<m:MyClass>
	</m:MyClass>
</Window>
The project should now run without errors. If you do see any errors then it will be due to loss of synchronisation between namespaces – simply run the project again. The need to keep namespaces and other generated files in sync is one of the problems of splitting instantiation from the runtime.

Name me that object

So we have a working program that creates an instance of MyClass, but this does us very little good as the instance is dynamic and goes out of scope as soon as the main form is loaded! To keep the instance in scope, and to allow us to work with it within the C# code, we need to give the instance a name. Standard WPF components are generally given a name via the NAME property, but a more direct mechanism for custom classes is to use the facilities provided by the XAML “x” namespace. The “x:Name” attribute can be used to set the name used by XAML to create the instance:
<m:MyClass x:Name=”MyObject1”>
</m:MyClass>
With this change you can now run the program and, with the help of a breakpoint and the debugger, you can confirm that there it really does create MyObject1. If you would like to see the generated C# code that does the instantiation then load the file called Window1.g.cs where you will discover that what happens boils down to:
internal WpfApplication1.
	MyClass MyObject1;
…followed a few lines later by:
this.MyObject1 = ((WpfApplication1.
	MyClass)(target));
Not exactly the way most of us would do it, but it serves a higher purpose!

As already mentioned, WPF classes inherit a Name property which can be used in place of x:Name. If a class doesn’t have a Name property then use x:Name, if it does use it, but don’t give your class a Name property unless it sets the name of the new object.

Properties

As with WPF objects in XAML we can set properties on custom objects. For example, if we add a trivial int property to MyClass:
public int MyProperty
{
	get{return m_MyProperty;}
	set{m_MyProperty=value;}
}
…we can set this to “20” within XAML using:
<m:MyClass x:Name=”MyObject1”
	m:MyProperty=”20”>
</m:MyClass>
Now if you run the program and examine it after an early breakpoint you will see that the object has been created with the property set to 20. Notice that the property type implied by XAML is string, but the conversion to int is automatically taken care of – essentially by XAML asking the Convert object to convert the string to the type of the property. This is fine as long as Convert has definitions for the appropriate ToType. In addition to converting to the usual primitives, XAML will also try to convert a string to a suitable value of an enumeration by performing string matching against its values.

A second problem arises if we are trying to initialise a property that takes a reference type. This is mostly solved by the use of a different property initialisation syntax. For example, instead of using an attribute to set MyProperty we use:

<m:MyClass x:Name=”MyObject1”>
	<m:MyClass.MyProperty>
		20
	</m:MyClass.MyProperty>
</m:MyClass>
The result is the same, but this syntax is more general because we can include a new object between the property tags.

A demonstration of an object property is a little messy, but if we add a new class:

public class MyDataClass
{
	private int m_MyProperty2;
	public MyDataClass()
	{
	}
	public int MyProperty2
	{
		get { return m_MyProperty2; }
		set { m_MyProperty2 = value; }
	}
}
This class has a single int property but in principle it could be as complicated as required. Now we can add a new class-valued property to our original class:
private MyDataClass m_MyDataClass;
public MyDataClass MyClassProp
{
	get { return m_MyDataClass; }
	set { m_MyDataClass = value; }
}
Clearly this cannot be set to a new value using a string. The way to do it is:
<m:MyClass x:Name=”MyObject1”>
	<m:MyClass.MyClassProp>
		<m:MyDataClass MyProperty2=”35”>
		</m:MyDataClass>
	</m:MyClass.MyClassProp>
</m:MyClass>
This creates a new, nameless, instance of the DataClass class and then sets its MyProperty2 to 35. Notice that Attribute setting can be used for MyProperty2 because it is a supported value property, however there would be no problem if MyProperty was another reference property – the nesting might get a little deep however!

Implementing a type converter

As an alternative to using nested property syntax to set reference properties, we can opt to handle the conversion from the initialiser string to the object properties ourselves. Surprisingly this isn’t difficult. For example, suppose we want to allow MyProperty2 to be set by a string. All we have to do is tag the type that the type converter will apply to with:
[TypeConverter(typeof(
	MyDataClassTypeConverter))]
public class MyDataClass
{
The rest of the class is unaltered. Now we have to implement the MyDataClassTypeConverter and to do this we need to add:
using System.ComponentModel;
The type converter class has to inherit from TypeConverter and it first implements CanConvertFrom which simply returns true or false depending on whether or not the type converter can do the requested conversion:
public class MyDataClassTypeConverter:
	TypeConverter
{
	public override bool CanConvertFrom(
		ITypeDescriptorContext
		context, Type t)
	{
	if (t == typeof(String)) return true;
		return false;
	}
The second method we need actually does the type conversion:
	public override object
		ConvertFrom(
		ITypeDescriptorContext context,
		System.Globalization.
		CultureInfo culture, object val)
	{
		MyDataClass temp=new MyDataClass();
		temp.MyProperty2= Convert.ToInt32(
			(string)val);
		return temp;
	}
}
Notice that it creates a new instance of the class that it performs type conversion for, and then processes the string to initialise the properties of the MyDataClass instance. Notice that the Type converter creates and then initialises the object to be stored in the property using the information supplied by the initialisation string. Clearly a real example would be more complex and would parse the string to extract multiple items of initialisation information. With the type converter in place we can create an instance of MyClass using:
<m:MyClass x:Name=”MyObject”
	MyProperty=”23” MyClassProp=”35” >
</m:MyClass>

Beyond initialisation

Suppose you want to make use of the objects created by the XAML in your code. The attribute:
x:Class=”WpfApplication1.Window1”
…defines the partial class that provides the “code behind” support for the XAML. Within this class all of the objects you have created are within the namespace and accessible. For example you can write, without any other modifications:
private void Window_Loaded(
	object sender, RoutedEventArgs e)
{
	MessageBox.Show(
		MyObject1.MyProperty.ToString());
}
You can similarly write code that manipulates any object or property initialised by XAML.

Where next?

It is clear that XAML is a very general and extensible object instantiation language. If you follow the example in this article I can promise you lots of difficulties caused by synchronisation – the project organisation used in this article was adopted to make the example simple and thus falls over in the real world. The problem is that XAML doesn’t expect the assemblies that contain the classes it is instantiating to change during development. This is easily cured by splitting out the class definitions into another assembly.

In the final analysis you have to ask yourself if XAML is worth it without the availability of a designer. Is it really so much better to instantiate objects declaratively with all of the type conversion and namespace problems this brings about? Even if you decide it isn’t, armed with the knowledge of the “bigger picture”, the way that XAML works with both WPF and Silverlight should be much easier to understand. You might even want to create custom designers for your own sub-space of XAML.


Ian Elliot is a development programmer with I/O Workshops Ltd, a consultancy which solves a wide range of real world problems.

You might also like...

Comments

About the author

Ian Elliot United Kingdom

Ian Elliot is a development programmer with I/O Workshops Ltd, a consultancy which solves real-world problems for clients with widely varying requirements.

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Walking on water and developing software from a specification are easy if both are frozen.” - Edward V Berard