Ugrás a tartalomhoz

.NET Programming Technologies

Gergely Kovásznai, Csaba Biró

Eszterházy Károly College

13. fejezet - Data Binding (written by Gergely Kovásznai)

13. fejezet - Data Binding (written by Gergely Kovásznai)

WPF is especially capable to implement dynamic user interfaces. This must involve the ability of updating the data, which are stored in the data model and change dynamically, on the GUI immediately, and also in the other direction: the data that get modified on the GUI (e.g., a phone number in a textbox) have to be immediately updated in the model.

By using the instrumentation we have introduced so far, this can be done the easiest by the help of event handlers. In order to update data in the data model, after being changed on the GUI, we need to handle events of the GUI. Let us assume, for instance, that we overwrite the phone number in the above-mentioned textbox, and then we move the focus out of the textbox. In this very moment, i.e. when the textbox has just lost the focus, we would like to update the corresponding data of the person stored in the model to the one in the textbox. For this, we need to implement an event handler for the LostFocus event of the textbox:

private void textbox_LostFocus(object sender, RoutedEventArgs e)

{

        TextBox textbox = sender as TextBox;

           Person.vipPerson.TelNumber = textbox.Text;

}

It is even more complicated to realize a flow of data from the model toward the GUI (e.g., the phone number changes in the model and has to be updated on the GUI). We need to define events for certain properties of the classes in the model; such an event is going to be fired whenever the value of the given property changes. Then, for those events, we need to implement event handlers, which are to update all(!) such GUI elements whose values depend on the given property.

Such a development process is inconvenient, in several senses. First, we would need to implement a dedicated event handler for almost each GUI element, and also for each property of each class in the model (e.g. for the static vipPerson property of the Person class). This would be an extremely boring and tedious work, since those event handlers look almost the same. Is it not possible to automate this process? Could we just “bind” the GUI elements to the objects “behind” them, instead of dedicating variables for all of them?

Furthermore, the above-mentioned solution is rather complicated and opens the door for numerous errors. It does not separate the model and the GUI as much as we would expect. In the case of a robust project that involves a lot of developers, the above-mentioned solution would result in total chaos and hell of bugs.

The Binding Class

In WPF, data binding frees us from the above-mentioned troubles. The classes that implement data binding are located in the System.Windows.Data namespace. Among them, the Binding class is the most important. In order to bind a source property with a target property, in each case we are going to instantiate Binding. The resulting Binding object can be visualized (c.f. Figure 1) as “taking place” between the source and target properties, in order somehow to mediate between them, by doing synchronization.

XIII.1. The architecture of data binding

The Binding object continuously monitors the properties, marked by us, of the source and target classes. If the value of one of those properties changes, the Binding object does the synchronization between the two, based on the settings we have specified. We have the opportunity to interfere in the process of synchronization by using an optional converter object; e.g., if the data being synchronized are of different types (e.g., one is a string, the other is an integer). One might also optionally apply validation rules, which can be used to check whether the data to set fulfill certain criteria (e.g., a phone number has the correct format).

The following table summarizes the most important properties of the Binding class:

Binding

  

Source

The source object.

ElementName

For a special case, when the source object is a GUI element.

Path

The “path” to the property of the source object that we are about to bind.

Mode

OneWay

The target is updated whenever the source changes.

TwoWay

The target is updated whenever the source changes, and the source is updated whenever the target changes.

OneTime

The target gets a value only at initialization (based on the source).

Converter

The converter object.

StringFormat

Special format for how to display the value of the property.

ValidationRules

Validation rules.

In the table, one might miss the possibility for specifying a target object and a target property. Well, the reason is that in XAML we simply need to assign the Binding instance (containing a reference to the source) to the target property, as we will see soon.

Binding in XAML. It is very intuitive in XAML to use Binding; however, as we have already discussed above, rather complex processes run in the background behind each data binding. One uses Binding in the form of a markup extension most frequently; therefore, we show this form first. The above-mentioned binding between a textbox and the Person.vipPerson.TelNumber property can be written in XAML as follows:

<TextBox Text="{Binding Source={x:Static Member=my:Person.vipPerson},

                                                                Path=TelNumber}"/>

In the above example, it is very important that the source is now referenced via a static property; this is why we can reference it in the form classname.propname and use the x:Static tag. (In the case of a non-static source, this could be done in a more complicated way.) By doing so, the compiler can uniquely identify the source; after that, it only has to find the Person class, requiring us to specify an alias (my) to the namespace that contains this class (Section III.3).

Path. As mentioned before, Path can be used for specifying the “path” to a source property. This means that, within the source specification, one can even reference a property of a property of a property etc., i.e., properties of embedded objects (separated by dots). For example, if the Person class has an Address property, which is of such a class which has City, Street etc. properties, then one can specify such a data binding, as follows:

<TextBox Text="{Binding Source={x:Static Member=my:Person.vipPerson},

                                                                Path=Address.City}"/>

Mode. The meaning of Mode is self-explanatory. It can have several other values, however the above-mentioned ones are worth to mention. The OneWay mode performs, in fact, only the initialization of a GUI element, later there is no more synchronization between the source and the target. Of course, it is more frequent to use the OneWay and TwoWay modes. In the case of the first one, the value of the source is prevented from any change by data binding, however the target is always updated whenever the value of the source has been modified. In the case of the latter one, the values are synchronized in both directions.

It is optional to specify the Mode, since every GUI element has its own default data binding mode, depending on which mode fits the best to the given GUI element. E.g., in the case of a Label or TextBlock, OneWay is the default mode, while in the case of a TextBox or CheckBox, it is TwoWay.

ElementName. Sometimes it might happen that not only the target of a data binding is a GUI element, but so is the source. Let us imagine a form that contains a TextBox and a Slider! We would like to make the TextBox always display the current value of the Slider, i.e., whenever we drag the Slider, the content of the TextBox should be updated, and, furthermore, whenever we overwrite the content of the TextBox, the Slider should reposition itself. All of these can be easily achieved in XAML, as follows:

<Slider Name="sliderToBind" Minimum="0" Maximum="100"/>

<TextBox Text="{Binding ElementName=sliderToBind, Path=Value, Mode=TwoWay}"/>

Note that it is necessary to assign a name to the source GUI element since this is the only way for referencing it. Note furthermore that, in the above example, it is actually unnecessary to specify the TwoWay mode, since this is default for a TextBox.