【C#】WPF给ListBox中的每一项添加右键菜单功能

  • 前言

    • 一、ContextMenu控件

      • ContextMenu 是实现在控件上右键点击时显示一个上下文菜单。
      • ContextMenu菜单可以添加多个菜单项(MenuItem),每个菜单项可以绑定指定事件或命令。
    • 二、ContextMenu 的 DataContext 绑定问题

      • 1、在ListBox 控件中创建DataTemplate,绑定数据源为DataCollection
      • 2、在ListBox创建了TextBlock控件。
      • 3、在TextBlock里面添加ContextMenu右键菜单。
    • ContextMenu 默认不继承父控件的 DataContext

    • 如果需要显式地绑定 DataContext,需要使用 PlacementTarget 逐级向上绑定到 DataContext

      DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}"
      
    • 但是,在此案例中如果将 【MenuDownItemCommand】命令创建在 【DataListViewModel 】中,然后对菜单项进行命令绑定,是无法生效的。

      <MenuItem Name="Delete" Header="删除" Command="{Binding MenuDownItemCommand}" CommandParameter="Delete"/>
      
    • 因为ListBox绑定的是DataCollection,DataCollection集合中的对象是DataModel ,而DataModel中又没有创建MenuDownItemCommand命令。

    • 所以正确的做法是将MenuDownItemCommand 创建在DataModel这个类中。

      public class DataModel : PropertyChangedBase
      {
          public string Content { get; set; }
          public string Date { get; set; }
          public ICommand MenuDownItemCommand { get; set; }
      }
      
  • 下面是一个在WPF中使用ContextMenu右键菜单的例子

    • 该案例使用DataContext数据上下文实现前后端绑定。
      • 1、创建一个属性变更基类 PropertyChangedBase,实现属性变革事件。
      • 2、创建数据集合视图模型类,处理视图视图和模型之间的数据交互。
      • 3、创建DataModel类,作为数据模型,保存到集合中。
      • 4、创建命令,实现命令绑定,触发相关操作。

  • 三、效果预览

    • 1、界面效果

在这里插入图片描述

  • 2、左键

在这里插入图片描述

  • 3、右键

在这里插入图片描述


  • 四、前端页面代码

    <UserControl x:Class="CS学习之WPF右键菜单.DataListView"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 xmlns:local="clr-namespace:CS学习之WPF右键菜单"
                 xmlns:behavior="http://schemas.microsoft.com/xaml/behaviors"
                 mc:Ignorable="d" 
                 d:DesignHeight="450" d:DesignWidth="800">
        <UserControl.DataContext>
            <local:DataListViewModel x:Name="ViewModel"/>
        </UserControl.DataContext>
        <Grid Background="#FFFFFF">
            <ScrollViewer Background="#AEAEAE" x:Name="RecordScrollViewer" HorizontalScrollBarVisibility="Visible"
                  VerticalScrollBarVisibility="Visible">
                <ListBox Margin="5" ItemsSource="{Binding DataCollection}">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Vertical" Margin="10,5,0,0">
                                <TextBlock Name="TitleDate" FontSize="14"  Margin="10,5,0,0" Foreground="#BBBBBB" Text="{Binding Date}" >
                                   
                                </TextBlock>
                                <!-- 显示消息内容 -->
                                <TextBlock FontSize="14"  Margin="10,5,0,0" 
                                   Text="{Binding Content}" >
                                    <TextBlock.ContextMenu>
                                        <ContextMenu Name="RightKeyMenu" DataContext="{Binding PlacementTarget.DataContext,RelativeSource={RelativeSource Self}}">    
                                            <MenuItem Name="Delete" Header="删除"
                                                Command="{Binding MenuDownItemCommand}" CommandParameter="{Binding}" />
                                            <Separator/>
                                        </ContextMenu>
                                    </TextBlock.ContextMenu>
                                    <behavior:Interaction.Triggers>
                                        <!--鼠标点击命令事件-->
                                        <behavior:EventTrigger EventName="PreviewMouseDown">
                                            <behavior:InvokeCommandAction
                                             Command="{Binding DataContext.DataMouseDownCommand, 
                                                       RelativeSource={RelativeSource AncestorType=ListBox}}"
                                                       PassEventArgsToCommand="True">
                                            </behavior:InvokeCommandAction>
                                        </behavior:EventTrigger>
                                    </behavior:Interaction.Triggers>
                                </TextBlock>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
            </ScrollViewer>
        </Grid>
    </UserControl>
    
  • 1、属性变更 == PropertyChangedBase

    	///属性变更基类
    		public class PropertyChangedBase: INotifyPropertyChanged
    		{
    			public event PropertyChangedEventHandler? PropertyChanged;
    			protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    			{
    				PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    			}
    		}
    
  • 2、数据集合视图模型 == DataListViewModel

    public class DataListViewModel : PropertyChangedBase
    {
        private ObservableCollection<DataModel> _dataCollection;
    
        public ObservableCollection<DataModel> DataCollection
        {
            get => _dataCollection;
            set
            {
                if (_dataCollection != value)
                {
                    _dataCollection = value;
                    OnPropertyChanged();
                }
            }
        }
        public ICommand DataMouseDownCommand { get; set; }
    
        //无参构造
        public DataListViewModel()
        {
            Initialize();
        }
    
        /// <summary>
        /// 初始化数据
        /// </summary>
        private void Initialize()
        {
            DataMouseDownCommand = new RelayCommand(OnDataMouseDown);
            DataCollection = new ObservableCollection<DataModel>();
            for (int i = 0; i < 10; i++)
            {
                DataModel dataModel = new DataModel();
                dataModel.Date = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                dataModel.Content = $"测试数据0000{i}";
                dataModel.MenuDownItemCommand = new RelayCommand(OnMenuItemMouseDown);
                DataCollection.Add(dataModel);
            }
        }
        private void OnDataMouseDown(object args)
        {
            //业务处理
        }
        public void OnMenuItemMouseDown(object args)
        {
            //业务处理
            MessageBox.Show("右键菜单被点击了");
        }
    }
    
  • 3、数据模型类 == DataModel

    public class DataModel : PropertyChangedBase
    {
        public string Content { get; set; }
        public string Date { get; set; }
        public ICommand MenuDownItemCommand { get; set; }
    }
    
  • 4、命令 == RelayCommand

     public class RelayCommand : ICommand
     {
         private readonly Action<object> _execute;
         private readonly Predicate<object> _canExecute;
         public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
         {
             _execute = execute ?? throw new ArgumentNullException(nameof(execute));
             _canExecute = canExecute;
         }
         public bool CanExecute(object parameter)
         {
             return _canExecute == null || _canExecute(parameter);
         }
         public void Execute(object parameter)
         {
             _execute(parameter);
         }
         public event EventHandler CanExecuteChanged
         {
             add { CommandManager.RequerySuggested += value; }
             remove { CommandManager.RequerySuggested -= value; }
         }
     }
    

  • 结语

    • 可以使用ContextMenu 在控件上实现右键点击显示菜单。
    • 可以给菜单添加多个菜单项(MenuItem),每个菜单项可以绑定事件或命令。
    • ContextMenu 默认不继承父控件的 DataContext,但可以使用 PlacementTarget 逐级向上绑定到 DataContext
    • 既是分享,也是备份。

  • 最后

    • 如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!
    • 如有疑问,欢迎评论区留言。
    • 也可以关注微信公众号 [编程笔记in] ,共同学习交流!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程笔记in

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值