How do I make my data-bound TextBox update the source value as I type?
By default, TextBox.Text has an UpdateSourceTrigger value of LostFocus. This means if you have a data-bound TextBox, the default behavior is that the source does not get updated until the TextBox loses focus. If you want the source to update as you type into the TextBox, set the UpdateSourceTrigger property to PropertyChanged:
<TextBox Text="{Binding Path=Name, UpdateSourceTrigger=PropertyChanged}"/>
One thing that’s really easy to miss is that the UpdateSourceTrigger default value varies for different properties. For most properties, the UpdateSourceTrigger property defaults to PropertyChanged. However, for TextBox.Text, it is LostFocus.
This table provides a summary of the UpdateSourceTrigger value for TextBox.Text. Again, remember that this is only true for the TextBox.Text property.
|
UpdateSourceTrigger value |
When the Source Value Gets Updated |
Example Scenario for TextBox |
|
LostFocus (default for TextBox.Text) |
When the TextBox control loses focus |
A TextBox that is associated with validation logic |
|
PropertyChanged |
As you type into the TextBox |
TextBox controls in a chat room window |
|
Explicit |
When the application calls UpdateSource |
TextBox controls in an editable form (updates the source values only when the user clicks the submit button) |
What does OneWay or TwoWay binding mean?
These are binding Mode values. In the simplest terms, the OneWay mode is read-only (with respect to the source) and the TwoWay mode is read-write. For instance, in a stock ticker application, your controls are only “reading” and displaying the source values, so the controls only need to have OneWay bindings. The read-write scenario with TwoWay bindings is more applicable to a business application that allows users to both view and edit data.
Similar to UpdateSourceTrigger, the default value for the Mode property varies for each property. User-editable properties such as TextBox.Text, ComboBox.Text, MenuItem.IsChecked, etc, have TwoWay as their default Mode value. To figure out if the default is TwoWay, look at the Dependency Property Information section of the property. If it says BindsTwoWayByDefault is set to true, then the default Mode value of the property is TwoWay. To do it programmatically, get the property metadata of the property by calling GetMetadata and then check the boolean value of the BindsTwoWayByDefault property.
There’s also OneWayToSource and OneTime. If you’re interested, see the Mode page in the SDK.
My target binds to a property on my custom object but the binding does not refresh when the source value changes.
Your custom object needs to implement a mechanism to provide notifications when a property changes. The recommended way to do that is to implement INotifyPropertyChanged. Here’s an example:
using System.ComponentModel;
namespace BindingSample
{
public class Person : INotifyPropertyChanged
{
private string name;
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public Person()
{
}
public Person(string value)
{
this.name = value;
}
public string Name
{
get { return name; }
set
{
name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("Name");
}
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
Also, remember that the properties you are binding to need to be public and you cannot bind to fields.
How do I create a binding in code?
One way is to call the SetBinding method on the target object:
Person myDataSource = new Person("Joe");
Binding myBinding = new Binding("Name");
myBinding.Source = myDataSource;
// myText is an instance of TextBlock
myText.SetBinding(TextBlock.TextProperty, myBinding);
Only FrameworkElements and FrameworkContentElements have a SetBinding method. Their SetBinding method is actually calling the BindingOperations.SetBinding method. Therefore, you can always use the BindingOperations.SetBinding method, especially when your target object is not a FrameworkElement or a FrameworkContentElement.
How do I bind to an existing object instance?
In some cases the source object that you bind to can only be created at run-time, such as a DataSet object that’s created in response to a database query. In those cases, you need to set the DataContext property of your target object to the instantiated object programmatically. Example:
// myListBox is a ListBox control
// myCustomBizObject is your custom business object
myListBox.DataContext = myCustomBizObject;
There are two special cases. First, if you are binding to a property of another element in the same scope, then you use the ElementName property to specify the source. For example, you can bind the TextBlock.Text property to the content of the SelectedItem of a ComboBox:
<ComboBox Name="myComboBox" SelectedIndex="0">
<ComboBoxItem>1</ComboBoxItem>
<ComboBoxItem>2</ComboBoxItem>
<ComboBoxItem>3</ComboBoxItem>
</ComboBox>
<TextBlock Text="{Binding ElementName=myComboBox, Path=SelectedItem.Content}"/>
Second, if you are binding between properties of the same control, then you use the RelativeSource property. Specially, you set RelativeSource to Self: RelativeSource={x:Static RelativeSource.Self}.
In the next data-binding post we’ll get into common questions that deal with binding to collections and other more advanced scenarios. In the meantime, there’s always the Data Binding Overview in the SDK docs and Beatriz’s excellent blog.
Happy binding,
Tina