问题 WPF TextBox拦截RoutedUICommands


我试图在我的WPF应用程序中使用Undo / Redo键盘快捷键(我有自己的自定义功能使用 命令模式)。然而,似乎是 TextBox 控制是拦截我的“撤消”RoutedUICommand。

什么是禁用此功能的最简单方法,以便我可以在UI树的根目录中捕获Ctrl + Z?我想避免将大量代码/ XAML放入每个代码中 TextBox 如果可能,在我的申请中。

以下简要说明问题:

<Window x:Class="InputBindingSample.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:loc="clr-namespace:InputBindingSample"
    Title="Window1" Height="300" Width="300">
    <Window.CommandBindings>
        <CommandBinding Command="loc:Window1.MyUndo" Executed="MyUndo_Executed" />
    </Window.CommandBindings>
    <DockPanel LastChildFill="True">
        <StackPanel>
            <Button Content="Ctrl+Z Works If Focus Is Here" />
            <TextBox Text="Ctrl+Z Doesn't Work If Focus Is Here" />
        </StackPanel>
    </DockPanel>
</Window>

using System.Windows;
using System.Windows.Input;

namespace InputBindingSample
{
    public partial class Window1
    {
        public static readonly RoutedUICommand MyUndo = new RoutedUICommand("MyUndo", "MyUndo", typeof(Window1),
            new InputGestureCollection(new[] { new KeyGesture(Key.Z, ModifierKeys.Control) }));

        public Window1() { InitializeComponent(); }

        private void MyUndo_Executed(object sender, ExecutedRoutedEventArgs e) { MessageBox.Show("MyUndo!"); }
    }
}

1529
2017-09-03 19:36


起源



答案:


没有简单的方法来压制所有绑定,不要设置 IsUndoEnabled 至 false 因为它只会陷阱和冲洗 按Ctrl + ž 键绑定。你需要重定向 CanUndoCanRedoUndo 和 Redo。这就是我用我的方式做的 UndoServiceActions 单身。

textBox.CommandBindings.Add(new CommandBinding(ApplicationCommands.Undo,
                                               UndoCommand, CanUndoCommand));
textBox.CommandBindings.Add(new CommandBinding(ApplicationCommands.Redo,
                                               RedoCommand, CanRedoCommand));

private void CanRedoCommand(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = UndoServiceActions.obj.UndoService.CanRedo;
    e.Handled = true;
}

private void CanUndoCommand(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = UndoServiceActions.obj.UndoService.CanUndo;
    e.Handled = true;
}

private void RedoCommand(object sender, ExecutedRoutedEventArgs e)
{
    UndoServiceActions.obj.UndoService.Redo();
    e.Handled = true;
}

private void UndoCommand(object sender, ExecutedRoutedEventArgs e)
{
    UndoServiceActions.obj.UndoService.Undo();
    e.Handled = true;
}

10
2018-05-23 05:42



真的似乎是唯一的方法......我发现这个蝙蝠屎很疯狂,好像想要拥有一个跨越许多输入控件的撤销堆栈会很奇怪...... - flq
我得到了这个工作。您必须确保使用applicationcommands而不是自定义命令。您还必须将IsUndoEnabled设置为false,否则它仍将执行文本框实现的撤消。 - Asheh


答案:


没有简单的方法来压制所有绑定,不要设置 IsUndoEnabled 至 false 因为它只会陷阱和冲洗 按Ctrl + ž 键绑定。你需要重定向 CanUndoCanRedoUndo 和 Redo。这就是我用我的方式做的 UndoServiceActions 单身。

textBox.CommandBindings.Add(new CommandBinding(ApplicationCommands.Undo,
                                               UndoCommand, CanUndoCommand));
textBox.CommandBindings.Add(new CommandBinding(ApplicationCommands.Redo,
                                               RedoCommand, CanRedoCommand));

private void CanRedoCommand(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = UndoServiceActions.obj.UndoService.CanRedo;
    e.Handled = true;
}

private void CanUndoCommand(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = UndoServiceActions.obj.UndoService.CanUndo;
    e.Handled = true;
}

private void RedoCommand(object sender, ExecutedRoutedEventArgs e)
{
    UndoServiceActions.obj.UndoService.Redo();
    e.Handled = true;
}

private void UndoCommand(object sender, ExecutedRoutedEventArgs e)
{
    UndoServiceActions.obj.UndoService.Undo();
    e.Handled = true;
}

10
2018-05-23 05:42



真的似乎是唯一的方法......我发现这个蝙蝠屎很疯狂,好像想要拥有一个跨越许多输入控件的撤销堆栈会很奇怪...... - flq
我得到了这个工作。您必须确保使用applicationcommands而不是自定义命令。您还必须将IsUndoEnabled设置为false,否则它仍将执行文本框实现的撤消。 - Asheh


如果要实现自己的撤消/重做并阻止TextBox拦截,请附加到命令预览事件 CommandManager.AddPreviewCanExecuteHandler 和 CommandManager.AddPreviewExecutedHandler 并将event.Handled标志设置为true:

MySomething()
{
    CommandManager.AddPreviewCanExecuteHandler(
        this,
        new CanExecuteRoutedEventHandler(OnPreviewCanExecuteHandler));
    CommandManager.AddPreviewExecutedHandler(
        this,
        new ExecutedRoutedEventHandler(OnPreviewExecutedEvent));
}
void OnPreviewCanExecuteHandler(object sender, CanExecuteRoutedEventArgs e)
{
    if (e.Command == ApplicationCommands.Undo)
    {
        e.CanExecute = true;
        e.Handled = true;
    }
    else if (e.Command == ApplicationCommands.Redo)
    {
        e.CanExecute = true;
        e.Handled = true;
    }
}
void OnPreviewExecutedEvent(object sender, ExecutedRoutedEventArgs e)
{
    if (e.Command == ApplicationCommands.Undo)
    {
        // DO YOUR UNDO HERE
        e.Handled = true;
    }
    else if (e.Command == ApplicationCommands.Redo)
    {
        // DO YOUR REDO HERE
        e.Handled = true;
    }
}

5
2018-06-28 22:06



当有多个控件(不同类型)在起作用时,这种解决方案似乎更为可取。 - karmasponge


TextBoxBase (因此 TextBox 和 RichTextBox)有 IsUndoEnabled 财产,违约 true。如果你把它设置为 false (并且您可以像往常一样通过样式和设置器对窗口上的所有文本框执行此操作),然后它们将不会拦截Ctrl + Z.


0
2017-09-03 19:43



奇怪。将IsUndoEnabled =“False”添加到TextBox仍然不会给出预期的结果(显示MessageBox)。还有什么我想念的吗? - Joseph Sturtevant


默认情况下是目标 RoutedUICommand 是具有键盘焦点的元素。但是,你可以设置 CommandTarget 在发出命令的控件上,以便更改接收命令的根元素。

<MenuItem Command="ApplicationCommands.Open"
          CommandTarget="{Binding ElementName=UIRoot}"
          Header="_Open" />

0
2017-09-03 19:53





Executed事件冒泡,因此在TextBox Executed事件之后将始终触发Window Executed事件。尝试将其更改为PreviewExecuted,它应该会产生巨大的差异。此外,您可能还需要为窗口连接CanExecute。即:

<CommandBinding Command="Undo" PreviewExecuted="MyUndo_Executed" CanExecute="SomeOtherFunction"/>

private void SomeOtherFunction(object sender, ExecutedRoutedEventArgs e) { e.CanExecute=true; }

当然,您可能需要一些逻辑来确定CanExecute何时应设置为true。您可能也不需要使用自定义命令(只需使用内置的撤消)。


0
2017-09-03 20:13





TextBox 控制提供了一个 IsUndoEnabled 你的财产 可以设置为 false 防止撤消功能。 (如果 IsUndoEnabled 是 true按Ctrl + ž 击键触发它。)

此外,对于不提供特殊属性的控件,您可以为要禁用的命令添加新绑定。这种约束力 然后可以提供新的 CanExecute 始终响应false的事件处理程序。这是一个 使用此技术删除对文本框的剪切功能的支持的示例:

CommandBinding commandBinding = new CommandBinding(
ApplicationCommands.Cut, null, SuppressCommand);
txt.CommandBindings.Add(commandBinding);

这是设置的事件处理程序 CanExecute 州:

private void SuppressCommand(object sender, CanExecuteRoutedEventArgs e)
{
  e.CanExecute = false;
  e.Handled = true;
}

0
2017-09-03 19:44