![](/img/trans.png)
[英]How to define WPF ContextMenu with a Separator before the last element, using binding on ItemsSource instead of individual MenuItems
[英]How to enable menuitems on a ContextMenu - WPF
我正在嘗試在 UserControl 派生的 object 上創建 ContextMenu。 注意:這不是 WinForms 應用程序,它是純 WPF。
所以我創建了 ContextMenu :
<UserControl.Resources>
<ContextMenu x:Key="cmLCD_CopyCutPaste">
<MenuItem Name="CutOption" Header="{x:Static p:Resources.Popup_Cut}" Command="Cut" Click="MenuItem_Cut" IsEnabled="True"/>
<MenuItem Name="CopyOption" Header="{x:Static p:Resources.Popup_Copy}" Command="Copy" Click="MenuItem_Copy" IsEnabled="True"/>
<MenuItem Name="PasteOption" Header="{x:Static p:Resources.Popup_Paste}" Command="Paste" Click="MenuItem_Paste" IsEnabled="True"/>
</ContextMenu>
</UserControl.Resources>
鼠標按鈕事件是這樣設置的:
d:DesignHeight="66" d:DesignWidth="340" Focusable="True" KeyDown="Grid_KeyDown" MouseRightButtonDown="EMS_UI_LCDscreen_MouseRightButtonDown" >
在后面的代碼中,構造函數是這樣開始的:
public EMS_UI_LCDscreen()
{
InitializeComponent();
// Find the ContextMenu created on this object - it is called cmLCD_CopyCutPaste
ContextMenu cm = FindResource("cmLCD_CopyCutPaste") as ContextMenu;
// If we found the contextMenu, assign it the ContextMenu placeholder for this instance.
if (cm != null)
{
ContextMenu = cm;
}
...
鼠標單擊(右鍵)的處理方式如下:
private void EMS_UI_LCDscreen_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
if (this.ContextMenu is ContextMenu cm)
{
if (cm != null)
{
if (DataContext is DeviceEditorData ded)
{
cm.DataContext = ded.Device.GetStructuredLocation(0);
cm.IsEnabled = true;
cm.PlacementTarget = sender as Button;
cm.IsOpen = true;
}
}
}
}
有三個事件處理程序來實際響應 ContextMenu 上的用戶選擇:
private void MenuItem_Cut(object sender, RoutedEventArgs e)
{
}
private void MenuItem_Copy(object sender, RoutedEventArgs e)
{
}
private void MenuItem_Paste(object sender, RoutedEventArgs e)
{
}
我創建的組件出現在幾個不同的上下文中,但我主要感興趣的是一個顯示各種數據項的 DataGrid 內部,每個數據項都有這些組件之一。 右鍵單擊時,組件如預期一樣忠實地顯示彈出上下文菜單......但是菜單上的所有項目都是灰色的,基本上沒有啟用。 所以我的問題是,有效地啟用菜單項以便可以單擊它們以執行所需操作的缺少的膠水是什么。 大多數已經在網上看到的答案 go 詳細介紹了如何在 WinForms 應用程序中執行此操作,但盡管搜索了數小時,但我找不到明確的解決方案來解決啟用菜單項的非常簡單的任務。 哪位好心人可以讓我擺脫痛苦,並在幾行代碼中告訴我如何做到這一點! 謝謝
更新:這是 LCD 組件的 XAML 實現:
<UserControl x:Class="EMS_Config_Tool.UIComponents.WPF.EMS_UI_LCDscreen"
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:p="clr-namespace:EMS_Config_Tool.Properties"
mc:Ignorable="d"
d:DesignHeight="66" d:DesignWidth="340" Focusable="True" KeyDown="Grid_KeyDown" MouseRightButtonDown="EMS_UI_LCDscreen_MouseRightButtonDown" >
<UserControl.Resources>
<ContextMenu x:Key="cmLCD_CopyCutPaste">
<MenuItem Name="CutOption" Header="{x:Static p:Resources.Popup_Cut}" Command="{Binding Cut}"/>
<MenuItem Name="CopyOption" Header="{x:Static p:Resources.Popup_Copy}" Command="{Binding Copy}"/>
<MenuItem Name="PasteOption" Header="{x:Static p:Resources.Popup_Paste}" Command="{Binding Paste}"/>
</ContextMenu>
</UserControl.Resources>
<UserControl.CommandBindings>
<CommandBinding Command="Cut"
CanExecute="CutCommand_CanExecute"
Executed="CutCommand_Executed" />
<CommandBinding Command="Copy"
CanExecute="CopyCommand_CanExecute"
Executed="CopyCommand_Executed" />
<CommandBinding Command="Paste"
CanExecute="PasteCommand_CanExecute"
Executed="PasteCommand_Executed" />
</UserControl.CommandBindings>
</UserControl>
可以看出,它非常簡單 - 控件中沒有添加任何項目 - 它僅用作 Canvas,其背后的代碼在其上繪制所有需要的項目,這些項目是純圖形“繪制”項目。
這是上述 xaml 對應的 .cs 文件中引用的 6 種方法的實現:
private void CutCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
// Verify relevant conditions and set CanExecuteRoutedEventArgs.CanExecute accordingly
e.CanExecute = true;
}
private void CutCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
// Execute the command action
if (sender is EMS_UI_LCDscreen lcd)
{
if (TheDevice != null)
{
Clipboard.SetDataObject(TheDevice.GetStructuredLocation(0).ToString());
TheDevice.SetLocation(0, "");
lcd.StructuredTextToShow = TheDevice.GetStructuredLocation(0);
}
else
{
Clipboard.SetDataObject(lcd.StructuredTextToShow.ToString());
lcd.StructuredTextToShow.SetString("");
}
}
}
private void CopyCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
// Verify relevant conditions and set CanExecuteRoutedEventArgs.CanExecute accordingly
e.CanExecute = true;
}
private void CopyCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
// Execute the command action
if (sender is EMS_UI_LCDscreen lcd)
{
if (TheDevice!=null)
Clipboard.SetDataObject(TheDevice.GetStructuredLocation(0).ToString());
else
Clipboard.SetDataObject(lcd.StructuredTextToShow.ToString());
}
}
private void PasteCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
// Verify relevant conditions and set CanExecuteRoutedEventArgs.CanExecute accordingly
IDataObject iData = Clipboard.GetDataObject();
// Is the Data Text?
if (iData.GetDataPresent(DataFormats.Text))
e.CanExecute = true;
else
e.CanExecute = false;
}
private void PasteCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
// Execute the command action
if (sender is EMS_UI_LCDscreen lcd)
{
// Retrieves data
IDataObject iData = Clipboard.GetDataObject();
// Is the Data Text?
if (iData.GetDataPresent(DataFormats.Text))
{
if (TheDevice != null)
{
TheDevice.SetLocation(0, (string)iData.GetData(DataFormats.Text));
lcd.StructuredTextToShow = TheDevice.GetStructuredLocation(0);
}
else
{
lcd.StructuredTextToShow.SetString((string)iData.GetData(DataFormats.Text));
}
}
}
}
最后,在構造函數中將 ContextMenu 分配給 object:
public EMS_UI_LCDscreen()
{
InitializeComponent();
// Find the ContextMenu created on this object - it is called cmLCD_CopyCutPaste
ContextMenu cm = FindResource("cmLCD_CopyCutPaste") as ContextMenu;
// If we found the contextMenu, assign it the ContextMenu placeholder for this instance.
if (cm != null)
{
ContextMenu = cm;
}
...
菜單顯示在組件上以響應右鍵單擊,剪切/復制/粘貼方法響應關鍵操作,但單擊菜單項不會觸發相應的方法。 我不明白為什么會這樣,這一切對我來說都是正確的,但也許缺少一些阻止它工作的東西。 一個建議是使用“中繼命令”類型的東西,但它可能是什么以及如何是不清楚的。
這是最終對我有用的解決方案。 elixir 正在分配命令,而不是使用“綁定”術語,而是直接:
<UserControl x:Class="EMS_Config_Tool.UIComponents.WPF.EMS_UI_LCDscreen"
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:p="clr-namespace:EMS_Config_Tool.Properties"
mc:Ignorable="d"
d:DesignHeight="66" d:DesignWidth="340" Focusable="True" KeyDown="Grid_KeyDown" MouseRightButtonDown="EMS_UI_LCDscreen_MouseRightButtonDown" >
<UserControl.Resources>
<Style TargetType="MenuItem">
<Setter Property="CommandTarget"
Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Path=PlacementTarget}" />
</Style>
</UserControl.Resources>
<UserControl.ContextMenu>
<ContextMenu>
<MenuItem Name="CutOption" Header="{x:Static p:Resources.Popup_Cut}" Command="Cut" />
<MenuItem Name="CopyOption" Header="{x:Static p:Resources.Popup_Copy}" Command="Copy" />
<MenuItem Name="PasteOption" Header="{x:Static p:Resources.Popup_Paste}" Command="Paste" />
</ContextMenu>
</UserControl.ContextMenu>
<UserControl.CommandBindings>
<CommandBinding Command="Cut"
CanExecute="CutCommand_CanExecute"
Executed="CutCommand_Executed" />
<CommandBinding Command="Copy"
CanExecute="CopyCommand_CanExecute"
Executed="CopyCommand_Executed" />
<CommandBinding Command="Paste"
CanExecute="PasteCommand_CanExecute"
Executed="PasteCommand_Executed" />
</UserControl.CommandBindings>
</UserControl>
圖像的構造函數和賦值如下所示:
public EMS_UI_LCDscreen()
{
InitializeComponent();
Image ObjImage1 = new Image();
Image ObjImage2 = new Image();
Image ObjImage3 = new Image();
ObjImage1.Source = new BitmapImage(new Uri(@"pack://application:,,,/Graphics\Misc Icons\Cut.png"));
CutOption.Icon = ObjImage1;
ObjImage2.Source = new BitmapImage(new Uri(@"pack://application:,,,/Graphics\Misc Icons\Copy.png"));
CopyOption.Icon = ObjImage2;
ObjImage3.Source = new BitmapImage(new Uri(@"pack://application:,,,/Graphics\Misc Icons\Paste.png"));
PasteOption.Icon = ObjImage3;
...
...並在組件上顯示上下文菜單以響應鼠標右鍵單擊:
private void EMS_UI_LCDscreen_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
if (ContextMenu is ContextMenu cm)
{
if (DataContext is DeviceEditorData ded)
{
cm.DataContext = ded.Device.GetStructuredLocation((uint)LocationIndex);
cm.IsEnabled = true;
cm.PlacementTarget = sender as EMS_UI_LCDscreen;
cm.IsOpen = true;
}
if (DataContext is EMSBasicDevice dev)
{
cm.DataContext = dev.GetStructuredLocation((uint)LocationIndex);
cm.IsEnabled = true;
cm.PlacementTarget = sender as EMS_UI_LCDscreen;
cm.IsOpen = true;
}
}
}
為簡潔起見,我沒有展示..._CanExecute /...Executed 方法,它們如前面問題中所示。
感謝所有幫助解決此問題的人。
因此,如果您使用DataContext
和Binding
的,您可以執行以下操作
Click
和Enabled
的直接設置。Binding
將菜單操作綁定到視圖 model: Command={Binding CutCommand}
(或任何其他處理程序)內的處理程序。ICommand.CanExecute
(上面示例中的CutCommand
)。 管理菜單項的Enabled
state。所以,讓我們把所有東西放在一起。
在 XAML
<MenuItem Name="CutOption" Header="{x:Static p:Resources.Popup_Cut}"
Command="{Binding CutCommand}"/>
在視圖模型中
class ViewModel: INotifyPropertyChanged
{
...
public ICommand CutCommand { get; }
public ViewModel()
{
//you can just omit second arg if you always can call Cut.
CutCommand = new RelayCommand(CutHandler, ()=>CanCut()));
}
...
}
這里RelayCommand
是https://docs.microsoft.com/en-us/windows/communitytoolkit/mvvm/relaycommand或任何其他簡單命令的實現。
如果您需要即時更新已Enabled
的 state,您可以調用CommandManager.InvalidateRequerySuggested();
重新評估所有命令的CanExecute
。
您應該刪除所有將IsEnabled
設置為true
的代碼。 這是多余的,因為默認情況下該值為true
。
這些按鈕被禁用是因為您(不小心?)將命令附加到MenuItem.Command
屬性,但沒有相應的命令處理程序。
框架將嘗試調用CanExecute
處理程序。 由於沒有定義,因此返回一個默認處理程序,將CanExecuteRoutedEventArgs.CanExecute
設置為false
,這將禁用按鈕 ( ICommandSource
)。
不確定您是否打算在這里使用命令,或者您錯誤地猜測Command
屬性就像一個名稱屬性(因為您還注冊了Click
事件處理程序)。 請參閱指揮概述了解更多信息。
無論如何,這是注冊命令處理程序的方法:
您已分配預定義的應用程序命令(剪切、復制和粘貼 - XAML 中的MenuItem.Command
字符串值隱式引用 static ApplicationCommands
命令)。 這些命令是路由命令(行為與路由事件相同 - 實際上路由命令是路由事件)。 因此,您必須在命令源的父元素(調用命令的元素)上定義命令綁定,因為命令會在樹中冒泡。
UIElement.CommandBinding
由指定的CommandBinding.Executed
處理程序和可選的CommandBinding.CanExecute
處理程序組成。
使用CanExecute
處理程序來控制命令源的禁用狀態,例如Button
。 如果應該始終啟用命令源,只需省略CanExecute
處理程序。
XAML
<Window>
<Window.CommandBindings>
<CommandBinding Command="Cut"
CanExecute="CutCommand_CanExecute"
Executed="CutCommand_Executed" />
</Window.CommandBindings>
</Window>
C#
partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var cutCommandBinding =
new CommandBinding(ApplicationCommands.Cut, CutCommand_Executed, CutCommand_CanExecute)
this.CommandBindings.Add(cutCommandBinding);
}
}
然后在代碼隱藏中創建相應的命令處理程序。
private void CutCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
// Verify relevant conditions and set CanExecuteRoutedEventArgs.CanExecute accordingly
e.CanExecute = true;
}
private void CutCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
// Execute the command action
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.