2009-09-03 21 views
11

Sto cercando di ottenere le scorciatoie da tastiera Annulla/Ripristina che funzionano nella mia applicazione WPF (ho implementato la mia funzionalità personalizzata usando lo Command Pattern). Sembra, tuttavia, che il controllo TextBox stia intercettando il mio RoutedUICommand "Annulla".WPF TextBox che intercetta RoutedUICommands

Qual è il modo più semplice per disattivare questa funzione in modo che sia possibile catturare Ctrl + Z alla radice della struttura della mia interfaccia utente? Vorrei evitare di inserire un sacco di codice/XAML in ogni TextBox nella mia applicazione, se possibile.

Il seguente dimostra brevemente il problema:

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

risposta

10

Non v'è alcun modo semplice per sopprimere tutti gli attacchi, non impostare IsUndoEnabled-false come solo trappola e filo vincolante Ctrl +Z chiave. È necessario reindirizzare CanUndo, CanRedo, Undo e Redo.Ecco come lo faccio con il mio singleton 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

Sembra davvero l'unico modo ... Trovo questa pazzia da pipistrello folle, come se fosse strano che volessi avere uno stack di annullamento che si estende su molti controlli di input ... – flq

+0

Ho capito che funziona. Devi assicurarti di utilizzare i comandi delle applicazioni e non quelli personalizzati. È inoltre necessario impostare IsUndoEnabled su false, oppure eseguirà comunque l'annullamento implementato dalla casella di testo. – Asheh

0

TextBoxBase (e quindi TextBox e RichTextBox) hanno IsUndoEnabled immobili, inadempiente a true. Se lo imposti su false (e puoi farlo per tutte le finestre di testo sulla tua finestra tramite uno stile e un setter, come al solito), allora non intercetteranno Ctrl + Z.

+0

Strano. Aggiungere IsUndoEnabled = "False" al TextBox continua a non fornire il risultato atteso (mostrando il MessageBox). C'è qualcos'altro che mi manca? –

0

Il controllo TextBox fornisce un IsUndoEnabled proprietà che è possibile impostare per false per evitare che la funzione Undo. (Se IsUndoEnabled è true, il Ctrl +Z battitura attiva.)

anche per un controllo che non fornisce una proprietà speciale è possibile aggiungere una nuova associazione per il comando che si desidera disattivare. Questo binding può quindi fornire un nuovo gestore di eventi CanExecute che risponde sempre in modo falso. Ecco un esempio che utilizza questa tecnica per rimuovere il supporto per la funzione di taglio della casella di testo:

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

e qui sta il gestore di eventi che imposta il CanExecute Stato:

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

Per impostazione predefinita l'obiettivo di lo RoutedUICommand è l'elemento con lo stato attivo della tastiera. Tuttavia, è possibile impostare CommandTarget sul controllo che emette il comando per modificare l'elemento radice che riceve il comando.

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

L'evento Eseguito si riempie di bolle, pertanto l'evento Esegui finestra verrà sempre colpito dopo l'evento Eseguito da TextBox. Prova a cambiarlo in PreviewExecuted e dovrebbe fare un'enorme differenza. Inoltre, potrebbe essere necessario collegare un CanExecute anche alla tua finestra. es .:

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

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

Ovviamente si vorrà probabilmente un po 'di logica per determinare quando CanExecute deve essere impostato su true. Probabilmente non hai bisogno di usare un comando personalizzato (usa solo l'Undo integrato).

5

Se si desidera implementare il proprio Undo/Redo e prevenire la TextBox da intercettare, collegare agli eventi comando Anteprima attraverso CommandManager.AddPreviewCanExecuteHandler e CommandManager.AddPreviewExecutedHandler e impostare il 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

Questa soluzione sembra preferibile quando ci sono più controlli (di vari tipi) in gioco. – karmasponge