Already tried to extend the Binding or BindingBase classes in order to write your own custom bindings? And failed because BindingBase.ProvideValue is sealed? Me too…
The result is a MarkupExtension that works around the issue that you cannot properly extend the Binding class. Basically, it allows you to write binding expressions with the usual syntax without having to worry about the underlying plumbing. Here’s a sample binding that adds an additional LookupKey property:
<TextBox Name="txtZipCode"
Text="{local:LookupExtension Path=ZipCode,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
LookupKey=F5}"
/>
Decorating the Binding class
As extending the Binding class did not work, I tried a different approach:
- Create a class that extends MarkupExtension rather than BindingBase. This class provides all the properties that are needed for a binding expression (Source, Path, Converter, …).
- Based on defined binding properties, the extension class internally creates a regular Binding and associates it with the targeted dependency object.
I wanted it to look somehow like this (dummy code!):
public class MyCustomExtension : MarkupExtension
{
public override object ProvideValue(IServiceProvider provider)
{
//we only use this extension on textboxes
TextBox tb = GetTextBoxFromProvider(provider);
//create a binding and associate it with the text box
Binding binding = CreateBinding(this.Source, this.Path);
tb.SetBinding(TextBox.TextProperty, binding);
//return a valid value
return ...;
}
}
And guess what – that worked 🙂
The result is an abstract base class that provides pretty much everything you need to create your custom binding classes. The BindingDecoratorBase provides properties for all kinds of binding expressions (Source, Path, Converter etc.) and handles binding creation and association with the targeted dependency object for you:
Basically, the class maintains its own binding class and just forwards the binding statements. For example, here’s the declaration of the Path property:
[DefaultValue(null)]
public PropertyPath Path
{
get { return binding.Path; }
set { binding.Path = value; }
}
Custom Binding Sample
Here’s a simple sample: Let’s say we have a TextBox that displays the ZipCode property of a bound item. The XAML for this bound control looks like this:
<TextBox Name="txtZipCode"
Text="{Binding Path=ZipCode}"
/>
However, we want to interfere with the binding in order to add some additional value. For this example, we’d like to register the bound control with some kind of handler class. We could also do this with an attached property, but this is more fun 😉
First, I created a custom extension called LookupExtension that derives from BindingDecoratorBase. BindingDecoratorBase provides all binding properties out of the box, so the binding expression itself (Path=ZipCode) remains intact. The only thing I had to change in XAML was the extension name and add the custom property for my extension (LookupKey):
<TextBox Name="txtZipCode"
Text="{local:LookupExtension Path=ZipCode,
LookupKey=F5}"
/>
As LookupExtension derives from BindingDecoratorBase, it is ensured that in the end, the text box will be bound to the ZipCode property just like with the original regular binding expression.
Below is a first implementation which does not yet add any additional value. Therefore, this extension would behave exactly like the original binding – the ZipCode property of the bound data item is displayed, and updating the TextBox changes the bound property value:
public class LookupExtension : BindingDecoratorBase
{
//A property that can be set in XAML
public string LookupKey { get; set; }
public override object ProvideValue(IServiceProvider provider)
{
//delegate binding creation etc. to the base class
return base.ProvideValue(provider);
}
}
So what’s left is adding some custom code to the extension. Below is the complete sample. Basically, the bound control (the text box of the sample) is determined by invoking TryGetTargetItems and then registered with an InputHandler. That’s it:
public class LookupExtension : BindingDecoratorBase
{
//A property that can be set in XAML
public string LookupKey { get; set; }
public override object ProvideValue(IServiceProvider provider)
{
//delegate binding creation etc. to the base class
object val = base.ProvideValue(provider);
//try to get bound items for our custom work
DependencyObject targetObject;
DependencyProperty targetProperty;
bool status = TryGetTargetItems(provider, out targetObject,
out targetProperty);
if (status)
{
//associate an input listener with the control
InputHandler.RegisterHandler(targetObject, LookupKey);
}
return val;
}
}
Note the status flag in the snippet above! ProvideValue is also invoked at design time (within Visual Studio). In this case, the provider parameter is null, so be careful to validate your parameters or you’ll end up with a broken designer.
Problem: Attribute syntax with resources
During the implementation of the sample, I came across an annoying issue: You cannot nest static or dynamic resources within a custom markup extension due to a framework bug. This means that if you wanted to set the Source property explicitly, you could not write it like this:
<TextBox Name="txtZipCode"
Text="{local:LookupExtension Source={StaticResource MyAddress}
Path=ZipCode,
LookupKey=F5}"
/>
The above snippet does not compile due to a bug I described here. As a result, you have to fall back to property element syntax if you need to explicitly setting the Source property or other resources (e.g. Converter):
<TextBox Name="txtZipCode">
<TextBox.Text>
<local:LookupExtension Source="{StaticResource MyAddress}"
Path="ZipCode"
LookupKey="F5" />
</TextBox.Text>
</TextBox>
Source Code / Sample
I’ve assembled a simple project that contains the BindingDecoratorBase class and the LookupExtension I used as a sample (VS 2008 project).
You can download it here: custom-bindings.zip