2009-07-14 46 views
27

我有一個Winforms對話框,其中包含一個允許單行輸入的TextBox。我想讓用戶能夠按Ctrl-Backspace刪除整個單詞。這不是開箱即用的TextBox的默認行爲;我得到一個矩形字符,而不是刪除這個詞。Winforms文本框 - 使用Ctrl-Backspace刪除整個字

我已確認ShortcutsEnabled屬性設置爲True

我確實發現我可以使用RichTextBox而不是TextBox來獲得我想要的行爲。問題在於RichTextBox(特別是邊框)的外觀與TextBox的外觀不同,我不需要或不需要標記文本的功能。

所以我的問題是如何最好地處理這種情況?我遺失了TextBox上的一些屬性?還是最好使用RichTextBox,更新外觀以保持一致,並禁用文本的標記?

如果沒有更好的方法,我相對高興編寫代碼來處理KeyDown和KeyPress事件,但認爲它值得先檢查。

+0

以我的經驗ATLEAST,遠離RichTextBox的weeell了......這是從我看到了一個時間,我想使用它的性能惡夢(誠然,用顏色和東西很多,但仍然是一個惡夢) – 2009-07-14 10:53:39

回答

22

/*更新:請看下面的Damir的答案,這可能是一個更好的解決方案:) */

我會通過發送Ctrl + Shift +左鍵模擬Ctrl + Backspace和退格到TextBox。效果幾乎相同,並且不需要手動處理控件的文本。

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類使用發送鍵的較新的方法:你可以使用此代碼實現它

<configuration> 
    <appSettings> 
    <add key="SendKeys" value="SendInput" /> 
    </appSettings> 
</configuration> 
+0

好的答案,作品非常好。謝謝。 – 2009-07-29 19:57:20

+1

請參閱http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.aspx中的說明,瞭解您爲什麼可能需要修改app.config的說明 – sjlewis 2011-01-28 05:22:04

+9

當我無法按預期工作時保持控制,並退回到一起。它首次刪除完整的單詞,然後逐個刪除字符。 – Prasanth 2012-10-04 08:50:13

2

這是正路走喲:)

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 = ""; 
     } 
    } 
} 
+0

感謝您的回覆;這工作,但需要也需要設置e.Handled = true。否則,它將刪除該單詞並創建* rectangle *。 – 2009-07-14 19:41:15

6

我不知道這是可能的,而不定製的KeyDown或按鍵事件,下面的代碼工作,但:

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 = ""; 
    } 
} 
+0

查看LukeSw的答案(http://stackoverflow.com/questions/1124639/winforms-textbox-using-ctrl-backspace-to-delete-whole-word/1197339#1197339)比我的更好的方法 – 2009-08-04 09:31:37

+0

剛看了一下在這個問題上再次;我認爲你是對的,我已經更新了接受的答案:) – 2009-11-09 09:28:52

3

這是我登陸上去使用,它同樣可以處理多行文本框

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

這工作不錯:

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

雖然給ProcessCmdKey重寫工作不錯,首先,它本身限制爲Ctrl + Backspace鍵只有一個迭代,主要是因爲使用SendWait模仿的按鍵,如果你在按住Ctrl鍵的同時再次按Backspace,系統似乎只能識別正在按下的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); 
     } 
    } 
} 
0

我回答在VB而不是C#因爲我一直在尋找在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 

無論選擇在字符串中的哪個位置,以及是否選擇了文本,這都會起作用,並且響應得非常好!

18

老問題,但我偶然發現了一個不需要任何額外代碼的答案。

爲文本框啓用自動完成,並且CTRL-Backspace應該按照您希望的方式工作。

CTRL-退格刪除插入符號左邊的整個單詞似乎是自動填充處理程序的'流氓功能'。這就是爲什麼啓用自動填充功能可以解決此問題。

Source 1 | Source 2

-

可以實現與AutoCompleteModeAutoCompleteSource設置任何你喜歡的自動完成功能(例如,SuggestRecentlyUsedList

0

DWFgiangurgolo,感謝你提供的信息。它的精煉版本下面。請注意,它也考慮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):

/// <summary> 
/// Returns the start index of the same character class. 
/// </summary> 
/// <param name="str">The <see cref="string"/> object to process.</param> 
/// <param name="startIndex">The search starting position.</param> 
/// <returns> 
/// The zero-based index position of the start of the same character class in the string. 
/// </returns> 
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' (<CR>, <LF>,...). 
     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); 
} 
0

正則表達式爲這個製成。用它。

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

我曾與這些方法的問題:

  • 更換。文本已滾動大文本的問題。
  • 在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); 
       } 
      } 
     } 
    } 
}