WPF上下文菜单 [英] WPF Context Menu

查看:108
本文介绍了WPF上下文菜单的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

TextBox 具有默认的上下文菜单。我想添加一个项目。好的,这意味着克隆默认项,并在其中添加一个额外的项。

TextBox has a default context menu. I would like to add an item to it. OK, that means cloning the default one, and adding an extra item to that.

我想在这里重用一些代码。我有五个文本框。每个人都需要在其上下文菜单上的其他项目。该项目需要对单击的文本框起作用。我知道复制和粘贴是WPF中推荐的代码重用方法,但如果可能的话,我宁愿不要在XAML中定义五个菜单,在后面的代码中不要定义五个命令。

I'd like to reuse some code here. I have five textboxes. Each needs the additional item on its context menu. The item needs act on the textbox that was clicked. I know "copy and paste" is the recommended method of code reuse in WPF, but if possible I'd prefer not to define five menus in XAML and five commands in the code behind.

在WPF中是否有任何合理的干净快捷的方法?

Is there any reasonably clean and quick way to do this in WPF?

public partial class MyGhastlyView
{
    /* blah blah */

    private void MenuCut_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            (sender as MenuItem).GetPlacementTarget<TextBox>().Cut();
        }
        catch (Exception)
        {
        }
    }

    /* blah blah */
}

public static class FurshlugginerExtensions
{
    public static bool TryGetPlacementTarget<TTargetType>(this MenuItem mi, 
        out TTargetType target) where TTargetType : class
    {
        target = null;

        var cm = mi.GetContextMenu();

        if (null != cm)
        {
            target = cm.PlacementTarget as TTargetType;
        }

        return null != target;
    }

    public static TTargetType GetPlacementTarget<TTargetType>(this MenuItem mi) 
        where TTargetType : class
    {
        var cm = mi.GetContextMenu();

        return (cm == null)
                ? null
                : cm.PlacementTarget as TTargetType;
    }

    public static ContextMenu GetContextMenu(this MenuItem mi)
    {
        var logicalParent = LogicalTreeHelper.GetParent(mi);

        if (logicalParent is ContextMenu)
        {
            return logicalParent as ContextMenu;
        }
        else if (logicalParent is MenuItem)
        {
            return (logicalParent as MenuItem).GetContextMenu();
        }

        return null;
    }
}

UPDATE

我正在寻找的东西实际上是一个RoutedUICommand,在XAML中有些混乱。它知道您单击的内容(由于事件冒泡而导致一些Kafkaesque异常-但可以仅在ContextMenu上设置CommandParameter)。

What I'm looking for turns out to be a RoutedUICommand, with some futzing around in XAML. It knows what you clicked on (with some Kafkaesque exceptions due to event bubbling -- but can just set the CommandParameter on the ContextMenu).

推荐答案

不幸的是, ContextMenuOpening 事件在这里不起作用。出于任何原因, TextBox 不会公开其上下文菜单,除非您自行设置,否则始终为null。

Unfortunately, ContextMenuOpening event will not work here. For whatever reason, TextBox does not expose its context menu, and is always null unless you set it with your own. Perhaps it simply pops a private menu on right mouse click.

Charles Petzold用 RichTextBox 谈到了这一点, social.msdn.microsoft.com/Forums/vstudio/zh-CN/1c6c2f6e-d2cf-47e0-959d-31c788b553fb/-add-my-own-item-to-a-wpf-elements-context-menu rel = ( TextBox RichTextBox 均源自 TextBoxBase ,它们似乎定义了该行为)

Charles Petzold speaks about that with RichTextBox here. (Both TextBox and RichTextBox derive from TextBoxBase, which appears to define that behavior)

似乎您必须创建自己的副本,并复制现有项目。

It seems you will have to create your own, and duplicate the existing items.

几篇文章对此进行了详细说明,例如在此处

Several articles demonstrate exactly this, like the one here.

希望这会有所帮助。

编辑:

但是,如果您坚持编辑当前菜单,似乎有人这样做了此处(使用扩展方法和反射)。

However if you insist on editing the current menu, it appears someone has done so here (using an extension method and reflection).

在进一步研究了上述尝试之后,作者似乎正在创建 EditorContextMenu 的实例(从 System.Windows中的 ContextMenu 派生的私有类) .Documents )并将其分配给 TextBox ContextMenu 属性,然后将参数菜单项添加到新创建的菜单中。实际上,将覆盖当前菜单。当您确实获得了原始实现时,我不确定是否会喜欢此解决方案。

After further investigation of the above attempt, it seems that the author is creating an instance of an EditorContextMenu (private class which derives from ContextMenu in System.Windows.Documents) and assigning it to the TextBox ContextMenu property, then adding the parameter menu items to the newly created menu. In effect, overriding the current menu. While you do get the original implementation, I am not sure I would favor this solution.

编辑2:

以下代码将仅创建一个自定义菜单实例,将Ctrl-D以及相关的ContextMenu项绑定到文本框。

The following code will create only one instance of custom menu, bind Ctrl-D to the textboxes, along with the correlating ContextMenu item.

    public static RoutedCommand ItemActionCommand = new RoutedCommand();

    public MainWindow()
    {
        InitializeComponent();

        CommandBinding commandBinding = new CommandBinding(ItemActionCommand, new ExecutedRoutedEventHandler(ItemActionCommandEventHandler));
        KeyBinding keyBinding = new KeyBinding(ItemActionCommand, new KeyGesture(Key.D, ModifierKeys.Control));

        MenuItem item = new MenuItem();
        item.Click += CustomContextMenuItem_Click;  // not really necessary
        item.Header = "Custom Menu Item";
        item.InputGestureText = "Ctrl+D";
        item.Command = ItemActionCommand;

        ContextMenu menu = new ContextMenu();
        menu.Items.Add(item);

        Grid container = new Grid();
        this.Content = container;

        for (int i = 0; i < 5; i++)
            container.Children.Add(this.CreateTextBox("Value: " + i.ToString(), (i + 1) * 30.0d, menu, commandBinding, keyBinding));
    }

    private void ItemActionCommandEventHandler(object sender, ExecutedRoutedEventArgs e)
    {
        TextBox textBox = e.Source as TextBox;
        Debug.Assert(textBox != null);
        // perform actions against textbox here
    }

    private void CustomContextMenuItem_Click(object sender, RoutedEventArgs e)
    {
        MenuItem item = sender as MenuItem;
        Debug.Assert(item != null);
        TextBox textBox = ((ContextMenu)item.Parent).PlacementTarget as TextBox;
        Debug.Assert(textBox != null);
        // no need to do anything here since the command handler above will fire
        // but for the sake of completeness
    }

    private TextBox CreateTextBox(string text, double topOffset, ContextMenu menu, CommandBinding commandBinding, KeyBinding keyBinding)
    {
        TextBox textbox = new TextBox();
        textbox.HorizontalAlignment = HorizontalAlignment.Center;
        textbox.VerticalAlignment = VerticalAlignment.Top;
        textbox.Margin = new Thickness(0.0d, topOffset, 0.0d, 0.0d);
        textbox.CommandBindings.Add(commandBinding);
        textbox.InputBindings.Add(keyBinding);
        textbox.ContextMenu = menu;
        textbox.Width = 150.0d;
        textbox.Height = 25.0d;
        textbox.Text = text;
        return textbox;
    }

屏幕截图:

这篇关于WPF上下文菜单的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆