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.