Kosmo零
Kosmo零

Reputation: 4151

How we can refresh items text in ListBox without reinserting it?

I have the class TestClass that has ToString overriden (it returns Name field). I have instances of TestClass added into ListBox and at certain point I need to change Name of one of this instances, how then I can refresh it's text in ListBox?

using System;
using System.Windows.Forms;

namespace TestListBox
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            listBox1.Items.Add(new TestClass("asd"));
            listBox1.Items.Add(new TestClass("dsa"));
            listBox1.Items.Add(new TestClass("wqe"));
            listBox1.Items.Add(new TestClass("ewq"));
        }

        private void button1_Click(object sender, EventArgs e)
        {
            ((TestClass)listBox1.Items[0]).Name = "123";
            listBox1.Refresh(); // doesn't help
            listBox1.Update(); // same of course
        }
    }

    public class TestClass
    {
        public string Name;

        public TestClass(string name)
        {
            this.Name = name;
        }

        public override string ToString()
        {
            return this.Name;
        }
    }
}

Upvotes: 8

Views: 14740

Answers (5)

leifel
leifel

Reputation: 31

I use the following code:

public static void RefreshItemAt (ListBox listBox, int itemIndex)
{
    if (itemIndex >= 0)
    {
        Rectangle itemRect = listBox.GetItemRectangle(itemIndex);
        listBox.Invalidate(itemRect);
        listBox.Update();
    }
}

Upvotes: 0

BLaminack
BLaminack

Reputation: 1493

I have encountered this same issue and tried all sorts of different ways to tr y to get the displayed text of an item to actually reflect the underlying item value. After going through all the available properties I found this to be the simplest. lbGroupList.DrawMode = DrawMode.OwnerDrawFixed; lbGroupList.DrawMode = DrawMode.Normal; It triggers the appropriate events within the control to update the displayed text.

Upvotes: 8

Martin Staufcik
Martin Staufcik

Reputation: 9490

try

listBox1.Items[0] = listBox1.Items[0];

Upvotes: 25

Josh Harrison
Josh Harrison

Reputation: 121

You could use a BindingList:

        items = new BindingList<TestClass>( );
        listBox1.DataSource = items;
        listBox1.DisplayMember = "_Name";

Then to refresh the list call:

        items.ResetBindings( );

edit: Also don't forget to create a get Property for Name

      public string _Name
    {
        get { return Name; }
        set { Name= value; }
    }

Upvotes: 2

CSharpie
CSharpie

Reputation: 9467

Your Testclass needs to implement INotifyPropertyChanged

public class TestClass : INotifyPropertyChanged
{
    string _name;

    public string Name
    {
        get { return _name;}
        set 
        {
              _name = value;
              _notifyPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void _notifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));  
    }

    public TestClass(string name)
    {
        this.Name = name;
    }

    public override string ToString()
    {
        return this.Name;
    }
}

However this only works if you use Columns that do not rely on the ToString() but bind the property Name

This can be done by altering your code:

somewhere in class declare

BindingList<TestClass> _dataSource = new BindingList<TestClass>();

In initializeComponent write

listBox1.DataSource = _dataSource;

Then do all operations on _dataSource instead of Listbox.

Upvotes: 3

Related Questions