Using XSLT transforms to convert XAML between different platforms
The markup in WPF and Silverlight is very similar, but there are annoying differences that greatly complicate its sharing. In our company, this problem was solved several years ago in the form of an internal tool called WPF2SL.
WPF2SL is too specific to be useful to the general public, so we do not plan to publish it. In this article, I will talk about the features of XSLT transformations with regard to XAML markup and some of the difficulties and features that we encountered.
The WPF2SL project started 4 years ago when we decided to create a line of components for the WPF and Silverlight platforms. We had ready WPF controls before, so the idea came up to do sharing markup between platforms. At that time, the gap between WPF and Silverlight markup was wider than it is now, because in Silverlight 3 there were no implicit styles, markup extensions and there were severely limited bindings.
By the way, some of our competitors took a different path. First they had Silverlight controls ready and their line of WPF controls was obtained from a priori truncated platform, so they still do not fully use all the features of the WPF platform.
Let's start by creating System.Xml.Xsl.XslCompiledTransform. That's it, as it is written in MSDN. However, it should be remembered that loading an XSLT file using the XslCompiledTransform.Load method takes a lot of time, because at this point a temporary assembly will be created in the memory for the state machine described in the XSLT file.
In one of the earlier versions of WPF2SL, a complete initialization was performed on each input XAML file with the call to XslCompiledTransform.Load. This greatly slowed down the utility. An XSLT file is loaded into the XslCompiledTransform, which contains the description of the transformations for the nodes and attributes of the source tree. Transformations in the XSLT file are sorted in ascending order of priority. The lowest priority rule is the first. This is a copy rule.
If there is no higher priority rule for the node or attribute, it will be copied from.
Lack of DynamicResource in Silverlight
If you simply replace DynamicResource with StaticResource, the resulting markup will contain many errors associated with incorrect resource tracing, because StaticResource requires that the resource be declared before it is used. The solution was to manually organize the resources within the file. The XSLT template for replacing DynamicResource with StaticResource looks like this.
…
The problem gets more complicated when there are links to resources declared in another file. This part of the problem could not be solved with XSLT transformations. To do this, we have a separate post-processing stage, about which we need to write a separate article.
Cutting Nodes and Attributes Not Available in Silverlight
Since WPF markup is much richer than Silverlight markup, we will have to cut nodes and attributes from the XAML tree. This is very easy to do in XSLT.
Attribute clipping example:
An example of cutting a subtree:
Resource Key Translation Features
In both WPF and Silvelight, you can specify a ResourceDictionary in the XAML markup in which resources will be stored. Resources are available by key. In WPF, the key can be any object, while in SL, the key must be a string key.
For unification, of course, you can introduce a restriction in WPF so that only the string is the key, but we like strong typing, which is achievable on object keys. In WPF it is possible to write like this
Where FloatingContainerThemeKey is a special generic object inherited from System.Windows.ResourceKey. The generic accepts an Enum type as a parameter, which describes the possible names of the keys.
public class FloatingContainerThemeKeyExtension : ThemeKeyExtensionBase { }
public enum FloatingContainerThemeKey {
FloatingContainerAdornerTemplate,
FloatingContainerPopupTemplate,
FloatingContainerWindowTemplate,
}

Due to this, in WPF it is more difficult to make a mistake in the key name in the resource declaration or in the resource link.
Back to the XAML transform. There are no object keys in Silverlight, so
converted to line
XML namespaces
Many similar elements in WPF and Silverlight are in different xml namespaces. This difference gave rise to such patterns.
When we realized that we would have to do a lot of such templates, we created our descendant from the standard XmlTextWriter class, whose WriteString method is overloaded.
public override void WriteString(string text) {
if(NamespacesReplacementTable.ContainsKey(text)) base.WriteString(NamespacesReplacementTable[text]);
else base.WriteString(text);
…
}
This heir can be given to the XslCompiledTransform.Transfrom (reader, writer) method as a second parameter. Overloaded WriteString in accordance with the replacement table replaces the namespace when writing.
Integration into the compilation process
WPF2SL is a console application. In our SL projects on the Pre-build event, a WPF2SL call with the appropriate parameters is registered.
But this is not so simple as it seems. Almost all now have machines with multi-core processors, on which msbuild does simultaneous assembly for several projects at once. WPF2SL in the process of creating temporary files in Temp. Since their names coincided, an access conflict arose. The problem was resolved by adding the process ID to the file name.
Diagnosing problems in XSLT conversion
Unfortunately, there is no convenient diagnostic tool for XSLT transformations (at least the author is not aware of them). When some of the XSLT transformations do not work as expected, the most efficient way is to iteratively modify XSLT with analysis of the results. If the result of the conversion is very different from the expected, feel free to put half of the XSLT file into the comment; if still not clear, another half and so on. This method has got our name: “half-comment method”.
The described method is universal for all declarative languages, including XAML. If it is not clear which of the templates generated the wrong line in the output file, you can temporarily enter a line in the template that will allow you to uniquely identify it.
conclusions
XSLT transformations work well in the task of converting XAML markup between different platforms, and the .NET implementation of XSLT transforms XslCompiledTransform is quite flexible, productive and extensible.
Literature
Sal Mangano. XSLT. Recipe Collection