Search code examples
c#wpfxamldata-binding

Binding with relative source


I am trying to understand how the RelativeSource works.

With the setup below I'd expect to see the text "I am the MainViewModel" displayed on the form, however I see an error in the debugger and no text on the MainWindow:

Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='UnderstandingBindings.ViewModels.MainViewModel', AncestorLevel='1''. BindingExpression:Path=SomeProperty; DataItem=null; target element is 'TextBlock' (Name='myText'); target property is 'Text' (type 'String')

I have a ViewModel like this:

class MainViewModel
{
    public string SomeProperty { get => "I am the MainViewModel"; }
    private readonly ChildViewModel _child = new ChildViewModel();
    public ChildViewModel Child => _child;
}

class ChildViewModel
{
    public string SomeProperty { get => "I am the ChildViewModel"; }
}

the MainWindow XAML looks like:

<Window x:Class="UnderstandingBindings.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:UnderstandingBindings.Views"
        xmlns:vm="clr-namespace:UnderstandingBindings.ViewModels"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel x:Name="pnlMain">
        <TextBlock x:Name="myText" Text="{Binding SomeProperty, RelativeSource={RelativeSource AncestorType={x:Type vm:MainViewModel}}}"/>
    </StackPanel>
</Window>

The data context is assigned like this:

public partial class MainWindow : Window
{
    private readonly MainViewModel _viewModel = new MainViewModel();
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = _viewModel.Child;
    }
}

Solution

  • wpf looks for the Ancestors of the xaml element the binding is declared on. You can think of it as walking up the Visual Tree.

    You'd use it to Bind to a property thats on that Ancestor or have to bind to a viewmodel by going through it's DataContext Property. So for example:

    <TextBlock x:Name="myText" Text="{Binding DataContext.Child.SomeProperty, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>  
    

    That only works if the DataContext is set to the MainViewModel in your example tho. If you want the Binding to go the direction you expected then you need to add a reference to the Parent Viewmodel on the Child.