How to support Bindings

"Love is a binding force, by which another is joined to me and cherished by myself." - Thomas Aquinas.

On Silverlight, bindings provide a mechanism to easily synchronize values between controls and data contexts. This guide will show how to add bindings support for a given property on a component.

Source code

The source code for the legacy Silverlight example and migrated application used through this guide is available on the resources section.

Binded Value example

Lets begin with a Slider example (SliderTest) which binds the Value property on the Slider with the FuzzyValue on the data context. The Text property on the TextBox is also bonded with FuzzyValue. The result is that the value on the Slider is synchronized with the text on the TextBox: when one changes, the other also does change:

Value property on Slider bonded with FuzzyValue on data context.

Note that when the slider is moved, the text changes immediately. But when the text changes, the value on the slider is not updated immediately: the value on the slider is updated when the TextBox lose the focus.

Look at the MainPage XAML code, particularly on the Value and Text bindings:

Both Value and Text properties are bonded with FuzzyValue. On the C# code, the data context is assigned, but note that there is no extra code to synchronize anything. The bindings mechanism is responsible of all data synchronization.

Migrated application

When the SliderTest code is initially migrated, Slider will be a stub, because there is no slider support on the migrator. The Value binding will also be lost during migration:

The slider will also be a stub on the MainPage model. The data context is assigned, so there is no extra work to do there:

There is already a basic slider model and component on wms-framework and i-component (if you don't see a slider model and component, you need to follow this guide). Let's change the migrated application to use the basic slider:

If you build the migrated application, you'll get this error:

Error because the handler is private.

The slider_ValueChanged() handler is private on the model. Silverlight is OK with this, but Angular requires the handler to be public in order to call it from the event binding. Just change the handler's visibility from private to public on the MainPage model and build again:

Migrated application, without Value binding support.

On the migrated application the value on the second TextBox does not change when the slider is moved. Neither does the slider change when the text on the TextBox is changed. Proper bindings support must be added to Value property on the slider model and component.

Model bindings support

On Silverlight, bindings are supported using dependency properties. A dependency property is a static property with the same name of the property that is going to be binded, plus "Property", with type DependencyProperty. So if the Value property can be binded, then there is a ValueProperty static property to support those bindings.

On Silverlight, ValueProperty is defined on Slider parent class (RangeBase). Open RangeBaseModel on the wms-framework repository and add a ValueProperty static property on it:

ValueProperty is declared on line 8, and a new DependencyProperty is assigned into it. On line 9 the name of property is given (as a string). On line 10 a default value for the property is provided: since the default value of Value is 0, then 0 is used as argument here. The third argument (line 11) is a change callback function: null is provided for now, but this is going to change soon. Finally a new RuntimeTypeInfoImpl(Number) is provided as fourth argument, on line 12. The dependency property uses this argument to know the type of Value, so that it can convert incoming values to the correct type when Value is binded to a different type.

Value property must be modified to use the dependency property that was just created. Previously Value was defined as a setter/getter pair and a backing field. Now the dependency property will handle that, so the setter/getter and backing field are no longer needed:

Note that no default value is given on the Value declaration. The default value will be handled by the dependency property now.

Also note the @Dependency() directive: it receives the dependency property as argument (since it is a static property, we should access it throw RangeBaseModel class). This directive will link the Value property with the ValueProperty dependency property.

There is something missing: previously the Value setter triggered the ValueChanged event when Value was changed. But no one triggers the event now. To fix this add a change callback to the dependency property declaration:

On line 11 we replace null with the callback function (called ValueChangeCallback()) which is a static function on the RangeBaseModel class.

Note the signature of the callback function on line 14: it receives the sender, which is the object on which the property is being changed, and args, which contain the old and new values.

On line 15 the ValueChanged event is fired, using sender as the origin of the change, and taking old and new values from args to create a new RoutedPropertyChangedEventArgs object.

Build wms-framework and copy it into i-components, in order to use the new ValueProperty on the slider component.

Component bindings support

On the slider component the value Angular input must be modified to handle binding information.

First the type of value Angular input is changed from number to any on lines 7 and 10, because it will receive not just numbers, but also binding objects.

Second, checkAndRegisterCompatibilityBinding() is called on line 11. This function performs several actions:

  • If the input value is a number, the function does nothing and returns false. Then enters the if block and assigns the input value into the model (line 12).

  • If the input value is a binding object, the function registers the information from the binding object into the dependency property (RangeBaseModel.ValueProperty), which kickstart the bindings mechanism to synchronize data. Then the function returns true and the if block is skipped.

Build i-components and copy it into the migrated application to make use of value bindings.

Back to migrated application

Now that value has bindings support on the slider component, the binding information lost during migration must be recovered. On Silverlight, the binding was created with the following syntax:

But this binding information was lost during migration. However binding information for tbDisplay TextBox was preserved. Copy the binding information from the text property on tbDisplay into the value property of the slider:

On the migrated application the binding information is encapsulated on a binding object. The bindingPath is the property which will be bound, twoWay specifies if both properties must be updated when the other changes, and ctxt defined the context: the object where the bound property will be searched for.

Build and run the migrated application to test the changes:

Migrated application, with Value binding support.

Great! With proper bindings support, the value on the slider and text on TextBox are properly synchronized.

Resources

Legacy application using Value bindings.
Original migrated application.
Slider model and component files.
Migrated application using value bindings.

Last updated

Was this helpful?