Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Flexible and Powerful Data Binding with WPF, Part 2 : Page 2

Microsoft has been building data binding frameworks for years. Each one promises to solve our data binding woes forever. We're still waiting for the perfect one—but we're getting closer!


Binding Direction

When you configure a binding, you can specify the direction that the data flows. A common binding direction is from the source to the target. This is implemented with the BindingMode.OneWay enumeration setting. In a OneWay binding, the target property gets updated when the binding source changes. Remember that change notification happens only if the data source object notifies WPF about the changes. See the "Change Notification" section of the first article in this series for details.

OneWay binding is common when the target is a non-editable element such as a TextBlock or Label. Because users can't change the value, it's not necessary for the data to flow back to the source:

<TextBlock Margin='20' Background='LightGray' Text="{Binding Source= {StaticResource emp}, Path=FirstName, Mode=OneWay}" />

In contrast, in a BindingMode.TwoWay binding, the target property gets updated when the source property changes, and vice versa. TwoWay binding is common when the target is an edit control such as Slider or TextBox.

If you don't specify a value for the Mode property, the type of binding direction you get depends on the type author preferences. Each dependency property has a FrameworkPropertyMetadataOptions enumeration flag set. If you've set the BindsTwoWayByDefault value then the default is TwoWay, otherwise it will be OneWay. For a TextBox, the default Mode is TwoWay, but most other controls are OneWay by default.

In a BindingMode.OneTime binding, the target property gets updated once, when the system establishes the binding. The WPF dependency property system disconnects the binding after it sets the initial value, and the system does not forward further changes. Usually you'll use this mode to reduce overhead when you know the value won't change in the source property.

In a BindingMode.OneWayToSource binding the data flows from the target property to the source property. This is another single-direction binding; no data flows back to the target. OneWayToSource exists to solve one specific problem in the WPF binding world. Data target properties are always dependency properties. There are no exceptions to this rule.

Occasionally you'll run across an element that has a property that you want to use as a target, but you discover that it wasn't implemented as a dependency property. The Run element is a classic example of this problem. A Run is a subsection of a FlowDocument. Run elements let you break a big document into manageable sections and apply formatting, or modify the Run text from your code. Here's an example:

<StackPanel> <FlowDocumentPageViewer Height='300'> <FlowDocument> <Paragraph> <Run>Intro Text</Run> <Run x:Name='changeMeRun'> Lorem Ipsum</Run> </Paragraph> </FlowDocument> </FlowDocumentPageViewer> <Button x:Name='ChangeTextButton' Click='ChangeTextButton_Click' Content='ChangeText' /> </StackPanel>

In the code behind, suppose you change the Run as shown here.

// C#: changeMeRun.Text = DateTime.Now.ToLongTimeString(); ' VB changeMeRun.Text = _ DateTime.Now.ToLongTimeString()

Now imagine that you want to bind a TextBox to the Run element so that it updates when you change the text programmatically. Sorry—you can't do that with a OneWay or TwoWay binding. Because Run.Text is not a dependency property, you'll have to use a OneWayToSource binding instead. Here's some XAML that demonstrates this binding:

<TextBox Text='{Binding ElementName=changeMeRun, Mode=OneWayToSource, Path=Text}' />

Binding Updates

TwoWay bindings exist for a good reason—to push modified data back to the data source. But in certain application scenarios, it's not wise to update the data immediately. For example, perhaps you have a slow connection back to the data server, and your design documents state that the changes should be batched. For these situations, WPF provides the UpdateSourceTrigger enumeration, which controls the immediacy of the update, granting you better control over the trigger condition. Setting the UpdateSourceTrigger property to PropertyChanged provides immediate gratification and causes the framework to update the data source whenever the user changes the target property:

<TextBox Text="{Binding Source ={StaticResource emp}, Path=FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

But setting the UpdateSourceTrigger property to a different event, such as LostFocus, delays the update until the focus leaves the bound control. Setting the UpdateSourceTrigger to Explicit divorces it from any event, and forces you to write some code to update the source. The following code shows how to update an Explicit binding.

// C#: private void UpdateButton_Click(object sender, System.Windows.RoutedEventArgs e) { // get the elements binding expression BindingExpression be = TextBox3.GetBindingExpression(TextBox. TextProperty); // update the bound source be.UpdateSource(); } ' VB Private Sub UpdateButton_Click( _ ByVal sender As Object, _ ByVal e As RoutedEventArgs) ' get the elements binding expression Dim be As BindingExpression = _ TextBox3.GetBindingExpression( _ TextBox.TextProperty) ' update the bound source be.UpdateSource() End Sub

For most elements the default UpdateSourceTrigger behavior is to update the data source when the target control data is changed. The TextBox control is one of the few elements that updates its source data on the LostFocus event.

Binding Errors

Setting up a binding requires getting the settings and properties correct in your XAML. One simple typo can turn your intended binding into an unfathomable mess for the binding class, which begs the question: What happens if you have an invalid binding configuration?

Instead of throwing an exception and bringing your application to a screeching halt, WPF ignores the error, displays the rest of your UI correctly, and quietly dumps the error information to the trace listener.

At first glance this seems an unreasonable choice by Microsoft, but further examination shows that it's not so impractical after all. Often a designer sets the binding in XAML using Expression Blend long before there's a valid data context in place for the binding. Working in an environment that is throwing exceptions while configuring the XAML would be a joyless experience.

As an example, the following XAML is invalid because I've misspelled the FirstName property:

<TextBlock Text="{Binding Source {StaticResource emp}, Path=FirstNam}" />

The typo causes a binding error that produces the following information in the Visual Studio output window:

System.Windows.Data Error: 35: BindingExpression path error: 'FirstNam' property not found on 'object' ''Employee' (HashCode=18307385)'. BindingExpression:Path=FirstNam; DataItem='Employee' (HashCode=18307385); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')

If you want more control over the trace process, you can set up a custom TraceOutput, which lets you dump trace output to external files, filter WPF namespaces for reporting, and control message verbosity flags.

Even if you configure your binding correctly, you can still end up with problems. You could have a valid binding, but somehow get incorrect data shown in the data target. WPF provides the PresentationTraceSources.TraceLevel attached property (introduced in .NET 3.5) to help in this situation. Enabling the TraceLevel switch causes WPF to dump detailed debugging information to the trace window. The first step in using this property is to add the correct xmlns attribute to your XAML:

<Page xmlns:diag='clr-namespace:System.Diagnostics; assembly=WindowsBase'

Next, use the attached property as shown below:

<TextBlock Text="{Binding Source={StaticResource emp}, Path=FirstNam, diag:PresentationTraceSources.TraceLevel=High}" />

Check out Listing 1 for an example of the debug trace that is sent to the Output window.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date