Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
5.1k views
in Technique[技术] by (71.8m points)

indexing - ComboBox - InvalidArgument=Value of '0' is not valid 'index'. Parameter name:index. (C# question)

I took an interesting source code example from here https://stackoverflow.com/a/43847970/11941220

There is an unusual bug in this code, here is the message and image:

See the end of this message for details on invoking 
just-in-time (JIT) debugging instead of this dialog box.

************** Exception Text **************
System.ArgumentOutOfRangeException: InvalidArgument=Value of '0' is not valid for 'index'.
Parameter name: index
   at System.Windows.Forms.ComboBox.ObjectCollection.get_Item(Int32 index)
   at System.Windows.Forms.ComboBox.get_SelectedItem()
   at System.Windows.Forms.ComboBox.get_Text()
   at System.Windows.Forms.ComboBox.WmReflectCommand(Message& m)
   at System.Windows.Forms.ComboBox.WndProc(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

https://i.ibb.co/WzvgnBS/Image-Combo-Box.png

The error occurs in three cases, if you erase everything and hold the backspace for a long time, this case is difficult to repeat. The second case, if you enter random data that is not in the list, delete it, and re-enter the random data. Case three, use random letters, move the mouse to the window, right-click, an error will appear as in the picture, this is the easiest way to generate an error.

By debugging, I stop here

    private void OnSelectionChangeCommitted(object sender, EventArgs e)
    {
        if (this.comboBox.SelectedItem == null)
        {
            return;
        }

It doesn't always work, sometimes the program just crashes. Sometimes it works without errors. The main problem is that 0 appears in the index from somewhere. The complete code can be seen below:

{
    private readonly ComboBox comboBox;
    private string previousSearchterm;

    private object[] originalList;

    public AutoCompleteBehavior(ComboBox comboBox)
    {
        this.comboBox = comboBox;
        this.comboBox.AutoCompleteMode = AutoCompleteMode.Suggest; // crucial otherwise exceptions occur when the user types in text which is not found in the autocompletion list
        this.comboBox.TextChanged += this.OnTextChanged;
        this.comboBox.KeyPress += this.OnKeyPress;
        this.comboBox.SelectionChangeCommitted += this.OnSelectionChangeCommitted;
    }

    private void OnSelectionChangeCommitted(object sender, EventArgs e)
    {
        if (this.comboBox.SelectedItem == null)
        {
            return;
        }

        var sel = this.comboBox.SelectedItem;
        this.ResetCompletionList();
        this.comboBox.SelectedItem = sel;
    }

    private void OnTextChanged(object sender, EventArgs e)
    {
        if (!string.IsNullOrEmpty(this.comboBox.Text) || !this.comboBox.Visible || !this.comboBox.Enabled)
        {
            return;
        }

        this.ResetCompletionList();
    }

    private void OnKeyPress(object sender, KeyPressEventArgs e)
    {
        if (e.KeyChar == '
' || e.KeyChar == '
')
        {
            e.Handled = true;
            if (this.comboBox.SelectedIndex == -1 && this.comboBox.Items.Count > 0
                && this.comboBox.Items[0].ToString().ToLowerInvariant().StartsWith(this.comboBox.Text.ToLowerInvariant()))
            {
                this.comboBox.Text = this.comboBox.Items[0].ToString();
            }

            this.comboBox.DroppedDown = false;

            // Guardclause when detecting any enter keypresses to avoid a glitch which was selecting an item by means of down arrow key followed by enter to wipe out the text within
            return;
        }

        // Its crucial that we use begininvoke because we need the changes to sink into the textfield  Omitting begininvoke would cause the searchterm to lag behind by one character  That is the last character that got typed in
        this.comboBox.BeginInvoke(new Action(this.ReevaluateCompletionList));
    }

    private void ResetCompletionList()
    {
        this.previousSearchterm = null;
        try
        {
            this.comboBox.SuspendLayout();

            if (this.originalList == null)
            {
                this.originalList = this.comboBox.Items.Cast<object>().ToArray();
            }

            if (this.comboBox.Items.Count == this.originalList.Length)
            {
                return;
            }

            while (this.comboBox.Items.Count > 0)
            {
                this.comboBox.Items.RemoveAt(0);
            }

            this.comboBox.Items.AddRange(this.originalList);
        }
        finally
        {
            this.comboBox.ResumeLayout(true);
        }
    }

    private void ReevaluateCompletionList()
    {
        var currentSearchterm = this.comboBox.Text.ToLowerInvariant();
        if (currentSearchterm == this.previousSearchterm)
        {
            return;
        }

        this.previousSearchterm = currentSearchterm;
        try
        {
            this.comboBox.SuspendLayout();

            if (this.originalList == null)
            {
                this.originalList = this.comboBox.Items.Cast<object>().ToArray(); // backup original list
            }

            object[] newList;
            if (string.IsNullOrEmpty(currentSearchterm))
            {
                if (this.comboBox.Items.Count == this.originalList.Length)
                {
                    return;
                }

                newList = this.originalList;
            }
            else
            {
                newList = this.originalList.Where(x => x.ToString().ToLowerInvariant().Contains(currentSearchterm)).ToArray();
            }

            try
            {
                // clear list by loop through it otherwise the cursor would move to the beginning of the textbox
                while (this.comboBox.Items.Count > 0)
                {
                    this.comboBox.Items.RemoveAt(0);
                }
            }
            catch
            {
                try
                {
                    this.comboBox.Items.Clear();
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }
            }

            this.comboBox.Items.AddRange(newList.ToArray()); // reset list
        }
        finally
        {
            if (currentSearchterm.Length >= 1 && !this.comboBox.DroppedDown)
            {
                this.comboBox.DroppedDown = true; // if the current searchterm is empty we leave the dropdown list to whatever state it already had
                Cursor.Current = Cursors.Default; // workaround for the fact the cursor disappears due to droppeddown=true  This is a known bu.g plaguing combobox which microsoft denies to fix for years now
                this.comboBox.Text = currentSearchterm; // Another workaround for a glitch which causes all text to be selected when there is a matching entry which starts with the exact text being typed in
                this.comboBox.Select(currentSearchterm.Length, 0);
            }

            this.comboBox.ResumeLayout(true);
        }
    }
}

Usage:

   new AutoCompleteBehavior(this.comboBoxItems);
    this.comboBoxItems.Items.AddRange(new object[] { "John", "Tina", "Doctor", "Alaska" }); 

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
等待大神解答

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...