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?

Without Data Binding

4.1. Without Data Binding

Consider a very simple application for editing a single person's name and age, as shown in Figure 4-1.

Figure 4-1. An exceedingly simple application


Figure 4-1 can be implemented with the simple XAML shown in Example 4-1.

Example 4-1. A simple Person editor layout
<!-- Window1.xaml -->
<Window ...>
  <Grid>
    ...
    <TextBlock ...>Name:</TextBlock>
    <TextBox x:Name="nameTextBox" ... />

    <TextBlock ...>Age:</TextBlock>
    <TextBox x:Name="ageTextBox" ... />
    <Button x:Name="birthdayButton" ...>Birthday</Button>
  </Grid>
</Window>


The data to be shown in our simple application can be represented in a simple class, as shown in Example 4-2.

Example 4-2. A simple Person class
public class Person {
  string name;
  public string Name {
    get { return this.name; }
    set { this.name = value; }
  }

  int age;
  public int Age {
    get { return this.age; }
    set { this.age = value; }
  }

  public Person(  ) {}
  public Person(string name, int age) {
    this.name = name;
    this.age = age;
  }
}

With this class, a naive implementation of the behavior of our application could look like Example 4-3.

Example 4-3. Naive Person editor code
// Window1.xaml.cs
...
public class Person {...}

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

  public Window1(  ) {
    InitializeComponent(  );

    // Fill initial person fields
    this.nameTextBox.Text = person.Name;
    this.ageTextBox.Text = person.Age.ToString(  );

    this.birthdayButton.Click += birthdayButton_Click;
  }

  void birthdayButton_Click(object sender, RoutedEventArgs e) {
    ++person.Age;

    MessageBox.Show(
      string.Format(
        "Happy Birthday, {0}, age {1}!",
        person.Name,
        person.Age),
      "Birthday");
  }
}

The code in Example 4-3 creates a Person object and initializes the text boxes with the Person object properties. When the Birthday button is pressed, the Person object's Age property is incremented, and the updated Person data is shown in a message box, as shown in Figure 4-2.

Figure 4-2. Our simple application is too simple


Our simple application implementation is, in fact, too simple. The change in the Person Age property does show up in the message box, but it does not show up in the main window. One way to keep the application's UI up to date is to write the code so that whenever a Person object is updated, it manually updates the UI at the same time, as shown in Example 4-4.

Example 4-4. Manually updating the UI when a Person is updated
void birthdayButton_Click(object sender, RoutedEventArgs e) {
  ++person.Age;

  // Manually update the UI
  this.ageTextBox.Text = person.Age.ToString(  );

  MessageBox.Show(
    string.Format(
      "Happy Birthday, {0}, age {1}!",
      person.Name,
      person.Age),
    "Birthday");
}

With a single line of code, we've "fixed" our application. This is a seductive and popular road, but it does not scale as the application gets more complicated and requires more of these "single" lines of code. To get beyond the simplest of applications, we'll need a better way.

4.1.1. Object Changes

A more robust way for the UI to track object changes is for the object to raise an event when a property changes. As of .NET 2.0, the right way for an object to do this is with an implementation of the INotifyPropertyChanged interface, as shown in Example 4-5.

Example 4-5. A class that supports property-change notification
using System.ComponentModel; // INotifyPropertyChanged

...
public class Person : INotifyPropertyChanged {
  // INotifyPropertyChanged Members
  public event PropertyChangedEventHandler PropertyChanged;
  protected void OnPropertyChanged(string propName) {
    if( this.PropertyChanged != null ) {
      PropertyChanged(this, new PropertyChangedEventArgs(propName));
    }
  }

  string name;
  public string Name {
    get { return this.name; }
    set {
      this.name = value;
      OnPropertyChanged("Name");
    }
  }

  int age;
  public int Age {
    get { return this.age; }
    set {
      this.age = value;
      OnPropertyChanged("Age");
    }
  }

  public Person(  ) {}
  public Person(string name, int age) {
    this.name = name;
    this.age = age;
  }
}


In Example 4-5, when either of the Person properties changes (as is caused by the implementation of the Birthday button), a Person object raises the PropertyChanged event. We can use this event to keep the UI synchronized with the Person properties, as in Example 4-6.

Example 4-6. Simple Person editor code
// Window1.xaml.cs
...
public class Person : INotifyPropertyChanged {...}
public partial class Window1 : Window {
  Person person = new Person("Tom", 9);

  public Window1(  ) {
    InitializeComponent(  );

    // Fill initial person fields
    this.nameTextBox.Text = person.Name;
    this.ageTextBox.Text = person.Age.ToString(  );

    // Watch for changes in Tom's properties
    person.PropertyChanged += person_PropertyChanged;

    this.birthdayButton.Click += birthdayButton_Click;
  }

  void person_PropertyChanged(
    object sender,
    PropertyChangedEventArgs e) {

    switch( e.PropertyName ) {
      case "Name":
      this.nameTextBox.Text = person.Name;
      break;

      case "Age":
      this.ageTextBox.Text = person.Age.ToString(  );
      break;
    }
  }

  void birthdayButton_Click(object sender, RoutedEventArgs e) {
    ++person.Age; // person_PropertyChanged will update ageTextBox
    MessageBox.Show(
      string.Format(
        "Happy Birthday, {0}, age {1}!",
        person.Name,
        person.Age),
      "Birthday");
  }
}

Example 4-6 shows a single instance of the Person class that's created when the main window first comes into existence, initializing the name and age text boxes with the initial person values and then subscribing to the property-change event to keep the text boxes up to date as the Person object changes. With this code in place, the birthday-button click event handler doesn't have to manually update the text boxes when it updates Tom's age; instead, updating the Age property causes a cascade of events that keeps the age text box up to date with the Person object's changes, as shown in Figure 4-3.

Figure 4-3. Keeping the UI up to date with changes in the object


The steps are as follows:

  1. User clicks on button, which causes Click event to be raised.

  2. Click handler gets the age (9) from the Person object.

  3. Click handler sets the age (10) on the Person object.

  4. Person Age property setter raises the PropertyChanged event.

  5. PropertyChanged event is routed to event handler in the UI code.

  6. UI code updates the age TextBox from "9" to "10".

  7. Button click event handler displays a message box showing the new age ("10").

By the time the message box is shown with Tom's new age, the age text box on the form has already been updated, as shown in Figure 4-4.

Figure 4-4. Manually populating two WPF controls with two object properties


With the handling of the INotifyPropertyChanged event, when the data in the object changes, the UI is updated to reflect that change. However, that only solves half the problem; we still need to handle changes in the UI and reflect them back to the object.

4.1.2. Control Changes

Without some way to track changes from the UI back into the object, we could easily end up with a case where the user has made some change (like changing the person's name), shows the object (as happens when pressing the Birthday button) and expects the change to have been made, only to be disappointed with Figure 4-5.

Figure 4-5. The need to keep controls and objects in sync


Notice in Figure 4-5 that the Name is "Thomsen Frederick" in the form, but "Tom" in the message box, which shows that while one part of the UI has been updated, the underlying object has not. To fix this problem, we have but to watch for the Text property in our TextBox object to change, updating the Person object as appropriate, as in Example 4-7.

Example 4-7. Tracking changes in the UI
public partial class Window1 : Window {
  Person person = new Person("Tom", 9);

  public Window1(  ) {
    InitializeComponent(  );

    // Fill initial person fields
    this.nameTextBox.Text = person.Name;
    this.ageTextBox.Text = person.Age.ToString(  );

    // Watch for changes in Tom's properties
    person.PropertyChanged += person_PropertyChanged;

    // Watch for changes in the controls
    this.nameTextBox.TextChanged += nameTextBox_TextChanged;
    this.ageTextBox.TextChanged += ageTextBox_TextChanged;

    this.birthdayButton.Click += birthdayButton_Click;
  }

  ...

  void nameTextBox_TextChanged(object sender, TextChangedEventArgs e) {
    person.Name = nameTextBox.Text;
  }

  void ageTextBox_TextChanged(object sender, TextChangedEventArgs e) {
    int age = 0;
    if( int.TryParse(ageTextBox.Text, out age) ) {
      person.Age = age;
    }
  }

  void birthdayButton_Click(object sender, RoutedEventArgs e) {
    ++person.Age;

    // nameTextBox_TextChanged and ageTextBox_TextChanged
    // will make sure the Person object is up to date
    MessageBox.Show(
      string.Format(
        "Happy Birthday, {0}, age {1}!",
        person.Name,
        person.Age),
      "Birthday");
  }
}

Now, no matter where the data changes, both the Person object and the UI showing the Person object are kept synchronized. Figure 4-6 shows the name changes in the UI correctly propagating to the Person object.

Figure 4-6. Manually keeping properties and controls in sync


While we've gotten the functionality we wanted, we had to write quite a bit of code to make it happen:

  • Window1 constructor code to set controls to initial values

  • Window1 constructor code to hook up the PropertyChanged event to track the Person object's property changes

  • PropertyChanged event handler to grab the updated data from the Person object, converting data to strings as appropriate

  • Window1 constructor code to hook up the TextBox object's TextChanged event to track the UI changes

  • TextChanged event handlers to push the updated TextBox data into the Person object, converting the data as appropriate

This code allows us to write our birthday button event handler safe in the knowledge that all changes are synchronized when we display the message box. However, it's easy to imagine how this code could quickly get out of hand as the number of object properties gets beyond two or as the number of objects we're managing grows. Plus, this seems like such a common thing to want to do that someone must have already provided a simpler way to do this. And, in fact, they have; it's called 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