如何让 ListBox 刷新其项目文本?

Posted

技术标签:

【中文标题】如何让 ListBox 刷新其项目文本?【英文标题】:How do I make a ListBox refresh its item text? 【发布时间】:2010-09-08 20:27:35 【问题描述】:

我正在为尚未意识到 ListBox 之类的控件不必包含字符串的人做一个示例;他一直在存储格式化的字符串并跳过复杂的解析循环以从ListBox 中取回数据,我想向他展示有更好的方法。

我注意到如果我有一个对象存储在ListBox 中,然后更新一个影响ToString 的值,ListBox 不会自行更新。我试过在控件上调用RefreshUpdate,但都不起作用。这是我正在使用的示例的代码,它需要您将一个列表框和一个按钮拖到表单上:

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,但这没有效果。 (我使用字段的原因是因为它是一个示例,我不想添加与我正在演示的主题无关的几十行。)

老实说,我以前从未尝试过像这样更新项目;过去我一直在添加/删除项目,而不是编辑它们。所以我从来没有注意到我不知道如何完成这项工作。

那么我错过了什么?

【问题讨论】:

【参考方案1】:

在列表框的数据源和数据源属性之间使用数据源属性和 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

【讨论】:

优秀。出于某种原因,无论我在 WPF 中使用了多少,WinForms 中的数据绑定都不会成为我的解决方案。 嘿,它曾经比这更有趣。类似于: ((CurrencyManager)this.BindingContext[ListBox1]).Refresh();从 BindingContext 中获取“隐藏”对象,然后将其转换为货币管理器。虽然这是 C#,因为我从未在 VB.NET 中这样做过。 这是一个很好的答案,但最终 geno 建议使用 BindingList 会导致工作量减少。【参考方案2】:

我对 vb.net 了解不多,但在 C# 中,您应该使用数据源,然后通过调用 listbox.bind() 绑定它就可以了。

【讨论】:

那是互联网。我认为这个人对 WinForm 感兴趣。【参考方案3】:

如果您从 ListBox 派生,则可以调用 RefreshItem 受保护的方法。只需在您自己的类型中重新公开此方法即可。

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

然后更改您的设计器文件以使用您自己的类型(在本例中为 ListBox2)。

【讨论】:

【参考方案4】:

当我需要更新列表框时,我会使用这个类。

更新列表中的对象,然后调用任一包含的方法,具体取决于您是否有可用的索引。如果要更新列表中包含的对象,但没有索引,则必须调用 RefreshItems 并更新所有项目。

public class RefreshingListBox : ListBox

    public new void RefreshItem(int index)
    
        base.RefreshItem(index);
    

    public new void RefreshItems()
    
        base.RefreshItems();
    

【讨论】:

注意,RefreshItem 仅在设置了DisplayMember 属性时才有效。【参考方案5】:

这有点不专业,但确实有效。 我刚刚删除并添加了该项目(也再次选择了它)。 该列表是根据“显示和更改”属性排序的,所以对我来说也很好。副作用是引发了额外的事件(索引已更改)。

if (objLstTypes.SelectedItem != null)

 PublisherTypeDescriptor objType = (PublisherTypeDescriptor)objLstTypes.SelectedItem;
 objLstTypes.Items.Remove(objType);
 objLstTypes.Items.Add(objType);
 objLstTypes.SelectedItem = objType;

【讨论】:

这将始终将所选项目放在列表框的末尾!【参考方案6】:

如果 objLstTypes 是您的 ListBox 名称 采用 objLstTypes.Items.Refresh(); 希望这行得通...

【讨论】:

【参考方案7】:

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);
            
        
    

【讨论】:

这实际上比当前接受的答案要少。壮丽的!我编辑了您的帖子以包含一个示例。 我认为您实际上可以进一步改进。您可以将父绑定和子绑定应用于控件,这意味着您可以在没有 _SelectedIndexChanged 事件处理程序的情况下执行此操作。我忘记了确切的代码...... :( 我已经更新了示例,删除了 SelectedIndexChanged 事件处理程序并在 Load 处理程序中替换为 2 个新行。 :) 我使用的是常规列表,但我认为这不是问题所在,而是在其他地方寻找麻烦。谢谢! 据我所知,这并没有解决实际更新所列项目文本的问题。【参考方案8】:
lstBox.Items[lstBox.SelectedIndex] = lstBox.SelectedItem;

【讨论】:

这实际上是一个有效的答案! 这个得到我的投票!简单,无需添加任何新的类或代码段即可完成。这就是我所需要的。 请注意,如果您通过 DataSource 属性设置项目,这将不起作用。 愚蠢的简单。很好的答案。【参考方案9】:
typeof(ListBox).InvokeMember("RefreshItems", 
  BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod,
  null, myListBox, new object[]  );

【讨论】:

我发现这很有用:它可以用来使列表框动态刷新【参考方案10】:

如果你使用如下的绘制方法:

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 将更新,只是另一种解决方案:)

【讨论】:

以上是关于如何让 ListBox 刷新其项目文本?的主要内容,如果未能解决你的问题,请参考以下文章

C#当用户控件变为活动状态时,如何自动刷新文本文件中的数据?

从ROOT - 在Tkinter中将Listbox类移动到Toplevel时松散yScroll

在 WPF 中刷新 ListBox 的简单方法?

MFC (C++):如何按设计设置 ListBox 的宽度?

VB LISTBOX如何一行一行显示文本文件的内容,单击listbox一行这行内容显示到text1中

如何让 ListBox ItemTemplate 水平拉伸 ListBox 的整个宽度?