如何检测向ListBox(或CheckedListBox)控件添加了项目

23
这似乎是一个根本上简单的问题。我有一个带有ListBox的WinForms对话框。该控件不是通过数据绑定填充的,而是通过调用进行填充。
listBox.Items.Add (obj);

这个调用可能可以从不同的位置异步进行,我想连接listbox并监视它的数据成员的更改,以便我可以执行其他UI更改(例如根据列表中项目的数量启用或禁用与列表框交互的控件)。

不幸的是,除非我完全不了解,否则似乎没有事件或虚拟方法可以挂钩以检测此类更改。我可以挂钩选择更改和(对于CheckedListBox)我可以挂钩检查状态更改。但不能检测到基础数据集合的更改。

我知道在Win32中这是可能的(有一个窗口消息可以实现此功能)。我缺少什么?


[由Simon编辑]

解决方案

我找到了正确的解决方案(我已将其标记为被接受的答案),即重写ListBox的WndProc方法,并手动处理listbox消息。这是我采用的解决方案(也有效)。可以修改它以在事件中提供更多细节,或将消息拆分为单独的事件,但对于我的需求,这已足够。

using System;
using System.Windows.Forms;

public class CheckedListBoxEx : CheckedListBox
{
    public CheckedListBoxEx() { }

    private const int LB_ADDSTRING = 0x180;
    private const int LB_INSERTSTRING = 0x181;
    private const int LB_DELETESTRING = 0x182;
    private const int LB_RESETCONTENT = 0x184;

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == LB_ADDSTRING ||
            m.Msg == LB_INSERTSTRING ||
            m.Msg == LB_DELETESTRING ||
            m.Msg == LB_RESETCONTENT)
        {
            ItemsChanged(this, EventArgs.Empty);
        }
        base.WndProc(ref m);
    }

    public event EventHandler ItemsChanged = delegate { };
}

@JohanPaul的链接已经失效。 - mrGreenBrown
抱歉,谢谢。我删除了链接,因为我不知道它应该链接到哪里。 - Johan Paul
4个回答

7
我不知道有任何事件可以观察以显示项已添加到 ListBox 中。也许您可以使用您描述的 Win32 方法(即获取句柄,使用 WndProc 等)来做这个事情。
或者,您可以使用另一个类来添加项目。例如,而不是直接调用 ListBox 上的 Add 方法,您可以让用户操作调用新类中的 Add 方法,然后将该项添加到 ListBox 中。您可以在该类内设置一个事件,以便您可以观察已添加了什么内容。
我还喜欢另一位发帖者提到的子类化 ListBox 的想法...

我忘记了可以覆盖WndProc方法。这很好地解决了我的问题。我可以创建一个自定义的ListBoxEx类,覆盖这个虚拟方法以获得新的行为,但仍将其作为一个ListBox传递。这正是我想要的。谢谢! - Simon Gillbee

3

这是很危险的,因为这将暴露两个添加方法(其中有经验的开发人员会默认使用第二个)。此外,多态引用(基类)不包括自定义添加方法。(1) listBox.Add(obj) <-=-> (2) listBox.Items.Add(obj) - Michael Meadows
我已经考虑过(甚至做了原型)这个想法,但我试图在现有的框架类型范围内保持。现有的遗留代码只知道System.Windows.Forms.ListBox,而不知道任何特定的子类。而且在这种情况下,框架完全没有提供覆盖的钩子。 - Simon Gillbee
嗯,我想我没有更多的建议了 :) - Kevin Laity
2
链接现在已经失效。 - Aracthor

2
这个解决方案似乎可行 - 在我的情况下,我在VB中实现了它。我只是创建了一个ExtendedListbox类,它继承了listbox控件,实现了INotifyPropertyChanged接口,并遮盖了Items属性。
Imports System.ComponentModel

Public Class ExtendedListBox:Inherits ListBox:Implements INotifyPropertyChanged

    Public Shadows Property Items() As ObjectCollection
        Get
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Items"))
            Return MyBase.Items
        End Get
        Set(value As ObjectCollection)
            RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("Items"))
            MyBase.Items.Clear()
            For Each o As Object In value
                MyBase.Items.Add(o)
            Next
        End Set
    End Property

    Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
End Class

1
虽然这会在每次访问Items集合时触发,但在我的情况下,这是可以接受的,但我理解这可能不是理想的。 - RLB

0

很遗憾,使用继承或事件没有简单的方法来实现这一点。您应该能够重写Items类的Add方法,但是您无法访问它!如果您的经验不足,您可能能够拦截消息循环以找出何时发生这种情况。

从您的问题中我注意到一件事,即您提到项目正在异步添加。不要这样做。如果您的问题是控件未更新,则可以在窗体线程上同步解决您的问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接