如何设置TreeView样式中定义的ContextMenu的DataContext



我有一个TreeView,它在Style中定义了ContextMenu。MenuItems有绑定到它们的命令,但我在绑定这些命令时遇到了问题。我意识到这是因为ContextMenu不存在于可视化树中,所以我尝试使用PlacementTarget对象和Tag属性,我在其他示例中看到过它,但它仍然不起作用。你知道我做错了什么吗。

<Grid Name="MyGrid" DataContext="{Binding}">
<TreeView Name="TreeView"
ItemsSource="{Binding TreeViewElements}"
Tag="{Binding DataContext, ElementName=MyGrid}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding PlacementTarget.Tag, RelativeSource={x:Static RelativeSource.Self}}">
<MenuItem Header="Do Something" 
Command="{Binding DoSomethingCommand}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding SubElements}">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="2" Text="{Binding HeaderText}" ></TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>

更新

下面的答案都是完全有效和正确的,但经过一番思考,我意识到我对这个问题的看法是错误的。我看过Josh Smith的这篇文章,他在文章中解释说,使用ViewModel模式意味着使用TreeView来显示数据,而不是放置数据

ContextMenu没有DataContext。您不需要显式设置它。

在您的情况下,ContextMenu中的DataContext与TreeViewItem相同。这意味着它不是ViewModel,而是项本身。

假设您有一个Person类型的对象列表。然后命令应该放在Person中。

public class Person : INotifyPropertyChanged
{
private string name;
public string Name
{
get { return name; }
set { name = value; this.OnPropertyChanged("Name"); }
}
public ICommand DoSomethingCommand
{
get
{
return new RelayCommand(x => MessageBox.Show("Works!"));
}
}
...

然后XAML应该是这样的:

<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="Do Something" 
Command="{Binding DoSomethingCommand}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>

但是,如果在ViewModel中有该命令,则需要将ViewModel的实例提供给ContextMenu的DataContext。

这里有一个例子:

class MainWindowViewModel : INotifyPropertyChanged
{
private ObservableCollection<Person> employee;
public MainWindowViewModel()
{
this.Employee = new ObservableCollection<Person>();
this.Employee.Add(new Person { Name = "Test" });
}
public ObservableCollection<Person> Employee
{
get { return this.employee; }
set { this.employee = value; this.OnPropertyChanged("Employee"); }
}
public ICommand DoSomethingCommand
{
get
{
return new RelayCommand(x => MessageBox.Show("Works!"));
}
}
...

然后XAML看起来是这样的:

<TreeView x:Name="treeView" ItemsSource="{Binding Employee}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource AncestorType=TreeView}, Path=DataContext}"></Setter>
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Do Something" 
Command="{Binding DoSomethingCommand}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>

或者XAML可能看起来像这样:

<TreeView x:Name="treeView" ItemsSource="{Binding Employee}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu DataContext="{x:Reference treeView}">
<MenuItem Header="Do Something" 
Command="{Binding DataContext.DoSomethingCommand}"/>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>

最新更新