2009-09-03 12 views
11

Próbuję uzyskać skróty klawiaturowe Undo/Redo działające w mojej aplikacji WPF (mam własną niestandardową funkcjonalność zaimplementowaną przy użyciu Command Pattern). Wydaje się jednak, że kontrolka TextBox przechwytuje moją "Cofnij" RoutedUICommand.WPF TextBox Przechwytywanie RoutedUICommands

Jaki jest najprostszy sposób, aby wyłączyć tę opcję, aby można było przechwycić Ctrl + Z w katalogu głównym mojego drzewa interfejsu użytkownika? Chciałbym uniknąć umieszczenia ton kodu/XAML w każdym TextBox w mojej aplikacji, jeśli to możliwe.

Poniżej krótko przedstawia problem:

<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!"); } 
    } 
} 

Odpowiedz

10

Nie ma prosty sposób, aby wyciszyć wszystkie powiązania, nie należy ustawiać IsUndoEnabled do false jak tylko będzie to pułapka i równo wiążące Ctrl + Z klucz. Musisz przekierować CanUndo, CanRedo, i Redo.Oto, jak to zrobić z moim singletonem 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; 
} 
+2

Naprawdę wydaje się, że jest to jedyny sposób ... Uważam, że ten nietoperz jest szalony, tak jakby to było dziwne, że chce mieć stos cofania, który obejmuje wiele kontrolek wejściowych ... – flq

+0

Mam to do pracy. Musisz upewnić się, że używasz poleceń aplikacji, a nie niestandardowych. Musisz także ustawić wartość IsUndoEnabled na false lub nadal będzie ona wykonywać operację cofania, którą implementuje pole tekstowe. – Asheh

0

TextBoxBase (a więc TextBox i RichTextBox) mają IsUndoEnabled właściwości domyślnie na true. Jeśli ustawisz ją na false (i możesz zrobić zdarzenie dla wszystkich pól tekstowych w twoim oknie za pomocą stylu i setera, jak zwykle), to nie przechwycą one Ctrl + Z.

+0

Dziwne. Dodawanie IsUndoEnabled = "False" do TextBox nadal nie daje oczekiwanego rezultatu (pokazuje MessageBox). Czy jest coś jeszcze, czego mi brakuje? –

0

Kontrola TextBox stanowi własność IsUndoEnabled że można ustawić na false aby zapobiec funkcji Cofnij. (Jeśli IsUndoEnabled jest true The Ctrl + Z klawiszy uruchamia go.)

także do kontroli, które nie przewiduje specjalną właściwość można dodać nowy wiążący dla polecenia, które chcesz wyłączyć. To wiążące może następnie dostarczyć nowy moduł obsługi zdarzenia CanExecute, który zawsze odpowiada błędem. Oto przykład że używa tej techniki, aby usunąć obsługę funkcji Cut pola tekstowego:

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

i oto obsługi zdarzeń, który ustawia stan CanExecute:

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

Domyślnie cel RoutedUICommand to element z ostrością klawiatury. Jednak można ustawić CommandTarget na formancie wysyłającym polecenie, aby zmienić element główny, który otrzymuje polecenie.

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

Wykonywane zdarzenie jest bąbelkowane, więc zdarzenie Uruchomione okno zawsze będzie trafione po zdarzeniu TextBox Executed. Spróbuj zmienić go na PreviewExecuted i powinna to zrobić ogromną różnicę. Być może będziesz musiał podłączyć CanExecute również do swojego okna. tj:

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

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

Oczywiście prawdopodobnie będziesz potrzebował pewnej logiki, aby określić, kiedy CanExecute powinno być ustawione na true. Prawdopodobnie nie musisz też używać niestandardowego polecenia (wystarczy użyć wbudowanego Undo).

5

Jeśli chcesz zaimplementować własną cofania/powtarzania i zapobiec przechwytywaniu TextBox, przywiązują do wydarzeń podglądu polecenia przez CommandManager.AddPreviewCanExecuteHandler i CommandManager.AddPreviewExecutedHandler i ustawić flagę 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; 
    } 
} 
+0

To rozwiązanie wydaje się lepsze, gdy w grze jest wiele elementów sterujących (różnych typów). – karmasponge