Как мне сделать 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();
}
}
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(); Надеюсь, это работает...