6
votes

How do we bind to a parent/sibling of a current datacontext (i.e. the source property representing the current datacontext)?

I am not talking about binding to the parent control's property (that case involves parent of target and not of source) - and that can be easily done by using RelativeSourceMode=FindAncestor.

RelativeSourceMode=PreviousData provides a limited support of binding to the previous sibling of the data item, but not to parent or other siblings.

Dummy Example:
(assume INPC in place)
How to bind ItemsSource of ComboBox to the Departments property of ViewModel?

public class Person
{
    public string Name { get; set; }
    public string Department { get; set; }
}

public class PersonViewModel
{
    public List<Person> Persons { get; set; }
    public List<string> Departments { get; set; }

    public PersonViewModel()
    {
        Departments = new List<string>();
        Departments.Add("Finance");
        Departments.Add("HR");
        Departments.Add("Marketing");
        Departments.Add("Operations");

        Persons = new List<Person>();
        Persons.Add(new Person() { Name = "First", Department = "HR" });
        Persons.Add(new Person() { Name = "Second", Department = "Marketing" });
        Persons.Add(new Person() { Name = "Third", Department = "Marketing" });
    }
}

XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="300" Width="300">
    <Grid>
        <DataGrid ItemsSource="{Binding Persons}" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Name" Binding="{Binding Name}" />
                <DataGridTemplateColumn>
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <ComboBox ItemsSource="{Binding Departments???}"
                                      SelectedValue="{Binding Department}"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>
3
so {Binding DataContext.Departments, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}} is not what you want?Markus Hütter
hmmm... didn't think that I could use the DataContext property of parent control to get the parent data item.. :).publicgk
added my comment as an answer for your convenience =)Markus Hütter

3 Answers

12
votes

you can access an ancestors DataContext like so:

<ComboBox ItemsSource="{Binding DataContext.Departments, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}}"
                                  SelectedValue="{Binding Department}"/>
3
votes

In most cases I use DataContext as Path root:

{Binding Path=DataContext.MyProperty,ElementName=MyElement}
{Binding Path=DataContext.MyProperty,RelativeSource={RelativeSource AncestorType=MyAnsectorTypr}
0
votes

I'm not sure why a RelativeSource approach doesn't work. (I do this all the time, AncestorType=UserControl) But if this is unacceptable, there are two ways that come to mind.

1) Give each Person a list of Departments to bind to. In your example, you could just pass the list in the Person constructor. Or, give each Person a reference back to the parent to get to any property of the parent like this: {Binding Parent.Departments}

2) Create an attached property "ParentDataContext" and set it outside of your items control, like this:

<Window local:Attached.ParentDataContext="{Binding}" ... >

Then you can bind to it anywhere lower in the tree:

{Binding Path=(local:Attached.ParentDataContext).Departments, RelativeSource=Self}

The attached property must inherit so everything lower in the tree has it set by virtue of setting it at the top level.