如何清除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恢复删除的数据。
我知道编写我自己的Undo
或Redo
实现可能会让我获得所需的结果,但必须有另一种方法
我只需要能够模仿代码中的手动全选和删除方法。
这就是我需要帮助的地方,我该如何实现这一点?
TextBox似乎没有我可以调用的默认Delete、Remove或Replace文本函数
作为概念验证,我尝试使用Cut
方法(或Ctrl+X)来实现这一点,它实际上实现了我想要的。唯一的缺点是,这会替换潜在的重要剪贴板数据。我也尝试过想出一个解决办法,但经过一些研究,似乎没有任何"正确"或至少"好"的方法来恢复剪贴板数据。
var cbData = Clipboard.GetText();
myTextBox.SelectAll();
myTextBox.Cut();
Clipboard.Clear();
if(!string.IsNullOrEmpty(cbData))
Clipboard.SetText(cbData);
TLDR
如何像用户手动操作一样,从代码中的TextBox中删除文本?这样默认的Undo
方法仍然可以被调用来恢复删除的文本?
如何聚焦TextBox,选择所有文本,然后模拟退格?这与用户手动清除TextBox相同。(至少,我用这种方式清除文本字段)
textBox1.Focus();
textBox1.SelectAll();
SendKeys.Send("{BACKSPACE}");
部分相关的事实-在WPF中,无论如何更改文本,Undo和Redo方法始终正常工作。
这里有一个有点技巧的方法,问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