-
前言
-
一、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、创建命令,实现命令绑定,触发相关操作。
- 该案例使用DataContext数据上下文实现前后端绑定。
-
四、前端页面代码
<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。
- 既是分享,也是备份。