WPF Tutorial – Part 4 : Databinding

Databinding and DataTemplates are perhaps the most powerful features of WPF. Whatever your experiences with databinding in previous technologies, MS or otherwise, I think you will be impressed with the new goods. To begin with, support for datadinding is built into WPF from its core. Almost every graphics/UI object that you will work with in WPF inherits from DependencyObject. The functionality supported by this base is what powers animation, styling and databinding. Without going into too much detail, objects that inherit from DependencyObject support a special type of property called a DependencyProperty. Most of the properties you will work with, such as Text, Content, Width, Height, etc, are DependencyProperties. Any DependencyProperty can be animated, styled and databound. WPF support for databinding is so rich, that I will only be able to demonstrate the most basic of examples here.. I recommend that you start from the very first post and read everything up to the present. This may seem like quite a task, but the time you will save by gaining a thorough understanding of databinding is worth it.

The first example I would like to show is a very common scenario. You have some object representing a Person, Customer, Employee, etc. and you want to display their information in the UI for editing purposes. Here’s some markup:

<Grid>
   <Grid.RowDefinitions>
      <RowDefinition/>
      <RowDefinition/>
      <RowDefinition/>
      <RowDefinition/>
      <RowDefinition/>
   </Grid.RowDefinitions>

   <Grid.ColumnDefinitions>
      <ColumnDefinition Width="75"/>
      <ColumnDefinition/>
   </Grid.ColumnDefinitions>

   <Label>First Name:</Label>
   <TextBox Grid.Column="1" Text="{Binding Path=FirstName}"/>

   <Label Grid.Row="1">Last Name:</Label>
   <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding LastName}"/>

   <Label Grid.Row="2">Street:</Label>
   <TextBox Grid.Row="2" Grid.Column="1" Text="{Binding Address.Street}"/>

   <Label Grid.Row="3">City:</Label>
   <TextBox Grid.Row="3" Grid.Column="1" Text="{Binding Address.City}"/>

   <Label Grid.Row="4">State:</Label>
   <TextBox Grid.Row="4" Grid.Column="1" Text="{Binding Address.State}"/>
</Grid>

Figure 8: A basic databinding example

A basic databinding example.

The screenshot is not very impressive, but you get the idea. I have created a Person object with FirstName and LastName public properties. There is also a public property called Address of type Address which has Street, City and State properties. I have assigned an instance of the Person object to the DataContext property of the window in the code beside. Every FrameworkElement has this property. When binding WPF searches from the current element up the UI tree until it finds a DataContext property it can bind to. Notice that the markup syntax is enclosed in curly braces and begins with the word Binding. You will see other Markup Extensions like this, but Binding is the most common. Notice the Path expression and the difference between how I have defined the binding to FirstName and LastName. These statements are equivalent; LastName demonstrates the abbreviated version. Also, notice how I have accessed the Address object. If you were to run this program, you would notice that all changes to the UI were propagated to the Person object, but changes to the Person object are not propagated back to the UI. For this to work you must implement the INotifyPropertyChanged interface, a trivial task.

Besides editing the Person object, let’s say that we want to display the information elsewhere in our application, in a Contact Card fashion. Let’s also say that we want to display this information in a consistent way in several different places. Using previous technologies we would likely create a custom control or user control to meet this need (or custom rendering logic for an existing control). In WPF we have both of these options, but additionally we can create a DataTemplate:

<Grid>
  <Grid.Resources>
    <DataTemplate DataType="{x:Type local:Person}">
      <Grid Margin="3">
        <Grid.BitmapEffect>
          <DropShadowBitmapEffect />
        </Grid.BitmapEffect>
        <Rectangle Opacity="1" RadiusX="9" RadiusY="9" Fill="Blue" StrokeThickness="0.35">
          <Rectangle.Stroke>
            <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
              <GradientStop Color="White" Offset="0" />
              <GradientStop Color="#666666" Offset="1" />
            </LinearGradientBrush>
          </Rectangle.Stroke>
        </Rectangle>
      <Rectangle Margin="2,2,2,0"
        VerticalAlignment="Top"
        RadiusX="6"
        RadiusY="6"
        Stroke="Transparent"
        Height="15px">
        <Rectangle.Fill>
          <LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
            <GradientStop Color="#ccffffff" Offset="0" />
            <GradientStop Color="transparent" Offset="1" />
          </LinearGradientBrush>
        </Rectangle.Fill>
      </Rectangle>
      <Grid Margin="5">
        <Grid.RowDefinitions>
          <RowDefinition Height="auto"/>
          <RowDefinition Height="auto"/>
          <RowDefinition Height="auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
          <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <StackPanel  Grid.Row="0" Orientation="Horizontal">
          <TextBlock FontSize="16" Foreground="White" Text="{Binding LastName}" />
          <TextBlock FontSize="16" Foreground="White" xml:space="preserve">, </TextBlock>
          <TextBlock FontSize="16" Foreground="White" Text="{Binding FirstName}" />
        </StackPanel>

        <TextBlock Grid.Row="1" FontSize="16" Foreground="White"
          Text="{Binding Address.Street}" />
        <StackPanel Orientation="Horizontal" Grid.Row="2">
          <TextBlock FontSize="16" Foreground="White" Text="{Binding Address.City}" />
          <TextBlock FontSize="16" Foreground="White" xml:space="preserve">, </TextBlock>
          <TextBlock FontSize="16" Foreground="White" Text="{Binding Address.State}" />
        </StackPanel>
      </Grid>
    </Grid>
  </DataTemplate>
  </Grid.Resources>

  <ListBox Name="personList" />

</Grid>

Figure 9: A contact card DataTemplate sample

A contact card DataTemplate sample.

I got a little more advanced with this sample. I wanted to do something that looked decent at least once during this article. To the explanation:

A DataTemplate is a visualization of something nonvisual. In this case we have a Person class, but what does that look like? Here, I have created a DataTemplate and set its DataType to person. Now WPF knows how to render this type of class when databinding occurs. You can also assign a key to a template and reference the template by this rather than having it automatically applied. This is the same idea as using CSS to apply a style to all DIV elements as apposed to applying the style based on class name. The difference is that here we are defining how .NET classes should be rendered. DataTemplates can also be scoped at any level of the application. Notice that the template is defined inside of a Grid.Resources tag. Every FrameworkElement has a resource collection. This means that you can store DataTemplate at virtually any level of the UI hierarchy. So I could define what Person looks like at the Application, Window, Panel or Control level. Here, I have defined the appearance of Person and scoped it to all controls within the Grid. The ListBox contained within the Grid picks up the template automatically when I set its ItemsSource to a collection of Person objects in the code beside. DataTemplates are also aware of .NET inheritance. So if I have Employee and Manager classes that both derive from Person, they will be rendered with this template as well. But, if I define a DataTemplate specifically for the Manager class, WPF will use the more specific template. Consider the possibilities.

There is so much more to databinding and DataTemplates than I have said here. I am quite embarrassed at the lack of coverage I have been able to give. But I have to move on. As food for thought, here are some other things supported out-of-box with WPF databinding: You can have conditional binding based on business rule validation, conditional template selection based on business rules, converters that shape data when binding to and from the UI, DataTriggers that change the appearance of the template based on values in the data, binding between different UI elements based on name or relative position in the UI hierarchy, a choice of four different binding modes and a choice of what type of event triggers the binding, and bindings to data sources that pass in other bindings as parameters, etc. The list goes on and on. Again, let me mention Beatriz Costa’s blog as a great source of info. Once you have learned the basics, go there and read everything.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s