Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

.net

winforms

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?

like image 333
Oceans Avatar asked Dec 04 '15 15:12

Oceans


2 Answers

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.

like image 137
Fᴀʀʜᴀɴ Aɴᴀᴍ Avatar answered Sep 22 '22 06:09

Fᴀʀʜᴀɴ Aɴᴀᴍ


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.

like image 21
Ivan Stoev Avatar answered Sep 26 '22 06:09

Ivan Stoev