22
votes

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
you said you varied his answer, can you post what you actually did?Miles
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.user200783
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.user200783
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.user200783

4 Answers

20
votes

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>
5
votes

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.

1
votes

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}" />
0
votes

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"