A lot of controls expose properties which are not DependencyProperties and then you can’t put a binding on it. On some other cases, you only have a getter as accessor and you can’t put a binding on it too…

This is for example the case for the ribbon’s group of the office ribbon or the converter’s parameter.

If you ever tried to do so, you surely had an exception throwned :

A 'Binding' cannot be set on the 'SetCEDEJDED' property of type 'Tralala'.
A 'Binding' can only be set on a DependencyProperty of a DependencyObject.


In this post we will discover a work-around…

The main idea is to use a kind of proxy/observer (a definition can be found in this post) which will reflect every change on the source object to the target object and vice versa.

Here are the main parts of the solution ..

Specification: the XAML code we'll use

Here is the code snippet which describe how we will use our proxy in the XAML. There will be no code-behind.

<Window x:Class="BindOnNonDependencyProperty.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:us="clr-namespace:BindOnNonDependencyProperty"
    Title="BindOnNonDependencyProperty" >
  <DockPanel >
    <TextBox x:Name="myTextBox" DockPanel.Dock="Top"  />
    <TextBox x:Name="monTextBlockCible"  DockPanel.Dock="Top"  />
    <us:ExtendedBinding Source="{Binding ElementName=myTextBox,Path=Text,Mode=TwoWay}"
              Target="{Binding ElementName=monTextBlockCible,Path=Text,Mode=TwoWay}"
              />
  </DockPanel>
</Window>



The correct base class for our proxy/observer

We will call it ExtendedBinding and it must be inherithing from DependencyObject at last to be abble to own DependencyProperty. But the only way to add a DependencyObject into our XAML is to add it into a resourceDictonary.

This is a drawnback because, by doing it, it will no more be into the control's tree and then it will be impossible to make a binding on one of it's property. Note that it's still possible to use it as a Target from another place in our XAML but you can't do a Binding on one of it's properties. This code will not work :

<Windows.Resources>
   <MyDependencyObject x:Key="myKey" MyProperty="{Binding Tralala, ElementName=myTarget}" />
</Windows.Resources>



Then to put it insode the control's tree, we only had to make it an UIElement will you say... No because in the actual version of the Framework you won't have inheritance of the DataContext and the use of the 'ElementName' binding will be prohibited. Hopefully, there is a solution, our proxy have to inherit from FrameworkElement and everything will work fine !

The DependencyProperties

We will add two dependencyProperties, one will be the target and the second will be the source.

These DP will be customize by using the FrameworkPropertyMetadata two enables these features :

  • Binding will be done using the TwoWay mode,
  • The UpdateSourceTrigger used will be the PropertyChanged event.


How it works

The core of our proxy is to override the DependencyObject's OnPropertyChanged method. Each change on the source or the target will update it's counterparty.

We have to take care not to fall into a loop : when we will update the target or the source we'll also raise a PropertyChanged event and we must ignore this one....


Final code

public class ExtendedBinding : FrameworkElement
  {
    #region Source DP
    //We don't know what will be the Source/target type so we keep 'object'.
    public static readonly DependencyProperty SourceProperty =
      DependencyProperty.Register("Source", typeof(object), typeof(ExtendedBinding),
      new FrameworkPropertyMetadata()
      {
        BindsTwoWayByDefault = true,
        DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
      });
    public Object Source
    {
      get { return GetValue(ExtendedBinding.SourceProperty); }
      set { SetValue(ExtendedBinding.SourceProperty, value); }
    }
    #endregion
 
    #region Target DP
      //We don't know what will be the Source/target type so we keep 'object'.
    public static readonly DependencyProperty TargetProperty =
      DependencyProperty.Register("Target", typeof(object), typeof(ExtendedBinding),
      new FrameworkPropertyMetadata()
      {
        BindsTwoWayByDefault = true,
        DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
      });
    public Object Target
    {
      get { return GetValue(ExtendedBinding.TargetProperty); }
      set { SetValue(ExtendedBinding.TargetProperty, value); }
    }
    #endregion
 
    protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
    {
      base.OnPropertyChanged(e);
      if (e.Property.Name == ExtendedBinding.SourceProperty.Name)
      {
	//no loop wanted
        if (!object.ReferenceEquals(Source, Target))
          Target = Source;
      }
      else if (e.Property.Name == ExtendedBinding.TargetProperty.Name)
      {
	//no loop wanted
        if (!object.ReferenceEquals(Source, Target))
          Source = Target;
      }
    }
 
  }



Shout it kick it on DotNetKicks.com