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

4

23

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".

Lag answered 28/10, 2009 at 11:56 Comment(4)
you said you varied his answer, can you post what you actually did?Mollusc
Note that I was looking for a solution to this problem in the context of the MVVM pattern. The property of the Window which I wanted to bind to was actually a property of the Window's ViewModel.Lag
ArsenMkrt suggested creating a property in the UserControl and binding to that, then in turn (in XAML) binding this property to the desired property of the Window. With the MVVM pattern, however, the UserControl never appeared in the XAML, so the second part was not possible.Lag
So, I followed the suggestion to create a new property to bind to, but instead of putting it in the UserControl, I put it in the UserControl's ViewModel. This property simply held a direct reference to the Window's ViewModel, allowing straightforward access to the desired property. The new property was easy to initialize: the reference to use could be passed directly into the constructor of the UserControl's ViewModel.Lag
K
21

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>
Kidderminster answered 28/10, 2009 at 12:4 Comment(8)
But I want to bind to a property on the containing Window (defined in another XAML file), not on the UserControl.Lag
Than it will be better to have property in usercontrol and bind that property with window's propertyKidderminster
Can you create dependency property in UserControl? don't you add usercontrol in window? <UserControl MyProperty = {...binding with window's property }/>Kidderminster
I see, thanks. But does anyone know why FindAncestor does not "just work"? Is its scope limited in some way, say, to within a single XAML file?Lag
do you try AncestorType={x:Type YourWindowType} instead of Window?Kidderminster
I don't know why it is not working to be honest, however my answer is better solution because parent type can be changed during development timeKidderminster
What happens if the container of UserControl changes the x:Name property to something else? Wouldn't that break any bindings that use ElementName instead of RelativeSource?Gosplan
I think the binding need to be refreshed after container change the name... but this need to be checked... may be better to use type instead of name in that case?Kidderminster
A
6

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.

Arv answered 16/11, 2011 at 4:47 Comment(1)
In my Case AncestorType={x:Type Window} was pretty helpful, because AncestorType=UserControl somehow did not point to my window - allthough it normally does.Publican
N
2

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"

Nuristan answered 2/9, 2020 at 20:46 Comment(0)
D
1

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}" />
Deathday answered 20/4, 2011 at 16:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.