views:

209

answers:

2

I have a wpf user control, which exposes a single custom dependency property. Inside the user control, a textblock binds to the value of the dp. This databinding works in all scenarios except when the data source is an object.

The minimal code necessary to reproduce this is:

this is the main part of the user control

<StackPanel Orientation="Horizontal">
    <TextBlock Text="**SimpleUC** UCValue: "/>
    <TextBlock Text="{Binding UCValue}"/>
</StackPanel>

and the user control code behind:

    public SimpleUC()
    {
        InitializeComponent();
        this.DataContext = this;
    }

    public string UCValue
    {
        get { return (string)GetValue(UCValueProperty); }
        set { SetValue(UCValueProperty, value); }
    }

    public static readonly DependencyProperty UCValueProperty =
        DependencyProperty.Register("UCValue", typeof(string), typeof(SimpleUC), new UIPropertyMetadata("value not set"));

this is the test window. I imported my project xml namespace as "custom"

<Window.Resources>
    <Style TargetType="{x:Type StackPanel}">
        <Setter Property="Margin" Value="20"/>
    </Style>
</Window.Resources>
<StackPanel>
    <StackPanel>
        <TextBlock Text="This fails to bind:"/>
        <custom:SimpleUC UCValue="{Binding SomeData}"/> 
    </StackPanel>
    <StackPanel>
        <TextBlock>The same binding on a regular control like Label</TextBlock>
        <Label Content="{Binding SomeData}"/>
    </StackPanel>
    <Slider x:Name="sld" />
    <StackPanel>
        <TextBlock>However, binding the UC to another element value, like a slider works</TextBlock>
        <custom:SimpleUC UCValue="{Binding ElementName=sld,Path=Value}"/>
    </StackPanel>
</StackPanel>

and the test window code behind is:

public TestWindow()
{
    InitializeComponent();
    this.DataContext = this;
}

//property to bind to
public string SomeData { get { return "Hello S.O."; } } 

When I turn on the diagnostic tracing on the TestWindow, it spits out the error "BindingExpression path error: 'SomeData' property not found on 'object' ''SimpleUC' (Name='')' ... "
The binding expression is the same as the one I used in the neighboring label and it worked fine. This behavior seems really bizarre to me. Can anyone shed some light?

+2  A: 

You set DataContext of your SimpleUC to itself here

public SimpleUC()
{
    InitializeComponent();
    this.DataContext = this; // wrong way!
}

so when you use binding here

<custom:SimpleUC UCValue="{Binding SomeData}"/>

it searches property SomeData in control's data context which is set to this object because code in SimpleUC constructor overrides value of DataContext and it is not set to TestWindow object anymore as you expected. That's why your solution works - it doesn't affect DataContext which is inherited from window. Also you can keep this.DataContext = this; but set element where to search property explicitly like this (skipped irrelevant)

<Window ... Name="wnd1">
    <custom:SimpleUC UCValue="{Binding SomeData, ElementName=wnd1}"/>
...

But my oppinion is that your variant from the answer looks more convenient to me, setting data context to this is not very good practice.

Hope it helps.

levanovd
Thanks for the explanation of why DataContext=this wasn't working as expected. that really threw me for a loop.
dan
+1  A: 

If you must use a UserControl, your

<TextBlock
  Text="{Binding RelativeSource={RelativeSource Self},
                 Path=Parent.Parent.UCValue}"
/>

is an ok way to do it and

<TextBlock
  Text="{Binding UCValue,
                 RelativeSource={RelativeSource FindAncestor,custom:SimpleUC,1}}"
/>

is better because you don't rely on the control hierarchy and possible instantiation order issues.

However I would recommend for this kind of situation that you use "custom controls" instead of "user controls". They take a little bit of getting used to, but they are much more powerful because their XAML is the template itself which means you can use TemplateBinding and {RelativeSource TemplatedParent}.

In any case, DataContext = this; is definitely to be avoided.

Ray Burns
i'll look into CCs, thanks
dan