Introduction
One of the most difficult aspects in creating applications from user requirements is the process of capturing the information that pertains to control behaviors. Situations where controls have many dependencies on both data and the state of other controls often lead to spaghetti code. In this article, I am going to look at some of the ways of capturing these requirements and turning them into manageable chunks of XAML and C#.
A Look at Data Binding
For a control to have its data saved somewhere, we can use a variety of methods. For example, we can respond to a Changed
event in a textbox
...
<TextBox Name="textBox1" TextChanged="textBox1_TextChanged"/>
... and then store the value manually:
private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
{
myString = textBox1.Text;
}
This method is fairly simple, but it has far too many disadvantages. In particular, if we handle all events for all controls, we'll end up with a lot of functions. And if these functions have any sort of dependencies, they will be unmanageable.
A better way to go about things is to use data binding. Data binding supports the model where a property in a control (such as the Text
property of a TextBox
) is mapped onto a variable in the code-behind class. When one changes, so does the other. Here's how it works. First, we need to give the enclosing window
a Name
attribute to identify it by:
<Window x:Class="WpfDataBinding.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" Name="myWindow">
Now that we have our window
name, we provide it as a DataContext
property to the grid
that encloses our textbox
:
<Grid>
<Grid.DataContext>
<Binding ElementName="myWindow" Path="."/>
</Grid.DataContext>
<TextBox Height="23" Name="textBox1" VerticalAlignment="Bottom"/>
</Grid>
The DataContext
definition establishes a scope for where the data source (myWindow
, in our case) actually works. Inside the data context of our grid
, we simply define the data source as myWindow
and the path as a ".". It has to be pointed out here that when dealing with data binding, paths typically follow an XPath convention, which makes it easier to navigate the XAML tree.
Anyways, we have our data context. Now, if we want to bind the textbox
to a variable, we need to specify a Binding
element for the Text
property of the textbox
:
<TextBox Height="23" Name="textBox1" VerticalAlignment="Bottom">
<TextBox.Text>
<Binding Path="MyString"/>
</TextBox.Text>
</TextBox>
All that remains is to make a property in the code-behind that will store the value:
private string MyString { get; set; }
There are two things to note here. First, as you type new characters into the textbox
, the value of MyString
will not be updated. It will only be updated when the control loses focus. In order to get the variable updating every time text is altered, we need to change the UpdateSourceTrigger
property of the binding. This property determines the conditions under which the values are synchronized. To have the effect we desire, the binding XAML definition needs to be changed to the following:
<TextBox Height="23" Name="textBox1" VerticalAlignment="Bottom">
<TextBox.Text>
<Binding Path="MyString" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
The second problem is that when you change the value of MyString
in code, the textbox
will not be updated (even though the binding is two-way). The reason is that the property must tell the control that its value has changed. The most straightforward way of doing so is using the INotifyPropertyChanged
interface, which can be used by classes to notify other components that their internal state has changed. So, to get this working, our window
class has to implement INotifyPropertyChanged
:
public partial class Window1 : Window, INotifyPropertyChanged
If you let Visual Studio generate the actual event
for you, you'll end up with the following code inside the window
class:
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
You now have an event
, but it is useful to have a function that actually checks for subscribers and fires the event
when something changes. A naïve implementation would look as follows:
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
Now, using those auto-properties is no longer an option, because the mutator (the property set
) has to call this new function we just made. An altered MyString
now has to have a backing field. The whole contraption would look as follows:
private string myString = string.Empty;
public string MyString {
get { return myString; }
set
{
myString = value;
NotifyPropertyChanged("MyString");
}
}
If you change MyString
now, the Text
property of textBox1
will change accordingly.
Encapsulation
The code presented so far might work as an academic example, but it's still very far from real-life use. One of the reasons why it's unrealistic is that, often, developers wrap their properties with specifically designed structures which encapsulate the value, plus some other additional information to boot. For instance, developers might need to keep the same value and output it using different measurement systems (such as Metric vs. Imperial), or they might need a stack of previous values to implement Undo/Redo or the Command pattern. Clearly, our existing functionality is not right for this task, so let's try adjusting it a bit.
First, I'm going to define a simple wrapper class called Property
that encapsulates the value we're binding to:
public class Property<T>
{
private T value;
public Property() { }
public Property(T value)
{
this.value = value;
}
public T Value
{
get
{
return value;
}
set
{
this.value = value;
}
}
}
As you can see, the class is just a simple wrapper. In real life, you would add extra functionality to make it a bit more useful. Now, let's change the definition of our MyString
property in the window
class:
private readonly Property<string> myString = new Property<string>(string.Empty);
public string MyString
{
get
{
return myString.Value;
}
set
{
myString.Value = value;
NotifyPropertyChanged("MyString");
}
}
The above solution isn't very smart. Consider what happens if we want, for example, to be notified when the Value
inside changes. For instance, what if we want to turn the border of a control a different color depending on whether the property has been changed or not, we'd be in a real bind (no pun intended): we would need to create a new bool
property not just inside the Property
class, but also inside the window
class. And this is just too much work.
To deal with this, let's try going about it in a different way. I'm going to change our Property
class in order to incorporate this Changed
property. I am also going to implement INotifyPropertyChanged
right inside the Property
class:
public class Property<T> : INotifyPropertyChanged
{
private T value;
private bool changed = false;
public Property() { }
public Property(T value)
{
this.value = value;
}
public T Value
{
get
{
return value;
}
set
{
this.value = value;
NotifyPropertyChanged("Value");
Changed = true;
}
}
public bool Changed
{
get
{
return changed;
}
set
{
changed = value;
NotifyPropertyChanged("Changed");
}
}
public void Commit() { Changed = false; }
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
We now have a rather lengthy class which does its own notifications. For testing purposes, I've also added a Commit
method so that we can reset its Changed
state. The property now knows its value, and whether it has been changed. However, it cannot tell us whether the border color will be ordinary or red. We can now write the following...
<TextBox Height="23" Name="textBox1" VerticalAlignment="Bottom">
<TextBox.Text>
<Binding Path="MyString.Value" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
<TextBox.BorderBrush>
<Binding Path="MyString.Changed"/>
</TextBox.BorderBrush>
</TextBox>
... but the brush
option will be meaningless because, guess what, you cannot just convert a bool
ean to a brush
. There are three ways to go about it. One way is to expose a BorderBrush
property from our Property
class, which is meaningless because this feature might not be required for all types of controls (and Property
, as you may have guessed, is meant to be reusable). More importantly, it's meaningless because Property
has no business dealing with UI aspects – we need to preserve separation between data and UI. The second option is to expose some kind of MyStringBrush
property from the window
, doing the conversion each time. This option is even worse because if we do that, window
has to subscribe to the Property
's notification mechanism and... well, you can imagine how messy it will get. We are left with option #3 – creating a custom converter
that changes bool
s into border
s.
First, we define a class that actually does the conversion:
[ValueConversion(typeof(bool), typeof(Brush))]
public class BoolToBrushConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
return (bool)value ? Brushes.Red : Brushes.Gray;
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
return false;
}
#endregion
}
According to MSDN, the converter
will not work until we define a namespace
for our code. To do so, we add the following line to the Window
tag:
xmlns:custom="clr-namespace:WpfDataBinding"
In the above line, we define our own namespace
called custom
and associate it with our assembly namespace
. There is no need to specify the assembly. Now we add our custom converter
to the grid
resources in order to be able to use it within the elements of the grid
:
<Grid.Resources>
<custom:BoolToBrushConverter x:Key="btbc"/>
</Grid.Resources>
Finally, we can specify the converter
of our value:
<TextBox.BorderBrush>
<Binding Path="MyString.Changed" Converter="{StaticResource btbc}"/>
</TextBox.BorderBrush>
This is all we need to do. We have just bound a brush
to a bool
ean value through a converter
. The backward conversion is not functional (we just return false
), because we assume the user will not manipulate the brush
.
To summarize, here is what we have learned so far:
- Properties notify their observers using
INotifyPropertyChanged
- Properties are often wrapped, and bindings to them become slightly elaborate (e.g.
MyString.Value
instead of just MyString
)
- Bindings can use custom conversions by specifying the
Converter
property
Binding in Code
There is a fairly annoying problem with these binding definitions: they tie the UI designer and the business logic designer together. In an ideal world, those two roles could synchronize, but in a large-scale development effort, it becomes somewhat impractical to have all those <Binding>
tags all over the place. What I mean is, suppose the navigation logic changes and controls start acting differently: we don't want to be modifying XAML elements if it's not our primary job. So, let's remove the MyString
bindings and make them in code instead.
First, we make a private
field for the converter
:
private IValueConverter boolConv = new BoolToBrushConverter();
Strictly speaking, we should have a Singleton here (since spawning converter
s is pointless), but you can do this as homework. Now that we have the converter
, we simply add the following to the constructor:
Binding strBinding = new Binding();
strBinding.Path = new PropertyPath("MyString.Value");
strBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
textBox1.SetBinding(TextBox.TextProperty, strBinding);
Binding boolBinding = new Binding();
boolBinding.Path = new PropertyPath("MyString.Changed");
boolBinding.Converter = boolConv;
textBox1.SetBinding(TextBox.BorderBrushProperty, boolBinding);
All done! The end result is exactly the same as with XAML-based data binding. The reason why I'm showing this code here is because when you have a large number of data interdependencies (coming soon!), writing all this XAML by hand is impractical, and generating it effectively implies doing XSLT or XQuery transformations on XAML code, which is rather complex and is probably not cost effective.
Dependencies
In the previous examples, we relied on INotifyPropertyChanged
as if it was some Holy Grail of change notification. Regrettably, it's not, and here's an example why.
Consider a situation where you have an edit box where a person types in her or his age. Below that, you have two radio buttons that allow the person to vote, but only if they are 16 or older:
The idea is fairly simple – if you're younger than 16, you can't vote. Now, just as with the previous example, we bind the age field to the Value
of a Property<int>
:
private readonly Property<int> age = new Property<int>();
public Property<int> Age
{
get
{
return age;
}
}
We can now define a CanVote
property in our window
class to determine whether a person can vote:
public bool CanVote
{
get
{
return age.Value >= 16;
}
}
And now we run into a problem: how does CanVote
know that age.Value
has changed? The problem is, it doesn't. These properties are in different classes, and even though, in theory, CanVote
could sign up to listen to NotifyPropertyChanged
of age.Value
, in practice, this approach doesn't scale and is unrealistic. If they were in the same class, Age
could call NotifyPropertyChanged("CanVote")
whenever its value changed but, of course, this doesn't scale well either.
Let's generalize the problem a bit: how does property SomeClass.A
get notified that OtherClass.B
has changed without actually signing up to the other class' NotifyPropertyChanged
? My guess is, it cannot; however, if we start doing this sort of manipulation en masse, it will pollute our code so greatly that eventually we'll end up with spaghetti. There has to be a better solution out there.
Identifying Dependencies
In this section, we are going to look at how property dependencies can be identified and made explicit by looking at the IL that they generate. As you can probably remember, the annoying thing about the CanVote
property in our sample is that it is unable to notify its own observer about its own changes (which are a result of changes in age.Value
). It knows because it uses the property. Here is the body of the get_CanVote
function when seen through the IL disassembler:
.method public hidebysig specialname instance bool
get_CanVote() cil managed
{
.maxstack 2
.locals init ([0] bool CS$1$0000)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld class WpfDataBinding.Property`1<int32> WpfDataBinding.Window1::age
IL_0007: callvirt instance !0 class WpfDataBinding.Property`1<int32>::get_Value()
IL_000c: ldc.i4.s 16
IL_000e: clt
IL_0010: ldc.i4.0
IL_0011: ceq
IL_0013: stloc.0
IL_0014: br.s IL_0016
IL_0016: ldloc.0
IL_0017: ret
}
If we take lines IL_2 and IL_7 and cut off the double colon ::
and everything to the left, we get age
and get_Value()
, from which it is safe to infer that CanVote
at least uses age.Value
. It does not necessarily mean that it relies on it, but the fact that it is here must mean something.
Okay, let us assume for a second that we have a magical tool that tells us which properties a particular property is affected by. How would we act on this information in our example? Well, for instance, our window class could do the following:
-
Sign up to listen on changes in Age
:
age.PropertyChanged += new PropertyChangedEventHandler(age_PropertyChanged);
-
In the generated function, do an invocation on the window
class:
void age_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyPropertyChanged("CanVote");
}
There are two ways of dealing with these dependencies. One is to simply record them somewhere. What I mean is, you just record that A
affects B
in a configuration file, and then generate some code that acts in the way I've described below: catches the change and does the extra notification. The other, more interesting way is to analyse the code and pull out the dependencies, either by analysing IL or the C# source code. In part II of this article, we shall consider those options and derive a solution that takes care of the property dependency problem once and for all.
History
- 7th February, 2008: Article published