如何清除TextBox,使默认的Undo方法仍然有效

本文关键字:Undo 默认 方法 有效 何清除 清除 TextBox | 更新日期: 2023-09-27 18:25:36

我已经找了一段时间了,还没有找到一个简单或正确的方法来解决以下问题。

我有一个带有ShortcutsEnabled = true的多行TextBox,当然,它允许用户使用键盘快捷键(例如Ctrl+Z)轻松撤消或重做任何更改
现在我希望实现一个按钮,它可以清除TextBox的文本,但仍然允许用户使用默认的undo函数来撤消操作。

从文本框中清除文本很容易:

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

问题是,以这种方式替换文本时,默认的撤消功能不起作用
但是,如果用户选择所有文本(Ctrl+A)并手动点击<kbd]删除>Z恢复删除的数据。

我知道编写我自己的UndoRedo实现可能会让我获得所需的结果,但必须有另一种方法
我只需要能够模仿代码中的手动全选和删除方法。

这就是我需要帮助的地方,我该如何实现这一点?

TextBox似乎没有我可以调用的默认DeleteRemoveReplace文本函数
作为概念验证,我尝试使用Cut方法(或Ctrl+X)来实现这一点,它实际上实现了我想要的。唯一的缺点是,这会替换潜在的重要剪贴板数据。我也尝试过想出一个解决办法,但经过一些研究,似乎没有任何"正确"或至少"好"的方法来恢复剪贴板数据。

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

TLDR

如何像用户手动操作一样,从代码中的TextBox中删除文本?这样默认的Undo方法仍然可以被调用来恢复删除的文本?

如何清除TextBox,使默认的Undo方法仍然有效

如何聚焦TextBox,选择所有文本,然后模拟退格?这与用户手动清除TextBox相同。(至少,我用这种方式清除文本字段)

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

部分相关的事实-在WPF中,无论如何更改文本,UndoRedo方法始终正常工作。

这里有一个有点技巧的方法,问MS为什么这样的方法不是公共的。

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

使用

myTextBox.UndoableClear();

如果您不喜欢破解,您可以使用p/Invoke并发送EM_REPLACESEL消息,该消息不会像WM_SETTEXT那样清除文本编辑器内部撤消缓冲区。

我在VB.net中搜索解决方案时发现了这个问题。Ivan的"黑客"解决方案在VB.中运行良好

作为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