简体   繁体   中英

How to set TextBox.SelectionStart after TextChanged?

Here is my code:

private void textBox_TextChanged(object sender, EventArgs e)
{
    int selectionStart = textBox.SelectionStart;
    textBox.Text = new string(textBox.Text.Distinct().ToArray());
    textBox.Select(selectionStart, 0);
}

This code works as it's supposed to:

  1. if I insert a new unique character (a character that is not in the string textBox.Text) at the end of the textBox.Text
  2. if I try to insert the character that is in the string textBox.Text at the end of the textBox.Text
  3. if I insert a new unique character in the middle of the string textBox.Text

But this code does not work correctly when I try to insert a character (that is in the string textBox.Text) in the middle of a string textBox.Text. The selection cursor moves to the right, although it shouldn't, because the text doesn't change.

Give this a shot:

private void textBox_TextChanged(object sender, EventArgs e)
    {
        int selectionStart = textBox.SelectionStart;
        int originalTextLength = textBox.Text.Length;
        textBox.Text = new string(textBox.Text.Distinct().ToArray());
        int lengthDif = originalTextLength - textBox.Text.Length;
        textBox.Select(selectionStart - lengthDif, 0);
    }

Basically it checks if the length was modified from the original entered text(before the extra character(s) were removed). If it was, it moves the cursor to compensate for any characters removed.

The TextChanged event cannot suppress the move of the caret to the right. It is its default behavior. But you can compensate adding one line of code inside the TextChanged event and modifying the selectionStart value

private void textBox_TextChanged(object sender, EventArgs e)
{
    int selectionStart = textBox.SelectionStart;
    int cnt = textBox.Text.GroupBy(t => t).Any(x => x.Count() > 1) ? -1 : 0;
    textBox.Text = new string(textBox.Text.Distinct().ToArray());
    textBox.Select(selectionStart + cnt, 0);
}

The key line calculates if we need to compensate for the right move of the caret first grouping the characters in the textbox and then counting if any group has a count bigger than 1. If this is the case the selectionStart variable is adjusted to compensate.

As you have discovered this approach fails short in keeping the characters in their original position. If you try to insert an existing character before one already present the code above keeps the first one and removes the last one.

If you want to keep the last one then you should use the KeyDown event or the KeyPress.

private void textBox_KeyDown(object sender, KeyEventArgs e)
{
    char c = (char)e.KeyValue;
    e.SuppressKeyPress = textBox.Text.Contains(c);
}

And if you need to process also the Paste command then you should write your own TextBox class and override the WndProc method called by the Windows API when you press CTRL+V or select the Paste menu

public class MyTextBox : TextBox
{
    protected override void WndProc(ref Message m)
    {
        if (m.Msg == 0x302 && Clipboard.ContainsText())
        {
            var pasteText = Clipboard.GetText().ToArray();
            var currText = this.Text.ToArray();
            var newText = pasteText.Except(currText).ToArray();
            Clipboard.SetText(new string(newText));
        }
        base.WndProc(ref m);
    }
}

Of course you need to add an instance of MyTextBox to your forms instead of the standard TextBox.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM