Search code examples
c#winformsexceptionlistboxselectedvalue

Does not find ValueMember after adding from another ListBox


Have two ListBox. Double-click on the lbSubject element, adds this element to the second lbSelectedSubject. Provided that this element is not already in lbSelectedSubject. The check for the presence of elements goes through the List<int> to which ValueMember is added.

List<int> selectedValueSubject;

private void Form_Load(object sender, EventArgs e)
{
    selectedValueSubject = new List<int>();
    lbSelectedSubject.DisplayMember = "Title";
    lbSelectedSubject.ValueMember = "Id";

    lbSubject.DataSource = bindingSubjectDefault;
    lbSubject.DisplayMember = "Title";
    lbSubject.ValueMember = "Id";
}

private void LbSubject_MouseDoubleClick(object sender, MouseEventArgs e)
{
    int index = lbSubject.IndexFromPoint(e.Location);
    if (index != ListBox.NoMatches)
    {
        int id = (int)lbSubject.SelectedValue;
        if (IsNotConstrain(id, selectedValueSubject.ToArray()))
        {
            selectedValueSubject.Add(id);
            lbSelectedSubject.Items.Add(lbSubject.Items[index]);
        }
    }
}

private void LbSelectedSubject_MouseDoubleClick(object sender, MouseEventArgs e)
{
    int index = lbSelectedSubject.IndexFromPoint(e.Location);
    if (index != ListBox.NoMatches)
    {
        selectedValueSubject.Remove((int)lbSelectedSubject.SelectedValue);
        lbSelectedSubject.Items.RemoveAt(index);
    }
}

private bool IsNotConstrain(int id, int[] keys)
{
    bool result = true;
    foreach(int key in keys)
    {
        if (key == id)
        {
            result = false;
            break;
        }
    }

    return result;
}

The addition is correct. In the sense that the person creates and displays the Title. The problem is the reverse process. Remove from lbSelectedSubject. A string in LbSelectedSubject_MouseDoubleClick throws an exception:

lbSelectedSubject.SelectedValue

System.NullReferenceException: "object Reference not pointing to object instance." System.Windows.Forms.ListControl.SelectedValue.get returned null.

This lbSelectedSubject.Items contains the correct item and settings for Display and Value. SelectedItem also normally, not null.

введите сюда описание изображения

введите сюда описание изображения

введите сюда описание изображения

введите сюда описание изображения

The same item in the list lbSubject contains the correct value Value, not null.

введите сюда описание изображения

Why after adding in the lbSelectedSubject through the line

lbSelectedSubject.Items.Add(lbSubject.Items[index]);

It turns out that SelectedValue does not work? Under other equal conditions lbSubject.

UPDATE It is not duplicate.


Solution

  • You cannot use the SelectedValue property if the ListBox has no DataSource.
    A simple workaround is to provide the second listbox with an empty datasource of the same type you have set for the first one.

    So suppose to have your first ListBox binded to a class of this type

    public class TitleData
    {
        public int Id { get; set; }
        public string Title{ get; set; }
    }
    

    Now when you build the first listbox DataSource you build as well the second DataSource with

    public void Form_Load(object sender, EventArgs e)
    {
        selectedValueSubject = new List<int>();
        bsSelected.DataSource = new List<TitleData>();
    
        lbSelectedSubject.DisplayMember = "Title";
        lbSelectedSubject.ValueMember = "Id";
        lbSelectedSubject.DataSource = bsSelected; // <= this is a BindingSource at the class level
        .....
    
    }
    

    Finally in the MouseDoubleClick event you can reference your data with SelectedItem and SelectedValue

    private void LbSelectedSubject_MouseDoubleClick(object sender, MouseEventArgs e)
    {
        int index = lbSelectedSubject.IndexFromPoint(e.Location);
        if (index != ListBox.NoMatches)
        {
            // Always do this before removing the element from the BindingSource
            selectedValueSubject.Remove((int)lbSelectedSubject.SelectedValue);
    
            bsSelected.Remove(lbSelectedSubject.SelectedItem);
            // lbSelectedSubject.Items.RemoveAt(index);
        }
    }
    

    Also, a minor problem. Never set the DisplayMember and ValueMember after setting the Datasource property. Doing in that order will cause the Binding to happen two times, once when you set the Datasource and another one when you set the ValueMember.