📜 ⬆️ ⬇️

Using XSLT transformations for converting XAML between different platforms

WPF and Silverlight use XAML markup language to describe user interface elements, templates, and styles. If you are developing simultaneously for different XAML platforms, then naturally, there is a desire to have common markup files for these 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’ll talk about the features of XSLT transformations applied to XAML markup and some of the complexities and features that we have encountered.

The WPF2SL project started 4 years ago when we decided to create component lines for WPF and Silverlight platforms. We had ready WPF controls earlier, so we had an idea to share sharing between platforms. At that time, the gap between the WPF and Silverlight markup was larger than it is now, because in Silverlight 3 there were no implicit styles, markup extensions and banding was severely limited.
')
By the way, some of our competitors have chosen a different path. They had Silverlight controls first ready and their WPF controls lineup was obtained from an a priori cut-down platform, so they still do not fully use all the features of the WPF platform.

Let's start by creating the System.Xml.Xsl.XslCompiledTransform. It's all as 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, which is described in the XSLT file.

In one of the earlier versions of WPF2SL, every initial XAML file was fully initialized with a call to XslCompiledTransform.Load. This greatly slowed down the work of the utility. The XslCompiledTransform loads an XSLT file containing transformation descriptions for the nodes and attributes of the source tree. Transformations in the XSLT file are sorted by priority. The rule with the lowest priority is the first. This is a copying rule.

<xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> 

If there is no higher priority rule for the node or attribute, it will be copied from.

Missing DynamicResource in Silverlight

If you simply replace DynamicResource with StaticResource, the resulting markup will contain many errors associated with incorrectly following resources, 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.

 <xsl:template match="@*"> … <xsl:attribute name="{local-name(.)}" namespace="{namespace-uri(.)}"> <xsl:variable name="tempValue1"> <xsl:call-template name="globalReplace"> <xsl:with-param name="outputString" select="."/> <xsl:with-param name="target" select="'DynamicResource'"/> <xsl:with-param name="replacement" select="'StaticResource'"/> </xsl:call-template> </xsl:variable> <xsl:value-of select="normalize-space($tempValue1)"/> </xsl:attribute> </xsl:template> 


The problem is compounded when there are links to resources declared in another file. This part of the problem could not be solved by 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 that are missing in Silverlight

Since WPF markup is much richer than Silverlight markup, we will have to cut the nodes and attributes from the XAML tree. This is very easy to do in XSLT.
Attribute example cut:

 <xsl:template match="@FocusVisualStyle"/> 


Example of cutting a subtree:

 <xsl:template match="wpf:Style.Triggers"/> 


Resource Key Translation Features

In both WPF and Silvelight, in XAML markup, you can define a ResourceDictionary in which resources will be stored. Resources are available by key. In WPF, any object can be a key, and in SL the key must be string.

For unification, of course, you can enter a restriction in WPF so that the key is only a string, but we like the strong typing that is achievable on the object keys. In WPF, it is possible to write like this
 <SolidColorBrush x:Key="{dxt:FloatingContainerThemeKey ResourceKey=FloatingContainerBackground}" Color="#FFA3C3EC" /> 

Where FloatingContainerThemeKey is a special generic object inherited from System.Windows.ResourceKey. The generic parameter accepts the Enum type, which describes the possible names of the keys.
 public class FloatingContainerThemeKeyExtension : ThemeKeyExtensionBase<FloatingContainerThemeKey> { } public enum FloatingContainerThemeKey { FloatingContainerAdornerTemplate, FloatingContainerPopupTemplate, FloatingContainerWindowTemplate, } 



Due to this, in WPF it is more difficult to make a mistake in the name of the key in the resource declaration or in the link to the resource.

Let's go back to the XAML transform. There are no object keys in Silverlight, so
 <SolidColorBrush x:Key="{dxt:FloatingContainerThemeKey ResourceKey=FloatingContainerBackground}" Color="#FFA3C3EC" /> 

converted to a string
 <SolidColorBrush x:Key="FloatingContainerThemeKey_FloatingContainerBackground" Color="#FFA3C3EC" /> 


XML namespaces

Many similar elements in WPF and Silverlight are in different xml namespaces. This difference gave rise to such patterns.

 <xsl:template match="wpf:Label"> <sdk:Label xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"> <xsl:apply-templates select="@* | node()"/> </sdk:Label> </xsl:template> <xsl:template match="wpf:TreeView"> <sdk:TreeView xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"> <xsl:apply-templates select="@* | node()"/> </sdk:TreeView> </xsl:template> 


When we realized that such templates would have to be done a lot, we created our successor to the standard class XmlTextWriter, which has overloaded the WriteString method.
 public override void WriteString(string text) { if(NamespacesReplacementTable.ContainsKey(text)) base.WriteString(NamespacesReplacementTable[text]); else base.WriteString(text); … } 

This successor can be given to the XslCompiledTransform.Transfrom method (reader, writer) as the 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, the WPF2SL call is registered with the appropriate parameters.

But this is not as simple as it seems. Almost everyone now has machines with multi-core processors, on which msbuild makes simultaneous builds for several projects at once. WPF2SL in the course of work created temporary files in Temp. Since their names coincided, an access conflict occurred. The problem was solved by adding the process ID to the file name.

Diagnosing Problems in XSLT Transformations

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 effective way is to iteratively modify the XSLT with the analysis of the results. If the result of the conversion is very different from the expected, feel free to place half of the XSLT file in the comment; if it is still not clear, another half and so on. This method has got the name: “half-comment method”.

The described method is universal for all declarative languages, including XAML. If it is not clear which of the templates formed the wrong line in the output file, you can temporarily enter a line in the template that will allow you to uniquely identify it.

findings

XSLT transformations work well in the task of converting XAML markup between different platforms, and the .NET implementation of the XSLT XslCompiledTransform transformation is quite flexible, productive and extensible.

Literature

Sal Mangano. Xslt. Collection of recipes

Source: https://habr.com/ru/post/218711/


All Articles