views:

3031

answers:

4

I'm trying to bind to a Readonly property with OneWayToSource as mode, but it seems this cannot be done in XAML:

<controls:FlagThingy IsModified="{Binding FlagIsModified, 
                                          ElementName=container, 
                                          Mode=OneWayToSource}" />

I get:

The property 'FlagThingy.IsModified' cannot be set because it does not have an accessible set accessor.

IsModified is a readonly DependencyProperty on FlagThingy. I want to bind that value to the FlagIsModified property on the container.

To be clear:

FlagThingy.IsModified --> container.FlagIsModified
------ READONLY -----     ----- READWRITE --------

Is this possible using just XAML?


Update: Well, I fixed this case by setting the binding on the container and not on the FlagThingy. But I'd still like to know if this is possible.

A: 

WPF will not use the CLR property setter, but seems it does some odd validation based on it.

May be in your situation this can be ok:

    public bool IsModified
    {
        get { return (bool)GetValue(IsModifiedProperty); }
        set { throw new Exception("An attempt ot modify Read-Only property"); }
    }
alex2k8
The CLR property is not used in this case.
Inferis
Do you mean that you just defined DependencyProperty and was able to write <controls:FlagThingy IsModified="..." />? For me it say: "The property 'IsModified' does not exist in XML namespace" if I don't add CLR property.
alex2k8
I believe design time uses the clr properties where as runtime actually goes directly to the dependency property (if it is one).
meandmycode
The CLR property is unnecessary in my case (I don't use IsModified from code), but it's there nevertheless (with only a public setter). Both designtime and runtime work fine with just the dependencyproperty registration.
Inferis
The binding itself is not using CLR property, but when you define the binding in XAML it have to be translated into code. I guess on this stage XAML parser see that IsModified property is readonly, and throws exception (even before binding created).
alex2k8
+1  A: 

You're doing the binding in the wrong direction right now. OneWayToSource will try and update FlagIsModified on container whenever IsModified changes on the control you are creating. You want the opposite, which is to have IsModified bind to container.FlagIsModified. For that you should use the binding mode OneWay

<controls:FlagThingy IsModified="{Binding FlagIsModified, 
                                          ElementName=container, 
                                          Mode=OneWay}" />

Full list of enumeration members: http://msdn.microsoft.com/en-us/library/system.windows.data.bindingmode.aspx

JaredPar
No, I want exactly the scenario that you describe that I don't want to do.FlagThingy.IsModified --> container.FlagIsModified
Inferis
Getting marked down because the questioner had an ambigous question seems a bit overkill.
JaredPar
+1  A: 

Some research results for OneWayToSource...

Option # 1.

// Control definition
public partial class FlagThingy : UserControl
{
    public static readonly DependencyProperty IsModifiedProperty = 
            DependencyProperty.Register("IsModified", typeof(bool), typeof(FlagThingy), new PropertyMetadata());
}

// XAML
<controls:FlagThingy x:Name="_flagThingy" />

// Binding Code
Binding binding = new Binding();
binding.Path = new PropertyPath("FlagIsModified");
binding.ElementName = "container";
binding.Mode = BindingMode.OneWayToSource;
_flagThingy.SetBinding(FlagThingy.IsModifiedProperty, binding);

Option # 2

// Control definition
public 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"); }
    }
}

// XAML
<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 definition
public 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;
}

// XAML
<controls:FlagThingy x:Name="_flagThingy" />

// Binding Code
Same 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");
        }
     ....
alex2k8
So that's a bug, actually.
Inferis
A: 

This is 'by design' according to bug report on MS connect

Christian