Oceans
Oceans

Reputation: 3509

How to clear a TextBox so that the default Undo method still functions?

I've been looking for a while now and haven't found an easy or correct way to solve the following issue.

I have a multi-line TextBox with the ShortcutsEnabled = true which, of course, allows the user to easily undo or redo any changes made by using the keyboard shortcuts (e.g. Ctrl + Z).
Now I wish to implement a button that clears the text of the TextBox but still allow the user to undo the action by using the default undo function.

Clearing the text from the TextBox is easy:

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

The problem is that the default undo function does not work when replacing the text these ways.
However, if the user selects all the text ( Ctrl + A ) and hits Delete manually, the TextBox becomes empty and the user can still restore the deleted data by Ctrl + Z.

I know that writing my own implementation of Undo or Redo would probably allow me to get the desired result but there has to be another way.
I simply need to be ably to mimic the manual select all and delete method in code.

And this is where I need some help, how can I implement this?

TextBox doesn't seem to have a default Delete, Remove or Replace text function that I can call.
As a proof of concept I've tried implementing this using the Cut method (or Ctrl + X ) , which actually does what I want. The only downside is that this replaces potentially important clipboard data. I've tried coming up with a workaround for this as well, but after doing some research there doesn't seem to be any "correct" or at least "great" way to restore clipboard data.

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

TLDR;

How can I delete the text from a TextBox in code the same way a user would do manually? So that the default Undo method can still be called to restore the deleted text?

Upvotes: 9

Views: 2362

Answers (3)

Patrick
Patrick

Reputation: 196

I found this question as I was searching for a solution in VB.net. Ivan's "hackish" solution works just fine in VB.

As 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))
    <Extension>
    Public Sub Delete(target As TextBox)
        If String.IsNullOrEmpty(target.Text) Then Return
        'target.SelectAll()
        SetSelectedTextInternal(target, String.Empty, False)
    End Sub
End Module

Upvotes: 2

Fᴀʀʜᴀɴ Aɴᴀᴍ
Fᴀʀʜᴀɴ Aɴᴀᴍ

Reputation: 6251

What about focusing the TextBox, selecting all text and then simulating a backspace? It's the same as the user manually clearing the TextBox. (Atleast, I clear text fields in this way)

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

A bit of partially relevant fact - In WPF, no matter how you change the text, the Undo and Redo methods always function properly.

Upvotes: 12

Ivan Stoev
Ivan Stoev

Reputation: 205779

Here is a bit hackish method, ask MS why such methods are not public.

public static class TextBoxUtils
{
    // internal virtual void SetSelectedTextInternal(string text, bool clearUndo)
    private static readonly Action<TextBox, string, bool> SetSelectedTextInternal =
        (Action<TextBox, string, bool>)Delegate.CreateDelegate(typeof(Action<TextBox, string, bool>),
        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);
    }
}

Usage

myTextBox.UndoableClear();

If you don't like hacks, you can use P/Invoke and send EM_REPLACESEL message which does not clear text editor internal undo buffer like WM_SETTEXT.

Upvotes: 4

Related Questions