次の方法で共有


Xaml Inspired Markup

A lot has been written about how Xaml maps tags and attributes to objects and properties. (Here’s a standard Xaml overview.) But it’s not necessary that Xaml actually be used to create .Net objects. For example, XPS is markup that can be treated like Xaml and used to create/initialize objects, but it can also be interpreted according to the definition of the markup, without creating any objects. For example, with this Xaml:

 

<Path xmlns="https://schemas.microsoft.com/xps/2005/06" Data="F1 M 29.92,0 L 156,0" Stroke="#ff00ff00" StrokeThickness="0.32" StrokeLineJoin="Round" StrokeStartLineCap="Round" StrokeEndLineCap="Round" Clip="M 0,0 L 0,1056 816,1056 816,0 z" />

 

You could call XamlReader.Load to get a System.Windows.Shapes.Path object created (with its Data, Stroke, etc. properties initialized). Or you could interpret this directly according to the XPS specification and put ink on paper.

 

Along the same lines, Xaml defines some syntax conventions that you could apply most any time you’re defining a new Xml schema. Maybe you don’t plan to for your markup to be mapped to objects, at least not today. But if you already work with Xaml, following the same conventions for your other markup provides some nice consistency and readability, and a possible future path to intuitive code manipulation.

 

As a thought experiment, I tried this out on the document property format that Office 2007 uses to store the document’s title, subject, description, etc. Here’s an example of what that format looks like:

 

<cp:coreProperties xmlns:cp=

   "https://schemas.openxmlformats.org/package/2006/metadata/core-properties"

   xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:dcterms="https://purl.org/dc/terms/"

   xmlns:dcmitype="https://purl.org/dc/dcmitype/" xmlns:xsi=

   "https://www.w3.org/2001/XMLSchema-instance">

  <dc:title>The Onomonopia.Net Programming Language</dc:title>

  <dc:subject> How to write code that does what it sounds like</dc:subject>

  <dc:creator> Ima D. Veloper </dc:creator>

  <cp:keywords></cp:keywords>

  <dc:description></dc:description>

  <cp:lastModifiedBy> </cp:lastModifiedBy>

  <cp:revision>2</cp:revision>

  <dcterms:created xsi:type="dcterms:W3CDTF">2006-05-03T01:13:00Z</dcterms:created>

  <dcterms:modified xsi:type="dcterms:W3CDTF">2006-05-03T01:14:00Z</dcterms:modified>

</cp:coreProperties>

 

There’s good reason that this markup isn’t in a Xaml syntax, but this is easy markup to relate to, and just an experiment. So if I were approaching this problem from a code/Xaml point of view, and without this existing precedent (or the Dublin Core schema in use here), I might come up with a class implementation that looked like this:

 

public class CoreProperties

{

    private string _title;

    public string Title

    {

        get { return _title; }

        set { _title = value; }

    }

    private string _subject;

    public string Subject

    {

        get { return _subject; }

        set { _subject = value; }

    }

    private string _description;

    public string Description

    {

   get { return _description; }

        set { _description = value; }

    }

    private DateTime _created;

    public DateTime Created

    {

        get { return _created; }

        set { _created = value; }

    }

     

    ...

}

 

And the usage of this might look like:

 

<CoreProperties xmlns="" "https://schemas.openxmlformats.org/package/2006/metadata/core-properties"

                Title="The Onomonopia.Net Programming Language"

                Subject="How to write code that does what it sounds like"

                Author="Ima D. Veloper"

                ... />

 

 

That’s an interesting experiment, but not really be-all-that-you-can-be interesting. Now let’s try something more illustrative, the classic <Employee> markup, and use it to suggest some general guidelines.

 

Of course the easiest way to define your markup schema in a Xaml-compatible way is to define classes first, like I did with CoreProperties above. But since this is all about Xaml-inspired markup that doesn’t necessarily have code backing it, and to make it more fun, I’ll try to do it without using the word “code” or any code concepts [oops, I said “code” already] .

 

“Things” are tags, simple relationships are attributes

 

Xml markups tend to follow the guideline that nouns are tags, and adjectives are attributes. In code [doh!] , classes and properties tend to be the same way. A little bit more generally, objects are tags, and attributes are relationships that map two objects. That is, the attribute name is a relationship that maps the tag object to the attribute value object.

 

So obviously our Employee is going to be a tag:

 

<Employee/>

 

Off to a good start already. And a lot of information about the employee can be represented as relationships to simple objects, such as date-of-birth, title, salary, etc, such as:

 

<Employee Name="John Doe" DateOfBirth="7/4/1976" Title="MTS" ... />

 

Relationships to complex objects are markup

 

You might want all of the employee’s address information to be in its own tag, call it <AddressInformation>. That might be better for readability, or so that you could share the same schema information with your <Customer> schema, etc. Since this can’t be represented as an attribute any more, we can’t use an attribute to describe the relationship, so we use Xaml’s property element syntax. In this syntax, we use a <Parent.Relationship> tag to relate a parent object to the referent object. So in

 

<Employee Name="John Doe" DateOfBirth="7/4/1976" Title="Investigator">

  <Employee.ContactInformation>

    <AddressInformation OfficePhone="425-555-1212" CellPhone="206-555-1212" StreetAddress="123 Main St" ... />

  </Employee.ContactInformation>

</Employee>

… The employee has a “ContactInformation” relationship to an AddressInformation object.

Relationships can be to individual objects or lists

 

The above showed a relationship to a single object (employee to address). Let’s promote the employee to a manager by adding a relationship to a list of other employee objects.

 

<Employee Name="Wilma" ... >

  <Employee.DirectReports>

    <Employee Name="Fred" .../>

    <Employee Name="..." .../>

    ...

  </Employee.DirectReports>

</Employee>

 

Looking at these last two examples, you might think that the <Employee.ContactInformation> and <Employee.DirectReports> tags seems redundant. For example you could guess what the <AddressInformation> is for without the explicit ContactInformation relationship. And in fact we used to allow this kind of domain-specific implicit relationship in Xaml. But other relationships are more ambiguous, and implicit relationships are more difficult for tools to reason about, which is why Xaml now almost always requires the property element syntax. That “almost” part, though, is because …

For one relationship, you can skip the property element tag

 

In Xaml content syntax you can have one relationship that is considered the default, to which Xml content (other than property element tags) is associated. Let’s use that for the “DirectReports” relationship. That way, while the above markup with direct reports is valid, it can be simplified by removing the <Employee.DirectReports> tag:

 

<Employee Name="Wilma" ... >

    <Employee Name="Fred" .../>

    <Employee Name="..." .../>

    ...

</Employee>

 

 

Follow naming conventions in the .Net Design Guidelines

 

This last guideline for Xaml-inspired markup, to follow the .Net Design Guidelines, isn’t a strict part of the Xaml specification. If you haven’t seen them before, the .Net Design Guidelines describe a lot of conventions for writing code [that “code” doesn’t count] . There’s exceptions to every rule, but generally the .Net framework follows these guidelines from end to end. The rules in there about naming conventions apply equally well to a pure markup problem we’re discussing here, and the consistency of following those guidelines is nice.

 

As I said, there are a lot of guidelines in that document, but I think the most important/relevant ones here can be reduced to the following:

 

Use capital letters

For example <Employee>, not <employee>. (This is “pascal casing”.)

 

Use words, not abbreviations

For example “Address” not “Addr”. And watch out for typos that you replicate all over your markup accidentally with copy/paste.

 

Use plural names for lists

For example “DirectReports” for the list of, er, direct reports.

 

Make names sounds like you would say it

For example “Employee.HasDependents” reads nicer than “Employee.Dependents”. (The latter also sounds like a list.)

 

Don’t create complex attribute values

For example, you could create a “mini-language”, as we call it, for the address information, where you use hyphens to recognize the phone number, semi-colons to separate the address from the phone numbers, parenthesis to distinguish the cell phone from the office phone, etc. It might look concise and elegant, but it’s difficult to remember how to write it, and difficult for an XML schema or other tool to define it. Simple attributes, like numbers, strings, or things you’d put in an enum [doh!] are great.

 

Use consistent attribute value syntax

.Net already defines attribute value syntax for a lot of types. That includes obvious types such as integers, reals, etc. That also includes a few more complex types, such as dates and time spans. Whenever possible, use a syntax that already exists somewhere.

 

 

 

And so with all that in place, here’s what our company hierarchy might look like:

 

<Employee Name="Rob" Title="President"

          xmlns="https://blogs.msdn.com/mikehillberg/archive/2006/09/27/XamlInspiredMarkup.aspx">

  <Employee.ContactInformation>

    <AddressInformation OfficePhone="123-4567" CellPhone="765-4321" Blog="https://rrelyea.spaces.live.com" />

  </Employee.ContactInformation>

  <Employee Name="Kevin" Title="Vice President">

    <Employee.ContactInformation>

      <AddressInformation OfficePhone="1" CellPhone="2" Blog="https://blogs.msdn.com/okoboji"/>

    </Employee.ContactInformation>

    <Employee Name="Lauren" Title="MTS">

      <Employee.ContactInformation>

        <AddressInformation OfficePhone="5" CellPhone="6" Blog="https://laurenlavoie.com/"/>

      </Employee.ContactInformation>

    </Employee>

    <Employee Name="Mike" Title="MTS">

      <Employee.ContactInformation>

        <AddressInformation OfficePhone="3" CellPhone="4" Blog="https://blogs.msdn.com/mikehillberg"/>

      </Employee.ContactInformation>

    </Employee>

  </Employee>

</Employee>

 

 

… And here’s what a representation of this markup might look like in code:

 

using System;

using System.Collections.Generic;

using System.Text;

using System.Globalization;

[assembly: System.Windows.Markup.XmlnsDefinition(

                                      "https://blogs.msdn.com/mikehillberg/archive/2006/09/27/XamlInspiredMarkup.aspx",

                                      "EmployeeDatabase")]

namespace EmployeeDatabase

{

    public class AddressInformation

    {

        private string _officePhone;

       public string OfficePhone

        {

            get { return _officePhone; }

            set { _officePhone = value; }

        }

        private string _cellPhone;

        public string CellPhone

        {

            get { return _cellPhone; }

            set { _cellPhone = value; }

        }

        private Uri _blog;

        public Uri Blog

        {

            get { return _blog; }

            set { _blog = value; }

        }

     

        // ...

    }

    [System.Windows.Markup.ContentProperty("DirectReports")]

    public class Employee

    {

        private AddressInformation _contactInformation;

        public AddressInformation ContactInformation

        {

            get { return _contactInformation; }

            set { _contactInformation = value; }

        }

        private List<Employee> _directReports = new List<Employee>(0);

        public List<Employee> DirectReports

        {

            get { return _directReports; }

        }

        private string _name;

        public string Name

        {

            get { return _name; }

            set { _name = value; }

        }

        private string _title;

        public string Title

        {

            get { return _title; }

            set { _title = value; }

        }

     

        // ...

    }

}

 

 

… And for fun, here’s that same Employee markup with a data template mapping it to a tree view (I stole the HandleRequestNavigate code for the hyperlink from Lauren):

 

<Window x:Class="Scratch.Window1"

    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:EmployeeDatabase="https://blogs.msdn.com/mikehillberg/archive/2006/09/27/XamlInspiredMarkup.aspx"

    Title="Org Structure"

     >

  <Window.Resources>

    <HierarchicalDataTemplate DataType="{x:Type EmployeeDatabase:Employee}" ItemsSource="{Binding DirectReports}">

      <StackPanel Margin="5">

        <WrapPanel>

          <TextBlock FontWeight="Bold" Text="{Binding Name}" />

          <TextBlock Text=" (" />

          <TextBlock Text="{Binding Title}" />

          <TextBlock Text=")" />

        </WrapPanel>

        <WrapPanel >

          <TextBlock Text="Office Phone: "/>

          <TextBlock Text="{Binding ContactInformation.OfficePhone}"/>

        </WrapPanel>

        <WrapPanel >

          <TextBlock Text="Cell Phone: "/>

          <TextBlock Text="{Binding ContactInformation.CellPhone}"/>

        </WrapPanel>

        <WrapPanel >

          <TextBlock Text="Blog: "/>

          <TextBlock>

            <Hyperlink NavigateUri="{Binding ContactInformation.Blog}" RequestNavigate="HandleRequestNavigate">

              <TextBlock Text="{Binding ContactInformation.Blog}"/>

            </Hyperlink>

          </TextBlock>

        </WrapPanel>

      </StackPanel>

    </HierarchicalDataTemplate>

  </Window.Resources>

  <TreeView>

    <TreeView.Background>

      <LinearGradientBrush>

        <GradientStop Color="White" Offset="0"/>

        <GradientStop Color="LightBlue" Offset="1"/>

      </LinearGradientBrush>

    </TreeView.Background>

    <Employee Name="Rob" Title="President"

              xmlns="https://blogs.msdn.com/mikehillberg/archive/2006/09/27/XamlInspiredMarkup.aspx">

      <Employee.ContactInformation>

        <AddressInformation OfficePhone="123-4567" CellPhone="765-4321" Blog="https://rrelyea.spaces.live.com" />

      </Employee.ContactInformation>

      <Employee Name="Kevin" Title="Vice President">

        <Employee.ContactInformation>

          <AddressInformation OfficePhone="1" CellPhone="2" Blog="https://blogs.msdn.com/okoboji"/>

        </Employee.ContactInformation>

        <Employee Name="Lauren" Title="MTS">

          <Employee.ContactInformation>

            <AddressInformation OfficePhone="5" CellPhone="6" Blog="https://laurenlavoie.com/"/>

          </Employee.ContactInformation>

        </Employee>

        <Employee Name="Mike" Title="MTS">

          <Employee.ContactInformation>

            <AddressInformation OfficePhone="3" CellPhone="4" Blog="https://blogs.msdn.com/mikehillberg"/>

          </Employee.ContactInformation>

        </Employee>

      </Employee>

    </Employee>

  </TreeView>

</Window>

 

When you build & run this, it looks like:

 

 

OrgChart.JPG

Comments

  • Anonymous
    May 19, 2008
    A lot has been written about how Xaml maps tags and attributes to objects and properties. But it’s not necessary that Xaml actually be used to create .Net objects. Maybe you don’t plan to for your markup to be mapped to objects, at least not today. Bu