TextBox
有一个默认的上下文菜单。我想添加一个项目。好的,这意味着克隆默认的一个,并添加一个额外的项目。
我想在这里重用一些代码。我有五个文本框。每个都需要其上下文菜单上的附加项。项目需要对单击的文本框执行操作。我知道“复制和粘贴”是WPF中推荐的代码重用方法,但如果可能的话,我宁愿不在XAML中定义五个菜单,在后面的代码中定义五个命令。
在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;
}
}
更新
我正在寻找的东西原来是一个RoutedUICommand,在XAML中有一些不可思议的东西。它知道你点击了什么(由于事件冒泡导致了一些Kafkaesque异常 - 但是可以在ContextMenu上设置CommandParameter)。
答案 0 :(得分:2)
不幸的是, ContextMenuOpening 事件在这里不起作用。无论出于何种原因, TextBox 不会公开其上下文菜单,除非您使用自己的上下文菜单设置它,否则它始终为null。也许它只是在鼠标右键单击时弹出一个私人菜单。
Charles Petzold使用 RichTextBox here.( TextBox 和 RichTextBox )来自 TextBoxBase ,似乎定义了这种行为)
您似乎必须创建自己的,并复制现有项目。
有几篇文章证明了这一点,就像here一样。
希望这有帮助。
编辑:
但是,如果您坚持编辑当前菜单,则有人已经这样做here(使用扩展方法和反射)。
在对上述尝试进行进一步调查后,作者似乎正在创建一个 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;
}
截图:
答案 1 :(得分:1)
可以使用AttachedProperty和ContextMenuOpening事件的处理。看here和here。应该在xaml中占用大约100行代码和一行。
为了完整起见:
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 https://stackoverflow.com/a/210981/3411327
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>