18 March 2006

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.



blog comments powered by Disqus