I have a tree view nicely connected to my model with a MVVM pattern. I'm running into an issue with the context menu. I do not know how to bind to an element outside the tree. I have created a sample that demonstrates what I'm trying. I hope someone explain to me what I'm doing wrong.
In the sample I have five TextBlocks in the header, each binding differently to the data. And I have a context menu with five entries, again binding differently. The commentary says which binding works and which doesn't. There's even a difference between the header and the context menu.
Some bindings try to bind to another element in the UI another to a property in the view model.
Here's the XAML:
<Window x:Class="WpfApp7_TreeView.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:WpfApp7_TreeView" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800" x:Name="root"> <Window.DataContext> <local:MainWindowViewModel/> </Window.DataContext> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <TextBlock Grid.Row="0" Text="{Binding SomeProperty}" /> <!-- ok --> <TextBlock Grid.Row="1" Text="Text from XAML" x:Name="tb" /> <!-- ok --> <TreeView Grid.Row="2" HorizontalAlignment="Stretch" ItemsSource="{Binding Departments}"> <TreeView.ItemTemplate> <HierarchicalDataTemplate DataType="{x:Type local:Department}"> <StackPanel Orientation="Horizontal"> <TextBlock Margin="5" Text="{Binding DepartmentName }" /> <!-- ok --> <TextBlock Margin="5" Text="{Binding ElementName=tb, Path=Text}"/> <!-- ok --> <TextBlock Margin="5" Text="{Binding SomeProperty}"/> <!-- no show --> <TextBlock Margin="5" Text="{Binding ElementName=root, Path=DataContext.SomeProperty}"/> <!-- ok --> <TextBlock Margin="5" Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=DataContext.SomeProperty}"/> <!-- ok --> <StackPanel.ContextMenu> <ContextMenu> <MenuItem Header="Some command" /> <!-- ok --> <MenuItem Header="{Binding ElementName=tb, Path=Text}" /> <!-- no show --> <MenuItem Header="{Binding SomeProperty}" /> <!-- no show --> <MenuItem Header="{Binding ElementName=root, Path=DataContext.SomeProperty}"/> <!-- no show --> <MenuItem Header="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=DataContext.SomeProperty}" /> <!-- no show --> </ContextMenu> </StackPanel.ContextMenu> </StackPanel> </HierarchicalDataTemplate> </TreeView.ItemTemplate> </TreeView> </Grid> And here's the view model:
using System; using System.Collections.Generic; using System.ComponentModel; namespace WpfApp7_TreeView { public class MainWindowViewModel : ViewModelBase { private string someProperty = "Text from the viewmodel"; public string SomeProperty { get { return someProperty; } set { someProperty = value; OnPropertyChanged("SomeProperty"); } } public MainWindowViewModel() { Departments = new List<Department>() { new Department("Department 1"), new Department("Department 2") }; } private List<Department> departments; public List<Department> Departments { get {return departments; } set { departments = value; OnPropertyChanged("Departments"); } } } public class Department : ViewModelBase { public Department(string depname) { DepartmentName = depname; } public string DepartmentName { get; set; } } public class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propname) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propname)); } } } }
ContextMenubecause it resides in its own element tree. What property do you want to bind to?