簡體   English   中英

如何在 ContextMenu 上啟用菜單項 - WPF

[英]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)
        {
        }

LCD 圖像組件上顯示的 ContextMenu 視圖

我創建的組件出現在幾個不同的上下文中,但我主要感興趣的是一個顯示各種數據項的 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 方法,它們如前面問題中所示。

感謝所有幫助解決此問題的人。

原位完成的上下文菜單的視圖

因此,如果您使用DataContextBinding的,您可以執行以下操作

  • 刪除帶有事件處理程序的直接控制,即ClickEnabled的直接設置。
  • 使用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())); 
    }
    ...
}

這里RelayCommandhttps://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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM