Как мне сделать ListBox обновить текст его элемента?

Я делаю пример для кого-то, кто еще не понял, что контроль, как ListBox не должны содержать строки; он хранил отформатированные строки и перепрыгивал через сложные анализирующие обручи, чтобы вернуть данные из ListBox и я хотел бы показать ему, что есть лучший способ.

Я заметил, что если у меня есть объект, хранящийся в ListBox затем обновите значение, которое влияет ToString, ListBox не обновляет себя. Я пробовал звонить Refresh а также Update на контроле, но ни один не работает. Вот код примера, который я использую, он требует, чтобы вы перетащили список и кнопку на форму:

Public Class Form1

    Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
        MyBase.OnLoad(e)

        For i As Integer = 1 To 3
            Dim tempInfo As New NumberInfo()
            tempInfo.Count = i
            tempInfo.Number = i * 100
            ListBox1.Items.Add(tempInfo)
        Next
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        For Each objItem As Object In ListBox1.Items
            Dim info As NumberInfo = DirectCast(objItem, NumberInfo)
            info.Count += 1
        Next
    End Sub
End Class

Public Class NumberInfo

    Public Count As Integer
    Public Number As Integer

    Public Overrides Function ToString() As String
        Return String.Format("{0}, {1}", Count, Number)
    End Function
End Class

Я подумал, что, возможно, проблема в использовании полей, и попытался реализовать INotifyPropertyChanged, но это не имело никакого эффекта. (Причина, по которой я использую поля, заключается в том, что это пример, и мне не хочется добавлять несколько десятков строк, которые не имеют ничего общего с темой, которую я демонстрирую.)

Честно говоря, я никогда не пробовал обновлять элементы на месте, как это раньше; В прошлом я всегда добавлял / удалял элементы, а не редактировал их. Поэтому я никогда не замечал, что не знаю, как заставить это работать.

Так чего мне не хватает?

13 ответов

Решение

BindingList обрабатывает обновления привязок самостоятельно.

using System;
using System.ComponentModel;
using System.Windows.Forms;

namespace TestBindingList
{
    public class Employee
    {
        public string Name { get; set; }
        public int Id { get; set; }
    }

    public partial class Form1 : Form
    {
        private BindingList<Employee> _employees;

        private ListBox lstEmployees;
        private TextBox txtId;
        private TextBox txtName;
        private Button btnRemove;

        public Form1()
        {
            InitializeComponent();

            FlowLayoutPanel layout = new FlowLayoutPanel();
            layout.Dock = DockStyle.Fill;
            Controls.Add(layout);

            lstEmployees = new ListBox();
            layout.Controls.Add(lstEmployees);

            txtId = new TextBox();
            layout.Controls.Add(txtId);

            txtName = new TextBox();
            layout.Controls.Add(txtName);

            btnRemove = new Button();
            btnRemove.Click += btnRemove_Click;
            btnRemove.Text = "Remove";
            layout.Controls.Add(btnRemove);

            Load+=new EventHandler(Form1_Load);
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            _employees = new BindingList<Employee>();
            for (int i = 0; i < 10; i++)
            {
                _employees.Add(new Employee() { Id = i, Name = "Employee " + i.ToString() }); 
            }

            lstEmployees.DisplayMember = "Name";
            lstEmployees.DataSource = _employees;

            txtId.DataBindings.Add("Text", _employees, "Id");
            txtName.DataBindings.Add("Text", _employees, "Name");
        }

        private void btnRemove_Click(object sender, EventArgs e)
        {
            Employee selectedEmployee = (Employee)lstEmployees.SelectedItem;
            if (selectedEmployee != null)
            {
                _employees.Remove(selectedEmployee);
            }
        }
    }
}

Я использую этот класс, когда мне нужно, чтобы список обновлялся.

Обновите объект в списке и затем вызовите любой из включенных методов, в зависимости от того, есть ли у вас доступный индекс или нет. Если вы обновляете объект, который содержится в списке, но у вас нет индекса, вам придется вызвать RefreshItems и обновить все элементы.

public class RefreshingListBox : ListBox
{
    public new void RefreshItem(int index)
    {
        base.RefreshItem(index);
    }

    public new void RefreshItems()
    {
        base.RefreshItems();
    }
}
lstBox.Items[lstBox.SelectedIndex] = lstBox.SelectedItem;
typeof(ListBox).InvokeMember("RefreshItems", 
  BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod,
  null, myListBox, new object[] { });

Используйте свойство источника данных и объект BindingSource между источником данных и свойством источника данных списка. Тогда обновите это.

обновить добавленный пример.

Вот так:

Public Class Form1

    Private datasource As New List(Of NumberInfo)
    Private bindingSource As New BindingSource

    Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
        MyBase.OnLoad(e)

        For i As Integer = 1 To 3
            Dim tempInfo As New NumberInfo()
            tempInfo.Count = i
            tempInfo.Number = i * 100
            datasource.Add(tempInfo)
        Next
        bindingSource.DataSource = datasource
        ListBox1.DataSource = bindingSource
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        For Each objItem As Object In datasource
            Dim info As NumberInfo = DirectCast(objItem, NumberInfo)
            info.Count += 1
        Next
        bindingSource.ResetBindings(False)
    End Sub
End Class

Public Class NumberInfo

    Public Count As Integer
    Public Number As Integer

    Public Overrides Function ToString() As String
        Return String.Format("{0}, {1}", Count, Number)
    End Function
End Class

Если вы наследуете от ListBox, есть защищенный метод RefreshItem, который вы можете вызвать. Просто переэкспонируйте этот метод по своему усмотрению.

public class ListBox2 : ListBox {
    public void RefreshItem2(int index) {
        RefreshItem(index);
    }
}

Затем измените ваш дизайнерский файл, чтобы использовать ваш собственный тип (в данном случае, ListBox2).

Если вы выполняете привязку данных, попробуйте следующее:

      private void CheckBox_Click(object sender, EventArgs e)
{
    // some kind of hack to make the ListBox refresh
    int currentPosition = bindingSource.Position;
    bindingSource.Position += 1;
    bindingSource.Position -= 1;
    bindingSource.Position = currentPosition;
}

В этом случае есть флажок, который обновляет элемент в связанном с данными ListBox. Переключение положения источника привязки назад и вперед, кажется, работает для меня.

Некоторый код, который я создал в VBnet, чтобы помочь в этом. Класс для anObject имеет переопределение ToString для отображения «названия/имени» объекта.

      Dim i = LstBox.SelectedIndex
LstBox.Items(i) = anObject
LstBox.Sorted = True

Это немного непрофессионально, но это работает. Я просто удалил и добавил элемент (также выбрал его снова). Список был отсортирован в соответствии со свойством "отображено и изменено", поэтому, опять же, это было хорошо для меня. Побочным эффектом является то, что возникает дополнительное событие (индекс изменен).

if (objLstTypes.SelectedItem != null)
{
 PublisherTypeDescriptor objType = (PublisherTypeDescriptor)objLstTypes.SelectedItem;
 objLstTypes.Items.Remove(objType);
 objLstTypes.Items.Add(objType);
 objLstTypes.SelectedItem = objType;
}

Если вы используете метод рисования, как:

private void listBox1_DrawItem(object sender, DrawItemEventArgs e)
{
    e.DrawBackground();
    e.DrawFocusRectangle();

    Sensor toBeDrawn = (listBox1.Items[e.Index] as Sensor);
    e.Graphics.FillRectangle(new SolidBrush(toBeDrawn.ItemColor), e.Bounds);
    e.Graphics.DrawString(toBeDrawn.sensorName, new Font(FontFamily.GenericSansSerif, 14, FontStyle.Bold), new SolidBrush(Color.White),e.Bounds);
}

Датчик мой класс.

Так что, если я изменю класс Color где-то, вы можете просто обновить его как:

int temp = listBoxName.SelectedIndex;
listBoxName.SelectedIndex = -1;
listBoxName.SelectedIndex = temp;

И Color обновлю, просто другое решение:)

вы также можете попробовать этот фрагмент кода, он отлично работает:

      Public Class Form1

Dim tempInfo As New NumberInfo()

Private Sub Form1_Load() Handles Me.Load
    For i As Integer = 1 To 3
        tempInfo.Count = i
        tempInfo.Number = i * 100
        ListBox1.Items.Add(tempInfo)
    Next
End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    Dim info As NumberInfo = tempInfo
    Dim obj As New Object
    info.Count += 1
    info.Number = info.Count * 100
    obj = info
    ListBox1.Items.Add(obj)
    ListBox1.Items.RemoveAt(0)
End Sub
End Class

Public Class NumberInfo
Public Count As Integer
Public Number As Integer
    Public Overrides Function ToString() As String
        Return String.Format("{0}, {1}", Count, Number)
    End Function
End Class

Я не знаю много о vb.net, но в C# вы должны использовать источник данных, а затем связать его, вызвав listbox.bind() сделал бы трюк.

Если objLstTypes - ваше имя ListBox, используйте objLstTypes.Items.Refresh(); Надеюсь, это работает...

Другие вопросы по тегам