WPF context menu

TextBoxhas a default context menu. I would like to add an element to it. OK, this means cloning by default and adding an extra element to it. Perhaps even with the radical, cosmic features of the distant future, such as keyboard shortcuts, but not greed. The morons who developed the framework saw everything the programmer might need and took steps to prevent this.

Now I would like to reuse the code here. I have five text fields. Each of them needs an additional element in its context menu. The item must act in the text box that was clicked. I know that copy and paste is the recommended method for reusing code in WPF, but if possible, I would prefer not to define five menus in XAML and five commands in code.

Is there a way to do this in WPF without spending a day and a half on some ridiculous monstrosity of the Rube Goldberg WTF, which takes four hours to research, eight hours to implement and includes 300 lines of code and 400 lines of XAML?

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, futzing XAML. , ( Kafkaesque - , CommandParameter ContextMenu).

+1
2

AttachedProperty ContextMenuOpening. . 100 xaml.

:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace WpfApplication1
{
    public class CustomMenuAction
    {
        public static bool GetHasMenuItemAction(DependencyObject obj)
        {
            return (bool)obj.GetValue(HasMenuItemActionProperty);
        }

        public static void SetHasMenuItemAction(DependencyObject obj, bool value)
        {
            obj.SetValue(HasMenuItemActionProperty, value);
        }

        // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty HasMenuItemActionProperty =
            DependencyProperty.RegisterAttached("HasMenuItemAction", typeof(bool), typeof(CustomMenuAction), new PropertyMetadata(default(bool),OnPropertyChanged));

        private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
           if((bool)e.NewValue)
           {
               var textbox = d as TextBox;
               if(textbox != null)
               {
                   textbox.ContextMenu = GetCustomContextMenu();
                   textbox.ContextMenuOpening += textbox_ContextMenuOpening;
               }
           }
        }

        private static ContextMenu GetCustomContextMenu()
        {
            var contextMenu = new ContextMenu();
            var standardCommands = GetStandardCommands();
            foreach (var item in standardCommands)
            {
                contextMenu.Items.Add(item);
            }
            return contextMenu;
        }

        private static IList<MenuItem> GetStandardCommands()
        {
            //From /questions/633841/add-menu-item-to-default-context-menu/2496839#2496839
            List<MenuItem> standardCommands = new List<MenuItem>();
            MenuItem item = new MenuItem();
            item.Command = ApplicationCommands.Cut;
            standardCommands.Add(item);
            item = new MenuItem();
            item.Command = ApplicationCommands.Copy;
            standardCommands.Add(item);
            item = new MenuItem();
            item.Command = ApplicationCommands.Paste;
            standardCommands.Add(item);
            return standardCommands;
        }


        static void textbox_ContextMenuOpening(object sender, ContextMenuEventArgs e)
        {
            //From MSDN example: http://msdn.microsoft.com/en-us/library/bb613568.aspx
            var textbox = e.Source as TextBox;
            ContextMenu cm = textbox.ContextMenu;
            foreach (MenuItem mi in cm.Items)
            {
                if ((String)mi.Header == "Item4") return;
            }
            MenuItem mi4 = new MenuItem();
            mi4.Header = "Item4";
            mi4.Click += (o, args) =>
                {
                    var menuItem = o as MenuItem;
                    MessageBox.Show(menuItem.Header.ToString(), textbox.Text);
                };
            textbox.ContextMenu.Items.Add(mi4);
        }   
    }
}

<TextBox namespace:CustomMenuAction.HasMenuItemAction="True"></TextBox>
+1

, ContextMenuOpening . - TextBox null, . , .

RichTextBox . ( TextBox RichTextBox TextBoxBase, , , )

, .

, , .

, .


EDIT:

, , , - ( ).

, EditorContextMenu ( , ContextMenu . Windows.Documents) TextBox ContextMenu, . , . , , .


2:

, Ctrl-D ContextMenu.

    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;
    }

:

ctx menu

+1

Source: https://habr.com/ru/post/1625986/


All Articles