Подтвердить что ты не робот

Текстовое поле Winforms - использование Ctrl-Backspace для удаления всего слова

У меня есть диалог Winforms, который содержит среди других элементов управления TextBox, который позволяет использовать одну строку ввода. Я хочу, чтобы пользователь мог нажать Ctrl-Backspace, чтобы удалить целое слово. Это не поведение по умолчанию с готовым TextBox; Я получаю символ прямоугольника, вместо того, чтобы удалить слово.

Я подтвердил, что для свойства ShortcutsEnabled установлено значение True.

Я обнаружил, что я могу использовать RichTextBox, а не TextBox, чтобы получить поведение, которое я хочу. Проблема заключается в том, что внешний вид RichTextBox (в частности, границы) отличается от атрибута TextBox, и мне не нужна или не требуется возможность разметки текста.

Итак, мой вопрос - как лучше всего справиться с этой ситуацией? Есть ли какое-то свойство в TextBox, которое мне не хватает? Или лучше использовать RichTextBox, обновить внешний вид, чтобы он был последовательным, и отключить разметку текста?

Я довольно счастлив написать код, чтобы обрабатывать события KeyDown и KeyPress, если нет лучшего способа, но подумал, что стоит сначала проверить.

4b9b3361

Ответ 1

/* Обновление: посмотрите также на ответ Дамирса ниже, возможно, это лучшее решение:) */

Я бы смоделировал Ctrl + Backspace, отправив Ctrl + Shift + Left и 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>

Ответ 2

Старый вопрос, но я просто наткнулся на ответ, который не требует дополнительного кода.

Включить автозаполнение для текстового поля, а CTRL-Backspace должно работать так, как вы хотите.

CTRL-Backspace, удаляющее целое слово слева от каретки, кажется, является "функцией изгоев" обработчика автозаполнения. Вот почему включение автозаполнения устраняет эту проблему.

Источник 1 | Источник 2

-

Вы можете включить функцию автозаполнения с настройкой AutoCompleteMode и AutoCompleteSource на все, что вам нравится (например, Suggest и RecentlyUsedList)

Ответ 3

Я не уверен, что это возможно без специального события 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 = "";
    }
}

Ответ 4

В то время как переопределение 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);
        }
    }  
}

Ответ 5

Это то, что я приземлился, используя, он также обрабатывает многострочные текстовые поля

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

Ответ 6

Вот так выходите:)

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

Ответ 7

Это хорошо работает:

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

Ответ 8

Я отвечаю на VB, а не на С# cuz. Я искал это решение в VB, но не смог его найти, но эти ответы С# помогли мне разобраться: -D

Создайте этот sub в модуле

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

Это будет работать независимо от того, где выбор находится внутри строки, и независимо от того, выбран ли текст и отлично ли он отвечает!

Ответ 9

DWF и giangurgolo, спасибо за предоставленную информацию. Ниже уточненная версия. Обратите внимание, что он также учитывает ComboBox, так как это имеет ту же проблему, что и TextBox. Также обратите внимание, что ярлыки активны, только если конфигурация TextBox или ComboBox позволяет так.

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

Вспомогательный элемент String (например, статический класс 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);
}

Ответ 10

Для этого было создано Regex. Используйте его.

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