WPF ContextMenu数据绑定问题
一、ContextMenu数据绑定问题
例如,我们将数组绑定到ListBox控件上,在其数据模板上添加ContextMenu实现每项选中删除功能。首先,声明如下所示的ViemModel:
public class MainWindowVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected void SetProperty<T>(ref T prop, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(prop, value) == false)
{
prop = value;
OnPropertyChanged(propertyName);
}
}
private bool _isCheckButton;
public bool IsCheckButton
{
get => _isCheckButton;
set => SetProperty(ref _isCheckButton, value);
}
private ObservableCollection<int> _testVMList;
public ObservableCollection<int> TestVMList
{
get => _testVMList;
set => SetProperty(ref _testVMList, value);
}
public ICommand DeleteCommand { get; set; }
private void ExecuteDeleteCommand(object args)
{
if(int.TryParse(args?.ToString(), out var index))
{
TestVMList.Remove(index);
MessageBox.Show($"{args}, 删除成功");
}
}
public MainWindowVM()
{
TestVMList = new ObservableCollection<int>()
{
1,2,3,4,5,6,7,8,9
};
DeleteCommand = new RelayCommand(ExecuteDeleteCommand);
}
}
对应的界面代码如下所示:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
d:DataContext="{d:DesignInstance local:MainWindowVM}">
<Grid>
<ListBox ItemsSource="{Binding TestVMList}" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Margin="10,3" Width="100" Background="Beige"
Text="{Binding}">
<TextBlock.ContextMenu>
<ContextMenu DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:MainWindow}, Path=DataContext}">
<MenuItem Header="删除" Command="{Binding DeleteCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.Text}"></MenuItem>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
此时,将ContextMenu的DataContext直接绑定到MainWindow的DataContext时会发生绑定报错,具体报错信息如下所示:
ContextMenu.DataContext Object 找不到源: RelativeSource FindAncestor, AncestorType='WpfApp1.MainWindow', AncestorLevel='1'
后来查阅资料发现,由于ContextMenu不属于目标可视化树,因此我需要使用例如Tag和 PlacementTarget.Tag 显式指定ContextMenu 的DataContext,具体使用如下所示:
<TextBlock Margin="10,3" Width="100" Background="Beige"
Text="{Binding}"
Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=local:MainWindow}, Path=DataContext}">
<TextBlock.ContextMenu>
<ContextMenu DataContext="{Binding RelativeSource={RelativeSource Self}, Path=PlacementTarget.Tag}">
<MenuItem Header="删除" Command="{Binding DeleteCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=PlacementTarget.Text}"></MenuItem>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
其中,ContextMenu.PlacementTarget 属性 的作用就是获取或设置UIElement,当它打开时相对于它确定 ContextMenu 的位置。