joemarini.com

Bloghorn: A Blog Reader Built in XAML

Article Summary: Demonstrates an all-XAML way to read data from an XML source and display the results to the user. In this example, we build a very simple RSS reader using no C# code. The user can scroll through a list of articles, see a summary for each one, and go to a given article by clicking a hyperlink. This example also shows how to use the databinding mechanism in XAML to create an interactive user interface with declarative markup.

UPDATE: This example was updated Feb 15, 2005 for the public Avalon CTP Build

download Download the code for this tutorial (NOTE: you need to have the November Avalon CTP Build installed to run this sample).

The next version of the Microsoft Windows® operating system, code named "Longhorn", contains an exciting new technology called XAML, which is an acronym for the "eXtensible Application Markup Language". XAML (pronounced "zam-el") allows you to specify the user interface portion of your Windows® applications using markup to represent the usual items in an application, such as controls, text, hyperlinks, images, etc. XAML itself is built on top of a technology named Avalon, which brings a compositing, vector-based rendering engine to the Windows desktop.

The implications of this are pretty profound. This type of approach makes it much easier to separate the business logic of an application from the user interface right from the beginning. It also provides a much more modular method for building applications, allowing, for example, a designer to work on the UI portion of an application while a developer creates the back-end code. That is, of course, assuming that the application even has any back-end code — there's a lot you can do in pure XAML without having to write any logic in a code-behind file. You can connect to data sources such as Web Services and XML feeds, tie UI elements directly to the data, and have them respond automatically to changes in the data and the selected element in a control.

In this example, I'm going to build a XAML-based blog reader that does exactly that, with no coding required other than the markup to create the user interface. MSDN subscribers who have Avalon installed on their machines can download the code for this article and try it out. Here's what the application, which I've named Bloghorn, looks like when it is running under Longhorn:

Screenshot of XAML Blog Reader in Longhorn

(click for larger version)

A Declarative Approach to Building an Application

If you're not familiar with XAML syntax, you can find a great deal of information about it on Microsoft's MSDN site under the Longhorn section. It is beyond the scope of this article to provide an introduction to XAML, so if you need a primer, you should first read up on it on MSDN before continuing with this example. A good place to start is MSDN's great introductory article to the Avalon graphics model.

The Bloghorn application is built entirely in XAML, with no C# code-behind. The application defines an XML data source for the blog data, creates a few user interface elements to present the data to the user, and wires everything together using Longhorn's data-binding mechanism to create a data-driven UI.

Examining The Code

The code for Bloghorn is a relatively compact 56 lines of XAML:

<Canvas xmlns="http://schemas.microsoft.com/2003/xaml" xmlns:def="Definition">
  <DockPanel Background="#DEDEDE" Width="100%">
    <DockPanel.Resources>
      <XmlDataSource def:Name="NewsData" Source="http://msdn.microsoft.com/rss.xml" XPath="rss/channel" />
      <Style def:Name="NewsDataStyle">
        <ContentPresenter/>
        <Style.VisualTree>
          <FlowPanel Margin="0 10 0 10 ">
            <Text TextWrap="Wrap" FontSize="12pt" Foreground="Black" TextContent="*Bind(XPath=title)"/>
            <Text FontSize="9pt" Foreground="Blue" TextContent="*Bind(XPath=author)"/>
            <HyperLink NavigateUri="*Bind(XPath=link)">
              <Text FontSize="10pt" TextContent="*Bind(XPath=link)"/>
            </HyperLink>
          </FlowPanel>
        </Style.VisualTree>
      </Style>
    </DockPanel.Resources>
    <DockPanel.DataContext>
      <Bind DataSource="{NewsData}" XPath="item"/>
    </DockPanel.DataContext>

    <FlowPanel DockPanel.Dock="Top">
      <Text Margin="0 5 5 5" TextWrap="Wrap" DockPanel.Dock="Top" FontSize="32px" TextContent="Blog Title:"/>
      <Text Margin="0 5 5 5" TextWrap="Wrap" DockPanel.Dock="Top" FontSize="32px" Foreground="DarkBlue"
            TextContent="*Bind(DataSource={NewsData};XPath=title)"/>
    </FlowPanel>

    <FlowPanel DockPanel.Dock="Top">
        <Text ID="theDesc" TextWrap="Wrap" DockPanel.Dock="Top" FontSize="12px"
            TextContent="*Bind(DataSource={NewsData};XPath=description)"/>
        <Text ID="theGenerator" TextWrap="Wrap" DockPanel.Dock="Top" FontSize="12px"         
            TextContent="*Bind(DataSource={NewsData};XPath=generator)"/>
        <HyperLink NavigateUri="*Bind(DataSource={NewsData};XPath=link)">
            <Text ID="theLink" TextWrap="Wrap" DockPanel.Dock="Top" FontSize="12px"         
                TextContent="*Bind(DataSource={NewsData};XPath=link)"/>
        </HyperLink>
    </FlowPanel>

    <ListBox DockPanel.Dock="Left" Margin="0 10 0 0" ID="NewsList" ItemStyle="{NewsDataStyle}" Width="50%" Background="*Bind(RelativeSource=/StyledParent;Path=Background)">
      <ListBox.Items>
        <CollectionContainer>
          <CollectionContainer.Collection>
            <Bind />
          </CollectionContainer.Collection>
        </CollectionContainer>
      </ListBox.Items>
    </ListBox>

    <Border Margin="0 10 0 0" DockPanel.Dock="Fill" BorderThickness="1px" BorderBrush="Gray">
    <DockPanel DockPanel.Dock="Fill" Margin="5">
      <Text DockPanel.Dock="Top" FontSize="Medium" TextContent="Article Description:"/>
      <Text Margin="30" TextWrap="Wrap" FontSize="Medium" ID="SelectedItemDetails"             
          DockPanel.Dock="Fill">
        <Text.TextContent>
          <Bind XPath="description"/>
        </Text.TextContent>
      </Text>
    </DockPanel>
    </Border>
  </DockPanel>
</Canvas>

Notice that there is no C# code-behind for this file - everything is accomplished directly in the markup.

The first part of the XAML file defines the Resources section of the DockPanel, under which I've defined an XML data source to retrieve the blog information from and a style to use to present the blog items in a list format.

Next, the DockPanel's DataContext is set to point to the "item" tags under the "rss/channel path" of the XML data source.

NOTE: this type of path syntax is used for version 2.0 RSS feeds, so if you want to use this example to point to other blogs, you will need to change the XPath used to point to the blog contents.

Since there will likely be several items in the blog, what this effectively does is set the DataContext to a collection of objects that can be represented in a UI element that knows how to deal with collections, such as a list box. More on this in a bit.

Some non-repeating UI elements follow, such as the title section for the blog and the blog's related description, generator, and link information. These elements are contained within FlowPanels so that they will flow with the size of the window, but will stay within the vertical bounds of the FlowPanel container.

The ListBox element will display the individual blog entries. Inside the ListBox is a CollectionContainer whose Collection is bound to the DockPanel's DataContext using the Bind tag. The empty Bind declaration here is just shorthand for saying "just bind to my parent's data context", which has been set by the earlier DataContext tag.

Finally, the Border area defines the section of the page that is used to display the description of the selected article. Here again, the SimpleText object is being bound to the parent DockPanel's DataContext with the additional path of "description", making the effective binding path a combination of the data source's XPath, the DockPanel's DataContext path, and the local path. In this case, the binding path is "rss/channel/item/description".

So how does the article description field update to match the selection in the list box? Both the ListBox and the SimpleText objects are bound to the same data collection, so the DataContext's notion of the current "item" will be changed each time the user makes a selection in the list box. This change will propagate through the rest of the UI to the SimpleText object.

Running the Application

Since Avalon has the ability to run XAML code just by double-clicking the file in the Windows Explorer, there's nothing special that has to be done to run Bloghorn - you just double-click it and go. If, on the other hand, it contained any code such as C#, it would first be necessary to build the finished executable using MSBuild or another tool like Visual Studio.

When Bloghorn starts up, the XmlDataSource connects to the MSDN XML feed, and finds the path to the "channel" tag in the RSS feed data. The data binding mechanism then populates the UI elements that are bound to the data source relative to this XPath statement. For example, the Blog Title feed is bound directly to the "title" tag of the RSS feed using the *Bind() directive. This kind of binding allows for the creation of user interfaces that are directly bound to data fields. The binding can be one-way, in which the UI element only receives data from the source, or they can be two-way, in which changes in the UI propagate the data back to the data source.

Conclusion

XAML is an exciting new technology that fundamentally changes how Windows applications are built. Essentially, if you are used to building Web applications today using a combination of markup and scripting, then you already have most of the skills you're going to need to build Windows applications in Longhorn.

end of article

download Download the code for this tutorial (NOTE: you need to have the November Avalon CTP Build installed to run this sample).

For information about how to obtain permission to re-publish this material, please contact us at info@joemarini.com.