0
votes

I have TreeView control and I need to bind property from root (window/usercontrol) DataContext in context menu in that treeview.

    <TextBox Text="{Binding Header}"></TextBox>
    <TreeView ItemsSource="{Binding Items}" Grid.Row="1">
        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
                <Setter Property="ContextMenu">
                    <Setter.Value>
                        <ContextMenu>
                            <MenuItem Header="{{ BINDING TO HEADER PROPERTY FROM WINDOW DATACONTEXT}}"/>
                        </ContextMenu>
                    </Setter.Value>
                </Setter>
            </Style>
        </TreeView.ItemContainerStyle>
    </TreeView>

    public ObservableCollection<string> Items { get; set; }
    public string Header { get { return _header; } set { _header = value; } }

I've tried multiple things: I've added x:Name="WindowRoot" to Window and {Binding Header, ElementName=WindowRoot} but it didn't work, I've tried multiple FindAncestor and RelativeSource but it didn't work.

Can someone help me?

Edit:

This is simplified case, in my normal application I use Unity + Prism, so ViewModel is AutoDiscovered (prism:ViewModelLocator.AutoWireViewModel="True") and it generally works. By "works" I mean: TreeView shows items from my collection, so it is connected, the problem is with context menu binding only.

In this simplified example I have ugly and simple code-behind, because I only want to test this ContextMenu binding:

public partial class MainWindow : Window
{
    public ObservableCollection<string> Items { get; set; }
    private string _header = "testtest";
    public string Header { get { return _header ; } set { _header  = value; } }
    public MainWindow()
    {
        Items = new ObservableCollection<string>();
        Items.Add("ItemTest");
        InitializeComponent();
        this.DataContext = this;
    }
}
1
Try adding DataContext to your binding so DataContext.Header - Nicolas Pierre
@NicolasPierre Thanks for the idea, sadly it doesn't work too (it works finein TextBox which is directly in Window, so it is not problem with the property Header) - Andy
Is your DataContext properly bound ? - Nicolas Pierre
Should I do anything beside window.DataContext = MyViewModel? Binding in the <TextBox Text="{Binding Header}"></TextBox> above TreeView works fine. - Andy
Edit your question with how you bound the viewmodel if you would please - Nicolas Pierre

1 Answers

1
votes

You could bind the Tag property of the TreeViewItem to the parent window or user control using a {RelativeSource} and then bind the Header property of the MenuItem to the Tag property of the PlacementTarget of the ContextMenu:

<TreeView x:Name="tv" ItemsSource="{Binding Items}" Grid.Row="1">
    <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource AncestorType=Window}}" />
            <Setter Property="ContextMenu">
                <Setter.Value>
                    <ContextMenu>
                        <MenuItem Header="{Binding PlacementTarget.Tag.DataContext.Header, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
                    </ContextMenu>
                </Setter.Value>
            </Setter>
        </Style>
    </TreeView.ItemContainerStyle>
</TreeView>

The reason you cannot bind directly to any property of the window from the MenuItem is that a ContextMenu resides in a different element tree than the parent window or user control.