Skip to content
Jean-Philippe Bruyère edited this page Nov 3, 2021 · 19 revisions

Introduction

The ability to bind graphic object's properties to some other values in a program is an important feature of modern toolkits. For example, Microsoft Presentation Foundation use Dependency properties and INotifyPropertyChanged interface to allow this.

In CROW, bindings are realized through code injection of dynamic methods for event handler of the ValueChanged event of object if implemented. If IValueChanged interface is not implemented, dynamic updates will not be possible, but the the initial value of the origin of the binding will be set as value for the destination object member.

Binding definition

Binding definition is done through serialization facilities. For example, in XML, bindings are defined using the accolades.

When setting up a new binding, we define two things:

  • the source: which object is declaring the binding, important for binding path resolution
  • the target: could be expressed as a binding path through another node of the graphic tree, or a datasource.

Binding Path

Binding resolution is the binding path resolution, the finding of the target. Bindings are compiled once when an Instantiator is created. Resulting dynamic methods are then cached in the instantiator.

binding picture

The source of the binding, which has to be a graphic object has two particularities:

  • It defines a Data Source, used as default target for binding resolution. If datasource is not set but the target expression do not follow the logical tree parsing syntax, the resolver will parse logical tree upward (calling LogicalParent properties) until a not null datasource is found. If none is found, the root of the logical tree (the Interface object) will be used as datasource.
  • It's (as a graphical object of CROW) part of the logical tree, defining one Parent, and possibly some children.

Note that logical tree is usually the same as the graphic tree but for example in a combobox, which has a poppup overlay, the parent in the graphic tree will be for the overlay, the top interface. But the parent in the logical tree will be the textbox group from the combo.

DataSource target

By default, the target instance of the binding is the datasource of the source. In the following example, the member will be searched among DataSource object members.

<Window>
    <Label Text="{Value}"/>

In order to be notified of the updates of the Value property, the datasource has to implement the IValueChanged interface.

class MainWindow : CrowWindow , IValueChange
{
	#region IValueChange implementation
	public event EventHandler<ValueChangeEventArgs> ValueChanged;
	#endregion

	string _value;
	public string Value {
		get { return _value; }
		set {
			if (_fps == value)
				return;
			_fps = value;
			ValueChanged.Raise(this, new ValueChangeEventArgs ("Value", Value));
		}
	}

Note that it's a good practice to prevent raising spurious ValueChanged by testing if the lastValue is effectively different from the new one. This will prevent querying layouting and drawing when not required. Making this test is mandatory if you want this property (Value) to appear in a two way binding.

Logical tree target

The target could be expressed as a binding path expression, which has a similar syntax as file directory parsing.

If the target expression start with '../', target will be searched starting from the source instance and parsing the logical tree. As for directory parsing, '..' will reference the parent, and multiple '../' will allow parsing logical tree upward.

This binding will referenced the Title property of the grand father of the source instance: the Window. Each time the Title is changed, the label text will be updated.

<Window Title="window1">
    <Group>
        <Label Text="{../../Title}"/> 

The target expression could also include multiple dot separated mnemonic, the last one referencing the member name, and the others the name of the instances parsed downward in the logical tree. In the following example, the target member will be searched downward among source's named children.

<Window Title="window1">
    <Group Name="topGrp">
        <Group Name="group1">
            <Label Name="label1"/> 
            <Label Name="label2" Text="origin"/> 
        </Group>
        <Group Name="group2">
            <Label Name="label1"/> 
            <Label Name="label2" Text="{../../group1.label2.Text}"/> 
        </Group>

Binding

Once the binding definition is resolved (the target is found), it creates one or two bindings (one for each direction of the update, from source to target, from target to source, or even in the two directions at the same time. The two members involved in a single binding are called:

  • Origin
  • Destination

The binding are created only if the Origin of the binding implements the IValueChanged Interface. If it's not the case, only the initial value of Origin will be applied to the Destination during the resolution of the binding.

Clone this wiki locally