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

前言

一、ContextMenu控件

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

二、ContextMenu 的 DataContext 绑定问题

案例:

1、在ListBox 控件中创建DataTemplate,绑定数据源为DataCollection 。

2、在ListBox 创建了TextBlock 控件,

3、在TextBlock 里面添加ContextMenu右键菜单。

ContextMenu 默认不继承父控件的 DataContext ,如果需要显式地绑定 DataContext ,可以使用 PlacementTarget 逐级向上绑定到 DataContext。
csharp 复制代码
DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}"
但是,在此案例中如果将 【MenuDownItemCommand 】命令创建在 【DataListViewModel 】中,然后对菜单项进行命令绑定,是无法生效的。
csharp 复制代码
<MenuItem Name="Delete" Header="删除" Command="{Binding MenuDownItemCommand}" CommandParameter="Delete"/>
因为ListBox绑定的是DataCollection,DataCollection集合中的对象是DataModel ,而DataModel中又没有创建MenuDownItemCommand命令。所以正确的做法是将MenuDownItemCommand 创建在DataModel这个类中。
csharp 复制代码
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、右键

四、前端页面代码

csharp 复制代码
<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

csharp 复制代码
///属性变更基类
public class PropertyChangedBase: INotifyPropertyChanged
{
	public event PropertyChangedEventHandler? PropertyChanged;
	protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
	{
		PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
	}
}

2、数据集合视图模型 == DataListViewModel

csharp 复制代码
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

csharp 复制代码
public class DataModel : PropertyChangedBase
{
    public string Content { get; set; }
    public string Date { get; set; }
    public ICommand MenuDownItemCommand { get; set; }
}

4、命令 == RelayCommand

csharp 复制代码
 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。

相关推荐
泛凡(Linyongui)9 小时前
基于 CW32L010 的智能厨卫调温旋钮方案--低成本 EC21 编码旋钮全解析
单片机·嵌入式硬件·c#
暴走约伯10 小时前
【VFX工具链源码拆解 02】告别机械劳动(上):一键统改百个子粒子颜色与批量重命名
3d·unity·c#·材质
信息安全失业大专人员12 小时前
每日了解一个智能体-WorkBuddy
开发语言·ai·c#·智能体
呆呆敲代码的小Y12 小时前
【实战精选面试题】C# 面试题详细解答,按难易程度排序
android·java·游戏·c#·游戏开发·unity面试题·c#面试题
淡海水13 小时前
09-01-内存-Unity资源生命周期全景
unity·c#·游戏引擎·.netcore·gc
椒颜皮皮虾྅16 小时前
让 C# 读懂整张图:DeploySharp 驱动 PaddleOCR v4/v5/v6,打通 OCR 全流程
开发语言·人工智能·深度学习·学习·c#·ocr·cuda
昂好的day16 小时前
C# 同步异步与多线程:Thread / Task / 线程池一次讲透(上篇)
开发语言·windows·c#
avi911117 小时前
电影开源Jevdo - 源码介绍(vue版)没有android 和 没有wpf
android·wpf·框架·数据爬虫·meego·电影管理
吃一碗粉只付一碗钱1 天前
12-给SPA友好的401和403响应
c#·状态模式
caimouse1 天前
MonoDevelop 编译用户应用程序时调用的是哪个编译器
c#