Windows Presentation Foundation

Hello, WPF

WPF from Scratch
Navigation Applications
Content Model
Layout
Controls
Data Binding
Dependency Properties
Resources
Styles and Control Templates
Graphics
Application Deployment
Where Are We?

Layout

Introduction
Layout Basics
DockPanel
StackPanel
Grid
Canvas
Viewbox
Text Layout
Common Layout Properties
When Content Doesn't Fit
Custom Layout
Where Are We?

Controls

Introduction
What Are Controls?
Handling Input
Built-In Controls
Where Are We?

Data Binding

Introduction
Without Data Binding
Data Binding
Binding to List Data
Data Sources
Master-Detail Binding
Where Are We?

Styles and Control Templates

Introduction
Without Styles
Inline Styles
Named Styles
Element-Typed Styles
Data Templates and Styles
Triggers
Control Templates
Where Are We?

Resources

Introduction
Creating and Using Resources
Resources and Styles
Binary Resources
Global Applications
Where Are We?

Graphics

Introduction
Graphics Fundamentals
Shapes
Brushes and Pens
Transformations
Visual-Layer Programming
Video and 3-D
Where Are We?

Animation

Animation Fundamentals
Timelines
Storyboards
Key Frame Animations
Creating Animations Procedurally
Where Are We?

Custom Controls

Introduction
Custom Control Basics
Choosing a Base Class
Custom Functionality
Templates
Default Visuals
Where Are We?

ClickOnce Deployment

A Brief History of Windows Deployment
ClickOnce: Local Install
The Pieces of ClickOnce
Publish Properties
Deploying Updates
ClickOnce: Express Applications
Choosing Local Install versus Express
Signing ClickOnce Applications
Programming for ClickOnce
Security Considerations
Where Are We?

Data Binding


Data Binding

Once we've got a set of controls and a way to lay them out, we still need to fill them with data and keep that data in sync with wherever the data actually lives. (Controls are a great way to show data but a poor place to keep it.)

For example, imagine that we'd like to build an actual WPF application for keeping track of people's nicknames. Something like Figure 1-14 would do the trick.

Figure 1-14. Data binding to a collection of custom types


In Figure 1-14, we've got two TextBox controls, one for the name and one for the nickname; the actual nickname entries in a ListBox in the middle; and a Button to add new entries. The core data of such an application could easily be built with a class, as shown in Example 1-27.

Example 1-27. A custom type with data binding support
public class Nickname : INotifyPropertyChanged {
  // INotifyPropertyChanged Member
  public event PropertyChangedEventHandler PropertyChanged;
  protected void OnPropertyChanged(string propName) {
    if( PropertyChanged != null ) {
      PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }
  }

  string name;
  public string Name {
    get { return name; }
    set {
      name = value;
      OnPropertyChanged("Name"); // notify consumers
    }
  }

  private string nick;
  public string Nick {
    get { return nick; }
    set {
      nick = value;
      OnPropertyChanged("Nick"); // notify consumers
    }
  }

  public Nickname(  ) : this("name", "nick") { }
  public Nickname(string name, string nick) {
    this.name = name;
    this.nick = nick;
  }
}


This class knows nothing about data binding, but it does have two public properties that expose the data, and it implements the standard INotifyPropertyChanged interface to let consumers of this data know when it has changed.

In the same way that we have a standard interface for notifying consumers of objects when they change, we also have a standard way to notify consumers of collections of changes called INotifyCollectionChanged. WPF provides an implementation of this interface called ObservableCollection, which we'll use to fire the appropriate event when Nickname objects are added or removed, as in Example 1-28.

Example 1-28. A custom collection type with data binding support
  // Notify consumers
  public class Nicknames : ObservableCollection<Nickname> { }

Around these classes, we could build nickname-management logic that looks like Example 1-29.

Example 1-29. Making ready for data binding
// Window1.xaml.cs
...
namespace DataBindingDemo {
  public class Nickname : INotifyPropertyChanged {...}
  public class Nicknames : ObservableCollection<Nickname> { }

  public partial class Window1 : Window {
    Nicknames names;

    public Window1(  ) {
      InitializeComponent(  );
      this.addButton.Click += addButton_Click;

      // create a nickname collection
      this.names = new Nicknames(  );

      // make data available for binding
      dockPanel.DataContext = this.names;
    }

    void addButton_Click(object sender, RoutedEventArgs e) {
      this.names.Add(new Nickname(  ));
    }
  }
}

Notice the window's class constructor provides a click event handler to add a new nickname and creates the initial collection of nicknames. However, the most useful thing that the Window1 constructor does is set its DataContext property so as to make the nickname data available for data binding.

Data binding is about keeping object properties and collections of objects synchronized with one or more controls' view of the data. The goal of data binding is to save you the pain and suffering associated with writing the code to update the controls when the data in the objects change and with writing the code to update the data when the user edits the data in the controls. The synchronization of the data to the controls depends on the INotifyPropertyChanged and INotifyCollectionChanged interfaces that we've been careful to use in our data and data-collection implementations.

For example, because the collection of our sample nickname data and the nickname data itself both notify consumers when there are changes, we can hook up controls using WPF data binding, as in Example 1-30.

Example 1-30. An example of data binding
<!-- Window1.xaml -->
<Window x:Class="DataBindingDemo.Window1"
    xmlns="http://schemas.microsoft.com/winfx/avalon/2005"
    xmlns:x="http://schemas.microsoft.com/winfx/xaml/2005"
    Text="Nicknames">
  <DockPanel x:Name="dockPanel">
    <StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
      <TextBlock VerticalAlignment="Center">Name: </TextBlock>

      <TextBox Text="{Binding Path=Name}" />
      <TextBlock VerticalAlignment="Center">Nick: </TextBlock>
      <TextBox Text="{Binding Path=Nick}" />

    </StackPanel>
    <Button DockPanel.Dock="Bottom" x:Name="addButton">Add</Button>
    <ListBox
      ItemsSource="{Binding}"
      IsSynchronizedWithCurrentItem="True" />
  </DockPanel>

</Window>

This XAML lays out the controls as shown in Figure 1-14, using a dock panel to arrange things top-to-bottom and a stack panel to arrange the editing controls. The secret sauce that takes advantage of data binding is the {Binding} values in the control attributes instead of hardcoded values. By setting the Text property of the TextBox to {Binding Path=Name}, we're telling the TextBox to use data binding to peek at the Name property out of the current Nickname object. Further, if the data changes in the Name TextBox, the Path is used to poke the new value back in.

The current Nickname object is determined by the ListBox because of the IsSynchronizedWithCurrentItem property, which keeps the TextBox controls showing the same Nickname object as the one that's currently selected in the ListBox. The ListBox is bound to its data by setting the ItemsSource attribute to {Binding} without a Path statement. In the ListBox, we're not interested in showing a single property on a single object, but rather all of the objects at once.

But how do we know that both the ListBox and the TextBox controls are sharing the same data? That's where setting the dock panel's DataContext comes in. In the absence of other instructions, when a control's property is set using data binding, it looks at its own DataContext property for data. If it doesn't find any, it looks at its parent and then that parent's parent, and so on, all the way up the tree. Because the ListBox and the TextBox controls have a common parent that has a DataContext property set (the DockPanel), all of the data-bound controls will share the same data.

1.6.1. XAML Markup-Extension Syntax

Before we take a look at the results of our data binding, let's take a moment to discuss the XAML markup-extension syntax, which is what you're using when you set an attribute to something inside of curly bracese.g., Text="{Binding Path=Name}". The markup-extension syntax adds special processing to XAML attribute values. For example, the BindingExtension class creates an instance of the Binding class, populating its properties with the parsed string that comes afterward. Logically, the following:

<TextBox Text="{Binding Path=Name}" />

turns into the following:

Binding binding = new Binding(  );
binding.Path = "Name";
textbox1.Text =
  binding.ProvideValue(textbox1, TextBox.TextProperty);

In fact, the binding-extension syntax is just a shortcut for the following (which you'll recognize as the property-element syntax):

<TextBox.Text>
  <Binding Path="Name" />

</TextBox.Text>

For a complete discussion of markup extensions, as well as the rest of the XAML syntax, you'll want to read Appendix A.

1.6.2. Data Templates

With the data-binding markup syntax explained, let's turn back to our sample data-binding application, which so far doesn't look quite like what we had in mind, as seen in Figure 1-15.

Figure 1-15. ListBox showing objects of a custom type without special instructions


It's clear that the data is making its way into the application, since the currently selected name and nickname are shown for editing. The problem is that, unlike the TextBox controls which were each given a specific field of the Nickname object to show, the ListBox is expected to show the whole thing. Lacking special instructions, the ListBox calling the ToString method of each object, which only results in the name of the type. To show the data, we need to compose a data template, as shown in Example 1-31.

Example 1-31. Using a data template
<ListBox
  ItemsSource="{Binding}"
  IsSynchronizedWithCurrentItem="True">

  <ListBox.ItemTemplate>
    <DataTemplate>

      <TextBlock>
        <TextBlock TextContent="{Binding Path=Name}" />:
        <TextBlock TextContent="{Binding Path=Nick}" />
      </TextBlock>

    </DataTemplate>
  </ListBox.ItemTemplate>

</ListBox>

The ListBox control has an ItemTemplate property that expects a data template: a template of elements that should be inserted for each listbox item, instead of the results of the call to ToString. In Example 1-31, we've composed a data template from a text block that flows together two other text blocks, each bound to a property on a Nickname object separated by a colon, as shown in Figure 1-16.

Figure 1-16. How a ListBox shows objects of a custom type with a data template


At this point, we've got a completely data-bound application. As data in the collection or the individual objects changes, the UI will be updated and vice versa. However, there is a great deal more to say on this topic, not least of which is pulling in XML as well as object data, which are covered in Chapter 4.


©2008 FAQ - WPF Labs - Discuss - Terms of Use - Privacy Policy - About WPF
- Interview Questions - Sharepoint Articles - Interview Questions Resource Library - All about LINQ - MS Knowledgebase Articles - Electronics and Hardware discussions