Come cancellare un controllo TextBox in modo che il metodo di annullamento predefinito funzioni ancora?

Ho cercato un po ‘di tempo e non ho trovato un modo facile o corretto per risolvere il seguente problema.

Ho un TextBox a più righe con ShortcutsEnabled = true che, naturalmente, consente all’utente di annullare o ripristinare facilmente qualsiasi modifica effettuata usando le scorciatoie da tastiera (ad esempio Ctrl + Z ).
Ora desidero implementare un pulsante che cancella il testo del TextBox ma consente comunque all’utente di annullare l’azione utilizzando la funzione di annullamento predefinita.

Cancellare il testo dal TextBox è facile:

 myTextBox.Clear(); //or myTextBox.Text = string.Empty; 

Il problema è che la funzione di annullamento predefinita non funziona quando si sostituisce il testo in questi modi.
Tuttavia, se l’utente seleziona tutto il testo ( Ctrl + A ) e fa clic su Elimina manualmente, il TextBox diventa vuoto e l’utente può comunque ripristinare i dati cancellati con Ctrl + Z.

So che scrivere la mia implementazione di Undo o Redo mi permetterebbe probabilmente di ottenere il risultato desiderato, ma deve esserci un altro modo.
Ho semplicemente bisogno di essere abile nel mimare il manuale selezionare tutto ed eliminare il metodo nel codice.

Ed è qui che ho bisogno di aiuto, come posso implementarlo?

TextBox non sembra avere una funzione predefinita Elimina , Rimuovi o Sostituisci testo che posso chiamare.
Come prova del concetto, ho provato a implementarlo usando il metodo Cut (o Ctrl + X ), che in realtà fa ciò che voglio. L’unico svantaggio è che questo sostituisce dati di appunti potenzialmente importanti. Ho provato a trovare una soluzione alternativa per questo, ma dopo aver fatto qualche ricerca non sembra esserci alcun modo “corretto” o almeno “ottimo” per ripristinare i dati degli appunti.

 var cbData = Clipboard.GetText(); myTextBox.SelectAll(); myTextBox.Cut(); Clipboard.Clear(); if(!string.IsNullOrEmpty(cbData)) Clipboard.SetText(cbData); 

TLDR;

Come posso cancellare il testo da un TextBox in codice nello stesso modo in cui un utente farebbe manualmente? In modo che il metodo di Undo predefinito possa ancora essere chiamato per ripristinare il testo eliminato?

Che ne dici di mettere a fuoco il TextBox, selezionare tutto il testo e quindi simulare un backspace? È la stessa cosa che l’utente cancella manualmente il TextBox. (Almeno, cancello i campi di testo in questo modo)

 textBox1.Focus(); textBox1.SelectAll(); SendKeys.Send("{BACKSPACE}"); 

Un dato di fatto parzialmente rilevante: in WPF, indipendentemente dal modo in cui si modifica il testo, i metodi Annulla e Ripristina funzionano sempre correttamente.

Ecco un metodo un po ‘hacker, chiedi a MS perché tali metodi non sono pubblici.

 public static class TextBoxUtils { // internal virtual void SetSelectedTextInternal(string text, bool clearUndo) private static readonly Action SetSelectedTextInternal = (Action)Delegate.CreateDelegate(typeof(Action), typeof(TextBox).GetMethod("SetSelectedTextInternal", BindingFlags.Instance | BindingFlags.NonPublic)); public static void UndoableClear(this TextBox target) { if (string.IsNullOrEmpty(target.Text)) return; target.SelectAll(); SetSelectedTextInternal(target, string.Empty, false); } } 

uso

 myTextBox.UndoableClear(); 

Se non ti piacciono gli hack, puoi usare P / Invoke e inviare il messaggio EM_REPLACESEL che non cancella il buffer di annullamento interno dell’editor di testo come WM_SETTEXT .

Ho trovato questa domanda mentre cercavo una soluzione in VB.net. La soluzione “hackish” di Ivan funziona bene in VB.

Come vb.net:

 Module Module1 Private ReadOnly SetSelectedTextInternal As Action(Of TextBox, String, Boolean) = DirectCast([Delegate].CreateDelegate(GetType(Action(Of TextBox, String, Boolean)), GetType(TextBox).GetMethod("SetSelectedTextInternal", BindingFlags.Instance Or BindingFlags.NonPublic)), Action(Of TextBox, String, Boolean))  Public Sub Delete(target As TextBox) If String.IsNullOrEmpty(target.Text) Then Return 'target.SelectAll() SetSelectedTextInternal(target, String.Empty, False) End Sub End Module