1

I have a combobox with DropDownStyle "DropDown". When I enter text the comboBox1.Text is not the text I see in text area of the comboBox and the SelectedIndex + SelectedItem does not correspond with the text inserted in my specific scenario.

It seems the selectedItem (+SelectedIndex) corresponds to the text when you open the dropdownlist and not when you close it.

Create a new form, place a combobox and a button on it. Add an eventhandler to the comboBox1 event for: - textchanged - dropdown - dropdownclosed and write the following properties of the combobox to the output window (or a textbox): - comboBox1.Text - comboBox1.SelectedIndex - comboBox1.SelectedItem - comboBox1.SelectedValue - comboBox1.FormattingEnabled I added an eventhandler for the click event of the button showing the comboBox1.Text as well.

Create a very simple class (e.g. TestItems) with 3 properties (a code, a description and a DescriptionUppercase). I tested it by filling the combobox items directly with instances of the TestItems class and by adding them first to a List and then setting the DataSource of the combobox to the List<>. It works a bit different, but both don't work the way I expect it to. I'll stick to the List. I fill the list with 100 testitems (Code="I0" .. "I99" and description "Item 1" .. Item 100"). I set the DisplayMember to "Description" and the ValueMember to "Code".

I insert three times the text "Item 578" using a slightly different scenario.

Scenario 1: SelectedIndex is -1 and SelectedItem is null. - just write "Item 578" in the combobox (don't open the dropdown) Logging from the output window:

TextChanged: ComboBox.Text = [ITEM 0] - ComboBox SelectedIndex = [0] - ComboBox SelectedItem = [I0 + Item 0] - ComboBox SelectedValue = [I0] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [I] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [It] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [Ite] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [Item] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [Item ] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [Item 5] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [Item 57] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [Item 578] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True]

Scenario 2: selectedIndex = 0, SelectedItem is "Item 0" - Open the dropdownlist (click on the arrow at the right side of the combobox) - Enter "Item 578" - close the dropdownlist Logging:

TextChanged: ComboBox.Text = [ITEM 0] - ComboBox SelectedIndex = [0] - ComboBox SelectedItem = [I0 + Item 0] - ComboBox SelectedValue = [I0] - Combobox FormattingEnabled = [True] Dropdown opened: ComboBox.Text = [ITEM 0] - ComboBox SelectedIndex = [0] - ComboBox SelectedItem = [I0 + Item 0] - ComboBox SelectedValue = [I0] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [I] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [It] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [Ite] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [Item] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [Item ] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [Item 5] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [Item 57] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [Item 578] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] DropDownClosed: ComboBox.Text = [Item 578] - ComboBox SelectedIndex = [0] - ComboBox SelectedItem = [I0 + Item 0] - ComboBox SelectedValue = [I0] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [Item 578] - ComboBox SelectedIndex = [0] - ComboBox SelectedItem = [I0 + Item 0] - ComboBox SelectedValue = [I0] - Combobox FormattingEnabled = [True]

Scenario 3: SelectedIndex is 57 and SelectedItem is "Item 57" - Insert "Item 57" - Open the dropdownlist - Add an "8" to the end Logging:

TextChanged: ComboBox.Text = [ITEM 0] - ComboBox SelectedIndex = [0] - ComboBox SelectedItem = [I0 + Item 0] - ComboBox SelectedValue = [I0] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [I] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [It] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [Ite] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [Item] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [Item ] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [Item 5] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [Item 57] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] Dropdown opened: ComboBox.Text = [Item 57] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [ITEM 578] - ComboBox SelectedIndex = [-1] - ComboBox SelectedItem = [] - ComboBox SelectedValue = [] - Combobox FormattingEnabled = [True] DropDownClosed: ComboBox.Text = [ITEM 578] - ComboBox SelectedIndex = [57] - ComboBox SelectedItem = [I57 + Item 57] - ComboBox SelectedValue = [I57] - Combobox FormattingEnabled = [True] TextChanged: ComboBox.Text = [ITEM 578] - ComboBox SelectedIndex = [57] - ComboBox SelectedItem = [I57 + Item 57] - ComboBox SelectedValue = [I57] - Combobox FormattingEnabled = [True]

Leaving the combobox (losing focus) doesn't change the SelectedItem. An additional remark: if you set the "FormattingEnabled" property of the combobox to false, then the combobox1.Text will be the text of the selectedItem (displaymember). So in the second scenario, you will see in the textarea of the combobox "Item 57", but when you click the button showing the combobox.Text it will say "Item 0".

The code to reproduce this:

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

namespace TestCombobox
{
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
        this.comboBox1.FormattingEnabled = true;
        comboBox1.DropDownClosed += ComboBox1_DropDownClosed;
        comboBox1.DropDown += ComboBox1_DropDown;
        comboBox1.TextChanged += ComboBox1_TextChanged;
        comboBox1.DisplayMember = "DescriptionUpperCase";
        comboBox1.ValueMember = "Code";
        FillCombo();
    }
    private void ComboBox1_DropDown(object sender, EventArgs e)
    {
        ShowDebugInfoCombobox("Dropdown opened");
    }

    private void ComboBox1_TextChanged(object sender, EventArgs e)
    {
        ShowDebugInfoCombobox("TextChanged");
    }

    private void ShowDebugInfoCombobox(string Info)
    {
        Debug.WriteLine($"{Info}: ComboBox.Text = <{comboBox1.Text}> - ComboBox SelectedIndex = <{comboBox1.SelectedIndex}> - ComboBox SelectedItem = <{comboBox1.SelectedItem}> - ComboBox SelectedValue = <{comboBox1.SelectedValue}>  - Combobox FormattingEnabled = <{comboBox1.FormattingEnabled}>");
    }

    private void ComboBox1_DropDownClosed(object sender, EventArgs e)
    {
        ShowDebugInfoCombobox("DropDownClosed");
    }

    private void FillCombo()
    {
        List<TestItems> aList = new List<TestItems>();
        for (int cnt = 0; cnt < 100; cnt++)
        {
            aList.Add(new TestItems($"I{cnt.ToString()}", $"Item {cnt.ToString()}"));
            //comboBox1.Items.Add(new TestItems($"I{cnt.ToString()}", $"Item {cnt.ToString()}"));
        }
        comboBox1.DataSource = aList;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        MessageBox.Show(this.comboBox1?.SelectedItem?.ToString() + " Text = " + this.comboBox1.Text);

    }
}
public class TestItems
{
    public string Code { get; set; }
    public string Description { get; set; }
    public string DescriptionUpperCase { get { return Description.ToUpper(); }  }

    public TestItems(string code, string description)
    {
        this.Code = code;
        this.Description = description;
    }
    public override string ToString()
    {
        return this.Code + " + " + this.Description;
    }
}

}

Looks like a bug to me, not sure what your opinions are.

Kind regards, Bart

TakeItEasy
  • 11
  • 4

1 Answers1

1

First, that's a whole lot of extra info, which is why you haven't already gotten an answer. Second, if you want to "enter" text into a comboBox, you should use the KeyPress event and take the text from that, filtered by the enter key. Third, if you want to set the selected item to null if it isn't in the source data, you have to add conditional code to do that. This is a very simple example using a generic string list:

    List<string> source = new List<string>();

    public Form1()
    {
        InitializeComponent();
        source.Add("Item 1");
        source.Add("Item 2");
        comboBox1.Items.AddRange(source.ToArray());
    }

    // Enter key detection as shown in https://stackoverflow.com/a/1226740/4034168
    private void comboBox1_KeyPress(object sender, KeyPressEventArgs e)
    {
        if (e.KeyChar == 13)
        {
            if (!source.Contains(comboBox1.Text))
            {
                comboBox1.SelectedIndex = -1;
                comboBox1.SelectedItem = null;
            }
        }
    }

Also, if you want to prevent the user from being able to type any key (and yet still select from the drop down items), just use the TextUpdate event like this (add remove the KeyPress event since you wouldn't need it):

    private void comboBox1_TextUpdate(object sender, EventArgs e)
    {
        if (!source.Contains(comboBox1.Text)) comboBox1.SelectedItem = null;
    }
Michael Tracy
  • 210
  • 3
  • 9
  • Hi Michael, I think you're missing the point here. The problem is that the selectedItem property is set automaticly to three different items when I close the dropdown after I entered the same text ("Item 578"). I don't want to add items, I just want the SelectedItem to be null when I enter a text that does not correspond to any of the displaymembers of the datasource. And it sounds pretty normal that the combobox.text property returns the text you see in the combobox's text area. – TakeItEasy Mar 28 '18 at 19:50
  • I edited the question to set the SelectedItem to null if the entered text is not in the datasource. – Michael Tracy Mar 28 '18 at 22:30
  • FormattingEnabled has no bearing on whether or not TextUpdate fires and since that fires before TextChanged, I would still use TextUpdate to process what the user typed in the box. If TextUpdate sets SelectedItem to null, TextChanged does not alter that. Here's the sequence of events (output to a textbox) where the user chooses Item 2 and then types a 1 after it and clicks somewhere else on the form: (DropDownClosed fired) then (TextChanged fired) ComboBox text and selected item are both Item 2 then TextUpdate fires with text Item 21 (and we set SelectedItem to null) THEN TextChanged fires. – Michael Tracy Mar 29 '18 at 11:53
  • Hi Michael, thx for responding again. I just tested it, but it doesn't work. First: the event TextUpdate doesn't fire after I close the dropdownlist. Second: in the TextUpdate the SelectedItem is the item it was before my new input, so when I set the SelectedItem to null it sets the text to an emptystring. This happens only when the selectedItem was not null before, so only the first character empties the text, but that spoils the user experience enough. If I use the textchanged event (gets fired after the dropdownclosed event) instead of the TextUpdate, then I trigger an endless loop. – TakeItEasy Mar 29 '18 at 11:59
  • User experience is a personal preference thing and in the sequence described in my previous comment, DropDownClosed *only* fires when the user selects an item from the list - it does not fire after text is typed into the box. – Michael Tracy Mar 29 '18 at 12:02
  • The formattingEnabled = true makes sure the combobox.text is the text entered. If set to false your line source.Contains(comboBox1.Text)) will contain the text of the selected item and not the text entered (very strange to me, but feel free to check it out). That's why I mentioned it. It's also important that I use an event that fires after the dropdownclosed because it seems the selecteditem is changed AFTER the textUpdate and before/in the dropdownclosed. – TakeItEasy Mar 29 '18 at 12:04
  • The dropdownclosed fires when you close the dropdown, independent if you select an item or not. Just open en close it using the arrow and you will get a DropDown event and a DropdownClosed event. Also when a user types an "I" they expect to see an I, not to have the text simply cleared. – TakeItEasy Mar 29 '18 at 12:05
  • Okay, I have to ask - *why* would someone click the arrow (which is meant to select from listed items) *after* typing something in the box?? Also, I don't understand what you expect from the user. If they type Item 21 and there is no 21, they need to know that's not valid so either you discard what they type or you tell them (via MessageBox) that it's not valid. – Michael Tracy Mar 29 '18 at 12:08
  • My point with the "extra info" was to make clear that when I enter a text it shouldn't matter if I open the dropdown or not. And it shouldn't matter when I open/close the dropdownlist. As long as I enter a text that is not in the dropdownlist the selectedItem should be null. – TakeItEasy Mar 29 '18 at 12:11
  • So you want the selected item to be null and the text to retain what the user typed? That makes no sense. – Michael Tracy Mar 29 '18 at 12:12
  • The style of the combobox is dropdown, if I want them to select only what is in the list I'll set it to dropdownlist. – TakeItEasy Mar 29 '18 at 12:14
  • It's how the dropdown works. Just type some text in a combobox and the text is what you type. The selecteditem is null and the selectedindex is -1. Seems very logical to me, I have no item that corresponds to my inputted text so the selectedItem should be null. – TakeItEasy Mar 29 '18 at 12:15
  • You are going in circles here - if you want to use the text from the box, use the text property only and if you want to use something selected from the list, use selected item. There's no point to using both. – Michael Tracy Mar 29 '18 at 12:28
  • My point is that the selecteditem gets set when it shouldn't get set. It should get set (like you said) when you select an item from the list (or, also nice) when you enter a text corresponding with an item in the dropdownlist. But it gets set when you close the dropdownlist when you don't select an item from the list to an item that doesn't correspond to the text you entered. So IF the SelectedItem is set you should be able to trust that property that no manual changes have been done to the selection(Text). But that is not the case. – TakeItEasy Mar 29 '18 at 12:32
  • The docs (https://msdn.microsoft.com/en-us/library/system.windows.forms.combobox.selecteditem(v=vs.110).aspx) say "If the object does not exist in the list, the SelectedIndex property is left at its current value." so its never going to do what you want it to. – Michael Tracy Mar 29 '18 at 12:40
  • The full text is:When you set the SelectedItem property to an object, the ComboBox attempts to make that object the currently selected one in the list. If the object is found in the list, it is displayed in the edit portion of the ComboBox and the SelectedIndex property is set to the corresponding index. If the object does not exist in the list, the SelectedIndex property is left at its current value. I don't set the selectedItem to an object. I type text – TakeItEasy Mar 29 '18 at 12:46
  • That same link says "The object that is the currently selected item or null if there is no currently selected item.". When I change the text after I selected an item should be enough to deselect my selection. Also when I don't select an item, I open the dropdownlist, I append text and I close the dropdown the combobox sets the selecteditem (when I didn't select any item) and it sets the selectedItem to the item corresponding to the value it was when I opened the dropdownlist (opening ain't selecting). – TakeItEasy Mar 29 '18 at 12:48
  • My point is that it works the way Microsoft intended it to work, If you want different behavior, then you should make your own custom DropDown control; there are several websites that show how to do that. – Michael Tracy Mar 29 '18 at 12:51
  • If you have an item selected (the first Item is selected automatic when starting the program if you use a datasource) and you type one character the SelectedItem is changed (automatic) from the first item to null. Looks like a clear indication that changing the text (to a value that doesn't respond to an item in the dropdown ?) should set the SelectedItem to null. – TakeItEasy Mar 29 '18 at 12:56