2012-06-20 11 views
7

Quando si utilizza textbox.Undo(); Ottengo il seguente errore:Impossibile utilizzare Annulla in TextChanged

Cannot Undo or Redo while undo unit is open.

Ora capisco perché questo è il caso (perché è un evento annullabile attiva), ma attraverso quello evento che posso eseguire la convalida in una casella di testo e annullare la modifica se l'utente ha ha digitato un carattere non valido?

+0

Si dovrebbero invece utilizzare i comportamenti. – SepehrM

+0

SepehrM - potresti fornire un esempio? E se non vuoi ancora convalidarlo ma vuoi solo fermare questo maledetto messaggio di eccezione per far saltare la tua sessione? Sto ottenendo questo quando un utente digita un bit troppo veloce o forse incolla un pezzo di dati nel campo. Neanche convalidante. – Allen

risposta

6

Invece di utilizzare Annulla e TextChanged, è necessario utilizzare gli eventi PreviewTextInput e DataObject.Pasting. Nel gestore di eventi PreviewTextInput, impostare e.Handled su true se il testo digitato è un carattere non valido. Nel gestore di eventi Pasting, chiamare e.CancelCommand() se il testo incollato non è valido.

Ecco un esempio di una casella di testo che accetta solo le cifre 0 e 1:

XAML:

<Window x:Class="BinaryTextBox.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="133" Width="329"> 
    <StackPanel> 
     <TextBox x:Name="txtBinary" Width="100" Height="24" 
       PreviewTextInput="txtBinary_PreviewTextInput" 
       DataObject.Pasting="txtBinary_Pasting"/> 
    </StackPanel> 
</Window> 

codice dietro:

using System.Text.RegularExpressions; 
using System.Windows; 
using System.Windows.Input; 

namespace BinaryTextBox 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
     } 

     private void txtBinary_PreviewTextInput(object sender, 
       TextCompositionEventArgs e) 
     { 
      e.Handled = e.Text != "0" && e.Text != "1"; 
     } 

     private void txtBinary_Pasting(object sender, DataObjectPastingEventArgs e) 
     { 
      if (!Regex.IsMatch(e.DataObject.GetData(typeof(string)).ToString(), "^[01]+$")) 
      { 
       e.CancelCommand(); 
      } 
     } 
    } 
} 
+0

Brilliant - soluzione perfetta grazie –

+0

Grande, il gestore "pasting" si convalida anche se si stanno inserendo caratteri usando il drag and drop. – Mishax

+0

Il problema con questo approccio è che PreviewTextInput visualizza solo l'anteprima di ciò che viene digitato e non il risultato finale.Questo può funzionare solo se si scrive un algoritmo molto complicato e non gestibile per tenere conto di tutti gli input possibili in relazione a un valore valido. Ad esempio, su un campo decimale, prova a convalidare quando un punto o un periodo è applicabile. Chiaramente, puoi solo digitare 1 di ciascuno, ma non in ogni punto. La soluzione migliore è convalidare il risultato finale, _regardless_ di come è stato digitato. WPF non ha il supporto per tale, purtroppo. – Neolisk

3

chiamata della funzione Annulla in modo asincrono dal Dal gestore di eventi TextChanged:

Dispatcher.BeginInvoke(new Action(() => tb.Undo())) 
+0

Come si confronta questa soluzione con la risposta accettata? – InvalidBrainException

7

Per rispondere all'approccio di simbay, che ritengo sia stato respinto.

Non è possibile chiamare Annulla in TextChanged perché l'operazione di annullamento è ancora in fase di elaborazione da parte del controllo TextBox. Sembra funzionare a volte e non altre volte, quindi questo suggerisce che c'è una condizione di competizione tra quando viene segnalato l'evento e il completamento della preparazione di annullamento.

Tuttavia, la chiamata di Annulla richiamata sul Dispatcher consente alla casella di testo di completare la preparazione di annullamento. È possibile convalidare i risultati della modifica del testo e decidere se si desidera mantenere o annullare la modifica. Questo potrebbe non essere l'approccio migliore, ma l'ho provato e fatto saltare una serie di modifiche e incolla del testo nella casella di testo e non è stato possibile riprodurre l'eccezione.

La "risposta accettata" è grande solo se si vuole evitare un carattere non valido da essere inserito o incollato, ma in generale ho spesso fare molto di più di validazione dell'input coinvolti TextBox e vogliono verificare il valore del testo finale. Non è facile distinguere il testo finale da un evento di anteprima perché per quanto riguarda il controllo non è ancora successo nulla.

Per rispondere alla domanda di Terribad, la risposta di simbay è migliore e più concisa in più situazioni.

tb.TextChanged =+ (sender, args) => 
{ 
    if(! MeetsMyExpectations(tb.Text)) 
     Dispatcher.BeginInvoke(new Action(() => tb.Undo())); 
} 

Ho letto un sacco di avventure selvagge nella validazione casella di testo e questo è quanto di più semplice come ho trovato.

Problemi correlati