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

4.2. Data Binding

Our manual code to keep the UI and the data synchronized has the effect of implicitly binding together two sets of properties, one from the Person object and one from the controls showing the Person object. Data binding is the act of explicitly binding properties from one object to another, keeping them synchronized and converting types as appropriate, as shown in Figure 4-7.

Figure 4-7. The synchronization and conversion duties of data binding


4.2.1. Bindings

Instead of setting the Text property of the TextBox objects manually in code and then keeping them up to date, data binding allows us to set the Text property using an instance of a Binding object, as in Example 4-8.

Example 4-8. Binding a dependency property to a CLR property
<TextBox ...>
  <TextBox.Text>
    <Binding Path="Age" />
  </TextBox.Text>
</TextBox>


In Example 4-8, we've used the property-element syntax introduced in Chapter 1 to create an instance of the Binding class, initialize its Path property to the string "Age" and set the Binding object as the value of the TextBox object's Text property. Using the binding markup extension (also introduced in Chapter 1), we can shorten Example 4-8 to Example 4-9.

Example 4-9. The shortcut binding syntax
<TextBox TextContent="{Binding Path=Age}" />

As an even shorter cut, you can drop the Path designation altogether, and the Binding will still know what you mean, as in Example 4-10.

Example 4-10. The shortest cut binding syntax
<TextBox TextContent="{Binding Age}" />

I prefer to be more explicit, so I won't use the syntax in Example 4-10, but I won't judge if you like it.

The Binding class has all kinds of interesting facilities for managing the binding between properties, but the one that we're most interested in is the Path property. For most cases, you can think of the Path as the name of the property on the object serving as the data source. So, the binding statement in Example 4-10 is creating a binding between the Text property of the TextBox and the Name property of some object to be named later, as shown in Figure 4-8.

Figure 4-8. Binding targets and sources


In this binding, the TextBox control is the binding target , as it acts as a consumer of changes to the binding source , which is the object that provides the data. The binding target can be a WPF element, but you're only allowed to bind to the element's dependency properties (described in Chapter 9).

On the other hand, you can bind to any public CLR property on the binding source object; the binding source is not named in this example specifically so that we can have some freedom as to where it comes from at runtime and so that it's easier to bind multiple controls to the same object (like our name and age text-box controls bound to the same Person object).

Commonly, the binding source data comes from a data context .

4.2.2. Implicit Data Source

A data context is a place for bindings to look for the data source if they don't have any other special instructions (which we'll discuss later). In WPF, every FrameworkElement and every FrameworkContentElement has a DataContext property. The DataContext property is of type object so you can plug anything you like into ite.g., string, Person, List<Person>, etc. When looking for an object to use as the binding source, the binding object traverses up the tree from where it's defined, looking for a non-null DataContext property.

This traversal is handy because it means that any two controls with a common logical parent can bind to the same data source. For example, both of our text box controls are children of the grid, and they each search for a data context, as shown in Figure 4-9.

Figure 4-9. Searching the element tree for a non-null DataContext


The steps work like this:

  • The binding looks for a non-null DataContext on the TextBox itself

  • The binding looks for a non-null DataContext on the Grid

  • The binding looks for a non-null DataContext on the Window

Providing a non-null DataContext for both of the text box controls is a matter of setting the shared Person object as the value of the grid's DataContext property in the Window1 constructor, as in Example 4-11.

Example 4-11. Editor code simplified with data binding
// Window1.xaml.cs
using System;
using System.Windows;
using System.Windows.Controls;

namespace PersonBinding {
  public partial class Window1 : Window {
    Person person = new Person("Tom", 9);

    public Window1(  ) {
      InitializeComponent(  );

      // Let the grid know its data context
      grid.DataContext = person;

      this.birthdayButton.Click += birthdayButton_Click;
    }

    void birthdayButton_Click(object sender, RoutedEventArgs e) {
      // Data binding keeps person and the text boxes synchronized
      ++person.Age;
      MessageBox.Show(
        string.Format(
          "Happy Birthday, {0}, age {1}!",
          person.Name,
          person.Age),
        "Birthday");
    }
  }
}


So, while the functionality of our app is the same as shown in Figure 4-9, the data-synchronization code has been reduced to a binding object for each property in the XAML where data is to be shown, and a data context for the bindings to find the data. There is no need for the UI initialization code or the event handlers that copy and convert the data (notice the lack of ellipses in Example 4-11).

To be clear, the use of the INotifyPropertyChanged implementation is not an accident. This is the interface that WPF's data-binding engine uses to keep the UI synchronized when an object's properties change. Without it, a UI change can still propagate to the object, but the binding engine will have no way of knowing when to update the UI.

It's not quite true that the binding engine will have no way of knowing when a change happens on an object that does not implement the INotifyPropertyChanged interface. One way it can know is if the object implements the PropertyNameChanged events as prescribed in .NET 1.x data bindinge.g. SizeChanged, TextChanged, etc.with which WPF maintains backward compatibility. Another way is a manual call to the UpdateTarget method on the BindingExpression object associated with the Binding in question.

However, it's safe to say that implementing INotifyPropertyChanged is the recommended way to enable property change notifications in WPF data binding.


4.2.3. Declarative Data

While our application is attempting to simulate a more complicated application that, perhaps, loads its "person data" from some persisted form and saves it between application sessions, it's not hard to imagine cases where some data is known at compile time. Maybe it's sample data (like our Tom) or well-known data that doesn't change between sessions, such as application settings defaults or error messages. Lots of applications have string resources that are kept separate from the workings of the UI, but are still bundled with the application. Keeping the data bundled with the app makes it easier to maintain and localize, while keeping it out of the UI logic itself reduces coupling between the data and the UI. In our sample thus far, we've been keeping this well-known data in the code, but XAML is a better choice, both because of the ease of maintaining data in XAML and XAML's support for localization (as described in Chapter 6).

As discussed in Chapter 1, XAML is a language for describing object graphs, so practically any type with a default constructor can be initialized in XAML. Luckily, as you'll recall from Example 4-2, our Person class has a default constructor, so we can create an instance of it in our application's XAML, as shown in Example 4-12.

Example 4-12. Creating an instance of a custom type in XAML
<?Mapping XmlNamespace="local" ClrNamespace="PersonBinding" ?>

<Window ... xmlns:local="local">
  <Window.Resources>
    <local:Person x:Key="Tom" Name="Tom" Age="9" />
  </Window.Resources>
  <Grid>...</Grid>

</Window

Here we've created a little "data island" inside the window's resources element, bringing the Person type in using the XAML mapping syntax described in Chapter 1.

With a named Person in our XAML code, we can declaratively set the grid's DataContext, instead of setting it in the code-behind file programmatically, as in Example 4-13.

Example 4-13. Binding to an object declared in XAML
<!-- Window1.xaml -->
<?Mapping XmlNamespace="local" ClrNamespace="PersonBinding" ?>
<Window ... xmlns:local="local">
  <Window.Resources>
    <local:Person x:Key="Tom" Name="Tom" Age="9" />

  </Window.Resources>
  <Grid DataContext="{StaticResource Tom}">
    ...
    <TextBlock ...>Name:</TextBlock>
    <TextBox ... Text="{Binding Path=Name}" />

    <TextBlock ...>Age:</TextBlock>
    <TextBox ... Text="{Binding Path=Age}" />
    <Button ... x:Name="birthdayButton">Birthday</Button>
  </Grid>

</Window>

Now that's we've moved the creation of the Person object to the XAML, we have to update our Birthday button click handler from using a member variable to using the data defined in the resource, as in Example 4-14.

Example 4-14. Binding to an object declared in XAML
public partial class Window1 : Window {
  ...
  void birthdayButton_Click(object sender, RoutedEventArgs e) {
    Person person = (Person)this.FindResource("Tom"));
    ++person.Age;
    MessageBox.Show(...);
  }
}

In Example 4-14, we're using the FindResource method (introduced in Chapter 1 and further detailed in Chapter 6) to pull the Person object from the main window's resources. With this minor change, the result is brought again into parity with Figure 4-6. The only thing that's different is that you don't have to touch the code-behind file to maintain or localize the data known at compile time. (Chapter 6 discusses the localization of XAML resources.)

4.2.4. Explicit Data Source

Once you've got yourself a named source of data, you can be explicit in the XAML about the source in the binding object instead of relying on implicitly binding to a non-null DataContext property somewhere in the tree. Being explicit is useful if you've got more than one source of datae.g., two Person objects. Setting the source explicitly is accomplished with the Source property in the binding, as in Example 4-15.

Example 4-15. Data binding using the Source property
<!-- Window1.xaml -->
<Window ...>
  <Window.Resources>
    <local:Person x:Key="Tom" ... />
    <local:Person x:Key="John" ... />

  </Window.Resources>
  <Grid>
    ...
    <TextBox x:Name="tomTextBox"
      Text="
        {Binding
          Path=Name,
          Source={StaticResource Tom}}" />

    <TextBox x:Name="johnTextBox"
      Text="
        {Binding
          Path=Name,
          Source={StaticResource John}}" />
    ...
  </Grid>
</Window>

In Example 4-15, we're binding two text boxes to two different person objects, using the Source property of the Binding object to bind each person explicitly.

Implicit Versus Explicit Binding

In general, I find implicit binding to be most useful when I'm sharing the same data between multiple controls, because all that's needed is a bit of code to set the the DataContext property on a single parent element. On the other hand, if I've got multiple data sources, I really like using the Source property on my Binding objects to make it clear where the data is coming from.


4.2.5. Value Conversion

So far, our sample application has shown the bound data as text in text boxes. However, there's absolutely nothing stopping you from binding to other properties of a controle.g., Foreground, FontWeight, Height, etc. For example, we might decide that anyone over age 25 is cool, so should be marked in the UI as red (or, they're in danger of dying soonerwhichever makes you more likely to recommend this book to your friends...). As someone ages at the press of the Birthday button, we want to keep the UI up to date, which means we've got ourselves a perfect candidate for data binding. Imagine the ability to do the following in Example 4-16.

Example 4-16. Binding to a non-Text property
<!-- Window1.xaml -->
<Window ...>
  <Window.Resources>
    <local:Person x:Key="Tom" ... />
  </Window.Resources>

  <Grid>
    ...
    <TextBox
      Text="{Binding Path=Age}"
      Foreground="{Binding Path=Age, ...}"
      ...
    />
    ...
  </Grid>
</Window>


In Example 4-16, we've bound the age text box's Text property to the Person object's Age property, as we've already seen, but we're also binding the Foreground property of the text box to the exact same property on the Person object. As Tom's age changes, we want to update the foreground color of the age text box. However, since the Age is of type Int32 and Foreground is of type Brush, there needs to be a mapping from Int32 to Brush applied to the data binding from Age to Foreground. That's the job of a value converter .

A value converter (or just "converter" for short) is an implementation of the IValueConverter interface, which contains two methods: Convert and ConvertBack. The Convert method is called when converting from the source data to the target UI datae.g., from Int32 to Brush. The ConvertBack method is called to convert back from the UI data to the source data. In both cases, the current value and the type wanted for the target data is passed to the method.

To convert an Age Int32 into a Foreground Brush, we can implement whatever mapping in the Convert function we feel comfortable with, as in Example 4-17.

Example 4-17. A simple value converter
public class AgeToForegroundConverter : IValueConverter {
  // Called when converting the Age to a Foreground brush
  public object Convert(object value, Type targetType, ...) {

    Debug.Assert(targetType == typeof(Brush));

    // DANGER! After 25, it's all down hill...
    int age = int.Parse(value.ToString(  ));
    return (age > 25 ? Brushes.Red : Brushes.Black);
  }

  // Called when converting a Foreground brush back to an Age
  public object ConvertBack(object value, ...) {
    // should never be called
    throw new NotImplementedException(  );
  }
}

In Example 4-17, we've implemented the Convert method to double-check that we're after a Foreground brush and then we hand out the brush that's appropriate for the age being displayed. Since we haven't provided any facility to change the Foreground brush being used to display the age, there's no reason to implement the ConvertBack method.

I chose the name AgeToForegroundConverter because I have specific semantics I'm building into my converter class that go above simply converting an Int32 to a Brush. Even though this converter could be plugged in anywhere that converted an Int32 to a Brush, I might have very different requirements for a HeightToBackgroundConverter, as just one example.


Once you've got a converter class, it's easy to create an instance of one in the XAML, just like we've been doing with our Person object, as shown in Example 4-18.

Example 4-18. Binding with a value converter
<!-- Window1.xaml -->
<?Mapping XmlNamespace="local" ClrNamespace="PersonBinding" ?>
<Window ... xmlns:local="local">

  <Window.Resources>
    <local:Person x:Key="Tom" ... />
    <local:AgeToForegroundConverter
      x:Key="AgeToForegroundConverter" />
  </Window.Resources>
  <Grid DataContext="{StaticResource Tom}">
    ...
    <TextBox
      Text="{Binding Path=Age}"
      Foreground="
        {Binding
          Path=Age,
          Converter={StaticResource AgeToForegroundConverter}}"

      ... />
    ...
  </Grid>
</Window>

In Example 4-18, once we have a named converter object in our XAML, we establish it as the converter between the Age property and the Foreground brush by setting the Converter property of the binding object. Figure 4-10 shows the result of our conversion.

Figure 4-10. A value converter in action (Color Plate 8)


In Figure 4-10, notice that as Tom's age increases past the threshold, the converter switches the foreground brush from black to red. This change happens immediately as the data changes without any explicit code to force it, just as with any other kind of data binding.


©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