Click here to Skip to main content
14,304,886 members

WPF Custom Visualization Intermezzo 2: Bindings

Rate this:
5.00 (12 votes)
Please Sign up or sign in to vote.
5.00 (12 votes)
24 Aug 2016CPOL
(Yet Another) Investigation of WPF bindings

Content

Introduction

In the three parts of this series of articles we touched the issue of Bindings without an extensive investigation, except where it was needed in the discussion at hand. Remember how we used them with DataTriggers to specify the property we want the trigger to react to, and in templates to reach out from our Template to the templated Control

I will (try to) correct this inbalance in this article.

Mind however that I will be talking exclusively about bindings in the light of what we are discussing: visual customization of WPF controls. This means objects which are instances of BindingBase or its descendants, but not CommandBindings

You can find the other parts of this series here:

  1. WPF Custom Visualization Part 1 of some: Styling
  2. WPF Custom Visualization Part 2 of some: Styling with Triggers
  3. WPF Custom Visualization Intermezzo: Resources
  4. WPF Custom Visualization Intermezzo 2: Binding (this article)
  5. WPF Custom Visualization Part 3 of N: Templating
  6. WPF Custom Visualization Part 4 of some: Addorners

 

Binding in WPF: what are the variables?

A Binding allows, as used in Triggers and Templates, to be notified of changes in the properties of an object, get the value of that property and apply it to the property of another object. They are the mediator between the source and the target.

So, we will need two pieces of information:

  1. A source for the binding, defining the property to monitor
  2. A target for the binding, defining who to notify.

Of course, there is no magic: as mentioned above the Binding simply acts as a mediator between the source and the target and thus somehow will have to get notified of any changes in the source to be able to forward them to the target. Thus, regular .NET properties won't work.

But let's just find out how to do it:

Basic Binding in WPF: a simple binding.

Concepts

A binding allows changes in the value of a property on one object to be propagated to the property of another object without any further intervention. This notification can be bidirectional: changes in the other object's property can be propagated back to the first property. It will be clear that the Binding must somehow be notified of changes in the properties to be able to propagate them.

Thus we need:

  • A source property: is defined by setting properties on the binding itself
  • A target property: is the property on which the binding is applied
  • A notification mechanism: either you implement INotifyPropertyChanged or use Dependency Properties

How to do it?

A very basic binding and the one you've probably seen the most is the following:

public partial class BindingToNotifyPropertyChangedProperties : Window
{
	public BindingToNotifyPropertyChangedProperties()
	{
		InitializeComponent();

		var dataContext = new SomeNotifyPropertyChangedImplementingClass();
		dataContext.NotifyingProperty = "Notifying Property value from start";

		this.DataContext = dataContext;
	}

	private void ButtonSet_Click(object sender, RoutedEventArgs e)
	{
		var dataContext = (this.DataContext as SomeNotifyPropertyChangedImplementingClass).NotifyingProperty = "Notifying Property new value";
	}

	private void ButtonGet_Click(object sender, RoutedEventArgs e)
	{
		MessageBox.Show((this.DataContext as SomeNotifyPropertyChangedImplementingClass).NotifyingProperty);
	}
}
<TextBlock Grid.Row="0" Text="{Binding Path=NotifyingProperty}"/>
<Button Grid.Row="1" Content="Change the notifying property to another value" Click="ButtonSet_Click" />
<Button Grid.Row="2" Content="Get the notifying property" Click="ButtonGet_Click" />

The above definitions will result in following visuals:

Image 1

Allthough this looks like a very simple syntax, there is allready a lot going on here:

  • The target of the binding is the property on which it is defined, in this case the TextBlock's Text property
  • The source object of our binding is not explicitely defined here: if we do not specify anything then the default is the DataContext of the target object.
  • The source property is defined in the Path variable: the NotifyingProperty of the DataContext object's class type, thus SomeNotifyPropertyChangedImplementingClass

A first remark here: there is no error if you bind to a non existing property. Thus, WPF will not walk the object hierarchy in search for an object implementing the property, neither will it throw an exception.

Notice how in the below definitions, the chkPropertyNotPresent Checkbox has a DataContext which does not have a property IsChecked. Also notice how this code just compiles AND runs without any compiler errors or exceptions. And allthough the CheckBox itself does support the IsChecked property, our binding does NOT use it: the Content property is empty: there is no text beside the Checkbox. Contrast this with the Checkbox bound to the DataContext with an IsChecked property.

public partial class BindingToNotifyPropertyChangedProperties : Window
{
	public BindingToNotifyPropertyChangedProperties()
	{
		InitializeComponent();

		chkPropertyNotPresent.DataContext = new SomeNotifyPropertyChangedImplementingClass();
		chkPropertyIsPresent.DataContext = new IsCheckedDataContext();

	}

	private void ToggleCheckBoxIsChecked_Click(object sender, RoutedEventArgs e)
	{
		chkPropertyNotPresent.IsChecked = !chkPropertyNotPresent.IsChecked;
	}

	private void ToggleDataContextIsChecked_Click(object sender, RoutedEventArgs e)
	{
		(chkPropertyIsPresent.DataContext as IsCheckedDataContext).IsChecked = !(chkPropertyIsPresent.DataContext as IsCheckedDataContext).IsChecked;
	}
}
<!-- binding to a property which exists on the control but not on the datacontext -->
<CheckBox Name="chkPropertyNotPresent" Grid.Row="3" Content="{Binding Path=IsChecked}" IsChecked="False" />
<Button Grid.Row="4" Content="Toggle the IsChecked of the above CheckBox" Click="ToggleCheckBoxIsChecked_Click" />
<!-- binding to a property which exists on the control and also on the datacontext -->
<CheckBox Name="chkPropertyIsPresent" Grid.Row="5" Content="{Binding Path=IsChecked}" IsChecked="False" />
<Button Grid.Row="6" Content="Toggle the DataContext IsChecked property in the above CheckBox" Click="ToggleDataContextIsChecked_Click" />

The above definitions will result in following visuals:

Image 2

The above binding works in both directions: the value of the DataContext property is shown in the TextBox, but if we change the value in the TextBox, then our source property also gets updated. You can see this by clicking the "Change the notifying property to another value" button and by changing the text and then click the "Get the notifying property" button.

This works for two reasons:

  1. The target property is a Dependency Property: this makes sure the DataContext is notified of changes in our TextBox control.
  2. The source property is backed by a INotifyPropertyChanged mechanism: this makes sure the TextBox control is notified of changes in the DataContext.

The fact that the target property is a Dependency Property is a requirement of the Binding: no other properties can be specified as the target, not even INotifyPropertyChanged backed properties.

Following will not work:

<!-- Following will not compile: you can only specify dependency properties of dependency objects as the target -->
<local:SomeNotifyPropertyChangedImplementingClass x:Key="theSource" NotifyingProperty="{Binding Source=chkPropertyIsPresent, Path=IsChecked}" />

If the DataContext did not implement the INotifyProeprtyChanged interface then changes in the source property would not propagate to the TextBox:

public class SomeMuteDataClass
{
	public string MuteProperty
	{
		get;
		set;
	}
}

public partial class BindingToMuteProperties : Window
{
	public BindingToMuteProperties()
	{
		InitializeComponent();

		var dataContext = new SomeMuteDataClass();
		dataContext.MuteProperty = "Mute Property value from start";

		this.DataContext = dataContext;

	}

	private void Button_Click(object sender, RoutedEventArgs e)
	{
		var dataContext = (this.DataContext as SomeMuteDataClass).MuteProperty = "Mute Property new value";
	}
}
<TextBlock Grid.Row="0" TextWrapping="Wrap" Text="{Binding Path=MuteProperty}" />
<Button Grid.Row="1" Content="Change the mute property to another value" Click="Button_Click" />

The above definitions will result in following visuals:

Image 3

It is of course possible to specify a Dependency Property as the source of a binding:

<TextBox Name="SrcTextBox" Text="type here to change text on button"/>
<Button Content="{Binding ElementName=SrcTextBox, Path=Text}" />

The above definitions will result in following visuals:

Image 4

But I don't care about the DataContext: How to specify another Source

Concepts

Binding the DataContext is very nice and also very usefull when you are using the MVVM pattern, but there will be use cases where you want to bind other sources. So, what are the other sources you can specify?

  • using the Source property
  • using the RelativeSource property
  • using the ElementName property

But for each there are multiple ways of doing things.

So let's get in...

How to do it?

By using the Source property on the Binding, you can directly specify the object acting as the source. You can do this by either providing the instance inline, or using the Resource syntax.

Following is an example of specifying the value for the Source property inline:

<TextBlock Grid.Row="1" HorizontalAlignment="Left" TextWrapping="Wrap"  VerticalAlignment="Top">
	<TextBlock.Text>
		<Binding Path="NotifyingProperty">
			<Binding.Source>
				<local:SomeNotifyPropertyChangedImplementingClass NotifyingProperty="Inline defined object" />
			</Binding.Source>
		</Binding>
	</TextBlock.Text>
</TextBlock>

The above definitions will result in following visuals:

Image 5

Following is an example of specifying the Source property as a Resource:

<Window.Resources>
	<local:SomeNotifyPropertyChangedImplementingClass x:Key="theSource" NotifyingProperty="Object defined in Resource section" />
</Window.Resources>
<Grid>
	<TextBlock Grid.Row="2" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top" Text="{Binding Path=NotifyingProperty, Source={StaticResource theSource}}"/>
</Grid>

The above definitions will result in following visuals:

Image 6

By using the RelativeSource we can get at objects relative in the visual tree to the Binding's target object. For this, the syntax provides a Mode property allowing you to navigate the tree. This property can have following values:

  • Self: the object on which the binding is defined
  •  
  • FindAncestor: allows access to a parent object in the visual tree by specifying its type.
  •  
  • PreviousData: used in a collection, allows access at the previous item in the collection
  • <!-- !!! NOG EENS ONDERZOEKEN !!! -->
  • TemplatedParent: allows access from within a template to the control on which the template is applied
  •  

Most of these Mode's have a shortcut notation which I will demonstrate in the following examples. In practice, this is the result of static properties on the RelativeSource class which return a kind of pre-configured RelativeSource objects with their Mode property set to a specific value. In the examples I start with the shortcut notation because you will most likely use it the most yourself, but also see it used the most.

Ok, so lets start with mode Self:

<!-- getting at properties of the control itself: use RelativeSource Self with shortcut notation -->
<TextBlock Text="{Binding Path=VerticalAlignment, RelativeSource={RelativeSource Self}}" />
<!-- above Self syntax is a kind of shortcut for following complete syntax -->
<TextBlock Text="{Binding Path=VerticalAlignment, RelativeSource={RelativeSource Mode=Self}}" />

The above definitions will result in following visuals:

Image 7

There is not much to say here: you can see that the Binding uses the value of the VerticalAlignment property of the target object, this is the TextBlock itself. Contrast this with the beginning of the article where we used the DataContext.

Next is the FindAncestor mode:

<!-- getting at an Ancestor by specifying its type and the level -->
<GroupBox Grid.Row="5" Header="Demo for Ancestor binding: level 2 above the bound element">
	<StackPanel>
		<TextBlock>Just a textblock control to have something inside the GroupBox</TextBlock>
		<GroupBox HorizontalAlignment="Right" Header="Demo for Ancestor binding: level 1b above the bound element">
		</GroupBox>
		<GroupBox HorizontalAlignment="Right" Header="Demo for Ancestor binding: level 1a above the bound element">
			<StackPanel>
				<TextBlock>Just a textblock control to have something inside the GroupBox</TextBlock>
				<TextBlock Text="{Binding Path=Header, RelativeSource={RelativeSource AncestorType={x:Type GroupBox}}}" />
				<TextBlock Text="{Binding Path=Header, RelativeSource={RelativeSource AncestorType={x:Type GroupBox}, AncestorLevel=2}}" />
				<!-- AncestorLevel=0 is illegal 
					You'll get an exception with message 
						"Specified argument was out of the range of valid values.
						Parameter name: AncestorLevel cannot be set to less than 1."-->
				<!--<TextBlock Text="{Binding Path=Header, RelativeSource={RelativeSource AncestorType={x:Type GroupBox}, AncestorLevel=0}}" />-->
				<!-- you canot get at the target itself using this syntax 
					you'll need to use the Self syntax for that -->
				<GroupBox HorizontalAlignment="Left"  Header="{Binding Path=HorizontalAlignment, RelativeSource={RelativeSource AncestorType={x:Type GroupBox}}}" />
				<GroupBox HorizontalAlignment="Left"  Header="{Binding Path=HorizontalAlignment, RelativeSource={RelativeSource Self}}" />
			</StackPanel>
		</GroupBox>
	</StackPanel>
</GroupBox>
<!-- Above is shortcut for following complete specification -->
<!--<GroupBox Grid.Row="5" Header="Demo for Ancestor binding: level 2 above the bound element">
	<StackPanel>
		<TextBlock>Just a textblock control to have something inside the GroupBox</TextBlock>
		<GroupBox Header="Demo for Ancestor binding: level 1 above the bound element">
			<StackPanel>
				<TextBlock>Just a textblock control to have something inside the GroupBox</TextBlock>
				<TextBlock Text="{Binding Path=Header, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type GroupBox}}}" />
				<TextBlock Text="{Binding Path=Header, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type GroupBox}, AncestorLevel=2}}" />
			</StackPanel>
		</GroupBox>
	</StackPanel>
</GroupBox>-->

The above definitions will result in following visuals:

Image 8

Allthough the idea will be clear, there are a few things to notice here.

If you don't specify a level, then the first object of the specified type which is a parent of the binding target is used as the source for the binding. If you want to climb higher up the tree you'll need to specify the AncestorLevel. If the target object is of this same type it is NOT used as the source for the binding. If you want that one you'll need to use the RelativeSource Self. Specifying an AncestorLevel of 0 will not help you neither as this value is not allowed: AncestorLevel must be 1 or more and thus starts immediately above the binding target object.

Specifying the combination AncestorType, AncestorLevel also walks the element TREE. As you can see from the above you cannot use it to get at sibling elements. Thus in the above XAML AncestorLevel=2 gets you at the GroupBox with Header "Demo for Ancestor binding: level 2 above the bound element" and NOT at the group GroupBox with Header "Demo for Ancestor binding: level 1b above the bound element".

There is also the possibility to get at the templated control from within a template. I'm not sure if I should bother you with this now, as we haven't yet discussed templates. I am planning a next article on the subject, so feel free to skip this section for now and return to it later. For those who know templates you can continue reading.

A most basic example:

<Button Grid.Row="6" Background="Red">
	<Button.Template>
		<ControlTemplate TargetType="{x:Type Button}">
			<Grid>
				<Ellipse Name="el1" Fill="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Background}" Width="10" Height="10">
				</Ellipse>
			</Grid>
		</ControlTemplate>
	</Button.Template>
</Button>
<!-- Above is shortcut for following complete specification -->
<!--<Button Grid.Row="6" Background="Red">
	<Button.Template>
		<ControlTemplate TargetType="{x:Type Button}">
			<Grid>
				<Ellipse Name="el1" Fill="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Background}" Width="10" Height="10">
				</Ellipse>
			</Grid>
		</ControlTemplate>
	</Button.Template>
</Button>-->

The above definitions will result in following visuals:

Image 9

Nothing exciting here: by specifying the TemplatedParent source, you have access to the templated object from within the template and you can bind to its properties.

There are few permutations possible on the above syntax.

You can also use a specific type of Binding: the TemplateBinding.

<!-- or use a TemplateBinding which acts as a kind of shortcut syntax to the above -->
<Button Grid.Row="8" Background="Red">
	<Button.Template>
		<ControlTemplate TargetType="{x:Type Button}">
			<Grid>
				<Ellipse Name="el1" Fill="{TemplateBinding Property=Background}" Width="10" Height="10">
				</Ellipse>
			</Grid>
		</ControlTemplate>
	</Button.Template>
</Button>

Allthough the endresult looks the same for both syntaxes, there is a difference: the TemplateBinding is evaluated at compile time. If you specify any non-existing properties in the path, then the xaml will not compile. If you use the RelativeSource TemplatedParent your xaml will compile and even execute. The only way to know something is wrong is looking at the visual studio Output pane.

<!-- The difference ? -->
<!-- Following will not compile: the existance of the referenced property is checked at compile time -->
<!--<Button Grid.Row="8" Background="Red">
	<Button.Template>
		<ControlTemplate TargetType="{x:Type Button}">
			<Grid>
				<Ellipse Name="el1" Fill="{TemplateBinding Property=NonExistingProperty}" Width="10" Height="10">
				</Ellipse>
			</Grid>
		</ControlTemplate>
	</Button.Template>
</Button>-->
<!-- Following will compile AND execute !!!
	However, in the Output pane in Visual Studio you will get following message:
	System.Windows.Data Error: 40 : BindingExpression path error: 'NonExistingProperty' property not found on 'object' ''Button' (Name='')'. BindingExpression:Path=NonExistingProperty; DataItem='Button' (Name=''); target element is 'Ellipse' (Name=''); target property is 'Fill' (type 'Brush')
	And of course, visually nothing will be applied
-->
<Button Grid.Row="9" Background="Red">
	<Button.Template>
		<ControlTemplate TargetType="{x:Type Button}">
			<Grid>
				<Ellipse Name="el1" Fill="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=NonExistingProperty}" Width="10" Height="10">
				</Ellipse>
			</Grid>
		</ControlTemplate>
	</Button.Template>
</Button>

Related to the AncestorType there is an alternative you can use:

<Window.Resources>
	<ControlTemplate x:Key="MyButtonTemplate" TargetType="{x:Type Button}">
		<Grid>
			<!-- if you're sure it'll be used inside a button and no buttons stand inbetween you and the templated button
								then you can also use the AncestorType syntax-->
			<Ellipse Name="el1" Fill="{Binding RelativeSource={RelativeSource AncestorType={x:Type Button}}, Path=Background}" Width="10" Height="10">
			</Ellipse>
		</Grid>
	</ControlTemplate>
</Window.Resources>

<GroupBox Grid.Row="7" Header="Binding the templated parent through the AncestorType">
	<StackPanel>
		<Button Background="Blue">
			<Button.Template>
				<ControlTemplate TargetType="{x:Type Button}">
					<Grid>
						<!-- if you're sure it'll be used inside a button and no buttons stand inbetween you and the templated button
							then you can also use the AncestorType syntax-->
						<Ellipse Name="el1" Fill="{Binding RelativeSource={RelativeSource AncestorType={x:Type Button}}, Path=Background}" Width="10" Height="10">
						</Ellipse>
					</Grid>
				</ControlTemplate>
			</Button.Template>
		</Button>
		<!-- Does the above also work when the template is defined inside a resource ? -->
		<Button Background="Green" Template="{StaticResource MyButtonTemplate}">
		</Button>
	</StackPanel>
</GroupBox>

The above definitions will result in following visuals:

Image 10

Untill now, we could either bind to an object defined directly in the binding, or defined in a way relative to the binding. But what if there is no exact relation? What if you want to specify a source somewhere random in the visual tree? For this, there is the ElementName specifier.

<!-- Specifying the source: using the ElementName property-->
<GroupBox x:Name="source1" Grid.Row="10" Header="Demo for ElementName property: toplevel">
	<StackPanel>
		<GroupBox x:Name="source3" Header="Out of the hierarchy">
			<GroupBox x:Name="source31" Header="Out of the hierarchy and nested">
				<StackPanel>
					<TextBlock Text="Just to have something inside" />
				</StackPanel>
			</GroupBox>
		</GroupBox>
		<GroupBox x:Name="source2" Header="Demo for ElementName property: level 1 above the bound element">
			<StackPanel>
				<TextBlock Text="{Binding ElementName=source1, Path=Header}" />
				<TextBlock Text="{Binding ElementName=source2, Path=Header}" />
				<TextBlock Text="{Binding ElementName=source3, Path=Header}" />
				<TextBlock Text="{Binding ElementName=source31, Path=Header}" />
				<TextBlock Text="{Binding ElementName=source4, Path=Header}" />
			</StackPanel>
		</GroupBox>
		<GroupBox x:Name="source4" Header="Declared after the referencing">
			<StackPanel>
				<TextBlock Text="Just to have something inside" />
			</StackPanel>
		</GroupBox>
	</StackPanel>
</GroupBox>

The above definitions will result in following visuals:

Image 11

I tried to incorporate all possible permutations in the above example:

  • source2: Some toplevel element still in the parent-child tree relation
  • source1: Some toplevel element with similar elements inbetween (source2) still in the parent-child tree relation
  • source3: Out of the hierarchy
  • source31: Out of the hierarchy and nested
  • source4: Declared after target object

 

Following will probably not come as a surprise, but you cannot get at an element defined in another window:

<Window x:Class="Bindings.CrossingWindowBoundaries"

		xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

		xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

		Title="CrossingWindowBoundaries" Height="300" Width="300">
	<Grid>
		<Button Name="BtnInOtherWindow" Background="Yellow">A button in another window</Button>
	</Grid>
</Window>
<GroupBox x:Name="source2" Header="Demo for ElementName property: level 1 above the bound element">
	<StackPanel>

		<!-- you cannot cross window boundaries -->
		<Button Background="{Binding ElementName=BtnInOtherWindow, Path=Background}" >Getting at a button in another window</Button>
	</StackPanel>
</GroupBox>

The above definitions will result in following visuals:

Image 12

As you can see, the target of the binding is not yellow.

We can also use the ElementName with Templates.

<Window.Resources>
	<!-- using an element by name inside the template -->
	<Style x:Key="GettingInStyle" TargetType="Button">
		<Setter Property="Template">
			<Setter.Value>
				<ControlTemplate TargetType="{x:Type Button}">
					<Grid>
						<Ellipse Fill="{Binding ElementName=theSource, Path=Background}" Stroke="{TemplateBinding BorderBrush}"/>
						<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
					</Grid>
				</ControlTemplate>
			</Setter.Value>
		</Setter>
	</Style>
	<!-- providing a named element inside the template -->
	<Style x:Key="GettingOutStyle" TargetType="Button">
		<Setter Property="Template">
			<Setter.Value>
				<ControlTemplate TargetType="{x:Type Button}">
					<Grid>
						<Ellipse x:Name="GetOutNameInStyle" Fill="Red" Stroke="{TemplateBinding BorderBrush}"/>
						<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
					</Grid>
				</ControlTemplate>
			</Setter.Value>
		</Setter>
	</Style>
</Window.Resources>

<GroupBox Header="Demo for ElementName property: usage inside Template">
	<StackPanel>
		<TextBlock x:Name="theSource" Text="A named source for the bindings" Background="Red"></TextBlock>
		<Button>
			<Button.Content>
				<TextBlock Text="{Binding ElementName=theSource, Path=Text}"></TextBlock>
			</Button.Content>
		</Button>
		<!-- using an element by name inside an inline defined template -->
		<Button>
			<Button.Template>
				<ControlTemplate>
					<TextBlock x:Name="GetOutNameInline" Background="Green" Text="{Binding ElementName=theSource, Path=Text}"></TextBlock>
			   </ControlTemplate>
			</Button.Template>
		</Button>
		<!-- template defined in resource -->
		<Button Style="{StaticResource GettingInStyle}" >Getting in from the template</Button>
		<!-- you cannot get at a named element in a template -->
		<Button Background="{Binding ElementName=GetOutNameInStyle, Path=Fill}" >Getting out from a template in the resourcesection</Button>
		<Button Background="{Binding ElementName=GetOutNameInline, Path=Fill}" >Getting out from a template defined inline</Button>
	</StackPanel>                    
</GroupBox>

The above definitions will result in following visuals:

Image 13

Ok, what do we have here? The first use case is getting at a named object from within a template. The second and third Button in the above example demonstrate this for an inline defined Template and a Template defined in the resource section. A second use case is getting at a named element inside a Template. Unfortunately, this second use case is not possible. If you think about this it is to be expected: a template can be instantiated inside multiple controls and which instance should the binding use as a source?

But I don't care about that Property: How to specify the path

Concepts

Binding to Properties is nice, but there will be times when you want to bind other things, like nested Properties, items in a collection, collections, etc... So, what is possible?

  • We are able to get at simple Properties (that's what we've been doing until now)
  • We can bind to nested Properties
  • We can bind to collections (which will be discussed in the section about the ItemsControl)
  • We can bind to items in a collection, even multi-dimensional collections
  • We can bind to the current item in a view of a collection

How to do it?

We will skip binding regular properties because that is what we have been doing all the time, so it should be clear by now how to do this.

So, let's fast forward to binding nested properties:

public partial class SpecifyingThePath : Window
{
	SpecifyingThePathDataContext dataContext = new SpecifyingThePathDataContext();

	public SpecifyingThePath()
	{

		dataContext.SubclassProperty = new SpecifyingThePathDataContext.SubClass();
		dataContext.SubclassProperty.SubNotifyingProperty = "Sub initial value";

		dataContext.NotifyPropertyChanged = true;
		dataContext.SubclassProperty.NotifyPropertyChanged = true;
		dataContext.MultiDimensionalArrayProperty[1, 2] = "Value at [1,2]";

		this.DataContext = dataContext;

		InitializeComponent();

		CheckMainClassNotification.IsChecked = dataContext.NotifyPropertyChanged;
		CheckSubClassNotification.IsChecked = dataContext.SubclassProperty.NotifyPropertyChanged;
	}

	private void ButtonSet_Click(object sender, RoutedEventArgs e)
	{
		dataContext.SubclassProperty.SubNotifyingProperty = "Sub new value";
	}

	private void ButtonGet_Click(object sender, RoutedEventArgs e)
	{
		MessageBox.Show(dataContext.SubclassProperty.SubNotifyingProperty);
	}

	private void CheckSubClassNotification_Click(object sender, RoutedEventArgs e)
	{
		dataContext.SubclassProperty.NotifyPropertyChanged = CheckSubClassNotification.IsChecked.Value;
	}

	private void CheckMainClassNotification_Click(object sender, RoutedEventArgs e)
	{
		dataContext.NotifyPropertyChanged = CheckMainClassNotification.IsChecked.Value;
	}
	
	private void ButtonSetObject_Click(object sender, RoutedEventArgs e)
	{
		dataContext.SubclassProperty = new SpecifyingThePathDataContext.SubClass() { SubNotifyingProperty = "new Object" };
	}
}
<!-- nested properties -->
<TextBox Text="{Binding Path=SubclassProperty.SubNotifyingProperty}" />
<CheckBox Name="CheckSubClassNotification" Content="Enable NotifyPropertyChanged on SubClass" Click="CheckSubClassNotification_Click" />
<CheckBox Name="CheckMainClassNotification" Content="Enable NotifyPropertyChanged on SpecifyingThePathDataContext" Click="CheckMainClassNotification_Click" />
<Button Content="Set subproperty to value 'Sub new value'" Click="ButtonSet_Click" />
<Button Content="Get subproperty value" Click="ButtonGet_Click" />
<Button Content="Set complete object to new value" Click="ButtonSetObject_Click" />

The above definitions will result in following visuals:

Image 14

There is nothing much surprising here I think, though I would like to digg a little deeper on how this works. With the CheckBoxes you can regulate if the PropertyChanged event is called on the main type or on the type of the property. You will notice that the Binding class is intelligent enough to bind to the type of the property and not the main class, which was to be expected: the object owning the property doesn't really change. The last button allows you to replace the object in the main property. As you can see from the above animated gif, the Binding class can also handle these changes.

Next is binding to a single element in a collection.

public partial class SpecifyingThePath : Window
{
	SpecifyingThePathDataContext dataContext = new SpecifyingThePathDataContext();

	public SpecifyingThePath()
	{
		dataContext.SimpleObservableCollectionProperty = new ObservableCollection<string>();
		dataContext.SimpleObservableCollectionProperty.Add("Value at index 0");
		dataContext.SimpleObservableCollectionProperty.Add("Value at index 1");
		dataContext.SimpleObservableCollectionProperty.Add("Value at index 2");

		this.DataContext = dataContext;

		InitializeComponent();
	}

	private void ButtonChangeObservableColl_Click(object sender, RoutedEventArgs e)
	{
		dataContext.SimpleObservableCollectionProperty[1] = "New Value at index 1";
	}

	private void ButtonDeleteObservableColl_Click(object sender, RoutedEventArgs e)
	{
		dataContext.SimpleObservableCollectionProperty.RemoveAt(1);
	}
<!-- get at element at index in a collection of simple values -->
<TextBox Grid.Row="5" Text="{Binding Path=SimpleObservableCollectionProperty[1]}" />
<!-- what if we, after the initial binding, change the object on that location? -->
<Button Grid.Row="6" Content="Observable: Change value at index 1" Click="ButtonChangeObservableColl_Click" />
<Button Grid.Row="7" Content="Observable: Delete value at index 1" Click="ButtonDeleteObservableColl_Click" />

The above definitions will result in following visuals:

Image 15

Again, nothing surprising here: you're binding the element in the collection whose index you specified in the Binding definition. And because we're binding an ObservableCollection if we add or delete items, our binding is updated. If we were to use a regular list, our initial binding would work but adding or deleting would not update the binding. This is similar to the mute properties of the previous section: again, if there is no notification mechanism, how is to binding to now anything changed?

Binding a regular List is demonstrated in the following code:

public partial class SpecifyingThePath : Window
{
	SpecifyingThePathDataContext dataContext = new SpecifyingThePathDataContext();

	public SpecifyingThePath()
	{
		dataContext.SimpleMuteCollectionProperty = new List<string>();
		dataContext.SimpleMuteCollectionProperty.Add("Value at index 0");
		dataContext.SimpleMuteCollectionProperty.Add("Value at index 1");
		dataContext.SimpleMuteCollectionProperty.Add("Value at index 2");

		this.DataContext = dataContext;

		InitializeComponent();
	}

	private void ButtonChangeMuteColl_Click(object sender, RoutedEventArgs e)
	{
		dataContext.SimpleMuteCollectionProperty[1] = "New Mute Value at index 1";
	}

	private void ButtonDeleteMuteColl_Click(object sender, RoutedEventArgs e)
	{
		dataContext.SimpleMuteCollectionProperty.RemoveAt(1);
	}
<!-- once again for a list of simple values -->
<TextBox Text="{Binding Path=SimpleMuteCollectionProperty[1]}" />
<Button Content="Mute: Change value at index 1" Click="ButtonChangeMuteColl_Click" />
<Button Content="Mute: Delete value at index 1" Click="ButtonDeleteMuteColl_Click" />

The above definitions will result in following visuals:

Image 16

In the above examples the elements in the Collection where simple values: in this case string values. But of course those can be class-type values and then we can bind to properties of an element in the collection:

dataContext.ComplexObservableCollectionProperty = new ObservableCollection<specifyingthepathdatacontext.subclass>();
dataContext.ComplexObservableCollectionProperty.Add(new SpecifyingThePathDataContext.SubClass() { SubNotifyingProperty = "Class value at index 0", NotifyPropertyChanged = true });
dataContext.ComplexObservableCollectionProperty.Add(new SpecifyingThePathDataContext.SubClass() { SubNotifyingProperty = "Class value at index 1", NotifyPropertyChanged = true });
dataContext.ComplexObservableCollectionProperty.Add(new SpecifyingThePathDataContext.SubClass() { SubNotifyingProperty = "Class value at index 2", NotifyPropertyChanged = true });

private void ButtonChangeObservableColComplexChange_Click(object sender, RoutedEventArgs e)
{
	dataContext.ComplexObservableCollectionProperty[1] = new SpecifyingThePathDataContext.SubClass() { SubNotifyingProperty = "New Class value at index 1", NotifyPropertyChanged = true };
}

private void ButtonChangeObservableCollComplexMove_Click(object sender, RoutedEventArgs e)
{
	dataContext.ComplexObservableCollectionProperty.Move(0, 1);
}

private void ButtonChangeObservableCollComplexPropChange_Click(object sender, RoutedEventArgs e)
{
	dataContext.ComplexObservableCollectionProperty[1].SubNotifyingProperty = "New Property value at index 1";
}
</specifyingthepathdatacontext.subclass>
<TextBox Text="{Binding Path=ComplexObservableCollectionProperty[1].SubNotifyingProperty}" />
<!-- what if we, after the initial binding, change the object on that location? -->
<Button Content="Observable: Change object at index 1" Click="ButtonChangeObservableColComplexChange_Click" />
<!-- what if we, after the initial binding, move to object to a different location?
	is our binding still valid ?-->
<Button Content="Observable: Move object at index 0 to index 1" Click="ButtonChangeObservableCollComplexMove_Click" />
<!-- what if we, after the initial binding, just change the property ?-->
<Button Content="Observable: Changeproeprty at index 1" Click="ButtonChangeObservableCollComplexPropChange_Click" />

The above definitions will result in following visuals:

Image 17

The result was to be expected as is the propagation of changes in the collection or the property. The Binding class is intelligent enough to monitor both changes in both the collection itself and in the property of the element in the collection it is bound to.

Besides binding to Arrays and ObservableCollections, there is a third type of container you can bind to: CollectionView. I know: purists will say this is not really a container but a view on a container. And they are right. However I made this simplification because the class CollectionView is a subject on its own and I just want to discuss it here with respect to the Path syntax. We will meet it again when we discuss binding the ItemsControl

public partial class SpecifyingThePath : Window
{
	SpecifyingThePathDataContext dataContext = new SpecifyingThePathDataContext();

	public SpecifyingThePath()
	{
		dataContext.SimpleCollectionView = (CollectionView)CollectionViewSource.GetDefaultView(dataContext.SimpleMuteCollectionProperty);
		dataContext.SimpleCollectionView.MoveCurrentToFirst();


		List<SpecifyingThePathDataContext.SubClass>  complexList = new List<SpecifyingThePathDataContext.SubClass>();
		List<string> simpleList0 = new List<string>();
		simpleList0.Add("Nested Value at index 0.0");
		simpleList0.Add("Nested Value at index 0.1");
		simpleList0.Add("Nested Value at index 0.2");
		complexList.Add(new SpecifyingThePathDataContext.SubClass()
		{ 
			SubNotifyingProperty = "Class value at index 0", 
			NotifyPropertyChanged = true,
			SubSimpleCollectionView = (CollectionView)CollectionViewSource.GetDefaultView(simpleList0)
		});

		List<string> simpleList1 = new List<string>();
		simpleList1.Add("Nested Value at index 1.0");
		simpleList1.Add("Nested Value at index 1.1");
		simpleList1.Add("Nested Value at index 1.2");
		complexList.Add(new SpecifyingThePathDataContext.SubClass()
		{
			SubNotifyingProperty = "Class value at index 1", 
			NotifyPropertyChanged = true,
			SubSimpleCollectionView = (CollectionView)CollectionViewSource.GetDefaultView(simpleList1)
		});

		List<string> simpleList2 = new List<string>();
		simpleList2.Add("Nested Value at index 2.0");
		simpleList2.Add("Nested Value at index 2.1");
		simpleList2.Add("Nested Value at index 2.2");
		complexList.Add(new SpecifyingThePathDataContext.SubClass()
		{
			SubNotifyingProperty = "Class value at index 2", 
			NotifyPropertyChanged = true,
			SubSimpleCollectionView = (CollectionView)CollectionViewSource.GetDefaultView(simpleList2)
		});

		dataContext.ComplexCollectionView = (CollectionView)CollectionViewSource.GetDefaultView(complexList);
		dataContext.ComplexCollectionView.MoveCurrentToFirst();

		this.DataContext = dataContext;

		InitializeComponent();
	}

	private void ButtonMoveCurrentSimpleView(object sender, RoutedEventArgs e)
	{
		dataContext.SimpleCollectionView.MoveCurrentToNext();
	}

	private void ButtonMoveCurrentComplexView(object sender, RoutedEventArgs e)
	{
		dataContext.ComplexCollectionView.MoveCurrentToNext();
	}

	private void ButtonMoveNestedCurrentComplexView(object sender, RoutedEventArgs e)
	{
		(dataContext.ComplexCollectionView.CurrentItem as SpecifyingThePathDataContext.SubClass).SubSimpleCollectionView.MoveCurrentToNext();
	}
}
<TextBox Text="{Binding SimpleCollectionView/}" />
<Button Content="Move CurrentItem to next item" Click="ButtonMoveCurrentSimpleView" />
<TextBox Text="{Binding ComplexCollectionView/SubNotifyingProperty}" />
<TextBox Text="{Binding ComplexCollectionView/SubSimpleCollectionView/}" />
<Button Content="Move CurrentItem to next item" Click="ButtonMoveCurrentComplexView" />
<Button Content="Move Nested CurrentItem to next item" Click="ButtonMoveNestedCurrentComplexView" />

The above definitions will result in following visuals:

Image 18

So, what is going on here?

We are creating CollectionViews on the collections which we want to get values from. A property of a CollectionView is, among others like sortation, filtering, that it can have a current item. See in the above code how we set this CurrentItem the the first item by calling the method MoveCurrentToFirst() of the CollectionView. Then by clicking the any of the Buttons we call MoveCurrentToNext() which moves the CurrentItem.

But I don't want my property updated: Specify the direction using the Mode

Concepts

Until now we've been using two way bindings: the value from the source is transferred to the target, but any changes made to the target are also propagated to the source. This is because our properties are defined with both setters and getters and the default Mode of the Binding is TwoWay.

But what if we have a read-only property? Or the target is a read-only property?

 

You can define the direction of the Binding with the Mode property:

  • TwoWay: changes in both target and source are propagated
  • OneWay: only changes in the source are propagated: for readonly source properties
  • OneWayToSource: only changes in the target are propagated to the source, but not the other way around.
  • OneTime: the source value is read only once and then never again

How to do it?

The most common case is the TwoWay binding mode:

<TextBox TextWrapping="Wrap" Text="{Binding Mode=TwoWay, Path=TwoWayBindingProperty}"/>
<Button Content="Set the datacontext property to 'Some Text'" Click="TwoWaySetPropertyValue_Click"/>
<Button Content="Get the datacontext property" Click="TwoWayGetPropertyValue_Click"/>
<TextBox TextWrapping="Wrap" Text="{Binding Path=TwoWayBindingProperty}"/>
public BindingMode()
{
	InitializeComponent();

	m_dataContext = new BindingModeDataContext();
	m_dataContext.TwoWayBindingProperty = "TwoWay initial value";
	
	DataContext = m_dataContext;
}

private void TwoWaySetPropertyValue_Click(object sender, RoutedEventArgs e)
{
	m_dataContext.TwoWayBindingProperty = "TwoWay: Some Text";
}

private void TwoWayGetPropertyValue_Click(object sender, RoutedEventArgs e)
{
	MessageBox.Show(m_dataContext.TwoWayBindingProperty);
}

The above definitions will result in following visuals:

Image 19

No surprises here: updating the control propagates to the bound property of the datacontext and vice versa: updates to the bound property of the datacontext propagate to the target. TwoWay mode is the default if you don't specify anything.

Trying to set a TwoWay binding on a readonly property will fail:

class BindingModeDataContext : INotifyPropertyChanged
{
	string m_readOnlyProperty;
	public string ReadOnlyProperty
	{
		get { return m_readOnlyProperty; }
	}
}
<TextBox TextWrapping="Wrap" Text="{Binding Mode=TwoWay, Path=ReadOnlyProperty}"/>

The code will compile but on execution you get an Exception with following message: A TwoWay or OneWayToSource binding cannot work on the read-only property 'ReadOnlyProperty' of type 'Bindings.BindingModeDataContext'.

This can be solved with the OneWay mode:

class BindingModeDataContext : INotifyPropertyChanged
{
	string m_oneWayBindingProperty;
	public string OneWayBindingProperty
	{
		get { return m_oneWayBindingProperty; }
	}
}
<TextBox TextWrapping="Wrap" Text="{Binding Mode=OneWay, Path=OneWayBindingProperty}"/>

The above definitions will result in following visuals:

Image 20

This shows what was to be expected: the target shows the value of the source and because we have no setter on the source we also cannot update it.

But what if we define a OneWay binding on a INotifyPropertyChanged backed property with getter and setter?

class BindingModeDataContext : INotifyPropertyChanged
{
	string m_oneWayBindingPropertyFullProp;
	public string OneWayBindingPropertyFullProp
	{
		get { return m_oneWayBindingPropertyFullProp; }
		set
		{
			m_oneWayBindingPropertyFullProp = value;
			if (PropertyChanged != null)
				PropertyChanged(this, new PropertyChangedEventArgs("OneWayBindingPropertyFullProp"));
		}
	}
}

public partial class BindingMode : Window
{
	private void OneWayFullPropSetPropertyValue_Click(object sender, RoutedEventArgs e)
	{
		m_dataContext.OneWayBindingPropertyFullProp = "OneWay: Some Text";
	}

	private void OneWayFullPropGetPropertyValue_Click(object sender, RoutedEventArgs e)
	{
		MessageBox.Show(m_dataContext.OneWayBindingPropertyFullProp);
	}
}
<TextBox TextWrapping="Wrap" Text="{Binding Mode=OneWay, Path=OneWayBindingPropertyFullProp}"/>
<Button Content="Set the datacontext property to 'OneWay: Some Text'" Click="OneWayFullPropSetPropertyValue_Click"/>
<Button Content="Get the datacontext property" Click="OneWayFullPropGetPropertyValue_Click"/>

The above definitions will result in following visuals:

Image 21

The above may come as a bit of a surprise: we are using the OneWay binding, but if we bind a property with setter and INotifyPropertyChanged notification and set the property from code, then the target of the binding is updated! So, the meaning of OneWay here is: changing the target does not update the source.

If you do not want changes in the source to propagate to the target you should use the OneWayToSource setting.

class BindingModeDataContext : INotifyPropertyChanged
{
	string m_oneWayToSourceBindingProperty;
	public string OneWayToSourceBindingProperty
	{
		get { return m_oneWayToSourceBindingProperty; }
		set
		{
			m_oneWayToSourceBindingProperty = value;
			if (PropertyChanged != null)
				PropertyChanged(this, new PropertyChangedEventArgs("OneWayToSourceBindingProperty"));
		}
	}
}

public partial class BindingMode : Window
{
	private void OneWayToSourceSetPropertyValue_Click(object sender, RoutedEventArgs e)
	{
		m_dataContext.OneWayToSourceBindingProperty = "OneWayToSource: Some Text";
	}

	private void OneWayToSourceGetPropertyValue_Click(object sender, RoutedEventArgs e)
	{
		MessageBox.Show(m_dataContext.OneWayToSourceBindingPropertyGetter);
	}
}
<TextBox TextWrapping="Wrap" Text="{Binding Mode=OneWayToSource, Path=OneWayToSourceBindingProperty}"/>
<Button Content="Set the datacontext property to 'OneWayToSource: Some Text'" Click="OneWayToSourceSetPropertyValue_Click"/>
<Button Content="Get the datacontext property" Click="OneWayToSourceGetPropertyValue_Click"/>

The above definitions will result in following visuals:

Image 22

You might be tempted to think you can use the OneWayToSource mode for readonly target properties, but you would be wrong. The following will not compile:

<TextBox TextWrapping="Wrap" Text="This doesn't work" ActualWidth="{Binding Mode=OneWayToSource, Path=OneWayToSourceBindingProperty}"/>

The last possible value for the Mode property is OneTime.

And yes, again no surprises, that is just what it does: put the source value in the target property and then never update anything again in neither direction, no mather what kind of property, mute or INotifyPropertyChanged backed

class BindingModeDataContext : INotifyPropertyChanged
{
	string m_oneTimeBindingProperty;
	public string OneTimeBindingProperty
	{
		get { return m_oneTimeBindingProperty; }
		set
		{
			m_oneTimeBindingProperty = value;
			if (PropertyChanged != null)
				PropertyChanged(this, new PropertyChangedEventArgs("OneTimeBindingProperty"));
		}
	}
}

public partial class BindingMode : Window
{
	private void OneTimeSetPropertyValue_Click(object sender, RoutedEventArgs e)
	{
		m_dataContext.OneTimeBindingProperty = "OneTime: Some Text";
	}

	private void OneTimeGetPropertyValue_Click(object sender, RoutedEventArgs e)
	{
		MessageBox.Show(m_dataContext.OneTimeBindingProperty);
	}
}
<TextBox TextWrapping="Wrap" Text="{Binding Mode=OneTime, Path=OneTimeBindingProperty}"/>
<Button Content="Set the datacontext property to 'OneTime: Some Text'" Click="OneTimeSetPropertyValue_Click"/>
<Button Content="Get the datacontext property" Click="OneTimeGetPropertyValue_Click"/>

The above definitions will result in following visuals:

Image 23

But I don't want that value now, I want it when ...: UpdateSourceTrigger

Concepts

Until now we have not explicitely defined when the propagation should take place. Most examples until now where binding to the Text property of a TextBox control. And this might have given you the false impression that the binding is always updated when the control looses the focus. But that isn't thru! In fact, most bindings update when the property changes its value.

You can define in the Binding which trigger it should use to propagate changes:

  • Default: the default of the target property. And because target properties of Bindings can only be Dependency Properties, this is defined in the definition of the property.
  • LostFocus: the value is propagated when the control on which the target property is defined loses it's focus.
  • PropertyChanged: the value is propagated from the moment the property changes.
  • Explicit: you will have to trigger the propagation yourself from inside the code

How to do it?

We've been using the default setting all along: if you don't specify anything that is what you are using. You can of course also explicitely specify it.

<GroupBox Grid.Row="0" Header="Unspecified on textbox">
	<StackPanel>
		<TextBox Name="TextSource" Text="TextBox"/>
		<TextBox Background="Green" Text="{Binding ElementName=TextSource, Path=Text}"/>
	</StackPanel>
</GroupBox>
<GroupBox Grid.Row="1" Header="Unspecified on slider">
	<StackPanel>
		<Slider x:Name="Slider0" Background="Green" Value="{Binding ElementName=SliderSource,Path=Text}"/>
		<TextBox x:Name="SliderSource" Text="6"/>
	</StackPanel>
</GroupBox>
<GroupBox Grid.Row="2" Header="Default on textbox">
	<StackPanel>
		<TextBox Name="TextSourceDef" Text="TextBox"/>
		<TextBox Background="Green" Text="{Binding ElementName=TextSourceDef, Path=Text, UpdateSourceTrigger=Default}"/>
	</StackPanel>
</GroupBox>
<GroupBox Grid.Row="3" Header="Default on slider">
	<StackPanel>
		<Slider Background="Green" Value="{Binding ElementName=SliderSourceDef,Path=Text, UpdateSourceTrigger=Default}"/>
		<TextBox x:Name="SliderSourceDef" Text="6"/>
	</StackPanel>
</GroupBox>

The above definitions will result in following visuals:

Image 24

I've been using two controls here with a different trigger on the target property of the Binding. The TextBox has a default of LostFocus while the Slider has a default of PropertyChanged

The visual result of this is that, if you don't specify anything and thus use the default, for the TextBox the value is propagated only when it looses focus, and for the Slider it is propagated while you are dragging the slider.

The interesting thing to notice here is if you explicitely set the UpdateSourceTrigger to something different then the default.

<GroupBox Grid.Row="4" Header="LostFocus on textbox">
	<StackPanel>
		<TextBox Name="TextSource1" Text="TextBox"/>
		<TextBox Background="Green" Text="{Binding ElementName=TextSource1, Path=Text, UpdateSourceTrigger=LostFocus}"/>
	</StackPanel>
</GroupBox>
<GroupBox Grid.Row="5" Header="PropertyChanged on textbox">
	<StackPanel>
		<TextBox Name="TextSource2" Text="TextBox"/>
		<TextBox Background="Green" Text="{Binding ElementName=TextSource2, Path=Text, UpdateSourceTrigger=PropertyChanged}"/>
	</StackPanel>
</GroupBox>
<GroupBox Grid.Row="6" Header="LostFocus on slider">
	<StackPanel>
		<Slider Background="Green" Value="{Binding ElementName=SliderSource1, Path=Text, UpdateSourceTrigger=LostFocus}"/>
		<TextBox x:Name="SliderSource1" Text="6"/>
	</StackPanel>
</GroupBox>
<GroupBox Grid.Row="7" Header="PropertyChanged on slider">
	<StackPanel>
		<Slider Background="Green" Value="{Binding ElementName=SliderSource2, Path=Text, UpdateSourceTrigger=PropertyChanged}"/>
		<TextBox x:Name="SliderSource2" Text="6"/>
	</StackPanel>
</GroupBox>

The above definitions will result in following visuals:

Image 25

Also notice how defining this property only influences the trigger for propagation of values from the target to the source and not the other way around.

A last possibility is to specify Explicit as the trigger. By doing this you say you will decide when to trigger the propagation. You do this by calling the UpdateSource() method on the binding:

private void ButtonTriggerText_Click(object sender, RoutedEventArgs e)
{
	BindingExpression binding = TextEplicit.GetBindingExpression(TextBox.TextProperty);
	binding.UpdateSource();
}

private void ButtonTriggerSlider_Click(object sender, RoutedEventArgs e)
{
	BindingExpression binding = SliderExplicit.GetBindingExpression(Slider.ValueProperty);
	binding.UpdateSource();
}
<GroupBox Grid.Row="8" Header="Explicit on textbox">
	<StackPanel>
		<TextBox Name="TextSource3" Text="TextBox"/>
		<TextBox Background="LightGreen" Name="TextEplicit" Text="{Binding ElementName=TextSource3, Path=Text, UpdateSourceTrigger=Explicit}"/>
		<Button Content="Trigger binding" Click="ButtonTriggerText_Click" />
	</StackPanel>
</GroupBox>
<GroupBox Grid.Row="9" Header="Explicit on slider">
	<StackPanel>
		<Slider Background="LightGreen" Name="SliderExplicit" Value="{Binding ElementName=SliderSource3, Path=Text, UpdateSourceTrigger=Explicit}"/>
		<TextBox x:Name="SliderSource3" Text="6"/>
		<Button Content="Trigger binding" Click="ButtonTriggerSlider_Click" />
	</StackPanel>
</GroupBox>

The above definitions will result in following visuals:

Image 26

But I want more ... properties I mean: MultiBinding

Concepts

You may come to a use case in which you need to bind to multiple source properties. An example (allthough some will argue a bad one) may be if you want to show a concatenated value of several source properties, or for example a calculation on multiple source properties.

For this, one can use the MultiBinding. A MultiBinding needs at least the following:

  • Multiple Bindings
  • An implementation of IMultiValueConverter which converts the source values into a single result value

As stated above, some will probably argue the examples are bad and if you are really into MVVM then indeed it can be discussed if MultiBinding is the way to go, or if one should calculate the property directly in the viewmodel. The reason I included the discussion of the MultiBinding is simply a matter of completeness.

How to do it?

A basic example of a MultiBinding is the following:

class MyMultiBindingConverter : IMultiValueConverter
{
	static string[] stringSeparators = new string[] { "_" };

	bool m_allowConvertBack = true;

	public string InstanceId { get; set; }

	public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
	{
		Debug.WriteLine("MyMultiBindingConverter.Convert[" + (string.IsNullOrEmpty(InstanceId) ? "Global" : InstanceId) + "]");
		string one = values[0] as string;
		string two = values[1] as string;
		string three = values[2] as string;
		Debug.WriteLine("MyMultiBindingConverter.Convert[" + (string.IsNullOrEmpty(InstanceId) ? "Global" : InstanceId) + "]"
			+ "[1:" + (string.IsNullOrEmpty(one) ? "NULL" : one) + "]"
			+ "[2:" + (string.IsNullOrEmpty(two) ? "NULL" : two) + "]"
			+ "[3:" + (string.IsNullOrEmpty(three) ? "NULL" : three) + "]"
		);
			return (string.IsNullOrEmpty(one)?"NULL":one)
				+ "_" + (string.IsNullOrEmpty(two) ? "NULL" : two)
				+ "_" + (string.IsNullOrEmpty(three) ? "NULL" : three);
	}

	public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
	{
		string valueToConvert = value as string;
		Debug.WriteLine("MyMultiBindingConverter.ConvertBack[" + (string.IsNullOrEmpty(InstanceId) ? "Global" : InstanceId) + "]");
		Debug.WriteLine("MyMultiBindingConverter.ConvertBack[" + (string.IsNullOrEmpty(InstanceId) ? "Global" : InstanceId) + "]"
			+ "[in:" + (string.IsNullOrEmpty(valueToConvert) ? "NULL" : valueToConvert) + "]"
		);
		if (m_allowConvertBack)
		{
			object[] result = valueToConvert.Split(stringSeparators, StringSplitOptions.None).Select(x => (x=="NULL"?null:x)).ToArray();
			Debug.WriteLine("MyMultiBindingConverter.ConvertBack[" + (string.IsNullOrEmpty(InstanceId) ? "Global" : InstanceId) + "]Object[" + result.Count() + "]");
			return result;
		}
		else
		{
			throw new NotImplementedException();
		}
	}
}
<Window.Resources>
	<local:MyMultiBindingConverter x:Key="myConverter"/>
</Window.Resources>

<TextBox Name="TB1" Text="Text1"/>
<TextBox Name="TB2" Text="Text2" />
<TextBox Name="TB3" Text="Text3" />
<TextBox>
	<TextBox.Text>
		<MultiBinding Converter="{StaticResource myConverter}">
			<Binding ElementName="TB1" Path="Text" />
			<Binding ElementName="TB2" Path="Text" />
			<Binding ElementName="TB3" Path="Text" />
		</MultiBinding>
	</TextBox.Text>
</TextBox>

The above definitions will result in following visuals:

Image 27

To be honest, above is not the most simple use case. Below is more simple:

<TextBox Name="TB1" Text="Text1"/>
<TextBox Name="TB2" Text="Text2" />
<TextBox Name="TB3" Text="Text3" />
<TextBlock>
	<TextBlock.Text>
		<!-- http://stackoverflow.com/questions/9001974/strange-multibinding-stringformat-issue -->
		<MultiBinding StringFormat="{}{0}, {1}, {2}">
			<Binding ElementName="TB1" Path="Text" />
			<Binding ElementName="TB2" Path="Text" />
			<Binding ElementName="TB3" Path="Text" />
		</MultiBinding>
	</TextBlock.Text>
</TextBlock>

However, if you concatenate values, most of the time you will want your output text formatted if some values aren't present and this is not possible with the StringFormat syntax. You will need a more intelligent solution for that which requires yout to use a IMultiValueConverter:

class IntelligentMultiBindingConverter : IMultiValueConverter
{
	public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
	{
		string one = values[0] as string;
		string two = values[1] as string;
		string three = values[2] as string;
		string result = one;
		if (string.IsNullOrEmpty(result) && !string.IsNullOrEmpty(two))
		{
			result = two;
		}
		else if (!string.IsNullOrEmpty(result) && !string.IsNullOrEmpty(two))
		{
			result = result + ", "+ two;
		}
		if (string.IsNullOrEmpty(result) && !string.IsNullOrEmpty(three))
		{
			result = three;
		}
		else if (!string.IsNullOrEmpty(result) && !string.IsNullOrEmpty(three))
		{
			result = result + ", " + three;
		}
		return result;
	}

	public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
	{
		throw new NotImplementedException();
	}
}
<Window.Resources>
	<local:IntelligentMultiBindingConverter x:Key="intelligentConverter"/>
</Window.Resources>

<TextBox Name="TB1" Text="Text1"/>
<TextBox Name="TB2" Text="Text2" />
<TextBox Name="TB3" Text="Text3" />
<TextBlock>
	<TextBlock.Text>
		<MultiBinding Converter="{StaticResource intelligentConverter}">
			<Binding ElementName="TB1" Path="Text" />
			<Binding ElementName="TB2" Path="Text" />
			<Binding ElementName="TB3" Path="Text" />
		</MultiBinding>
	</TextBlock.Text>
</TextBlock>

Of course, we can combine this with previous learned stuff:

What happens if we try to set the UpdateSourceTrigger?

Well, we cannot set it on any of the inner bindings. Following will compile but throw an exception on execution:

<TextBox Name="USTTB1" Text="Text1"/>
<TextBox Name="USTTB2" Text="Text2" />
<TextBox Name="USTTB3" Text="Text3" />
<!-- Following will throw an exception at runtime:
	you cannot use the UpdateSourceTriger on an inner binding-->
<!--<TextBox>
	<TextBox.Text>
		<MultiBinding Converter="{StaticResource myConverter}">
			<Binding ElementName="USTTB1" Path="Text" />
			<Binding ElementName="USTTB2" Path="Text" UpdateSourceTrigger="LostFocus"/>
			<Binding ElementName="USTTB3" Path="Text" />
		</MultiBinding>
	</TextBox.Text>
</TextBox>-->

It was of course to be expected: remember how the UpdateSourceTrigger defines on when to update the SOURCE if you change the value in the TARGET. But we have three sources here, so what we are saying is that we want one source to change at a different rate hen the others when modifying the target. That would of course lead to inconsistend results.

But nothing keeps us from setting it on the MultiBinding:

<TextBox Name="USTTB1" Text="Text1"/>
<TextBox Name="USTTB2" Text="Text2" />
<TextBox Name="USTTB3" Text="Text3" />
<TextBox>
	<TextBox.Text>
		<MultiBinding Converter="{StaticResource myConverter}" UpdateSourceTrigger="LostFocus">
			<Binding ElementName="USTTB1" Path="Text" />
			<Binding ElementName="USTTB2" Path="Text"/>
			<Binding ElementName="USTTB3" Path="Text" />
		</MultiBinding>
	</TextBox.Text>
</TextBox>
<TextBox>
	<TextBox.Text>
		<MultiBinding Converter="{StaticResource myConverter}" UpdateSourceTrigger="PropertyChanged">
			<Binding ElementName="USTTB1" Path="Text" />
			<Binding ElementName="USTTB2" Path="Text"/>
			<Binding ElementName="USTTB3" Path="Text" />
		</MultiBinding>
	</TextBox.Text>
</TextBox>

The above definitions will result in following visuals:

Image 28

Contrary to the UpdateSourceTrigger, a Mode can be specified on an inner binding. And what is more, this seems to work also.

<GroupBox Header="OneWay on inner Binding">
	<StackPanel>
		<TextBox Name="MD1WITB1" Text="Text1"/>
		<TextBox Name="MD1WITB2" Text="Text2" />
		<TextBox Name="MD1WITB3" Text="Text3" />
		<TextBox>
			<TextBox.Text>
				<MultiBinding Converter="{StaticResource myConverter}">
					<Binding ElementName="MD1WITB1" Path="Text" />
					<Binding ElementName="MD1WITB2" Path="Text" Mode="OneWay"/>
					<Binding ElementName="MD1WITB3" Path="Text" />
				</MultiBinding>
			</TextBox.Text>
		</TextBox>
	</StackPanel>
</GroupBox>

The above definitions will result in following visuals:

Image 29

But how then does it know what you did? Well, it sequentially calls the ConvertBack and Convert methods on your IMultiValueConverter implementing class:

Image 30

And on after calling your ConvertBack method, it doesn't forward the returned value at the index of the inner Binding you put the Mode property on. So it then calls the ConvertBack method with the new value of the Bindings not specifying a Mode and the old unchanged value of the Binding which does set the Mode. No Hocus Pocus here.

And of course we can also set it on the MultiBinding:

<GroupBox Header="OneWay on MultiBinding">
	<StackPanel>
		<TextBox Name="MD1WMTB1" Text="Text1"/>
		<TextBox Name="MD1WMTB2" Text="Text2" />
		<TextBox Name="MD1WMTB3" Text="Text3" />
		<TextBox>
			<TextBox.Text>
				<MultiBinding Converter="{StaticResource myConverter}" Mode="OneWay">
					<Binding ElementName="MD1WMTB1" Path="Text" />
					<Binding ElementName="MD1WMTB2" Path="Text"/>
					<Binding ElementName="MD1WMTB3" Path="Text" />
				</MultiBinding>
			</TextBox.Text>
		</TextBox>
	</StackPanel>
</GroupBox>

The above definitions will result in following visuals:

Image 31

There is a thing to notice here: when we specified the Mode on one of the inner bindings and changed the target value, the original value was restored (notice how, if we change "Text2", in the target Textbox, after tabbing out of it the value is restored to "Text2") This does not happen when specifying the Mode on the MultiBinding itself!

We can do the same with a OneWayToSource Mode. Let's try it.

Setting the Mode on an inner Binding:

<local:MyMultiBindingConverter x:Key="oneWayToSourceOnInnerBinding2Converter" InstanceId="OneWayToSourceOnInnerBinding2"/>

<GroupBox Header="OneWayToSource on second inner Binding">
	<StackPanel>
		<TextBox Name="MD1SITB1" Text="Text1"/>
		<TextBox Name="MD1SITB2" Text="Text2" />
		<TextBox Name="MD1SITB3" Text="Text3" />
		<TextBox>
			<TextBox.Text>
				<MultiBinding Converter="{StaticResource oneWayToSourceOnInnerBinding2Converter}">
					<Binding ElementName="MD1SITB1" Path="Text" />
					<Binding ElementName="MD1SITB2" Path="Text" Mode="OneWayToSource" />
					<Binding ElementName="MD1SITB3" Path="Text" />
				</MultiBinding>
			</TextBox.Text>
		</TextBox>
	</StackPanel>
</GroupBox>

The above definitions will result in following visuals:

Image 32

There are a few things to notice here which, after some thinking, are not that surprising. First, when we open the window for the first time, the TextBox on which the MultiBinding is defined has a center value of NULL. After all, we defined a OneWayToSource on the second inner Binding which has the second TextBox as its source, so the value of the second TextBox is not forwarded to the converter, hence, the converter replaces it with the string NULL. Then, when we change the value in the second TextBox, still nothing is happening because of the OneWayToSource on the second inner Binding. Only when we change the NULL in the composite value, does it end up in the second TextBox. The other TextBoxs behave as usual.

But I want this first and if I can't then ...: PriorityBinding

Concepts

Where the MultiBinding allows you to bind to multiple sources and perform some action on those sources, the PriorityBinding also allows you to bind multiple sources, but it chooses a single value from those sources to propagate to the target.

Which source is eventually shown depends on its priority in the binding: that is its order in the list of sources, and if any of the property preceeding it provide a valid value or not.

The MSDN website on PriorityBinding discusses when a source value is considered valid:

A binding returns a value successfully if:

  1. The path to the binding source resolves successfully.
  2. The value converter, if any, is able to convert the resulting value.
  3. The resulting value is valid for the target property.

The value DependencyProperty.UnsetValue is not considered a successful return value.

How to do it?

A simple PriorityBinding is the following:

<TextBox Name="TB11" Text="Text1" />
<TextBox Name="TB12" Text="Text2" />
<TextBox>
	<TextBox.Text>
		<PriorityBinding>
			<Binding ElementName="TB11" Path="Text" />
			<Binding ElementName="TB12" Path="Text" />
		</PriorityBinding>
	</TextBox.Text>
</TextBox>

The above definitions will result in following visuals:

Image 33

As you can see, the value of the TextBox TB11 is shown in the target of the binding

Note also that making the TextBox empty does not make the target use the second source because an empty source is not considered an unsuccessfull value.

To be able to experiment with this I constructed the following example:

class SimpleValueConverter : IValueConverter
{

	public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
	{
		switch (value.ToString().ToLower())
		{
			case "unset":
				// only this is considered an invalid value
				return DependencyProperty.UnsetValue;
			case "null":
				return null;
			case "exception":
				throw new Exception();
			case "donothing":
				return Binding.DoNothing;
			default:
				return "CONVERTED: " + value.ToString();
		}
	}

	public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
	{
		if (false)
		{
			return value;
		}
		else
		{
			throw new NotImplementedException();
		}
	}
}
<Window.Resources>
	<local:SimpleValueConverter x:Key="myConverter"/>
</Window.Resources>

<TextBox Name="TB21" Text="Text1" />
<TextBox Name="TB22" Text="Text2" />
<TextBox>
	<TextBox.Text>
		<PriorityBinding>
			<Binding ElementName="TB21" Path="Text" Converter="{StaticResource myConverter}" />
			<Binding ElementName="TB22" Path="Text" />
		</PriorityBinding>
	</TextBox.Text>
</TextBox>

The above definitions will result in following visuals:

Image 34

This is very much the smae example, except that we are using a converter now. With this converter we can experiment with different settings and returnvalues.

As you can see from this, the only value which makes the PriorityBinding use the second binding is if the coverter returns DependencyProperty.UnsetValue. Neither Binding.DoNothing or throwing an Exception are considered an unsuccessfull return value.

If you specify a non existing property as the source, then also the next source is selected (if that is a legal one of course):

<TextBox Name="TB31" Text="Text1" />
<TextBox Name="TB32" Text="Text2" />
<TextBox>
	<TextBox.Text>
		<PriorityBinding>
			<!-- There is no property with the name PropertyWhichDoesNotExist -->
			<Binding ElementName="TB31" Path="PropertyWhichDoesNotExist" />
			<Binding ElementName="TB32" Path="Text" />
		</PriorityBinding>
	</TextBox.Text>
</TextBox>

The above definitions will result in following visuals:

Image 35

Another case where the next binding will be considered is if the binding returns a value which is not bij default convertable to the target property:

<TextBox Name="TB41" Text="Text1" HorizontalAlignment="Right" Background="Blue" />
<TextBox Name="TB42" Text="Text2" HorizontalAlignment="Left" Background="Green" />
<TextBox Text="Take the alignment">
	<TextBox.HorizontalAlignment>
		<PriorityBinding>
			<!-- A Brush is not a valid value for the HorizontalAlignment -->
			<Binding ElementName="TB41" Path="Background" />
			<Binding ElementName="TB42" Path="HorizontalAlignment" />
		</PriorityBinding>
	</TextBox.HorizontalAlignment>
</TextBox>

The above definitions will result in following visuals:

Image 36

A Brush is not a valid value for the HorizontalAlignment property, so the first binding is skipped and the target is bound to the HorizontalAlignment property of the TextBox named TB42.

All this is done at runtime, as the following example shows:

private void ChangeObjectPropertyToBrush_Click(object sender, RoutedEventArgs e)
{
    m_dataContext.ObjectProperty = Brushes.Blue;
}

private void ChangeObjectPropertyToAlignment_Click(object sender, RoutedEventArgs e)
{
    m_dataContext.ObjectProperty = HorizontalAlignment.Right;
}
<TextBox Name="TB41" Text="Text1" HorizontalAlignment="Right" Background="Blue" />
<TextBox Name="TB42" Text="Text2" HorizontalAlignment="Left" Background="Green" />
<TextBox Text="Dynamic">
	<TextBox.HorizontalAlignment>
		<PriorityBinding>
			<Binding Path="ObjectProperty" />
			<Binding ElementName="TB42" Path="HorizontalAlignment" />
		</PriorityBinding>
	</TextBox.HorizontalAlignment>
</TextBox>
<Button Click="ChangeObjectPropertyToBrush_Click">Change property to Brush</Button>
<Button Click="ChangeObjectPropertyToAlignment_Click">Change property to Alignment</Button>

The above definitions will result in following visuals:

Image 37

As you can see from the above example, when you click the change property to brush button, then the Dynamic TextBox takes the value of the TextBox TB42 for its HorizontalAlignment property. The first Binding of the PriorityBinding returns a value of the wrong type, so it is skipped. The second Binding does provide a value of the correct type, so it is applied to to the target property. But if we click the Change property to Alignment button,then the first Binding does provide a valid value, so it is applied.

But I have multiple items in my source ...: ItemsControl

Concepts

The ItemsControl has in this series always been something special, and this is no exception.

On the surface al looks very similar to regular Binding we've been using untill now, and from a binding-syntax perspective it is. However, under the hood all kinds of things are happening:

  • To have the control update on adding and deleting items, you must implement INotifyCollectionChanged
  • Internally, the binding is actually done on a CollectionView giving you some extras:
    • Notification of changes in the CurrentItem through the SelectedItem property
    • Synchronization with the CurrentItem through the IsSynchronizedWithCurrentItem property
    • .

So, let's get in and see how to do all this.

How to do it?

Let's start with something obvious: binding to an ObservableCollection:

CollectionDataContext m_dataContext;

public MainWindow()
{
	m_dataContext = new CollectionDataContext();

	m_dataContext.ObservableSource = new ObservableCollection<string>();

	m_dataContext.ObservableSource.Add("Observable Value 1");
	m_dataContext.ObservableSource.Add("Observable Value 2");
	m_dataContext.ObservableSource.Add("Observable Value 3");
	m_dataContext.ObservableSource.Add("Observable Value 4");

	this.DataContext = m_dataContext;

	InitializeComponent();
}

private void AddToObservableSource(object sender, RoutedEventArgs e)
{
	m_dataContext.ObservableSource.Add("Added value");
}

private void RemoveFromObservableSource(object sender, RoutedEventArgs e)
{
	m_dataContext.ObservableSource.RemoveAt(0);
}

private void ChangeInObservableSource(object sender, RoutedEventArgs e)
{
	m_dataContext.ObservableSource[0] = "Changed value";
}
<GroupBox Header="Simple Observable Collection">
	<StackPanel>
		<ItemsControl ItemsSource="{Binding Path=ObservableSource}" />
		<!-- you will be notified of changes in the collection -->
		<Button Click="AddToObservableSource">Add to ObservableSource</Button>
		<Button Click="RemoveFromObservableSource">Remove from ObservableSource</Button>
		<Button Click="ChangeInObservableSource">Change in ObservableSource</Button>
	</StackPanel>
</GroupBox>

The above definitions will result in following visuals:

Image 38

Nothing special here, except perhaps we're binding a INotifyCollectionChanged implementing object. Classes imlementing this interface provide notification of changes in the collection, thus notification of add, remove and change operations. And that is exactly what we are doing in de Click handlers of the Buttons. Notice however that we are binding to an ObservableCollection of Strings, so the fact that we are seeing a change in clicking the change button is because we are replacing the value in the collection. I'll come back to this later.

If we use a Converter on the Binding it is applied on the ObservableCollection and not on the items in the collecion, which is not that surprising. However, if for some reason you want to use a converter on the items, you'll need to define a ItemsTemplate in which you define the converter, see also this StackOverflow question. This is demonstrated in the follwowing example:

class PrependingValueConverter : IValueConverter
{
	public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
	{
		return "Prepended - " + value;
	}

	public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
	{
		return value;
	}
}
<GroupBox Header="Simple Observable Collection">
	<StackPanel>
		<!-- The converter is applied to the collection and NOT to the items in the collection -->
		<ItemsControl ItemsSource="{Binding Path=ObservableSource, Converter={StaticResource myConverter}}" />
		<!-- If you want to provide a custom Converter, you'll need to implement an ItemTemplate -->
		<ItemsControl ItemsSource="{Binding Path=ObservableSource}" >
			<ItemsControl.ItemTemplate>
				<DataTemplate>
					<TextBlock Text="{Binding Converter={StaticResource prependingConverter}}"/>
				</DataTemplate>
			</ItemsControl.ItemTemplate>
		</ItemsControl>
	</StackPanel>
</GroupBox>

The above definitions will result in following visuals:

Image 39

As mentioned above, you only see changes in the collection because it implements INotifyCollectionChanged. If you bind a regular List your ItemsControl will not be updated on changes in the List.

m_dataContext.ListSource = new List<string>();

m_dataContext.ListSource.Add("List Value 1");
m_dataContext.ListSource.Add("List Value 2");
m_dataContext.ListSource.Add("List Value 3");
m_dataContext.ListSource.Add("List Value 4");
<GroupBox Header="Simple List Collection">
	<StackPanel>
		<ItemsControl ItemsSource="{Binding Path=ListSource}"/>
		<!-- you will NOT be notified of changes in the list, after all
			there is no notification mechanism -->
		<Button Click="AddToListSource">Add to ListSource</Button>
		<Button Click="RemoveFromListSource">Remove from ListSource</Button>
		<Button Click="ChangeInListSource">Change in ListSource</Button>
	</StackPanel>
</GroupBox>

The above definitions will result in following visuals:

Image 40

Of course, we can also bind to collections of complex objects. There will be a lot of times you will use Templates to display the items in the collection. I will not discuss this here, because a later article will be about using Templates to customize visualization, but the ItemsControl provides some properties which allow you to display members of the items without needing to use Templates.

// We have a class implementing INotifyPropertyChanged
class ComplexClass : INotifyPropertyChanged
{
	public event PropertyChangedEventHandler PropertyChanged;

	public string MuteProperty
	{
		get;
		set;
	}

	string m_notifyingProperty;
	public string NotifyingProperty
	{
		get { return m_notifyingProperty; }
		set
		{
			m_notifyingProperty = value;
			if (PropertyChanged != null)
				PropertyChanged(this, new PropertyChangedEventArgs("NotifyingProperty"));
		}
	}
}

// We setup our datacontext
m_dataContext.ComplexObservableSource = new ObservableCollection<ComplexClass>();

m_dataContext.ComplexObservableSource.Add(new ComplexClass() { MuteProperty = "Mute 1", NotifyingProperty = "Notify 1" });
m_dataContext.ComplexObservableSource.Add(new ComplexClass() { MuteProperty = "Mute 2", NotifyingProperty = "Notify 2" });
m_dataContext.ComplexObservableSource.Add(new ComplexClass() { MuteProperty = "Mute 3", NotifyingProperty = "Notify 3" });
m_dataContext.ComplexObservableSource.Add(new ComplexClass() { MuteProperty = "Mute 4", NotifyingProperty = "Notify 4" });

// The Click handlers
private void AddToComplexObservableSource(object sender, RoutedEventArgs e)
{
	m_dataContext.ComplexObservableSource.Add(new ComplexClass() { MuteProperty = "Added mute value", NotifyingProperty = "Added notifying value" });
}

private void RemoveFromComplexObservableSource(object sender, RoutedEventArgs e)
{
	m_dataContext.ComplexObservableSource.RemoveAt(0);
}

private void ChangeItemInComplexObservableSource(object sender, RoutedEventArgs e)
{
	m_dataContext.ComplexObservableSource[0] = new ComplexClass() { MuteProperty = "Changed Mute", NotifyingProperty = "Changed Notify" };
}

private void ChangeMuteInComplexObservableSource(object sender, RoutedEventArgs e)
{
	m_dataContext.ComplexObservableSource[0].MuteProperty = "Changed mute value";
}

private void ChangeNotifyingInComplexObservableSource(object sender, RoutedEventArgs e)
{
	m_dataContext.ComplexObservableSource[0].NotifyingProperty = "Changed notifying value";
}
<GroupBox Header="Complex Observable Collection">
	<StackPanel>
		<ItemsControl ItemsSource="{Binding Path=ComplexObservableSource}" DisplayMemberPath="MuteProperty" />
		<ItemsControl ItemsSource="{Binding Path=ComplexObservableSource}" DisplayMemberPath="NotifyingProperty" />
		<!--<ItemsControl ItemsSource="{Binding Path=ComplexObservableSource}" DisplayMemberPath="NotifyingProperty" />-->
		<ItemsControl ItemsSource="{Binding Path=ComplexObservableSource}" DisplayMemberPath="NotifyingProperty" ItemStringFormat="Formatted - {0}" />
		<!-- you will be notified of changes in the collection -->
		<Button Click="AddToComplexObservableSource">Add to ComplexObservableSource</Button>
		<Button Click="RemoveFromComplexObservableSource">Remove from ComplexObservableSource</Button>
		<Button Click="ChangeItemInComplexObservableSource">Change item in ComplexObservableSource</Button>
		<Button Click="ChangeMuteInComplexObservableSource">Change Mute in ComplexObservableSource</Button>
		<Button Click="ChangeNotifyingInComplexObservableSource">Change Notifying in ComplexObservableSource</Button>
		<!-- ItemStringFormat does not give you access to properties -->
		<ItemsControl ItemsSource="{Binding Path=ComplexObservableSource}" ItemStringFormat="Formatted - {MuteProperty}" />
	</StackPanel>
</GroupBox>

The above definitions will result in following visuals:

Image 41

That is a lot, but most of it is well known stuff. First things first: we are using the DisplayMemberPath on the ItemsControl and as such it shows the value of the property with this name of the items class. Again, we have a INotifyCollectionChanged implementing collection, so addition, removal and changing of members in the collection updates our ItemsControl. Notice however how using the DisplayMemberPath on a property backed by INotifyPropertyChanged also updates our visuals when this property is changed, but not if the property is a mute property. DisplayMemberPath effectively results in a Binding on the item listening for changes. So if we click the Change Mute button the visuals don't change and if we click the Change Notifying button the visuals do change. Notice also how the operations on the collection also update what is shown in the ItemsControl with a DisplayMemberPath to the mute property. This is not surprising: the ItemsControl is notified of changes at a position in the collections and simply reads the complete object at that position. No need for INotifyPropertyChanged here.

But I have multiple items in my source of which I want one selected: ListBox

Concepts

ItemsControl is a big subject and the class also has some derivatives, like the System.Windows.Controls.Primitives.Selector which allows for selection of the items in the ItemsSource. Remember how previously we discussed the CollectionView? This class comes into play here also.

As previously stated, the CollectionView has a CurrentItem property which can act as the source for the SelectedItem property of the Selector.

Let's try a few things to see how this all works

How to do it?

Again, a most basic example:

private void GetSelectedItemNoSync(object sender, RoutedEventArgs e)
{
	if (ListSyncNoSyncing.SelectedItem == null)
		MessageBox.Show("There is no current Item");
	else
		MessageBox.Show((ListSyncNoSyncing.SelectedItem as ComplexClass).NotifyingProperty);
}

private void GetCurrentItemNoSync(object sender, RoutedEventArgs e)
{
	ICollectionView view = CollectionViewSource.GetDefaultView(m_dataContext.ComplexObservableSource);
	if(view.CurrentItem == null)
		MessageBox.Show("There is no current Item");
	else
		MessageBox.Show((view.CurrentItem as ComplexClass).NotifyingProperty);
}

private void GetSelectedItemWtSync(object sender, RoutedEventArgs e)
{
	if (ListSyncWtSyncing.SelectedItem == null)
		MessageBox.Show("There is no current Item");
	else
		MessageBox.Show((ListSyncWtSyncing.SelectedItem as ComplexClass).NotifyingProperty);
}

private void GetCurrentItemWtSync(object sender, RoutedEventArgs e)
{
	ICollectionView view = CollectionViewSource.GetDefaultView(m_dataContext.ComplexObservableSource);
	if (view.CurrentItem == null)
		MessageBox.Show("There is no current Item");
	else
		MessageBox.Show((view.CurrentItem as ComplexClass).NotifyingProperty);
}
<GroupBox Header="Simple Observable Collection: What is Synchronization">
	<StackPanel>
		<!-- we are not simply binding to the ObservableCollection but instead
			we are binding to a view on the ObservableCollection -->
		<Label Content="Listbox with No Sync" />
		<ListBox Name="ListSyncNoSyncing" ItemsSource="{Binding Path=ComplexObservableSource}" DisplayMemberPath="NotifyingProperty" />
		<Button Click="GetSelectedItemNoSync">Show Selected Item on Not Synced</Button>
		<Button Click="GetCurrentItemNoSync">Show Current Item on Not Synced</Button>
		<Label Content="Listbox with Sync" />
		<ListBox Name="ListSyncWtSyncing" ItemsSource="{Binding Path=ComplexObservableSource}" DisplayMemberPath="NotifyingProperty" IsSynchronizedWithCurrentItem="True" />
		<Button Click="GetSelectedItemWtSync">Show Selected Item with Syncing</Button>
		<Button Click="GetCurrentItemWtSync">Show Current Item with Syncing</Button>
		<ListBox Name="ListSyncWtSyncing2" ItemsSource="{Binding Path=ComplexObservableSource}" DisplayMemberPath="NotifyingProperty" IsSynchronizedWithCurrentItem="True" />
	</StackPanel>
</GroupBox>

The above definitions will result in following visuals:

Image 42

So, if you set the IsSynchronizedWithCurrentItem = True, then the SelectedItem property of the ListBox is equal to the CurrentItem of the default view of the collection acting as the ItemsSource of the ListView. This becomes clear in a few scenario's:

  1. If, upon opening the window, you select in the ListSyncNoSyncing ListBox items by clicking on the ListBox and then click the Show Current Item on Not Synced Button, you will see the message "There is no current Item". However, if you click the Show Selected Item on Not Synced Button you will see the selected item in a messagebox.
  2. If you now select an item in the ListSyncWtSyncing ListBox and then click the Show Current Item on Synced Button, you will see the item you selected in the ListSyncWtSyncing ListBox: the CurrentItem on the default view is independent of the ListBox. Notice also how the selection of the ListSyncWtSyncing2 ListBox also changes: this ListBox is also synchronized with the CurrentItem on the default view.

Notice also how this has nothing to do with what we display, or the type of property of what we display. With two ListBoxes, each set to a different DisplayMemberPath are kept synchronized when selecting different items:

<GroupBox Header="Simple Observable Collection: Synchronization">
	<StackPanel>
		<!-- we are not simply binding to the ObservableCollection but instead
			we are binding to a view on the ObservableCollection -->
		<ListBox ItemsSource="{Binding Path=ComplexObservableSource}" DisplayMemberPath="MuteProperty" IsSynchronizedWithCurrentItem="True" />
		<ListBox ItemsSource="{Binding Path=ComplexObservableSource}" DisplayMemberPath="NotifyingProperty" IsSynchronizedWithCurrentItem="True" />
	</StackPanel>
</GroupBox>

The above definitions will result in following visuals:

Image 43

Also notice how this has nothing to do with the type of collection the ListBox is bound to: this also works with a List. All this is implemented by using the ICollectionView and has nothing to do with the collection itself. After all, we're not changing anything in the collection itself, but are maintaining information perpendicular to the collection functionality.

<GroupBox Header="Simple List Collection: Synchronization">
	<StackPanel>
		<!-- The above is not caused by the use of ObservableCollection
			it also works with a simple List -->
		<ListBox ItemsSource="{Binding Path=ComplexListSource}" DisplayMemberPath="MuteProperty" IsSynchronizedWithCurrentItem="True" />
		<ListBox ItemsSource="{Binding Path=ComplexListSource}" DisplayMemberPath="NotifyingProperty" IsSynchronizedWithCurrentItem="True" />
	</StackPanel>
</GroupBox>

The above definitions will result in following visuals:

Image 44

It will of course be no surprise that, when binding to the SelectedItem and then changing it in code, will maintain it in all synchronized ListBoxes:

ComplexClass m_selectedInObservable;
public ComplexClass SelectedInObservable
{
	get { return m_selectedInObservable; }
	set
	{
		m_selectedInObservable = value;
		if (PropertyChanged != null)
			PropertyChanged(this, new PropertyChangedEventArgs("SelectedInObservable"));
	}
}

private void SetSelectedInObservable(object sender, RoutedEventArgs e)
{
	m_dataContext.SelectedInObservable = m_dataContext.ComplexObservableSource[2];
}
<GroupBox Header="Simple Observable Collection: Synchronization and SelectedItem">
	<StackPanel>
		<!-- The synchronization is maintained -->
		<ListBox ItemsSource="{Binding Path=ComplexObservableSource}" SelectedItem="{Binding Path=SelectedInObservable}" DisplayMemberPath="MuteProperty" IsSynchronizedWithCurrentItem="True" />
		<ListBox ItemsSource="{Binding Path=ComplexObservableSource}" DisplayMemberPath="NotifyingProperty" IsSynchronizedWithCurrentItem="True" />
		<Button Content="Set Selected to second item in collection" Click="SetSelectedInObservable" />
	</StackPanel>
</GroupBox>

The above definitions will result in following visuals:

Image 45

And finally it also works when using the SelectedValuePath property instead of the SelectedItem:

string m_selectedValueInObservable;
public string SelectedValueInObservable
{
	get { return m_selectedValueInObservable; }
	set
	{
		m_selectedValueInObservable = value;
		if (PropertyChanged != null)
			PropertyChanged(this, new PropertyChangedEventArgs("SelectedValueInObservable"));
	}
}

private void SetSelectedValueInObservable(object sender, RoutedEventArgs e)
{
	m_dataContext.SelectedValueInObservable = m_dataContext.ComplexObservableSource[2].NotifyingProperty;
}
<GroupBox Header="Simple Observable Collection: Synchronization and SelectedValue">
	<StackPanel>
		<!--  -->
		<ListBox ItemsSource="{Binding Path=ComplexObservableSource}" SelectedValue="{Binding SelectedValueInObservable}" SelectedValuePath="NotifyingProperty" DisplayMemberPath="MuteProperty" IsSynchronizedWithCurrentItem="True" />
		<ListBox ItemsSource="{Binding Path=ComplexObservableSource}" DisplayMemberPath="NotifyingProperty" IsSynchronizedWithCurrentItem="True" />
		<!-- we are only setting the value of the property, but the selection is updated to the complete object 
			and as above, the synchronization is maintained-->
		<Button Content="Set Selected to a property of second item in collection" Click="SetSelectedValueInObservable" />
	</StackPanel>
</GroupBox>

The above definitions will result in following visuals:

Image 46

Conclusion

A lot allready has been written about binding in WPF and I hold no illusion as to have written something new here. I wrote this article partly for myself as explaining something to someone else always helps me to understand the subject myself. Also, allthough a lot has been written, I wanted to add a twist: supply examples on what works, but also on what doesn't. Hence some xml in comments which wouldn't compile. But I invite you to uncomment these sections to see what happens.

Anyway, I hope you learned something by reading this article, I know I did explaining the concepts.

Version history

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Serge Desmedt
Software Developer (Senior)
Belgium Belgium
No Biography provided

Comments and Discussions

 
QuestionGreat Article Pin
marc borgers30-Sep-15 22:46
membermarc borgers30-Sep-15 22:46 
AnswerRe: Great Article Pin
Serge Desmedt1-Oct-15 0:41
professionalSerge Desmedt1-Oct-15 0:41 
GeneralRe: Great Article Pin
marc borgers1-Oct-15 1:25
membermarc borgers1-Oct-15 1:25 
AnswerRe: Great Article Pin
Serge Desmedt1-Oct-15 4:07
professionalSerge Desmedt1-Oct-15 4:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Article
Posted 28 Sep 2015

Stats

12.2K views
177 downloads
13 bookmarked