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));
}
}
}
}