Home Publications Tutorials Downloads Archives About Contact
 

Building Data-Bound User Interfaces in XAML

Article Summary: Illustrates a new way of building dynamic user interfaces that automatically respond to changes in a data model and update their appearance by using binding relationships between the elements in the UI. This example uses a 100% XAML approach to demonstrate these relationships, called "bindings".

download Download the code for this tutorial

One of the best new features of XAML, the declarative UI language coming in Longhorn, is its completely new way of thinking about how to build and connect user interfaces by tying different elements of the UI directly to each other or an underlying data model using a technique called data binding.

The concept itself is pretty simple, and is a clear departure from how things are typically done today. If you've been reading Chris Sells' ongoing articles chronicling his rebuilding of the Solitaire application under Longhorn, then you've seen an example of data binding in the user interface already. Essentially, user interfaces built today generate events that are handled internally by the logic code of the application. More often than not, these events result in some type of change to the data that the application is tracking, and these event handlers are responsible for updating the data model in response to the change.

Data binding in Longhorn all but eliminates this need. One of the clear benefits of writing user interfaces with this technique is that it eliminates the traditional (and annoying) "housekeeping" event handling code that you usually have to write to keep the different parts of the UI in sync with each other or with an underlying data model in the application when either one of them changes. While you can, of course, still write event handlers to respond to UI-generated events, you'll find yourself doing much less of this when the main task performed by the event handler is to just update the app's internal data model. Instead, you just hook up the appropriate UI control to the right part of the data model and everything happens for you automatically. If the data first has to undergo some type of transformation or validating before it can be applied to the model, you can do that to by writing a piece of code called a Transformer (which I'll cover in a future article).

This type of approach to building applications is a key pillar of the Longhorn platform and represents a departure from the more typical event-handling approach of other platforms, such as Visual Basic, Macromedia Flex, and Laszlo Presentation Server.

The syntax for binding a property to a value is rather straightforward. You can either use the more-verbose <Bind> tag, or the compact *Bind() operator inside of a dependency property attribute on the desired element.

Types of Data Binding

Your application can exert varying degrees of control over a binding, such as:

  • Which property on the data source should serve as the source for the binding
  • When the bind operation actually takes place (immediately or explicitly)
  • Whether the binding executes only once or is continuous
  • What direction the binding operates in (one-way or two-way)
  • How the bound data is transformed between the source and the target

The more advanced concepts listed above will be covered in a future tutorial. For now, this article will just concentrate on the main issue - showing how to bind properties of various different controls to a simple data model so that we can see the results of the changes in real time.

A Simple, Interactive Binding Demonstration

The following example demonstrates binding different types of data values to the various properties of a XAML <Button> control. The program constructs a UI that contains several controls that provide values to the properties on the button. By manipulating these controls, the properties of the button change in real time. The code for this example totals approximately 60 or so lines of XAML:

<DockPanel ID="root" xmlns="http://schemas.microsoft.com/2003/xaml"
xmlns:def="Definition">

<DockPanel DockPanel.Dock="Top" Background="Goldenrod">
  <FlowPanel Margin="5" DockPanel.Dock="Top">
    <Text>Text String:</Text>
    <TextBox ID="textString" Height="24" Width="100%">Bound Text String</TextBox>
  </FlowPanel>
  <FlowPanel Margin="5" DockPanel.Dock="Top">
    <Text>Font Size: </Text>
    <HorizontalSlider ID="s1" Width="150" Value="36" Minimum="12" Maximum="200" SmallChange="1" LargeChange="10"/>
    <TextBox ID="textSize" Height="24" Width="60" Text="*Bind(ElementSource=s1;Path=Value;BindType=TwoWay)"/>
  </FlowPanel>
  <FlowPanel Margin="5" DockPanel.Dock="Top">
    <Text>Font Weight: </Text>
    <TextBox ID="fontWeight" Height="24" Width="60">Normal</TextBox>
  </FlowPanel>
  <FlowPanel Margin="5" DockPanel.Dock="Top">
    <Text>Width: </Text>
    <TextBox ID="textWidth" Height="24" Width="60">100%</TextBox>
  </FlowPanel>
  <FlowPanel Margin="5" DockPanel.Dock="Top">
    <Text>Height: </Text>
    <TextBox ID="textHeight" Height="24" Width="60">50</TextBox>
  </FlowPanel>
  <FlowPanel Margin="5" DockPanel.Dock="Top">
    <Text>Foreground Color: </Text>
    <TextBox ID="textColor" Height="24" Width="60" ToolTip="Enter a named color for the text">Black</TextBox>
  </FlowPanel>
  <FlowPanel Margin="5" DockPanel.Dock="Top">
    <Text>Opacity: </Text>
    <HorizontalSlider ID="s2" Width="150" Value="1" Minimum="0" Maximum="1" ToolTip="Change the transparency setting of the button"/>
  </FlowPanel>
  <FlowPanel Margin="5" DockPanel.Dock="Top">
    <Text>Margin: </Text>
    <TextBox ID="margin" Height="24" Width="60" ToolTip="Enter a margin setting for the button">0</TextBox>
  </FlowPanel>
  <FlowPanel Margin="5" DockPanel.Dock="Top">
    <Text>Font Family: </Text>
    <TextBox ID="fontFam" Height="24" Width="90">Arial</TextBox>
  </FlowPanel>
</DockPanel>

<FlowPanel DockPanel.Dock="Fill" Background="Silver">
  <Button Height="*Bind(ElementSource=textHeight;Path=Text)"
      Width="*Bind(ElementSource=textWidth;Path=Text)"
      FontSize="*Bind(ElementSource=s1;Path=Value)"
      FontWeight="*Bind(ElementSource=fontWeight;Path=Text)"
      FontFamily="*Bind(ElementSource=fontFam;Path=Text)"
      Foreground="*Bind(ElementSource=textColor;Path=Text)"
      Opacity="*Bind(ElementSource=s2;Path=Value)"
      Margin="*Bind(ElementSource=margin;Path=Text)"
      Content="*Bind(ElementSource=textString;Path=Text)"
      />
</FlowPanel>
</DockPanel>

Notice that, like my previous XAML examples shown thus far, there is no C# code-behind for this file - everything is accomplished directly in the XAML markup.

Running the Application

Since this example does not contain any C# code, you can just double-click the XAML file to have it automatically run in Longhorn. When the application launches, the embedded button control's properties will be set to the default values provided by the controls in the control section of the window.

You can change the appearance of the button by manipulating these controls and watch the effect on the button in real time. For example, changing the font of the button from the default Arial to Courier New produces the following result:

You can also interactively change the button's opacity setting by sliding the associated slider control up and down:

An obvious question presents itself here - why use things like text boxes for the font and color options, instead of using more appropriate controls like combo boxes? The simple answer is that I wanted to try and avoid writing C# code for this example, and in the current WinHEC version of Longhorn, text boxes are the simplest controls to use for direct data binding. Using a combo box would have required the addition of C# logic to transform each item into the appropriate string value for the font or color.

Conclusion

Avalon's XAML Data Binding mechanism provides an entirely new way of thinking about how to build responsive user interfaces that automatically update and propagate changes in the underlying application model. While the example shown in this column was obviously contrived to show how binding different types of data and controls works, the main point should be clear - it's not necessary to write a bunch of event handlers to make everything work. The code just ties the right source property to the right data property, and that's it. In a future column, I'll show how to perform more advanced data binding operations such as transformations that can take a source value and transform it into something else - like a string into a color or a number into a string that represents a range of values, like a star rating for a movie or restaurant.

download Download the code for this tutorial (NOTE: you need to have the WinHEC Longhorn Build installed to run this sample. You must be an MSDN subscriber to have access to this build.