简体   繁体   中英

The event comboBox_TextChanged of the ComboBox does not always fire

I have an input application which selects an integer value from a ComboBox. The Random function has a seed so the same values sequence is generated every time the application is run. By TYPING input into the ComboBox, if the first 2 digits form a value contained in the ComboBox.Items list and a third digit is added so that the resulting value is NOT CONTAINED in the ComboBox.Items list, the _availableValuesForSelection_TextChanged event handler rejects this digit. The first 2 digits remain. The problem is that if the same digit is added a few times, finally the resulting value is accepted. My conclusion is that after a few times the event _availableValuesForSelection_TextChanged of the ComboBox does not fire. Try adding 231, where the value 23 exists in the ComboBox.Items list and also 230 but not 231. The code is:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace IntegerSelection
{
    public partial class Form_SelectInteger : Form
    {
        public Form_SelectInteger(List<int> selectableValuesSource)
        {
            InitializeComponent();

            _validValue = "";
            _selectableValuesSource = selectableValuesSource;
            _availableValuesForSelection = new ComboBox();
            // The Source should be set first and then the AutoCompleteMode, otherwise it throws a NotSupportedException.
        _availableValuesForSelection.BindingContext = new BindingContext(); // Create a new context.
        _availableValuesForSelection.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
        _availableValuesForSelection.AutoCompleteSource = AutoCompleteSource.ListItems;
        _availableValuesForSelection.DropDownStyle = ComboBoxStyle.DropDown;
        _availableValuesForSelection.DroppedDown = false;
        _availableValuesForSelection.DrawMode = DrawMode.Normal;
        _availableValuesForSelection.Enabled = true;
        _availableValuesForSelection.BackColor = Color.LightBlue;
        _availableValuesForSelection.ForeColor = Color.DarkBlue;
        _availableValuesForSelection.IntegralHeight = false;
        _availableValuesForSelection.MaxDropDownItems = 1;
        _availableValuesForSelection.MaxDropDownItems = 80;
        _availableValuesForSelection.DropDownHeight = 510;
        _availableValuesForSelection.DropDownWidth = 50;
        _availableValuesForSelection.TabStop = true;
        _availableValuesForSelection.Visible = true;
        _availableValuesForSelection.Width = 100; 
        _availableValuesForSelection.Location = new Point((this.Width - _availableValuesForSelection.Width) / 2, 150);
        _availableValuesForSelection.DataSource = _selectableValuesSource; // DataSource should be the last line.
        _availableValuesForSelection.SelectedIndex = 5;
        this.Controls.Add(_availableValuesForSelection);

        _button_SelectionCompleteWasPressed = false;

        _availableValuesForSelection.TextChanged += new EventHandler(_availableValuesForSelection_TextChanged);  

        _selectedValue = (int)_availableValuesForSelection.Items[_availableValuesForSelection.SelectedIndex];
    }

    void _availableValuesForSelection_TextChanged(object sender, EventArgs e)
    {
            _textChangedEventDisabled = true;
            string resultingText = _availableValuesForSelection.Text;
            textBox1.Text = resultingText;
            if (_availableValuesForSelection.FindString(resultingText) == -1)
            {
                while (_availableValuesForSelection.FindString(resultingText) == -1)
                {
                    resultingText = resultingText.Substring(0, resultingText.Length - 1);
                }
                _availableValuesForSelection.DroppedDown = false;                    
                _availableValuesForSelection.SelectedIndex = _availableValuesForSelection.FindString(resultingText);
                _availableValuesForSelection.Text = resultingText;
                _availableValuesForSelection.SelectionStart = _availableValuesForSelection.Text.Length;
                _availableValuesForSelection.SelectionLength = 0;                    
            }
            _textChangedEventDisabled = false;            
    }

    private ComboBox _availableValuesForSelection;
    private List<int> _selectableValuesSource;

    private int _selectedValue;
    public int SelectedValue
    {
        get
        {
            return _selectedValue;
        }
    }

    private string _validValue;
    bool _textChangedEventDisabled = false;

    private bool _button_SelectionCompleteWasPressed;

    private void ValuesForSelection_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (sender is ComboBox)
        {
            ComboBox displayValuesForSelection = (ComboBox)sender;
            if (_button_SelectionCompleteWasPressed)
            {
                _selectedValue = (int)displayValuesForSelection.Items[displayValuesForSelection.SelectedIndex];
                DialogResult = DialogResult.OK;
            }
        }
    }

    private void button_SelectionComplete_Click(object sender, EventArgs e)
    {
        DialogResult = DialogResult.OK; // This line sets the DialogResult to OK to let the other form know that it wasn't forced closed or canceled.
        _availableValuesForSelection.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
        _availableValuesForSelection.AutoCompleteSource = AutoCompleteSource.ListItems;
        _availableValuesForSelection.DropDownStyle = ComboBoxStyle.DropDown;
        _button_SelectionCompleteWasPressed = true;
        _selectedValue = (int)_availableValuesForSelection.Items[_availableValuesForSelection.SelectedIndex];
        this.Close();
    }
}

}

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace IntegerSelection
{
    public partial class Form_Main : Form
    {
    Random rnd = new Random(0);

    public Form_Main()
    {
        InitializeComponent();

        PopulateValuesForSelection();
        Form_SelectInteger integerSelection = new Form_SelectInteger(_selectableValues);
        DialogResult result = integerSelection.ShowDialog(); // This line calls it as a Modal dialog, and retrieves its response once it's closed.
        if (result == DialogResult.OK) // Test to see if the DialogResult was "OK". 
        {
             _selectedValue = integerSelection.SelectedValue;
             textBox1.ReadOnly = true;
            textBox1.Text = _selectedValue.ToString();
        }
        else
        {
            this.Close();
            Application.Exit();
        }
    }

    private List<int> _selectableValues;
    private int _selectedValue;

    /// <summary>
    /// The values included into _selectableValues must be unique (no duplicates are allowed).
    /// </summary>
    private void PopulateValuesForSelection()
    {
        _selectableValues = new List<int>();
        HashSet<int> uniqueValues = new HashSet<int>();
        int i = 0;
        while (i < 256)
        { 
            int currentValue = rnd.Next(0, 1000);
            List<int> constituentDigits = SplitNumberIntoDigits(currentValue);
            HashSet<int> digits = new HashSet<int>(constituentDigits);
            if (!constituentDigits.Contains(-1) && uniqueValues.Add(currentValue))
            {
                i = uniqueValues.Count;
                _selectableValues.Add(currentValue);
            }
        }
        _selectableValues.Sort();
    }

    private List<int> SplitNumberIntoDigits(int number)
    {
        List<int> constituentDigits = new List<int>();
        while (number > 0)
        {
            int digit = number % 10; // Get the last digit in the number.
            constituentDigits.Insert(0, digit); // Insert the digit in the first location in the list, as it is the digit preceding the digit that is now in the first location in the list.
            number = number / 10; // Shorten the integer by one digit, from the right (the least significant digit).
        }

        return constituentDigits;
    }
}

}

Please help. Thank you in advance.

The TextChanged event is raised when the Text property value is updated. Having looked into this it seems that when you reject the value after the invalid character is entered, the value is still stored somewhere inside the object, and not triggering the event on repeated attempts.

Whereas the TextUpdate event is triggered every time the text is changed before it is shown. This is used to validate text input, and does not trigger when the Text property is changed programatically. Which sounds exactly what you want.

Look at the documentation: TextUpdate Event

Your code will then work by just changing one line:

_availableValuesForSelection.TextUpdate += new EventHandler(_availableValuesForSelection_TextChanged);

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