OneWayToSource Binding for ReadOnly Dependency Property

Updated 2017-07-03: Apparently dropbox changed something that broke the download links after 6 years 🙂 They are fixed now.

Have you ever tried to do something like this?

<TextBlock Name="myTextBlock"            ActualWidth="{Binding Path=Width, Mode=OneWayToSource}"            ActualHeight="{Binding Path=Height, Mode=OneWayToSource}"/>

If you did, you probably received the following error

error MC3065: ‘ActualWidth’ property is read-only and cannot be set from markup.

A Binding can’t be set on a ReadOnly Dependency Property. Fair enough. But why can’t it do a OneWayToSource Binding? This should only affect the Source and not the Target. Apperently, this is by design and the problem is reported here on connect:
OneWayToSource binding from a readonly dependency property

The Solution
Various solutions and workarounds to this have been posted across the Internet and here is my take on it.

The aim was to create an intuitive solution which

• Adds support for a “PushBinding” on any Dependency Property in the Target• Works with DataContext, ElementName, RelativeSource and Source Bindings

• Can specify any number of “PushBindings” on any FrameworkElement

And here is the result

<TextBlock Name="myTextBlock">
    <pb:PushBindingManager.PushBindings>
        <pb:PushBinding TargetProperty="ActualHeight" Path="Height"/>
        <pb:PushBinding TargetProperty="ActualWidth" Path="Width"/>
    </pb:PushBindingManager.PushBindings>
</TextBlock>

The attached project contains source code and demo usage
Download Project Here

Implemention

PushBindings is an Attached Property of type PushBindingCollection which can be set on any FrameworkElement.

PushBinding won’t be in the Visual or Logical tree so it will need an Inheritance Context to be able to do DataContext, ElementName and RelativeSource Bindings. One way to get an Inheritance Context is to inherit from Freezable (More details of why this works can be found here and here). PushBindingCollection needs Inheritance Context as well so it derives from FreezableCollection.

PushBinding has two Dependency Properties

• Listener – Binds OneWay to the TargetProperty and calls SetValue on Mirror whenever it changes through the PropertyChangedCallback• Mirror – Binds OneWayToSource to whatever is specified in the Binding (DataContext, RelativeSource etc.)

public class PushBinding : FreezableBinding
{
    #region Dependency Properties

    public static DependencyProperty TargetPropertyMirrorProperty = ...
    public static DependencyProperty TargetPropertyListenerProperty = ...

    public string TargetProperty{get; set;}

    public void SetupTargetBinding(FrameworkElement targetObject)
    {
        Binding listenerBinding = new Binding
        {
            Source = targetObject,
            Path = new PropertyPath(TargetProperty),
            Mode = BindingMode.OneWay
        };
        BindingOperations.SetBinding(this,
                                     TargetPropertyListenerProperty,
                                     listenerBinding);

        BindingOperations.SetBinding(this,
                                     TargetPropertyMirrorProperty,
                                     Binding);
    }

    private void TargetPropertyValueChanged()
    {
        object targetPropertyValue = GetValue(TargetPropertyListenerProperty);
        this.SetValue(TargetPropertyMirrorProperty, targetPropertyValue);
    }
}

At first, I used Reflection to get the specified DependencyProperty and used it to subscribe to ValueChanged of DependencyPropertyDescriptor. The problem with this was that it leaked memory and I had a hard time to find out when it was time to call RemoveValueChanged. Instead I introduced the Listener Dependency Property and now it’s leak-free.

Update: Using PushBinding in a Style

Initially, there were some problem when trying to use PushBindings in a Style.

• The workaround used to be able to add items directly to the attached property PushBindings in Xaml (without having to create a new instance of PushBindingsCollection) doesn’t work in a Style. Instead, we just get an exception.

• A FreezableCollection created in Xaml is Freezed. This means that the PushBindings in the collection becomes Freezed as well and a Freezed object can’t be edited. So when trying to setup the Bindings in PushBinding we’ll get another exception.

• Unlike a regular Binding, a PushBinding can only be used for one object. This means that each PushBinding in a Style have to be cloned before it can be used.

To overcome these issues, I created another attached property in PushBindingManager called StylePushBindings and implemented the overridable methods CreateInstanceCore and CloneCore in PushBinding and its base-class FreezableBinding. And when a PushBinding is added to StylePushBindings, it is cloned and added to the PushBindings collection.

public static void StylePushBindingsChanged(DependencyObject target,
                                        DependencyPropertyChangedEventArgs e)
{
    FrameworkElement targetObject = target as FrameworkElement;
    if (targetObject != null)
    {
        PushBindingCollection stylePushBindings =
            e.NewValue as PushBindingCollection;
        PushBindingCollection pushBindingCollection =
            GetPushBindings(targetObject);
        foreach (PushBinding pushBinding in stylePushBindings)
        {
            PushBinding pushBindingClone = pushBinding.Clone() as PushBinding;
            pushBindingCollection.Add(pushBindingClone);
        }
    }
}

So when using a PushBinding in a Style, use StylePushBindings, create a new PushBindingCollection and add the PushBindings like this

<TextBlock ...>
  <TextBlock.Style>
<Style TargetType="TextBlock">
      <Setter Property="pb:PushBindingManager.StylePushBindings">
        <Setter.Value>
          <pb:PushBindingCollection>
            <pb:PushBinding TargetProperty="ActualHeight" Path="Height"/>
            <pb:PushBinding TargetProperty="ActualWidth" Path="Width"/>
          </pb:PushBindingCollection>
        </Setter.Value>
      </Setter>
    </Style>

  </TextBlock.Style>
</TextBlock>

I added a new demo project with sample usage for PushBindings in both Styles and DataTemplates.
It can be downloaded here

Enjoy! 🙂

One last note, since .NET 4.0 we are even further away from a solution to this, since a OneWayToSource Binding reads the value it set back from the Source after it has updated it so I doubt we will ever see built-in-support for this.

25 Responses to OneWayToSource Binding for ReadOnly Dependency Property

  1. R says:

    Is it possible to use this solution from a style or datatemplate? I’m facing a scenario where I need to bind a readonly property of a dynamically generated ListViewItem.

    • Meleak says:

      Hi Per and sorry for the late reply! Yes, it is possible but it took a little more work to get it to work from a Style/DataTemplate. Unfortunately, I haven’t had time to update the post and now I won’t have access to my computer for another 5 month since me and my girlfriend is on a long vacation around the world. Sorry about that, hope you manage to solve your problem! Best regards /Fredrik

    • Meleak says:

      Finally got some time to update the post. The modified version is usable in a Style as well. Cheers

  2. Daniel Wiebe says:

    This is great. What kind of software license is this code under? I would like to use your code in a commercial application, but I cannot unless it is under a license that is compatible with my company’s. Something like the BSD, MIT, or CPOL license would work great (http://www.codeproject.com/info/Licenses.aspx). I have to ask since under US copyright law all code is implicitly under copyright unless stated otherwise (via some license).

    • Meleak says:

      Thanks for your comment Daniel. Actually, I wasn’t aware that I had to license my code at all, I just considered it to be code that anyone can use however they see fit 🙂 Anyway, it’s in the public domain now with Unlicense so you’re free to use it anyway you like. Cheers

  3. Salim says:

    Thank you so much for this article, it helped me and saved a lot of time and headache 🙂

  4. Ioan Samfirescu says:

    Elegant solution! Thank you.

  5. tilonthuduc says:

    How can we use this PushBinding in code since it’s not derived from BindingBase? Sorry for my stupid question. I’m a new baby in WPF.

  6. tilonthuduc says:

    Sorry. I found my own anwser. It’s just need to set PathProperty and SetupTargetBinding to make it work. Thanks.

  7. Jerome says:

    Very nice solution.
    Thank you for this code sharing.

    Regards

  8. karan sharma says:

    Is it possible to use this in DataTrigger

  9. wpfblendperson says:

    Hi, Your downloadable project doesn’t have the StylePushBindings … Is there a way you can upload one with it too? That would be useful. THanks!

  10. Shawn says:

    Thank you so much for writing this article. I have been struggling for days to add Behaviors in a Style and this lead me in the right direction, thanks again!

  11. Harold says:

    This was awesome! I spent all day trying to figure this out. Can’t believe that this isn’t native to WPF.. seems like a really common scenario!

  12. DharmaRat says:

    Hi, Nice article. I tried it out in my project and I don’t seem to be able to apply it to Grid.RowDefinitions or GridColumnDefinitions. It complains that “Property ‘PushBindings’ is not attachable to items of type ‘RowDefinition'”. Is there some way to make this work? I need the ActualWidth and ActualHeight of a specific cell in the grid.

    • Meleak says:

      Hi and sorry for the late reply. I don’t think you should get any error for using PushBinding on RowDefinition or ColumnDefinition because they are DependencyObjects. But the properties ActualWidth and ActualHeight aren’t dependency properties so they don’t tell anyone when their values change so, unfortunately, PushBinding won’t work in this direction.

  13. Bjorn says:

    Hi,

    this article seems very useful.
    Do you know if there is an equivalent for WinRT / Windows 8.1 framework ? Did you ever eard about it ?

    I tired to adapt the PushBindingExtension, but i failed, this is based on class and methods unacessible in WinRT 😦

    Thank you

  14. Bill Lee says:

    I found this after hours of trying other solutions. Most excellent!

    One suggestion. You use this at the level of FrameworkElement. It can be more generalized. In fact I had to do this for my problem, which involved Developer Express WPF grid GridColumn objects, which derive from FrameworkContentElement. I modified your solution to use the lowest level possible, which is DependencyObject. Replacing FrameworkElement with DependencyObject, and the metadata down to FrameworkMetadata made this work for most everything.

  15. Josh Smith Humps Goats says:

    the source does not contain StylePushBindings class mention. How does one get this

  16. Alain says:

    The download link does not work

  17. Gerard Hur says:

    I enjoy looking at your site. Thanks for your time!

  18. Julien Amsellem says:

    Hello,
    I don’t understand why but it seems it does not work with “SelectedItems” property of a TreeView or a ListBox. Do you have any idea on how to fix it?

  19. Tim U. says:

    Many many thanks for this great enhancement. It had driven me nearly crazy.

    As I find it that useful I will implement it in my own WPF-Controls-Library. 👍🙏

  20. Joe O'Leary says:

    This looks like exactly what I need. What an excellent and thoughtful technical solution to a really tricky problem!

    Question: My typical approach to crafting added functionality like this is to make it into an attached Behavior. Was there any particular reason that you did not do that here?

Leave a reply to Salim Cancel reply