RelativeSource works on (nested) sub-property, while ElementName does not
Asked Answered
G

1

5

The problem with the code below is: the binding to SomeClassProp.SubTextProp doesn't work (the source property is not being set to textbox content), while to TextProp it does.

XAML:

<Window x:Class="TestWPF.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Name="wMain"
        SizeToContent="WidthAndHeight">
    <StackPanel>
        <TextBox Text="{Binding ElementName=wMain, Path=SomeClassProp.SubTextProp}" Width="120" Height="23" />
        <TextBox Text="{Binding ElementName=wMain, Path=TextProp}" Width="120" Height="23" />
    </StackPanel>
</Window>

and the code:

public partial class MainWindow : Window
{
    public SomeClass SomeClassProp { get; set; }
    public string TextProp { get; set; }

    public MainWindow()
    {
        InitializeComponent();
        SomeClassProp = new SomeClass();
    }
}

public class SomeClass
{
    public string SubTextProp { get; set; }
}

Am I missing something obvious here?

Note that I need this binding to work from target (textbox) to source (class property).

UPDATE: When I change the binding from ElementName=wMain to RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}} - BOTH bindings work. So the problem is specific to ElementName binding property.

Gwenni answered 24/8, 2013 at 13:19 Comment(5)
This ought to work, that why it's called Path. Did you look in the Output Window for binf=ding errors? Verys sure the string was set at all?Harewood
Does it work with a view model?Helsell
No binding errors in the output. The SubTextProp remains 'null', while TextProp is set to whatever I type into textbox.Gwenni
@retailcoder - not sure what you mean by that; I implemented INotifyPropertyChanged on SomeClass, it didn't help - but this is imho irrelevant, as I'm trying to bind from target to source, not the other way round.Gwenni
I mean it's easier to bind with the window's DataContext than with its own xaml, you would only need the Path part. If you created a class to hold TextProp and SomeClassProp and assigned an instance of that to the window's DataContext instead, you could do {Binding SomeClassProp.SubTextProp}.Helsell
G
7

Ok, finally, I found the problem!

After adding diag:PresentationTraceSources.TraceLevel=High to binding defs (very useful thing btw, in the absence of normal ol' step-by-step debugging), I saw the following in the output:

System.Windows.Data Warning: 108 : BindingExpression (hash=54116930):   At level 0 - for MainWindow.SomeClassProp found accessor RuntimePropertyInfo(SomeClassProp)
System.Windows.Data Warning: 104 : BindingExpression (hash=54116930): Replace item at level 0 with MainWindow (hash=47283970), using accessor RuntimePropertyInfo(SomeClassProp)
System.Windows.Data Warning: 101 : BindingExpression (hash=54116930): GetValue at level 0 from MainWindow (hash=47283970) using RuntimePropertyInfo(SomeClassProp): 
System.Windows.Data Warning: 106 : BindingExpression (hash=54116930):   Item at level 1 is null - no accessor
System.Windows.Data Warning: 80 : BindingExpression (hash=54116930): TransferValue - got raw value {DependencyProperty.UnsetValue}
System.Windows.Data Warning: 88 : BindingExpression (hash=54116930): TransferValue - using fallback/default value ''
System.Windows.Data Warning: 89 : BindingExpression (hash=54116930): TransferValue - using final value ''

The problem was in the order of MainWindow initialization!

So at the moment when the binding was constructed, my level 0 property (SomeClassProp) was not yet initialized, which resulted in binding failing completely (without issuing a normal-level binging warning for some reason).

Long story short - moving SomeClassProp intitialization before the InitializeComponent() in MainWindow constructor did the trick, binding started to work with ElementName too:

public MainWindow()
{
    SomeClassProp = new SomeClass();
    InitializeComponent();
}

The answer to question - why it worked using RelativeSource property - lies in these lines of the output log:

System.Windows.Data Warning: 66 : BindingExpression (hash=28713467): RelativeSource (FindAncestor) requires tree context
System.Windows.Data Warning: 65 : BindingExpression (hash=28713467): Resolve source deferred

Data context initialization with RelativeSource requires tree context, and is deferred to some point in time after construction of the Window (by that time SomeClassProperty was already initialized).

Gwenni answered 24/8, 2013 at 14:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.