What exactly does WPF Data Binding's "RelativeSource FindAncestor" do?

77.3k views Asked by At

I am currently working within a WPF user control (the root element of my XAML file is "UserControl"), which I know is being hosted inside a Window. How can I access a property of the Window using data binding?

Does anyone know why simply

<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Window}}" Path="..." />

does not work? The error message I get is:

System.Windows.Data Warning: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1''.

Edit: I ended up using a variation on ArsenMkrt's approach, so have accepted his answer. However, I am still interested in finding out why FindAncestor does not "just work".

4

There are 4 answers

8
Arsen Mkrtchyan On BEST ANSWER

The best way is to give a name to UserControl

Create dependency property MyProperty in UserControl with two way binding and bind it in main Window, than bind in UserControl like this

<UserControl x:Name = "myControl">
     <Label Content={Binding ElementName= myControl, Path=MyProperty}/>
</UserControl>
0
Samer On

If you are using a view model as your Window's DataContext and the property you need to bind to is from that view model then you should prefix the path with DataContext.MyPropertyPath, something like this:

<TextBox Text="{Binding DataContext.MyProperty, RelativeSource={RelativeSource AncestorType={x:Type Window}, Mode=FindAncestor}}"/>

this translates as "Find me an ancestor window and then look in it's data context for MyProperty"

1
Simon_Weaver On

If you're trying to 'escape' from an ItemsControl or DataGridView to get to a Window you may be finding that AncestorType of x:Type Window doesn't work. Or at least doesn't seem to...

If this is the case you're probably running Blend or Visual Studio and expecting the data to be visible at design time - which it won't because VS + Blend both create their own instances that aren't really Windows. It will work at runtime just fine, but not during design mode.

There's a couple things you can do:

  • Wrap in a UserControl

  • Here's an alternative solution I've come up with. It has one advantage in that you're not referencing a UserControl or Window directly, so if you change the parent container your code won't break.

    <Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:views="clr-namespace:MyWPFApplication.Views"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"                  
    x:Class="MyWPFApplication.Views.UPCLabelPrinterWindow"
    mc:Ignorable="d"
    x:Name="LayoutRoot"
    Title="UPCLabelPrinterWindow">
    
    <views:DataContextWrapper>
        <DockPanel>
            ...
        </DockPanel>
    </views:DataContextWrapper>
    

Where DataContextWrapper is just a Grid

namespace MyWPFApplication.Views {
   public class DataContextWrapper : Grid
   {

   }
}

Then when you bind you do this :

<TextBlock Text="{Binding="{Binding DataContext.SomeText, 
  RelativeSource={RelativeSource AncestorType={x:Type views:DataContextWrapper}, 
  Mode=FindAncestor}}" />

Note: if you want to bind to a property ON Window itself it's trickier and you should probably bind via a dependency property or something like that. But if you are using MVVM then this is one solution I found.

0
MahmudReza Tari On

I Think You Should SET Mode="OneWayToSource" Like this:

<TextBox Text="{Binding RelativeSource={RelativeSource FindAncestor ,AncestorType={x:Type Grid}},Path=BackGround , Mode=OneWayToSource , UpdateSourceTrigger = PropertyChanged}" />