OneWayToSource binding from readonly property in XAML OneWayToSource binding from readonly property in XAML wpf wpf

OneWayToSource binding from readonly property in XAML


Some research results for OneWayToSource...

Option # 1.

// Control definitionpublic partial class FlagThingy : UserControl{    public static readonly DependencyProperty IsModifiedProperty =             DependencyProperty.Register("IsModified", typeof(bool), typeof(FlagThingy), new PropertyMetadata());}
<controls:FlagThingy x:Name="_flagThingy" />
// Binding CodeBinding binding = new Binding();binding.Path = new PropertyPath("FlagIsModified");binding.ElementName = "container";binding.Mode = BindingMode.OneWayToSource;_flagThingy.SetBinding(FlagThingy.IsModifiedProperty, binding);

Option # 2

// Control definitionpublic partial class FlagThingy : UserControl{    public static readonly DependencyProperty IsModifiedProperty =             DependencyProperty.Register("IsModified", typeof(bool), typeof(FlagThingy), new PropertyMetadata());    public bool IsModified    {        get { return (bool)GetValue(IsModifiedProperty); }        set { throw new Exception("An attempt ot modify Read-Only property"); }    }}
<controls:FlagThingy IsModified="{Binding Path=FlagIsModified,     ElementName=container, Mode=OneWayToSource}" />

Option # 3 (True read-only dependency property)

System.ArgumentException: 'IsModified' property cannot be data-bound.

// Control definitionpublic partial class FlagThingy : UserControl{    private static readonly DependencyPropertyKey IsModifiedKey =        DependencyProperty.RegisterReadOnly("IsModified", typeof(bool), typeof(FlagThingy), new PropertyMetadata());    public static readonly DependencyProperty IsModifiedProperty =         IsModifiedKey.DependencyProperty;}
<controls:FlagThingy x:Name="_flagThingy" />
// Binding CodeSame binding code...

Reflector gives the answer:

internal static BindingExpression CreateBindingExpression(DependencyObject d, DependencyProperty dp, Binding binding, BindingExpressionBase parent){    FrameworkPropertyMetadata fwMetaData = dp.GetMetadata(d.DependencyObjectType) as FrameworkPropertyMetadata;    if (((fwMetaData != null) && !fwMetaData.IsDataBindingAllowed) || dp.ReadOnly)    {        throw new ArgumentException(System.Windows.SR.Get(System.Windows.SRID.PropertyNotBindable, new object[] { dp.Name }), "dp");    } ....


This is a limitation of WPF and it is by design. It is reported on Connect here:
OneWayToSource binding from a readonly dependency property

I made a solution to dynamically be able to push read-only dependency properties to the source called PushBinding which I blogged about here. The example below does OneWayToSource Bindings from the read-only DP's ActualWidth and ActualHeight to the Width and Height properties of the DataContext

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

PushBinding works by using two Dependency Properties, Listener and Mirror. Listener is bound OneWay to the TargetProperty and in the PropertyChangedCallback it updates the Mirror property which is bound OneWayToSource to whatever was specified in the Binding.

Demo Project can be Downloaded Here.
It contains source code and short sample usage.


Wrote this:

Usage:

<TextBox Text="{Binding Text}"         p:OneWayToSource.Bind="{p:Paths From={x:Static Validation.HasErrorProperty},                                         To=SomeDataContextProperty}" />

Code:

using System;using System.Windows;using System.Windows.Data;using System.Windows.Markup;public static class OneWayToSource{    public static readonly DependencyProperty BindProperty = DependencyProperty.RegisterAttached(        "Bind",        typeof(ProxyBinding),        typeof(OneWayToSource),        new PropertyMetadata(default(Paths), OnBindChanged));    public static void SetBind(this UIElement element, ProxyBinding value)    {        element.SetValue(BindProperty, value);    }    [AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]    [AttachedPropertyBrowsableForType(typeof(UIElement))]    public static ProxyBinding GetBind(this UIElement element)    {        return (ProxyBinding)element.GetValue(BindProperty);    }    private static void OnBindChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)    {        ((ProxyBinding)e.OldValue)?.Dispose();    }    public class ProxyBinding : DependencyObject, IDisposable    {        private static readonly DependencyProperty SourceProxyProperty = DependencyProperty.Register(            "SourceProxy",            typeof(object),            typeof(ProxyBinding),            new PropertyMetadata(default(object), OnSourceProxyChanged));        private static readonly DependencyProperty TargetProxyProperty = DependencyProperty.Register(            "TargetProxy",            typeof(object),            typeof(ProxyBinding),            new PropertyMetadata(default(object)));        public ProxyBinding(DependencyObject source, DependencyProperty sourceProperty, string targetProperty)        {            var sourceBinding = new Binding            {                Path = new PropertyPath(sourceProperty),                Source = source,                Mode = BindingMode.OneWay,            };            BindingOperations.SetBinding(this, SourceProxyProperty, sourceBinding);            var targetBinding = new Binding()            {                Path = new PropertyPath($"{nameof(FrameworkElement.DataContext)}.{targetProperty}"),                Mode = BindingMode.OneWayToSource,                Source = source            };            BindingOperations.SetBinding(this, TargetProxyProperty, targetBinding);        }        public void Dispose()        {            BindingOperations.ClearAllBindings(this);        }        private static void OnSourceProxyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)        {            d.SetCurrentValue(TargetProxyProperty, e.NewValue);        }    }}[MarkupExtensionReturnType(typeof(OneWayToSource.ProxyBinding))]public class Paths : MarkupExtension{    public DependencyProperty From { get; set; }    public string To { get; set; }    public override object ProvideValue(IServiceProvider serviceProvider)    {        var provideValueTarget = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));        var targetObject = (UIElement)provideValueTarget.TargetObject;        return new OneWayToSource.ProxyBinding(targetObject, this.From, this.To);    }}

Have not tested it in styles and templates yet, guess it needs special casing.