2009-09-03 3 views
11

Я пытаюсь получить быстрые клавиши Undo/Redo, работающие в моем приложении WPF (у меня есть собственные пользовательские функции, реализованные с использованием Command Pattern). Кажется, однако, что управление TextBox перехватывает мою команду «Отменить» RoutedUICommand.WPF TextBox Перехват RoutedUICommands

Каков самый простой способ отключить это, чтобы я мог поймать 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!"); } 
    } 
} 

ответ

10

Там нет простого способа подавить все привязки, не устанавливайте IsUndoEnabled в false, как это только ловушка и флеш Ctrl +Z ключ привязки. Вам необходимо перенаправить CanUndo, CanRedo, Undo и Redo.Вот как я делаю это с моим UndoServiceActions singleton.

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

На самом деле это единственный способ ... Я нахожу это сумасшедшим, безумным, как если бы было странно хотеть иметь стопку отмены, которая охватывает множество элементов управления вводами ... – flq

+0

Я получил это, чтобы работать. Вы должны убедиться, что используете команды приложений, а не пользовательские. Вы также должны установить значение IsUndoEnabled в значение false, или оно все равно выполнит отмену, которую выполняет текстовое поле. – Asheh

0

TextBoxBase (и, таким образом TextBox и RichTextBox) имеют IsUndoEnabled свойство, по умолчанию true. Если вы установите его на false (и вы можете сделать это для всех текстовых полей в вашем окне с помощью стиля и сеттера, как обычно), то они не будут перехватывать Ctrl + Z.

+0

Странный. Добавление IsUndoEnabled = «False» в TextBox по-прежнему не дает ожидаемого результата (показывая MessageBox). Есть что-то еще, что мне не хватает? –

0

управления TextBox обеспечивает IsUndoEnabled свойство, которое вы можете установить в false, чтобы предотвратить возможность отмены. (Если IsUndoEnabled является true, то Ctrl + Z нажатие клавиши запускает его.)

Кроме того, для контроля, которая не обеспечивает специальное свойство, которое вы можете добавить новую привязку для команды, которую вы хотите отключить. Это связывание может затем предоставить новый обработчик события CanExecute, который всегда отвечает ложным. Вот пример, который использует эту технику, чтобы убрать поддержку Cut функции текстового поля:

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

По умолчанию мишени RoutedUICommand - это элемент с фокусом клавиатуры. Однако вы можете установить CommandTarget на элемент управления, испускающий команду, чтобы изменить корневой элемент, который получает эту команду.

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

Выполненная событие всплывает вверх, так что окно Выполненной событие всегда будет хит после TextBox Выполненной событии. Попробуйте изменить его на PreviewExecuted, и это должно иметь огромное значение. Кроме того, вам может понадобиться подключить CanExecute для вашего окна. то есть:

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

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

Конечно, вам, вероятно, понадобится какая-то логика, чтобы определить, когда CanExecute должно быть установлено в true. Вам, вероятно, не нужно использовать пользовательскую команду (просто используйте встроенное Undo).

5

Если вы хотите реализовать свой собственный Undo/Redo и предотвратить TextBox от перехвата, прикрепляются к команде предварительного просмотра событий через CommandManager.AddPreviewCanExecuteHandler и CommandManager.AddPreviewExecutedHandler и установить флаг event.Handled к истинным:

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

Это решение кажется предпочтительным, когда в игре есть несколько элементов управления (разных типов). – karmasponge