ChuckJ's Blog Please read my disclaimer. It is better to be silent and thought a fool, than to speak and remove all doubt." -- Silvan Engel

...Removing All Doubt

Chuck Jazdzewski

A Tour of XAML VIII: More fun with Markup Compatibility

In a previous post about markup compatibility I described how it can be used to enable forward compatibility in XAML documents; documents that take advantage of features in, say, a 2.0 version of the document format but can also be viewed when only the 1.0 version is available. Using a technique similar the previous XAML post, we can also use AlternateContent to get the effect of conditional compilation.

Let say you want to have a label in a dialog that indicates that this is beta software so your customers will not confuse it with your production version. One way to accomplish this is to conditionally compile an XmlnsDefinition attribute into an assembly referenced by the project. When the conditional symbol is defined, the WPF XAML compiler will consider the namespace defined by the XmlnsDefinition attribute as known. If the conditional is not defined, the assembly will not have the XmlnsDefinition attribute and the namespace will be considered unknown. This can be done by adding the following code to a referenced assembly,

#if BETA
[assembly:XmlnsDefinition("BetaVersion", "Example.Technology")]
#endif

Remember, this must be an assembly referenced by the project, not in the project itself (I explain why below).

You can then conditionally include a label, as in,

<Window 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
  xmlns:beta="BetaVersion"
  
  ... 
  
  <mc:AlternateContent>
    <mc:Choice Requires="beta">
      <Label>
        This is BETA software. Do not distribute. 
        Use at your own risk.
      <Label>
    </mc:Choice>
  <mc:AlternateContent>
  
  ...
  
<:/Window>

The label will only be included in the beta version of the assembly, not the production version (assuming that BETA is not defined for the production version, a mistake I have made before...). You can even make it more complicated by having other dummy namespaces for other versions and a fallback to catch when the build configuration was not correctly set up, such as,

<Window 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
  xmlns:alpha="AlphaVersion"
  xmlns:beta="BetaVersion"
  xmlns:production="ProductionVersion"
  ... 
  
  <mc:AlternateContent>
    <mc:Choice Requires="alpha">
      <Label>
        This is ALPHA software. Confidential.
        Internal use only. Do not distribute
      <Label>
    </mc:Choice>
    <mc:Choice Requires="beta">
      <Label>
        This is BETA software. Do not distribute. 
        Use at your own risk.
      <Label>
    </mc:Choice>
    <mc:Choice Requires="production">
    </mc:Choice>
    <mc:Fallback>
      <Label>
        Franken-build, beware! For test purposes only. 
        Confidential. Internal use only.
        Do not distribute.
        The official build script was not used to 
        produce this assembly.
      </Label>
    </mc:Fallback>
  <mc:AlternateContent>
  
  ...
  
</Window>

I believe this technique is superior to other techniques I have seen described involving data binding and/or triggers because the resulting production version has no runtime overhead for the beta label. The production choice doesn't mention the label at all; the BAML file will not contain any reference to the label and no label will be created at runtime. It is equivalent deleting the entire alternate content block. Also, since it only uses XAML features, not WPF features, it is usable in non-WPF dialects.

If you adopt this technique, I recommend adding the dummy namespaces to an assembly that contains only XmlnsDefinition attributes. Since this assembly doesn't contain any code, the C# compiler will not include it among the references in the final assembly therefore you don't have to distribute it.

The reason you cannot include the XmlnsDefinition in the project itself is because the XAML is compiled before the assembly is built (most of the time, I will get into the details of this in a later post). The compiler cannot know what attributes are in the project until the project's assembly has been built. Since the BAML file needs to be in included in the assembly, it must be compiled before the assembly. This is a catch-22 so the compiler ignores XmlnsDefintiion attributes in the project assembly but will respect attributes in referenced assemblies (which are built before the XAML is compiled).

In a future version of XAML we might consider adding formal support for conditional compilation but, for now, you can get the effect of conditional compilation through the use of AlternateContent, even if it is a bit indirect.

11/01/2006 1:55 PM | Add comment | #XAML

Content © 2008 Chuck Jazdzewski | Subscribe to my RSS feed.

All source code above is usable under the Microsoft Permissive License (Ms-PL) unless otherwise specified in the containing entry.

Powered by BlogX