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.

XAML Part VII: Fun with markup compatibility

If you misspell an attribute name in an HTML document the browser will simply ignore it. For example, if you spell the href attribute as zhref the <a> tag the attribute will be ignored. For example, this is text is in such an <a> tag. It is displayed like a link but it doesn't go anywhere because it doesn't contain an href attribute. Intentional misspellings can be used to temporarily comment out an attributes or an element. XAML doesn't support this technique directly. If you misspell an attribute or an element the XAML parser will emit an error message and refused to produce a BAML file or load the document dynamically. This is the behavior you want if you unintentionally misspelled an attribute. The compiler will tell you exactly where the error is and you can quickly find and fix the problem. But temporarily commenting out an attribute can sometimes be useful when experimenting or prototyping. XAML provides a way to duplicate the this commenting out technique through the use of the markup compatibility namespace. Markup compatibility tells the parser which namespaces are not required to be recognized when loading a XAML document. If you declare a namespace that will never be recognized, such as "Comment", you can use that namespace to comment out items by using its prefix. For example, let say you want to see the effect of removing the Width property of a Button but not lose the value it currently has which would happen if you simply deleted it. Consider the following,

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> 
  <Canvas>
    <Button Canvas.Top="50" Canvas.Left="50" Width="100" Height="50">
      Hello
    </Button>
  </Canvas>
</Window>

To comment out the Width attribute you can add the markup compatibility namespace, then an unknown namespace, and finally declare the unknown namespace ignorable. This might look like,

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:c="Comment"
    mc:Ignorable="c">
  <Canvas>
    <Button Canvas.Top="50" Canvas.Left="50" Width="100" Height="50">
      Hello
    </Button>
  </Canvas>
</Window>

You can then comment out the Width and Height attributes,

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:c="Comment"
    mc:Ignorable="c">
  <Canvas>
    <Button Canvas.Top="50" Canvas.Left="50" c:Width="100" c:Height="50">
      Hello
    </Button>
  </Canvas>
</Window>

You can even comment out the entire Button with,

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:c="Comment"
    mc:Ignorable="c">
  <Canvas>
    <c:Button Canvas.Top="50" Canvas.Left="50" Width="100" Height="50">
      Hello
    </c:Button>
  </Canvas>
</Window>

I only recommend this when you are prototyping. You shouldn't have commented out text in production XAML files. Fortunately, this technique makes it easy to determine if you have removed all such commented out items. Simply delete the xmlns:c and mc:Ignorable declarations from Window. The compiler will now generate and error if you haven't removed comment prefixes. Finally, this technique makes it clear to any reader (both mechanical and human) that you intended to comment out this property, you simple didn't just misspell it.

August CTP of Cider

Another month and another version of Cider. The August CTP is now available here. You can read the release notes and so forth from Channel 9 here. Not only did we make substantial changes, a list of which can be found here, we also updated it to work with the new .NET Framework 3.0 RC1 available here.

OK, I know we released a CTP in July I didn't blog about, but it was substantially a refresh to the CTP available for June (and I am a lazy blogger). Yes, yes, I also realize it is September and not August. I am blogging about it in September because it has just become available. The reason we call it the August CTP and not the September CTP is complicated but you can probably guess at least one reason...

BTW, new and improved versions of stuff we removed from the main tree are slowing winding their way back. I will be a bit coy about them now so as not to steal the thunder away from the current CTP but some cool and exciting things are still to come.

As always, we really want your feedback so be sure to check out the discussion site here.

June CTP of Cider

We updated Cider to be compatible with the June CTP of WPF which is part of the June CTP of .NET Framework 3.0 (the new name for WinFX) and threw in some bonus bug fixes. Check out the new release here. The release notes can be found here and, as always, we really want your feedback. If you have questions or comments about Cider let us know here!

May CTP of Cider

We have announced the May CTP of Cider here and release notes and download instructions can be found here. Some highlights include,

  • A much, much improved Grid experience
  • It can handle a much larger set of XAML files but there are still some limitations we are working on for a future CTP.
  • Delete key support!

But we removed some things as well,

  • Document Outline support.
  • Zoom support.
  • Snap Lines.
  • Design support for StackPanel, DockPanel, and Canvas.

This reflects a quality focus on this release. We are transitioning, as a team, from a small incubation project to a production team. We are committing to be "close to ship" quality on all features we release to the public. The features we removed didn't qualify and we are working on bringing them up to snuff.

A Tour of XAML - Part VI - Forward Compatibility (continued)

Returning to the Party example we where using, lets say you want to author a document that has 10 kazoos when the version 2 assembly is present but replaces them with 4 extra balloons if we only have the version 1 assembly. This can be written using a markup compatibility AlternateContent block. This might look like,

    <Party mc:Ignorable="v2"
      xmln="...assembly-v1-uri..."
      xmlns:v2="...assembly-v2-uri..."
      xmlns:mc="http://schemas.micrsoft.com/winfx/2006/markup-compatibility">
        <Balloon Color="Red" />
        <Balloon Color="Blue" />
        <mc:AlternateContent>
            <mc:Choice Requires="v2">
                <Balloon Color="Green" Shape="Dog" />
                <v2:Favor Kind="Kazoo" Quantity="10" />
            </mc:Choice>
            <mc:Fallback>
                <Balloon Color="Green" />
                <Balloon Color="Violet" />
                <Balloon Color="Orange" />
                <Balloon Color="Yellow" />
            </mc:Fallback>
        </mc:AlternateContent>
    </Party>

When the version 1 assembly is present this is interpreted as,

    <Party xmln="...assembly-v1-uri...">
        <Balloon Color="Red" />
        <Balloon Color="Blue" />
        <Balloon Color="Green" />
        <Balloon Color="Violet" />
        <Balloon Color="Orange" />
        <Balloon Color="Yellow" />
    </Party>

but in version 2 it is interpreted as,

    <Party xmlns="...assembly-v2-uri...">
        <Balloon Color="Red" />
        <Balloon Color="Blue" />
        <Balloon Color="Green" Shape="Dog" />
        <Favor Kind="Kazoo" Quantity="10" />
    </Party>

The content of the Choice element is used when the v2 namespace is available because it is mentioned in the Requires attribute. This corresponds to when the version 2 assembly is in use. If v2 is not available, the content of the Fallback element is used. AlternateContent supports an arbitrary list of Choice elements and the Requires can be an arbitrary list of prefix names.

Of course exchanging balloons for kazoos is not a very compelling example. For a more practical example, let's assume we have following classes in version 1 of an assembly,

    [ContentProperty("Content")]
    class Paragraph {
        private List<Inline> _content = new List<Inline>();
        public List<Inline> Content { get { return _content; } }
    }
    
    abstract class Inline { }
    
    [ContentProperty("Text")]
    class Run : Inline {
        string _text;
        public string Text { get { return _text; } set { _text = value; } }
    }
    
    class Image : Inline {
        private Uri _source;
        public Uri Source { get { return _source; } set { _source = value; } }
    }

Now in the version 2 assembly we add a new class that looks like,

    class Graph : Inline {
        private List<Point> _points = new List<Point>();
        public List<Point> Points { get { return _points; } }
    }

Now we want to write a paragraph that uses the new Graph class when version 2 of the assembly is present, because, for example, it scales better, but in version 1 we will just use a bitmap image of the graph. This might look like,

    <Paragraph mc:Ignorable="v2"
      xmln="...assembly-v1-uri..."
      xmlns:v2="...assembly-v2-uri..."
      xmlns:mc="http://schemas.micrsoft.com/winfx/2006/markup-compatibility">
        <Run>The sales projects for the next quarter are</Run>
        <AlternateContent>
            <mc:Choice Requires="v2">
                <v2:Graph Data="1,2 2,3 3,5" />
            </mc:Choice>
            <mc:Fallback>
                 <Image Source="SalesData.jpg" />
            </mc:Fallback>
        </mc:AlternateContent>
    </Paragraph>

When the version 2 assembly is present, this is interpreted as,

    <Paragraph xmlns="...assembly-v2-uri...">
        <Run>The sales projects for the next quarter are</Run>
        <Graph Data="1,2 2,3 3,5" />
    </Paragraph>

but, when only version 1 available, it is interpreted as,

    <Paragraph xmln="...assembly-v1-uri...">
        <Run>The sales projects for the next quarter are</Run>
        <Image Source="SalesData.jpg" />
    </Paragraph>

using the bitmap instead of of the Graph class. This allows a document author to produce a file that takes advantage of a new feature, such as Graph with its better scaling properties, but still allows older versions to see a the graph at a lower fidelity.

These classes make a better example for another feature of markup compatibility, the ProcessContent attribute. Let's add another class in the version 2 assembly,

    [ContentProperty("Content")]
    class Glow : Inline {
        private List<Inline> _content = new List<Inline>();
        public List<Inline> Content { get { return _content; } }
    }

This class allows us to make the content to appear to glow when drawn. This allows us to write a document that looks like,

    <Paragraph mc:Ignorable="v2"
      xmln="...assembly-v1-uri..."
      xmlns:v2="...assembly-v2-uri..."
      xmlns:mc="http://schemas.micrsoft.com/winfx/2006/markup-compatibility">
        <Run>The word</Run> 
        <v2:Glow><Run>glow</Run></v2:Glow> 
        <Run>will appear to glow when it viewed with version 2.</Run>
    </Paragraph>

Unfortunately, with version 1, this is interpreted as,

    <Paragraph xmln="...assembly-v1-uri...">
        <Run>The word</Run> 
        <Run>will appear to glow when it viewed with version 2.</Run>
    </Paragraph>

The word "glow" disappeared because, by default, when an element is ignored its content is ignored as well. This default can be modified by using the ProcessContent attribute. If the original document is modified to be,

    <Paragraph 
      mc:Ignorable="v2" 
      mc:ProcessContent="v2:Glow"
      xmln="...assembly-v1-uri..."
      xmlns:v2="...assembly-v2-uri..."
      xmlns:mc="http://schemas.micrsoft.com/winfx/2006/markup-compatibility">
        <Run>The word</Run> 
        <v2:Glow><Run>glow</Run></v2:Glow> 
        <Run>will appear to glow when it viewed with version 2.</Run>
    </Paragraph>

This will tell the reader to just ignore the tags for Glow if it doesn't understand it but leave the content intact. This is then interpreted as,

    <Paragraph xmln="...assembly-v1-uri...">
        <Run>The word</Run> 
        <Run>glow</Run>
        <Run>will appear to glow when it viewed with version 2.</Run>
    </Paragraph>

for version 1, and will be interpreted as,

    <Paragraph xmlns:v2="...assembly-v2-uri...">
        <Run>The word</Run> 
        <Glow><Run>glow</Run></Glow> 
        <Run>will appear to glow when it viewed with version 2.</Run>
    </Paragraph>

when version 2 is present which is exactly what we wanted.

In general, forward compatibility allows previous versions of a document reader to read a document intended for later versions and render the content at a lower fidelity. This helps immensely when deploying a newer version of an assembly. During initial deployment it might not be that wide spread. You can have your users start taking advantage of the new features immediately because older versions will still be able to read newer documents, at a lower fidelity, even when they don't have the new assembly.

A Tour of XAML V: Forward compatibility

As we saw last time, XAML supports backwards compatibility. XAML also supports forward compatibility. Forward compatibility is easiest to understand from through a set of examples. Take the balloon example before. Say we want to write a XAML document that says, when you have the version 2 assembly, I want a dog shaped balloon. If, however, you only have the version 1 assembly, a default shaped balloon is fine. In other words, I want a document that contains some version 2 specific references in it but it is OK to just ignore them when you have a version 1 assembly. In our example, we want to mark the Shape property some way that the version 1 reader will know it can ignore the property. In XAML you can use the markup compatibility namespace to accomplish this. You can write,

    <Balloon Color="Red" v2:Shape="Dog" 
      xmlns="...assembly-v1-uri..."
      xmlns:v2="...assembly-v2-uri..."
      xmlns:mc="http://schemas.micrsoft.com/winfx/2006/markup-compatibility"
      mc:Ignorable="v2" />

This takes advantage of the Ignorable attribute of from the markup compatibility namespace. mc:Ignorable="v2" says that any attribute or element from the namespace associated with the "v2" prefix can be ignored and you would still have a document that would have an acceptable meaning. If a XAML reader only has the version 1 assembly, it would interpreted the above document as,

    <Balloon Color="Red" xmlns="...assembly-v1-uri..."  />

If the reader has the version 2 assembly, it would interpret it as,

    <Balloon Color="Red" Shape="Dog" xmlns="...assembly-v2-uri..." />

mc:Ignorable can also applies to elements. To see example of this lets define a class, Party, that is define as,

    [ContentProperty("Balloons")]
    public class Party {
        List<Balloon> _balloons = new List<Balloon>();
        public List<Balloon> Balloons { get { return _balloons; } 
    }

This allows us to define a Party instance that contains balloons. For this example, let's assume that Party was defined in the Version 1 assembly and is unmodified in the Version 2 assembly. We can then write a XAML document that looks like,

    <Party xmln="...assembly-v1-uri...">
        <Balloon Color="Red" />
        <Balloon Color="Blue" />
    </Party>

We can now also write a document that will include a third dog shaped balloon but only if we are using the version 2 assembly, and not include if we are still using version 1. It looks like,

    <Party mc:Ignorable="v2"
      xmln="...assembly-v1-uri..."
      xmlns:v2="...assembly-v2-uri..."
      xmlns:mc="http://schemas.micrsoft.com/winfx/2006/markup-compatibility">
        <Balloon Color="Red" />
        <Balloon Color="Blue" />
        <v2:Balloon Color="Green" Shape="Dog" />
    </Party>

This will be interpreted exactly as the prior document when reading it with version 1. With version 2, however, it would be interpreted as if it was,

    <Party xmlns="...assembly-v2-uri...">
        <Balloon Color="Red" />
        <Balloon Color="Blue" />
        <Balloon Color="Green" Shape="Dog" />
    </Party>

We can also add a new a new class, such as Favor, that can be used in a document such as,

    <Party mc:Ignorable="v2"
      xmln="...assembly-v1-uri..."
      xmlns:v2="...assembly-v2-uri..."
      xmlns:mc="http://schemas.micrsoft.com/winfx/2006/markup-compatibility">
        <Balloon Color="Red" />
        <Balloon Color="Blue" />
        <v2:Balloon Color="Green" Shape="Dog" />
        <v2:Favor Kind="Kazoo" Quantity="10" />
    </Party>

This doesn't affect the version 1 interpretation of the document, but adds 10 kazoos to the version 2 interpretation. If the author determines that the kazoos are really necessary to the interpretation of either the reference to v1 can be removed and the document rewritten to,

    <Party xmlns="...assembly-v2-uri...">
        <Balloon Color="Red" />
        <Balloon Color="Blue" />
        <Balloon Color="Green" Shape="Dog" />
        <Favor Kind="Kazoo" Quantity="10" />
    </Party>

or the the ignorable attribute can just be removed which amounts to an equivalent document given subsumption described in the previous entry.

Tour of XAML IV: Backward compatibility

XAML has support for both forward and backward compatibility of XAML documents. To understand backwards compatibility in a more formal sense, think of a XAML reader combined with some set of assemblies as the predicate that defines a set. If the reader with the given set of assemblies accepts a XAML document without error it is part of the set. If it rejects a document, it is not part of the set. You can think of the XAML reader and assemblies as defining a set, {r(s,x)|x} where x is the XAML file and r(s,x) is the XAML reader and s is a set of assemblies. You can then describe the relationship between  two sets of assemblies with respect to XAML. The assemblies imply set schemas that the XAML reader uses to interpret the XAML document. The schema implied by the WPF assemblies states there is a Button element and a DockPanel element and the Button element can be included in a DockPanel as one of it children, for example. The a set of implied schemas  s2 is backwards compatible with s1 if {r(s1, x)|x} ∩ {r(s2, x)|x} = {r(s1, x)|x} or, in other words, s2 is backwards compatible with s1 if the reader will accept same XAML documents with s2 as it accepts with s1.

XAML really doesn't have to do much to claim it supports backwards compatibility because backwards compatibility is really a relationship between two sets of XAML schemas (and the implying assemblies) than it is between XAML documents. XAML, however, simplifies establishing this relationship by allowing one schema to declare it subsumes another schema. That means that, given both s1 and s2, XAML allows s2 to declare that any reference elements of s1 should be considered a reference to elements of s2.

To get more concrete, suppose you had an assembly that declared a single type,

    public class Balloon {
        Color _color;
        
        public Color Color { get { return _color; } set { _color = value; } }
    }

A XAML reader will infer a schema that would allow you to write a XAML document like,

    <Balloon Color="Red" xmlns="...assembly-v1-uri..." />

Now, lets say, in version 2 of the assembly you want to add a shape to the balloon class to support different balloon shapes. In the version 2 assembly you can modify the class to,

    public class Balloon {
        Color _color;
        Shape _shape;
        
        public Color Color { get { return _color; } set { _color = value; } }
        public Shape Shape { get { return _shape; } set { _shape = value; } }
    }

Now we can write a XAML document that refers to the new assembly by writing,

    <Balloon Color="Red" Shape="Dog" xmlns="...assembly-v2-uri..."/>

We, however, still want to read all XAML documents that refer to the previous assembly as well using the new assembly. We can do this by declaring, in the new assembly, that this assembly is compatible with the old assembly with a XmlnsCompatibleWith("...assembly-v2-uri...","...assembly-v1-uri...") attribute. When the XAML reader sees that attribute, it will silently interpret all references to "...assembly-v1-uri..." as if they where referring to "...assembly-v2-uri...". A XAML reader, presented with the version 2 assembly, will treat,

    <Balloon Color="Red" xmlns="...assembly-v1-uri..." />

exactly as if it was written as,

    <Balloon Color="Red" xmlns="...assembly-v2-uri..." />

This allows all XAML documents referring to version 1 assembly to be readable when you only have version 2 of the assembly. In fact, assuming you follow CLR guidelines for evolving an assembly, any XAML file that was legal using the version 1 assembly will be legal using the version 2 assembly.

A Tour of XAML - Part III - Type converters

Fundamentally XAML is an object initialization language. It creates object and sets values to properties. XAML, however, is not limit to creating reference types. You can also create instances of any type that has an associated type converter. For example, XAML can create an instance of a brush with the following code,

  <Brush>Red</Brush>

which creates a red solid color brush by calling the type converter associated with Brush. This means that,

  <Button>
    <Button.Background>
      <Brush>Red</Brush>
    <Button.Background>
  </Button>

is equivilent to,

  <Button Background="Red" />

XAML can be used to create value types, such as an integer, through its type converter as in,

  <s:Int32>27</s:Int32>

or a string,

  <s:String>It is the time for all good men...</s:String>

by using string's type converter. Note that XAML always refers to the CLR name for the type, not a language specific name such as "int" in C# or "Integer" in Pascal. Both of these types refer to the System.Int32 class in the CLR and XAML uses the CLR name, Int32. Note also that neither the XAML nor the WPF (Avalon) namespaces refer to the System namespace in mscorlib.dll where these types live. This mean that somewhere in the document you need to declare the prefix. This might look something like:

  <?Mapping ClrNamespace="System" XmlNamespace="System" 
  Assembly="mscorlib"?>
  <Window
    xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
    xmlns:s="System">
      <Button>
        <s:String>It is the time for all good men...</s:String>
      </Button>
  </Window>

Of course, for string, it is easier to write it,

  <Window xmlns="http://schemas.microsoft.com/winfx/avalon/2005">
    <Button>It is the time for all good men...</Button>
  </Window>

but the string for is useful when you want multiple strings in a collection, say, for example, as the content of a list box,

  <?Mapping ClrNamespace="System" XmlNamespace="System" 
  Assembly="mscorlib"?>
  <Window
    xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
    xmlns:s="System">
      <ListBox>
        <s:String>It is the time for all good men...</s:String>
        <s:String>to come to the aid of their country.</s:String>
      </ListBox>
  </Window>

Note that if this had been written,

  <Window xmlns="http://schemas.microsoft.com/winfx/avalon/2005">
    <ListBox>
      It is the time for all good men...
      to come to the aid of their country.
    </ListBox>
  </Window>

the list box would only contain one string, not two. This is because all continuous text content is considered one string and not two. If you load this in WPF you will also notice that the string get treated as "It is time for all good men... to come to the aid of their country" where the spaces and line breaks have been collapsed. I will discuss the rules for whitespace normalization (collapsing) in a subsequent post. For now, if you are familiar with HTML, what XAML does shouldn't come as any surprise.

A Tour of XAML - Part II: Property Elements?

XAML provides two ways to set a property, either through a Property Attribute or through a Property Element. We covered Property Attributes last time, this time we will focus on the Property Element.

A Property Element is an element that, instead of creating an object instance, set the value of a property. The following is equivalent to the example given in Part I but uses Property Elements instead of Property Attributes.

  <Button xmlns="http://schemas.microsoft.com/winfx/avalon/2005">
    <Button.Content>
      Hello, World!
    </Button.Content>
  </Button>

The loader distinguishes a Object Element from a Property Element by the presents of the '.' in the name. The first part of the name is a reference to a type and the second part, after the dot, refers to the property name (we will discuss exactly why this is when we discuss Attached Properties). Here we use Button in both Property Elements. Property Elements are necessary when the value of the property cannot be expressed as a string. For example, you can use a Property Element to set the content of a Button to be another Button (not sure why you would want that, but suspend disbelieve for a second) as in,

  <Button xmlns="http://schemas.microsoft.com/winfx/avalon/2005">
    <Button.Content>
      <Button Content="Hello, World!" />
    </Button.Content>
  </Button>

This can be written more explicitly as,

  <Button xmlns="http://schemas.microsoft.com/winfx/avalon/2005">
    <Button.Content>
      <Button>
        <Button.Content>
          Hello, World!
        </Button.Content>
      </Button>
    </Button.Content>
  </Button>

As you can see, using Property Elements can become a bit repetitive. To mitigate this, one of the properties of a class can be designated as the Content Property which tells the load to assign direct content of the element to that property. In the case of Button (and all ContentControls) the Content Property is, oddly enough, set to the property named Content. This allows us to write something like,

  <Button xmlns="http://schemas.microsoft.com/winfx/avalon/2005">
    Hello, World!
  </Button>

for the first example above, and,

  <Button xmlns="http://schemas.microsoft.com/winfx/avalon/2005">
    <Button>Hello, World!</Button>
  </Button>

for the second. Most Avalon classes have a Content Property defined. For Panel's, such as DockPanel, Grid, and Canvas, their Content Property is set to their Children collection. Any direct content of a Panel is assumed to be content intended for the Children property. This means the following,

  <StackPanel xmlns="http://schemas.microsoft.com/winfx/avalon/2005">
    <Button>OK</Button>
    <Button>Cancel</Button>
  </StackPanel>

is an abbreviated version of,

  <StackPanel xmlns="http://schemas.microsoft.com/winfx/avalon/2005">
    <StackPanel.Children>
      <Button>
        <Button.Content>
          OK
        </Button.Content>
      </Button>
      <Button>
        <Button.Content>
          Cancel
        </Button.Content>
      </Button>
    </StackPanel.Children>
  </StackPanel>

Now, with Property Elements and Content Property Attributes and Object Element and Property Attribute from Part I, we have covered the fundamentals of XAML.

A Tour of XAML - Part I: What is XAML?

Today I begin a tour of XAML. As Chris said, I have been part of development of XAML. I came fairly late to the game, however, more closely to the end of the Avalon development cycle than at the beginning, and XAML was already fairly well defined when I arrived. I did work to push some changes into the language, primarily driving consistency and regularity in the language as well as put developing features that support versioning and extensibility. We will cover some of those things in our tour, but lets start at the beginning.

The first question we will cover in our tour is what is XAML anyway? Simply, XAML is an XML based object initialization language. Its job in life is to create a tree of objects. For example, consider the following XAML file,

  <Button Content="Hello, world!" 
    xmlns="http://schemas.microsoft.com/winfx/avalon/2005" />

This creates an instance of System.Windows.Controls.Button and sets the Content property to the sting value "Hello, world!". The reason System.Windows.Button is selected and not, say System.Windows.Forms.Button, is because of the xmlns declaration in the Button tag. The magic behind the URI "http://schemas.microsoft.com/winfx/avalon/2005" is hidden in the Avalon assemblies themselves. They contain an assembly attribute that looks like,

  [assembly: XmlnDefinition(
	"http://schemas.microsoft.com/winfx/avalon/2005",
	"System.Windows.Controls")]

It actually has several more definitions like this to associate several other CLR namespaces with the Avalon XML namespace, but this is the one used above. Because PresentationFramework.dll is in the GAC or referenced by the project if the XAML is compiled into assembly, the Avalon loader will find the XmlnsDefinition in the assembly and it is able to determine that it is Avalon's button we want to create.

Now lets take a look at the phrase Content="Hello, world!". This causes the loader to look up the property called Content and sets its value. The Content property is introduced in Button's ContentControl base class and is of type System.Object. Since there is no applicable type converter for System.Object, the loader uses the default type of an attribute, string, and calls the setter method for Content. Things are slightly more complicated than this, but this should give you the basic idea. This gives us an instance of the System.Windows.Controls.Button class with its Content property set to the value "Hello, world!", which is, hopefully, what we wanted.

If we change the XAML to,

  <Button Content="Hello, world!" IsDefault="True"
    xmlns="http://schemas.microsoft.com/winfx/avalon/2005" />

adding the phrase IsDefault="True" which causes the loader to find the IsDefault property introduced on the Button class itself. The IsDefault property is of type System.Boolean which has a type converter, System.ComponentModel.BooleanConverter, associated with it. The loader will pass the string, "True", to the type converter associated with the property (or, as in this case, the property's type) prior to setting the property's value; after which we will have an instance of the System.Windows.Controls.Button class with its Content property set to the value "Hello, world!", and its IsDefault property set to True.

This time we covered an Object Element, an XML element used to create an object, and we used a Property Attribute, an XML element used to set the value of an object instance's property. Next time we will get use a Property Element and take advantage of the Content Property Attribute.

Cider is Announced

Cider is now announced! In case you missed it at the PDC, Cider is the code name for the Visual Studio Designer for WPF (pronounced Avalon according to Chris) that will be delivered in Orcas. We also announced the Expression product line that includes a different designer called Sparkle. Other than the obvious difference between Sparkle and Cider, Cider is in VS, Sparkle is a stand-alone product, Cider will be a designer for developers, Sparkle will be a designer targeted at professional designers.

For those that were asking about what I was working on, I wasn't trying to be coy, I just didn't want to spill the beans early.

I will be more forthcoming in the months ahead and try to keep you up-to-date with our progress.

If I missed you at the PDC, I am sorry about that. Feel free to ask your questions here or via e-mail (chuckj directed through microsoft.com).