Items on ListBox show up as a class name
Asked Answered
E

2

6

The problem is as follows: I connected the ListBox with a list of elements of some custom class (List<Person> persons = new List<Person>()) using DataSource property. Of course ValueMember and DisplayMember are both assigned to appropriate properties of this class. When I first load data, everything looks ok. However, when I click on some item (i.e. 7th position, counting from 1) and then rebuild the list AND the number of elements is LESS than 7, as a result I can't see the proper texts on the list. Instead, every item shows up as a class name, preceded by the namespace.

In other words, instead of the list:

  • John Doe
  • Jane Doe
  • Somebody Else

I see this:

  • MyNamespace.Person
  • MyNamespace.Person
  • MyNamespace.Person

It looks like it depends on last SelectedIndex. If there is no longer an item with that index (there are less items), the problem occurs.

I've tried different combinations of reassigning ValueMember and DisplayMember, as well as assigning null to the DataSource property of the list and reassign the list to this property, even tried to assign -1 to SelectedIndex before unbinding, but none of them helped.

[Edit]

I was asked to show some code. I'll paste the relevant fragments:

1. Class Person:

public class Person
{
    private int id;
    private string name;

    public Person(int m_id, string m_name)
    {
        id = m_id;
        name = m_name;
    }

    public int Id
    {
        get
        {
            return id;
        }
    }

    public string Name
    {
        get
        {
            return name;
        }
    }
}`

2. In a constructor of the form:

List<Person> persons = new List<Person>();

3. In a method fired on buton1 click:

listBox1.DataSource = null;    // this is optional. Commenting this line out doesn't help
persons.Add(new Person(1, "John Doe"));
persons.Add(new Person(2, "Jane Doe"));
persons.Add(new Person(3, "Somebody Else"));
listBox1.ValueMember = "Id";
listBox1.DisplayMember = "Name";
listBox1.DataSource = persons;

4. In a method fired on buton2 click:

listBox1.DataSource = null;    // this is optional. Commenting this line out doesn't help
persons.Add(new Person(1, "Person One"));
persons.Add(new Person(2, "Person Two"));
listBox1.ValueMember = "Id";
listBox1.DisplayMember = "Name";
listBox1.DataSource = persons;

When I click button1, the listbox is filled and everything works fine. When I select last item ("Somebode Else") and then clisk button2, the listbox shows 2 identical items: "MyNamespace.Person".

[Edit 2 - complete code of form]

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace MyNamespace
{
    public partial class Form1 : Form
    {
        private List<Person> persons = new List<Person>();
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            persons.Clear();
            persons.Add(new Person(1, "John Doe"));
            persons.Add(new Person(2, "Jane Doe"));
            persons.Add(new Person(1, "Somebody Else"));
            listBox1.DataSource = null;
            listBox1.ValueMember = "Id";
            listBox1.DisplayMember = "Name";
            listBox1.DataSource = persons;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            persons.Clear();
            persons.Add(new Person(1, "Person One"));
            persons.Add(new Person(2, "Person Two"));
            listBox1.DataSource = null;
            listBox1.ValueMember = "Id";
            listBox1.DisplayMember = "Name";
            listBox1.DataSource = persons;
        }
    }

    class Person
    {
        private int id;
        private string name;

        public Person(int m_id, string m_name)
        {
            id = m_id;
            name = m_name;
        }

        public int Id
        {
            get
            {
                return id;
            }
        }

        public string Name
        {
            get
            {
                return name;
            }
        }

        public string ToString()
        {
            return id + ". " + name;
        }
    }
}

Steps to reproduce the problem:

  1. Run the form
  2. Click button1
  3. Select last position on the list ("Somebody Else")
  4. Click button2

If you select "John Doe" or "Jane Doe" on the list, everything works fine. It seems to "crash" when the selected index is not valid after rebuilding the list. I guess it's some bug.

Eppie answered 25/5, 2014 at 16:40 Comment(2)
You have to show your code in order for us to help you.Loathly
I can't reproduce your problem...I have tried your code and it work great on both buttons....without overriding ToString()Agathy
Y
14

When one sets the DataSource to null it clears the DisplayMember value. So to resolve, set it after you set a new DataSource and the problem disappears.

listBox1.DataSource = null;    // this is optional. Commenting this line out doesn't help
persons.Add(new Person(1, "John Doe"));
persons.Add(new Person(2, "Jane Doe"));
persons.Add(new Person(3, "Somebody Else"));
listBox1.DataSource = persons;
listBox1.DisplayMember = "Name";

Otherwise in the Person class override the ToString method to ensure that the proper property will be shown if DataMember is empty:

public class Person
{
    private int id;
    private string name;

    public Person(int m_id, string m_name)
    {
        id = m_id;
        name = m_name;
    }

    public int Id
    {
        get
        {
            return id;
        }
    }

    public string Name
    {
        get
        {
            return name;
        }
    }
    public override string ToString()
    {
        return name;
    }
}

With this whenever you set the listbox datasource to a List<Person> the listbox will automatically use the ToString method as the display. Using the selecteditem is simply a matter of casting it as Person, (Person)listBox1.SelectedItem.

Yearround answered 25/5, 2014 at 20:1 Comment(5)
Sorry, I forgot to mention: I've already overriden this method and - as I said in the first post - the list shows appropriate content as long as the recently selected index is not too big according to the new content of the list. As for now, setting this index to 0 just before reloading the list solves the problem. It's quite protetic, but worksEppie
@Eppie - There must be something else that your code is doing. Try as I might I can't duplicate your results. The only way I can get the listbox to show the class name is by taking out the ToString override.Yearround
No, my sample code is no more than I pasted. I'm going to show all the code that results in such a strange behaviourEppie
@Eppie - Sorry it took so long. I was able to duplicate your problem and I added the answer for you.Yearround
Thank you for your response. As you may see, I showed the complete code. It implements ToString() with class Person and also reassigns DisplayMember after listBox1.DataSource = null. So the only thing which may cause the problem is the place where I reassign my list to DataSource. It obviously should go before setting DisplayMember. Thanks for pointing this!Eppie
R
0

Try setting the "ValueMember" and "DisplayMember" properties only once before any datasource binding. Can you set them in the designer? Then always before changing the datasource, run ListBox's "ClearSelected()"-method to clear any selections. Then unbind the datasource, edit the list, then set the edited list as datasource. Seems that this behavior is some kind of bug. Try if this helps.

Righthanded answered 26/5, 2014 at 8:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.