How can I link class instances and ListBox values?

There is a ListBox to which elements are added using a loop, each ListBox element is a property of a class instance in a string representation(there are several instances of the class and they are combined into an array). You need to make it possible to edit an instance of the class by selecting the appropriate element in the ListBox

Author: deced, 2018-07-27

1 answers

You can put any objects in the ListBox component, including objects of different types. In this case, the list will display the text that gives the virtual method ToString() of the Object class for the displayed object.

(let me remind you that the Object class is the very first "progenitor" of any class, even when you don't explicitly inherit your class from anything).

Accordingly, the options for solving the problem are:

  1. In its own class, which describes the subject matter to be solved scope, redefine the ToString() method so that it outputs the desired text. Throw objects of this class into ListBox, and ListBox will display the output of the ToString() method.
  2. If the first option is not suitable (for example, it is not possible to make changes to an existing class), then you can create a new wrapper class, in which we redefine the ToString() method and give a reference to the object to be edited.
  3. If you are going to put objects of the same type( class), and this class has public property with a suitable one (to be displayed in the list) with a text value, then specify the name of this property in the ValueMember property of the ListBox component. (if there is no property with the specified name, the element text will be taken from ToString())

In any case, to access the selected object, we refer to the SelectedItem property of the ListBox component - this property is expected to have the type Object, so do not forget to cast it to the desired type.

Demo code example (a simple form with a single ListBox component):

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        // вариант 1
        listBox1.Items.Add((int) 1);
        listBox1.Items.Add((long) 2);
        listBox1.Items.Add((char) '3');
        listBox1.Items.Add((string) "4");
        listBox1.Items.Add((Type) typeof(Form1));

        // вариант 2
        listBox1.Items.Add(new ObjectInfo(typeof(Form1), "'typeof(Form1)' (reflection)"));
        listBox1.Items.Add(new ObjectInfo(this, "This Form"));
        listBox1.Items.Add(new ObjectInfo(listBox1, "This ListBox object"));

        /*
            альтернативный (3) вариант: отображать свойство "Name";
            сработает для последних 4-х элементов списка (типы разные, но свойство с именем "Name" имеют)
            (для первых же 4-х элементов списка будет отображена выдача метода "ToString()", за неимением свойства "Name")
        */
        // listBox1.ValueMember = "Name";
    }

    private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (listBox1.SelectedItem is ObjectInfo oi)
            MessageBox.Show($"Выбран элемент: '{oi.Name}' класса '{oi.Object.GetType().Name}'");
    }
}

// класс для объекта-обёртки
public class ObjectInfo
{
    public Object Object { get; private set; }
    public string Name { get; private set; }
    // ... прочие свойства и методы

    public ObjectInfo(Object obj, string name)
    {
        this.Object = obj;
        this.Name = name;
        // ... прочая инициализация
    }

    // здесь вернём то, что хотим видеть в "ListBox" по дефолту
    // (при закомментированной строке с "listBox1.ValueMember")
    public override string ToString()
    {
        return $"'{Name}' of type '{Object.GetType().Name}'";
    }
}
 1
Author: velial, 2018-07-27 18:20:01