Winforms文本框 – 使用Ctrl-Backspace删除整个单词

我有一个Winforms对话框,其中包含一个允许单行输入的TextBox。 我想允许用户能够按Ctrl-Backspace删除整个单词。 这不是开箱即用的TextBox的默认行为; 我得到一个矩形字符,而不是删除单词。

我已确认ShortcutsEnabled属性设置为True

我确实发现我可以使用RichTextBox而不是TextBox来获取我想要的行为。 这个问题是RichTextBox的外观(特别是边框)与TextBox的外观不同,我不需要或不想要标记文本的能力。

所以我的问题是如何最好地处理这种情况? 我错过了TextBox上的一些属性吗? 或者最好使用RichTextBox,更新外观以使其一致,并禁用文本的标记?

如果没有更好的方法,我很乐意编写处理KeyDown和KeyPress事件明确的代码,但认为值得先检查一下。

/ *更新:请看下面Damir的答案,这可能是一个更好的解决方案:) * /

我会通过将Ctrl + Shift + Left和Backspace发送到TextBox来模拟Ctrl + Backspace。 效果几乎相同,无需手动处理控件的文本。 您可以使用以下代码实现它:

 class TextBoxEx : TextBox { protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { if (keyData == (Keys.Control | Keys.Back)) { SendKeys.SendWait("^+{LEFT}{BACKSPACE}"); return true; } return base.ProcessCmdKey(ref msg, keyData); } } 

您还可以修改app.config文件以强制SendKey类使用更新的发送键的方法:

      

老问题,但我偶然发现了一个不需要任何额外代码的答案。

启用文本框的自动完成function,CTRL-Backspace应该按照您的意愿工作。

CTRL-Backspace删除插入符号左侧的整个单词似乎是自动完成处理程序的“ 流氓function ”。 这就是启用自动完成function修复此问题的原因。

来源1 | 来源2

您可以通过将AutoCompleteModeAutoCompleteSource设置为您喜欢的任何内容来启用自动完成function(例如, SuggestRecentlyUsedList

我不确定如果没有自定义KeyDown或KeyPress事件,以下代码可以正常工作:

 private void textBox1_KeyDown(object sender, KeyEventArgs e) { if ((e.KeyCode == Keys.Back) && e.Control) { e.SuppressKeyPress = true; int selStart = textBox1.SelectionStart; while (selStart > 0 && textBox1.Text.Substring(selStart - 1, 1) == " ") { selStart--; } int prevSpacePos = -1; if (selStart != 0) { prevSpacePos = textBox1.Text.LastIndexOf(' ', selStart - 1); } textBox1.Select(prevSpacePos + 1, textBox1.SelectionStart - prevSpacePos - 1); textBox1.SelectedText = ""; } } 

虽然ProcessCmdKey覆盖function很好,但它只限于Ctrl + Backspace的一次迭代,主要是因为使用SendWait模仿击键,如果你在按Backspace时再按住Ctrl键,系统似乎只能识别按下Backspace键。 如果您要记录覆盖的击键,您会发现一组您从未实际按下的额外键。

另一种方法是在ProcessCmdKey覆盖中显式管理文本框的外观,而不是向系统发送更多密钥。 这也可以轻松应用于Ctrl + Delete。

我已经为Ctrl + Backspace行为包含了一些常见的“停止点”,并且使用了switch语句而不是RegEx。 他们从来没有觉得足够干净,我通常最终会错过一个角色

如果您发现我的代码有任何问题,请务必告诉我。 对于仍然被这个难题迷惑的人来说,祝你好运!

 public class TextBoxEx : TextBox { protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { if (keyData == (Keys.Back | Keys.Control)) { for (int i = this.SelectionStart - 1; i > 0; i--) { switch (Text.Substring(i, 1)) { //set up any stopping points you want case " ": case ";": case ",": case "/": case "\\": Text = Text.Remove(i, SelectionStart - i); SelectionStart = i; return true; case "\n": Text = Text.Remove(i - 1, SelectionStart - i); SelectionStart = i; return true; } } Clear(); //in case you never hit a stopping point, the whole textbox goes blank return true; } else { return base.ProcessCmdKey(ref msg, keyData); } } } 

这是我使用的,它还处理多行文本框

 private void HandleCtrlBackspace_KeyDown(object sender, KeyEventArgs e) { switch (e.KeyData) { case (Keys.Back | Keys.Control): e.SuppressKeyPress = true; TextBox textbox = (TextBox)sender; int i; if (textbox.SelectionStart.Equals(0)) { return; } int space = textbox.Text.LastIndexOf(' ', textbox.SelectionStart - 1); int line = textbox.Text.LastIndexOf("\r\n", textbox.SelectionStart - 1); if (space > line) { i = space; } else { i = line; } if (i > -1) { while (textbox.Text.Substring(i - 1, 1).Equals(' ')) { if (i.Equals(0)) { break; } i--; } textbox.Text = textbox.Text.Substring(0, i) + textbox.Text.Substring(textbox.SelectionStart); textbox.SelectionStart = i; } else if (i.Equals(-1)) { textbox.Text = textbox.Text.Substring(textbox.SelectionStart); } break; } } 

这就是你去的方式:)

 private void textBox1_KeyPress(object sender, KeyPressEventArgs e) { //if ctrl+bcksp if (e.KeyChar == 127) { //if not last word if (textBox1.Text.Split (' ').Count() > 1) { //remoce last word form list and put it back together (gotta love lambda) textBox1.Text = textBox1.Text.Split (' ').Take (textBox1.Text.Split (' ').Count() - 1).Aggregate ((a,b) => a + " " + b); //set selection at the end textBox1.SelectionStart = textBox1.Text.Length; } else if (textBox1.Text.Split (' ').Count() == 1) { textBox1.Text = ""; } } } 

这很好用:

 static Regex RegExWholeWord = new Regex(@"(\r\n|[^A-Za-z0-9_\r\n]+?|\w+?) *$", RegexOptions.Compiled); 

在按键时,使用

 var m = RegExWholeWord.Match(textbox.Text, 0, textbox.SelectionStart); if (m.Success) { textbox.Text = textbox.Text.Remove(m.Index, m.Length); textbox.SelectionStart = m.Index; } 

正则表达式是为此而做的。 用它。

  private void TextBox_KeyDown(object sender, KeyEventArgs e) { TextBox box = (TextBox)sender; if (e.KeyData == (Keys.Back | Keys.Control)) { if (!box.ReadOnly && box.SelectionLength == 0) { RemoveWord(box); } e.SuppressKeyPress = true; } } private void RemoveWord(TextBox box) { string text = Regex.Replace(box.Text.Substring(0, box.SelectionStart), @"(^\W)?\w*\W*$", ""); box.Text = text + box.Text.Substring(box.SelectionStart); box.SelectionStart = text.Length; } 

我在VB中回答而不是C#cuz我在VB中寻找这个解决方案却找不到一个,但是这些C#响应帮助我解决了这个问题:-D

在模块中创建此子

 Public Sub ctrl_bksp(ByRef t As TextBox) Dim ss As Integer = t.SelectionStart Dim sl As Integer = t.SelectionLength Dim tl As Integer = t.TextLength '//Split either side of selection start Dim strPre As String = Strings.Left(t.Text, tl - (tl - ss)) Dim strPost As String = Strings.Right(t.Text, tl - ss - sl) '//Get Last Space Location in StrPre Dim s As Integer = Strings.InStrRev(RTrim(strPre), " ") strPre = Strings.Left(strPre, s) t.Text = strPre & strPost t.SelectionStart = s End Sub 

然后你可以在任何文本框的KeyPress事件中调用这个子:

 Private Sub Textbox1_KeyPress(sender As Object, e As System.Windows.Forms.KeyPressEventArgs) Handles Textbox1.KeyPress Select Case e.KeyChar Case Chr(127) '//Ctrl+Backspace e.Handled = True Call ctrl_bksp(Textbox1) End Select End Sub 

无论选择在字符串中的哪个位置,以及是否选择了文本,这都会有效,并且响应非常好!

DWF和giangurgolo ,感谢您提供的信息。 下面是它的精致版本。 请注意,它也考虑了ComboBox ,因为它与TextBox具有相同的问题。 另请注意,只有TextBoxComboBox配置允许时,快捷方式才有效。

TextBoxEx:

 public class TextBoxEx : TextBox { protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { // Attention: // Similar code exists in ComboBoxEx.ProcessCmdKey(). // Changes here may have to be applied there too. if (ShortcutsEnabled) { if (keyData == (Keys.Control | Keys.Back)) { if (!ReadOnly) { if (SelectionStart > 0) { int i = (SelectionStart - 1); // Potentially trim white space: if (char.IsWhiteSpace(Text, i)) i = (StringEx.StartIndexOfSameCharacterClass(Text, i) - 1); // Find previous marker: if (i > 0) i = StringEx.StartIndexOfSameCharacterClass(Text, i); else i = 0; // Limit i as it may become -1 on trimming above. // Remove until previous marker or the beginning: Text = Text.Remove(i, SelectionStart - i); SelectionStart = i; return (true); } else { return (true); // Ignore to prevent a white box being placed. } } } else if (keyData == (Keys.Control | Keys.A)) { if (!ReadOnly && Multiline) { SelectAll(); return (true); } } } return (base.ProcessCmdKey(ref msg, keyData)); } } 

ComboxBoxEx:

 public class ComboBoxEx : ComboBox { protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { // Attention: // Similar code exists in TextBoxEx.ProcessCmdKey(). // Changes here may have to be applied there too. if (keyData == (Keys.Control | Keys.Back)) { if (DropDownStyle != ComboBoxStyle.DropDownList) { if (SelectionStart > 0) { int i = (SelectionStart - 1); // Potentially trim white space: if (char.IsWhiteSpace(Text, i)) i = (StringEx.StartIndexOfSameCharacterClass(Text, i) - 1); // Find previous marker: if (i > 0) i = StringEx.StartIndexOfSameCharacterClass(Text, i); else i = 0; // Limit i as it may become -1 on trimming above. // Remove until previous marker or the beginning: Text = Text.Remove(i, SelectionStart - i); SelectionStart = i; return (true); } else { return (true); // Ignore to prevent a white box being placed. } } } return (base.ProcessCmdKey(ref msg, keyData)); } } 

字符串辅助(例如静态类StringEx):

 ///  /// Returns the start index of the same character class. ///  /// The  object to process. /// The search starting position. ///  /// The zero-based index position of the start of the same character class in the string. ///  public static int StartIndexOfSameCharacterClass(string str, int startIndex) { int i = startIndex; if (char.IsWhiteSpace(str, i)) // Includes 'IsSeparator' (Unicode space/line/paragraph { // separators) as well as 'IsControl' (, ,...). for (/* i */; i >= 0; i--) { if (!char.IsWhiteSpace(str, i)) return (i + 1); } } else if (char.IsPunctuation(str, i)) { for (/* i */; i >= 0; i--) { if (!char.IsPunctuation(str, i)) return (i + 1); } } else if (char.IsSymbol(str, i)) { for (/* i */; i >= 0; i--) { if (!char.IsSymbol(str, i)) return (i + 1); } } else { for (/* i */; i >= 0; i--) { if (char.IsWhiteSpace(str, i) || char.IsPunctuation(str, i) || char.IsSymbol(str, i)) return (i + 1); } } return (0); } 

我有这些方法的问题:

  • 替换.Text具有大文本的滚动问题。
  • 在textBox.KeyDown事件处理程序中执行SendKeys.SendWait(“^ + {LEFT} {BACKSPACE}”)对我来说根本不稳定。
  • 使用.Cut()更改剪贴板(否则可以正常工作)。

查看.NET参考源的内容.Cut()确实引出了以下解决方案:在TextBox中选择文本,然后使用WM_CLEAR清除它。 似乎工作正常,它不发送人工按键事件。

 class CtrlBackspaceSupport { TextBox textBox; public CtrlBackspaceSupport(TextBox textBox) { this.textBox = textBox; textBox.KeyDown += new KeyEventHandler(textBox_KeyDown); } [DllImport("user32.dll", SetLastError = true)] static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam); const int WM_CLEAR = 0x0303; void textBox_KeyDown(object sender, KeyEventArgs e) { if (e.Control && e.KeyCode == Keys.Back) { // Ctrl+Backspace -> remove till word border before cursor e.SuppressKeyPress = true; if (0 == textBox.SelectionLength && textBox.SelectionStart > 1) { // nothing selected var text = textBox.Text; int indexOfSpace = text.LastIndexOf(' ', textBox.SelectionStart - 2); if (-1 != indexOfSpace) { // found something indexOfSpace++; textBox.Select(indexOfSpace, textBox.SelectionStart - indexOfSpace); SendMessage(new HandleRef(textBox, textBox.Handle).Handle, WM_CLEAR, 0, 0); } } } } }