sourcenouveau
sourcenouveau

Reputation: 30534

Binding to ancestors from within a ResourceDictionary

How can I bind to a UserControl's property from within its ResourceDictionary? I want an object I declare in my resources to have the same DataContext as the UserControl it is contained in:

<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Some.Namespace"
    DataContext="{Binding Path=ViewModel, RelativeSource={RelativeSource Self}}">
    <UserControl.Resources>
        <local:SomeClass
            x:Key="SomeClass"
            DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />
    </UserControl.Resources>
</UserControl>

At runtime I get the error:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='UserControl', AncestorLevel='1''. BindingExpression:Path=DataContext; DataItem=null; target element is 'SomeClass' (Name=''); target property is 'DataContext' (type 'Object')

Upvotes: 7

Views: 6317

Answers (6)

Marcin Wisnicki
Marcin Wisnicki

Reputation: 4701

Set x:Shared="False", this will clone resource on each use and make it a child of your element, enabling usage of bindings.

<local:SomeClass
            x:Key="SomeClass"
            x:Shared="False"
            DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}" />

Upvotes: 2

sourcenouveau
sourcenouveau

Reputation: 30534

My workaround was to set the DataContext of the resource in the code-behind.

.xaml

<local:SomeType x:Key="SomeKey" SomeProperty="{Binding ... }" />

.xaml.cs

public SomeControl()
{
    InitializeComponent();
    ((SomeType)this.Resources["SomeKey"]).DataContext = this;
}

Upvotes: 2

CodeNaked
CodeNaked

Reputation: 41403

When using FindAncestor, the target element needs to be a descendent (either logical or visual) of the source. Your object does not appear in either the visual or logical tree, it's simply in the resources. So you can't use RelativeSource with FindAncestor for your object.

You can use ElementName in your Binding though. Something like this should work:

<UserControl x:Name="userControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Some.Namespace"
    DataContext="{Binding Path=ViewModel, RelativeSource={RelativeSource Self}}">
    <UserControl.Resources>
        <local:SomeClass
            x:Key="SomeClass"
            DataContext="{Binding Path=DataContext, ElementName=userControl}" />
    </UserControl.Resources>
</UserControl>

Upvotes: 1

Aran Mulholland
Aran Mulholland

Reputation: 23945

when you add your resource to the visual tree it should inherit the data context. but... have a look at element spy it might just do what you need.

Upvotes: 0

Sergey Aldoukhov
Sergey Aldoukhov

Reputation: 22744

What I would do is to create an attached behavior (ContextualizeResourceBehavior) on the user control, and specify the resource key on that attached behavior. The behavior would lookup the resource (not sure you would be able to do it on attach, if not you would need to hook up Loaded event) and transfer the data context.

Upvotes: 0

Mark Synowiec
Mark Synowiec

Reputation: 5445

I think what you're looking for is just {Binding} which binds to the inherited DataContext. Here's an example, though a bit strange shows how you can grab a color through binding to the DataContext:

<Window x:Class="AncestorBinding.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <SolidColorBrush x:Key="MyBrush" Color="Blue" />
    </Window.Resources>
    <StackPanel>
        <Button DataContext="{Binding Source={StaticResource MyBrush}}" Content="My Button">
            <Button.Resources>
                <Style TargetType="{x:Type Button}">
                    <Setter Property="Background" Value="{Binding}" />
                </Style>
            </Button.Resources>
        </Button>
    </StackPanel>
</Window>

Upvotes: 0

Related Questions