Loading

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 的位置。

posted @ 2022-12-21 10:26  Dwaynerbing  阅读(741)  评论(0编辑  收藏  举报