简体   繁体   中英

Parsing user input as a user types in a winforms Textbox control

I am trying to create some sort of license verification text box that automatically splits the user inputs into chunks separated by hyphens. My license is 25 characters long and it separated as such:

XXXXX-XXXXX-XXXXX-XXXXX-XXXXX

I have come up with the following code to parse the user input while he is typing or through copy/paste by handling the TextChanged event of the Textbox Control like so:

public static string InsertStringAtInterval(string source, string insert, int interval)
        {
            StringBuilder result = new StringBuilder();
            int currentPosition = 0;
            while (currentPosition + interval < source.Length)
            {
                result.Append(source.Substring(currentPosition, interval)).Append(insert);
                currentPosition += interval;
            }
            if (currentPosition < source.Length)
            {
                result.Append(source.Substring(currentPosition));
            }
            return result.ToString();
        }

        private bool bHandlingChangeEvent = false;
        private void txtLicense_TextChanged(object sender, EventArgs e)
        {
            if (bHandlingChangeEvent == true)
                return;

            bHandlingChangeEvent = true;
            string text = txtLicense.Text.Replace("-", "").Replace(" ","");
            int nPos = txtLicense.SelectionStart;
            if((text.Length==5||text.Length==10||text.Length==15||text.Length==20) && txtLicense.Text[txtLicense.Text.Length-1]!='-')
            {
                txtLicense.Text += "-";
                txtLicense.SelectionStart = nPos + 1;
            }
            if(text.Length>=25)
            {
                string tmp = text.Substring(0, 25);
                tmp = InsertStringAtInterval(tmp, "-", 5);
                txtLicense.Text = tmp;
                txtLicense.SelectionStart = nPos;
            }


            bHandlingChangeEvent = false;
        }

While this is working perfectly when I user types and pastes inside the box. My only problem is that when the user tries to delete characters from the entered key by either pressing backspace or delete.

Due to the forced hyphen insertion @ positions 5,10,15,20 once a user reaches one of these marks on a backspace press the logic above forces the hyphen addition to the string and the user cannot go beyond that.

I tried fiddling with KeyDown events but couldn't come up with anything useful. can someone help please.

I also tried using a MaskedTextbox but that thing is ugly as I don't want the mask/hyphens to be visible on focus and I certainly don't want to replace the prompt with white spaces as it will give some confusion when clicking inside the box as the cursor will not always position at the beginning of the box when its "supposedly" empty.

You can try this version:

void txtLicense_KeyDown(object sender, KeyEventArgs e) {
  if (e.KeyCode == Keys.Back) {

    int index = txtLicense.SelectionStart;
    while (index > 0 && txtLicense.Text[index - 1] == '-') {
      --index;
    }

    if (index > 0) {
      --index;
    }

    txtLicense.Select(index, txtLicense.SelectionLength + Math.Max(index, 1));
    txtLicense.SelectedText = string.Empty;

    e.SuppressKeyPress = true;
  }
}

This sort of thing has been the cause of a lot of suffering for me over the years. The way I approach it is to treat each change to the text as if it were pasted from scratch - I strip out the hyphens, then put them back in in the appropriate position. Then you can just handle the special case where the user has deleted the last character. In this case, you compare the text from just before the TextChanged event to the text afterward. If they are the same, but the previous text is one character longer, just don't do anything special.

Here is some code that seems to work for text entry, cut/copy/paste, etc. It could be improved with a little fancy LINQ, some error handling, etc. but hopefully it gets the idea across.

private string previousText = string.Empty;
private bool processing = false;

private void textBox1_TextChanged(object sender, EventArgs e)
{
    // If we're already messing around with the text, don't start again.
    if (processing)
        return;

    processing = true;

    // The current textbox text
    string newText = textBox1.Text;

    // What was there before, minus one character.
    string previousLessOne = previousText == string.Empty ? string.Empty : previousText.Substring(0, previousText.Length - 1);

    // Get where the user is, minus any preceding dashes
    int caret = textBox1.SelectionStart - (textBox1.Text.Substring(0, textBox1.SelectionStart).Count(ch => ch == '-'));

    // If the user has done anything other than delete the last character, ensure dashes are placed in the correct position.
    if (newText.Length > 0 && newText != previousLessOne)
    {
        textBox1.Text = string.Empty;
        newText = newText.Replace("-", "");

        for (int i = 0; i < newText.Length; i += 5)
        {
            int length = Math.Min(newText.Length - i, 5);
            textBox1.Text += newText.Substring(i, length);

            if (length == 5)
                textBox1.Text += '-';
        }
    }

    // Put the user back where they were, adjusting for dashes.
    textBox1.SelectionStart = caret + (caret / 5);

    previousText = textBox1.Text;

    processing = false;
}

I've done something similar to this before. What I would do is not add a hyphen until there is a character after its insertion point. For example, instead of adding a hyphen after 5 chars have been entered, do it at 6 so that it is never added at the end.

Here is the algorithm I use to insert hyphens at specific places:

public static string FormatLicenseNumber(string str)
{
    if (string.IsNullOrEmpty(str))
        return str;
    else
    {
        str = str.Replace("-", string.Empty);
        if (str.Length > 20)
            str = str.Insert(20, "-");
        if (str.Length > 15)
            str = str.Insert(15, "-");
        if (str.Length > 10)
            str = str.Insert(10, "-");
        if (str.Length > 5)
            str = str.Insert(5, "-");
        return str;
    }
}

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