-2

I have two CheckedListBoxes that look the same (except for the contents). I load one like so:

private void PopulateReportsListBox()
{         
    checkedListBoxReports.Items.AddRange(
        ReportSchedulerConstsAndUtils.Reports.ToArray<object>());
}

public static List<string> Reports = new List<string>
{ 
        "Produce Usage", 
        "Delivery Performance",
        "Fill Rate by Customer / Location",
        "Price Compliance"
};

With that one, I can get the value displayed in the CLB's ItemCheck event like so:

private void checkedListBoxReports_ItemCheck(object sender, 
    ItemCheckEventArgs iceargs)
{
    for (int i = 0; i < checkedListBoxReports.Items.Count; ++i)
    {
        if (i != iceargs.Index) checkedListBoxReports.SetItemChecked(i, 
false);
    }
    String selectedRpt = checkedListBoxReports.SelectedItem.ToString();
    DisableParameterGroupBoxes();
    EnableParameterGroupBox(selectedRpt);
}

"selectedRpt" holds the value I expect ("Produce Usage" if the first item is selected, etc.).

However, I load the other CLB like this, from a DB:

private void PopulateUnitsListBox()
{
    using (SqlConnection con = new 
        SqlConnection(ReportSchedulerConstsAndUtils.CPSConnStr))
    {
        using (SqlCommand cmd = new 
            SqlCommand(ReportSchedulerConstsAndUtils.SelectUnitsQuery, con))
        {
            cmd.CommandType = CommandType.Text;
            using (SqlDataAdapter sda = new SqlDataAdapter(cmd))
            {
                DataTable dt = new DataTable();
                sda.Fill(dt);
                ((ListBox)checkedListBoxUnits).DataSource = dt;
                ((ListBox)checkedListBoxUnits).DisplayMember = "Unit";
                ((ListBox)checkedListBoxUnits).ValueMember = "Unit";
            }
        }
    }
}

...and I cannot access the display value in its ItemCheck event. I have to use the CLB's Text proprty rather than SelectedItem.ToString(). If I use the latter, I get (for all items), "System.Data.DataRowView"

Why? And are there any "gotchas" I should be aware of when using "Text"? Is it reliable/do I need to Trim() it?

B. Clay Shannon-B. Crow Raven
  • 8,547
  • 144
  • 472
  • 862
  • 2
    In the second case you bound the control, so each Item (including `SelectedItem`) is one row of the datatable. When they are bound I usually use `SelectedValueChanged` event and examine the `SelectedValue` becaue that is where the piece of info I want is mapped to. – Ňɏssa Pøngjǣrdenlarp Feb 12 '16 at 00:42

2 Answers2

1

SelectedItem property returns the currently selected object in the collection that are using as a source, not the text displayed in the control.

When CheckedListBox will display the items, control first checks for property DisplayMember, if the specified property does not exist on the object or the value of DisplayMember is an empty string, the results of the object's ToString() method are displayed instead.

In your first CheckedListBox you are using an array of objects as item collection, where elements are actually strings and DisplayMember property is empty. Therefore the item and displayed text are the same, the string.

In your second CheckedListBox you are using a DataTable (which you can see it as an enumerable of DataRowView) as item collection, using DisplayMember = "Unit". Therefore, in this case SelectedItem is a DataRowView and text displayed is the member "Unit".

If you want to work always with text displayed in both CheckedListBoxes, then use the property you said Text. This property gets the text displayed of the currently selected item (no matter the source).

private void checkedListBoxReports_ItemCheck(object sender, ItemCheckEventArgs iceargs)
{
    for (int i = 0; i < checkedListBoxReports.Items.Count; ++i)
    {
        if (i != iceargs.Index) 
            checkedListBoxReports.SetItemChecked(i, false);
    }

    string selectedRpt = checkedListBoxReports.Text;

    DisableParameterGroupBoxes();
    EnableParameterGroupBox(selectedRpt);
}

About your concerns for this property:

Text will return exactly the value of the function ToString() or the value of property specified in DisplayMember.

Also, you can searches for a item setting this property, the item whose displayed text is equal to the specified text will be selected.

If the SelectionMode property is set to SelectionMode.MultiExtended, this property returns the text of the first selected item.

I hope this helps you.

Arturo Menchaca
  • 15,783
  • 1
  • 29
  • 53
1

Why do CheckedListBox's SelectedItem work differently depending on how the CLB was populated?

It doesn't work differently. It always returns an object from Items collection or null. It's a shortcut to something like this

int selectedIndex = listBox.SelectedIndex;
object selectedItem = selectedIndex >= 0 ? listBox.Items[selectedIndex] : null;

"selectedRpt" holds the value I expect ("Produce Usage" if the first item is selected, etc.).

and then

I have to use the CLB's Text property rather than SelectedItem.ToString(). If I use the latter, I get (for all items), "System.Data.DataRowView"

You shouldn't be using SelectedItem (or any Items collection item) .ToString() method for getting the display text at the first place.

The control itself uses some logic to determine that text - and yes, in some case in could be ToString() method, but not always - for instance DisplayMember property changes that behavior.

But the point is that you don't need to know that logic. The control exposes publicly the method it's using internally. It's called (surprisingly) GetItemText

public string GetItemText(
    object item
)

and according to the documentation

Returns the text representation of the specified item.

and also

If the DisplayMember property is not specified, the value returned by GetItemText is the value of the item's ToString method. Otherwise, the method returns the string value of the member specified in the DisplayMember property for the object specified in the item parameter.

Shortly, you should always use that method.

  • for selected item:

    listBox.GetItemText(listBox.SelectedItem)

  • for specific index:

    listBox.GetItemText(listBox.Items[index])

Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343