|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionThis article explains the absolute basics of WPF data binding. It shows four different ways how to perform the same simple task. Each iteration moves closer to the most compact, XAML-only implementation possible. This article is for people with no experience in WPF data binding. BackgroundProgramming in WPF involves a lot of data binding. WPF user interfaces typically use much more data binding than most Windows Forms or ASP.NET user interfaces. Most, if not all, data movement in the user interface is accomplished with data binding. This article should help WPF newbies to start thinking in terms of WPF data binding, by showing how to translate a code-only solution into a compact XAML-only solution. This article does not discuss the binding API much. It only discusses what is relevant to the simple example. If you would like to read more about the technical details of WPF data binding, you can read my article about it here. The Demo AppThroughout this article, we will examine several ways to implement the same simple functionality. Our goal is to create a WPF program that allows us to edit a person’s first and last name. The application should also display that person’s name, formatted as <LastName>, <FirstName>. The formatted name should immediately update whenever the first or last name changes. The user interface should look something like this:
Version 1 – Manually Moving DataFirst, we will not use data binding to implement this. Let’s create a simple class to hold the person’s name: public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName
{
get
{
return String.Format("{0}, {1}",
this.LastName, this.FirstName);
}
}
} Next, we declare a simple user interface in XAML. These controls will display the three properties of our <StackPanel>
<TextBox
x:Name="firstNameTextBox"
Width="200"
Margin="0,4"
/>
<TextBox
x:Name="lastNameTextBox"
Width="200"
Margin="0,4"
/>
<TextBlock
x:Name="fullNameTextBlock"
Background="LightBlue"
Margin="0,4"
/>
</StackPanel> Finally, we can write some code in the Person _person;
// This method is invoked by the Window's constructor.
private void ManuallyMoveData()
{
_person = new Person
{
FirstName = "Josh",
LastName = "Smith"
};
this.firstNameTextBox.Text = _person.FirstName;
this.lastNameTextBox.Text = _person.LastName;
this.fullNameTextBlock.Text = _person.FullName;
this.firstNameTextBox.TextChanged += firstNameTextBox_TextChanged;
this.lastNameTextBox.TextChanged += lastNameTextBox_TextChanged;
}
void lastNameTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
_person.LastName = this.lastNameTextBox.Text;
this.fullNameTextBlock.Text = _person.FullName;
}
void firstNameTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
_person.FirstName = this.firstNameTextBox.Text;
this.fullNameTextBlock.Text = _person.FullName;
} Bugs are born in this type of code, like a swamp. This implementation requires the UI code to keep track of what controls need to be updated when certain property values change. This forces us to duplicate knowledge of the problem domain in our UI code, which is never a good thing. If we were dealing with a more complex problem domain, this type of code can get very ugly very fast. There must be a better way… Version 2 – Binding in CodeUsing the exact same XAML in our private void BindInCode()
{
var person = new Person
{
FirstName = "Josh",
LastName = "Smith"
};
Binding b = new Binding();
b.Source = person;
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
b.Path = new PropertyPath("FirstName");
this.firstNameTextBox.SetBinding(TextBox.TextProperty, b);
b = new Binding();
b.Source = person;
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
b.Path = new PropertyPath("LastName");
this.lastNameTextBox.SetBinding(TextBox.TextProperty, b);
b = new Binding();
b.Source = person;
b.Path = new PropertyPath("FullName");
this.fullNameTextBlock.SetBinding(TextBlock.TextProperty, b);
}In this version, we are no longer directly assigning values to the This seems all well and good, but there is a problem. If you run the program now, the formatted full name will not update when you edit the first or last name. In the previous version the formatted full name updated because we hooked each The WPF data binding system is not magical. It has no way to know that our public class Person : INotifyPropertyChanged
{
string _firstName;
string _lastName;
public string FirstName
{
get { return _firstName; }
set
{
_firstName = value;
this.OnPropertyChanged("FirstName");
this.OnPropertyChanged("FullName");
}
}
public string LastName
{
get { return _lastName; }
set
{
_lastName = value;
this.OnPropertyChanged("LastName");
this.OnPropertyChanged("FullName");
}
}
public string FullName
{
get
{
return String.Format("{0}, {1}",
this.LastName, this.FirstName);
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(
this, new PropertyChangedEventArgs(propName));
}
#endregion
} Notice that the new implementation of If we run the app now, the formatted full name text updates as we edit the first or last name. This shows that the binding system is listening to the We still have quite a bit of code. It would be better if we could declare the relationships between controls and data in XAML. That would neatly separate the UI layout and configuration away from the application logic. This is especially appealing if you want to use a design tool, such as Microsoft Expression Blend, to create your user interfaces. Version 3 – Binding in XAML (Verbose)Now let’s comment out the second version and see how to move all of this binding code into XAML. In the code-behind we will have the private void BindInXaml()
{
base.DataContext = new Person
{
FirstName = "Josh",
LastName = "Smith"
};
}The rest of the work is done in XAML. Here is the content of the <StackPanel>
<TextBox
x:Name="firstNameTextBox"
Width="200"
Margin="0,4"
>
<TextBox.Text>
<Binding
Path="FirstName"
UpdateSourceTrigger="PropertyChanged"
/>
</TextBox.Text>
</TextBox>
<TextBox
x:Name="lastNameTextBox"
Width="200"
Margin="0,4"
>
<TextBox.Text>
<Binding
Path="LastName"
UpdateSourceTrigger="PropertyChanged"
/>
</TextBox.Text>
</TextBox>
<TextBlock
x:Name="fullNameTextBlock"
Background="LightBlue"
Margin="0,4"
>
<TextBlock.Text>
<Binding Path="FullName" />
</TextBlock.Text>
</TextBlock>
</StackPanel>That XAML uses the property-element syntax to establish bindings for each control’s Running the application at this point shows that the XAML-based bindings work identically to the code-based bindings seen before. Both examples are creating instances of the same class and setting the same properties to the same values. However, this seems like a lot of XAML, especially if you are typing it by hand. It would be nice if there were a less verbose way to create the same bindings… Version 4 – Binding in XAMLUsing the same method in the code-behind as the previous example, and the same <StackPanel>
<TextBox
x:Name="firstNameTextBox"
Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged}"
Width="200"
Margin="0,4"
/>
<TextBox
x:Name="lastNameTextBox"
Text="{Binding Path=LastName, UpdateSourceTrigger=PropertyChanged}"
Width="200"
Margin="0,4"
/>
<TextBlock
x:Name="fullNameTextBlock"
Text="{Binding FullName}"
Background="LightBlue"
Margin="0,4"
/>
</StackPanel>That XAML is almost as short as in the original version. However, in this example there is no plumbing code wiring the controls up to the data source. Most data binding scenarios in WPF use this approach. Using the ConclusionThere are many ways to hook a user interface up to data. None of them are wrong, and all of them are useful in certain situations. Most of the time WPF developers use data binding via the convenient markup extension syntax. In more complicated, dynamic scenarios, it can be useful to create bindings in code. I hope that this article has shed some light on the topic, so that you can make an informed decision about how you want to get the job done. | ||||||||||||||||||||