65.9K
CodeProject 正在变化。 阅读更多。
Home

显示(用户)控件的 ListBox

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.55/5 (9投票s)

2008 年 5 月 25 日

CPOL

4分钟阅读

viewsIcon

65271

downloadIcon

2125

使用此控件将控件排列在 ListBox 中

Sample Image - ARBOScrollableListBox.png

引言

不时地,我一直在寻找一种简单的方法来在我的程序中显示信息集合。搜索一番后,我总是以在ListView中显示信息告终。有一天,我在 Code Project 上找到了一篇关于多行ListBox的文章,但这并没有真正解决显示地址等数据集合的问题。因此,我开始编写一个用户控件,它应该易于使用并能满足我的需求。也许它也能帮助别人...

背景

我的第一个想法是:很简单……拿一个用户控件,让它可滚动,然后用DockStyle.Top添加每个子控件。其余的由用户控件本身和框架来完成。
好的,5分钟后,第一个版本就准备就绪了。添加 100 个控件(我创建了一个显示姓名、街道、邮编、城市和电话的地址控件)大约需要 17 秒。这个想法不好……经过一些测试(禁用DockStyle等)后,我从头开始。唯一与第一个方法相同的是——它仍然是一个用户控件。

速度...

下一步是考虑一种方法,它仍然显示项目,但构建列表不需要花费几秒钟。解决方案就像一个“虚拟”可见列表。这意味着,用户控件保存要显示的控件列表,并在控件滚动时重建要显示的项。

这也意味着我必须自己控制滚动条。为此,我需要记住屏幕上的第一个元素和元素的数量。在每个滚动事件中,我都需要重建用户控件可见区域内的项。

... 更多速度

用户控件的新版本几乎是我想要的。它(几乎)很快,并且可以滚动项目,即使项目的高度不相同。
但是,如果我添加很多元素(比如说…… 1000 个),它仍然会有点慢,并且会出现严重的闪烁。因此,下一步是添加一个“更新”机制。每次我调用Update()时,一个成员update会增加,并在每次EndUpdate()时减少。当update大于0时,不会进行任何视觉操作。当update再次等于0时,就会执行视觉操作。

项目选择

现在我们的速度足够快,可以处理大量项目,可以添加/删除它们,并且可以滚动它们。但是没有人会收到关于哪个项目被选中的通知。

我首先想到的是“添加一个点击事件处理程序”。但这还不够,因为如果子控件本身包含其他子控件,我们将永远不会收到点击通知。因此,我们需要递归地注册所有子控件的点击处理程序(并在删除时取消注册)。因此,添加一个控件会导致调用AttachClickEvents(ChildControl)。

private void AttachClickEvents( Control Control ) {
    Control.Click += new EventHandler( Control_Click );
    if( Control.Controls != null ) {
        foreach( Control C in Control.Controls ) {
            AttachClickEvents( C );
        }
    }
}

现在,如果任何子控件被点击,我们就会收到通知。我们需要比较,点击的控件是否在我们控件列表中。如果不在,我们需要获取父控件并再次执行此比较。如果我们找到了控件在列表中,我们就找到了需要触发ControlSelected事件的控件。

排序项目

最后一件遗留的事情是排序。这非常简单。我们需要一个public属性来获取一个IComparer,它可以从用户控件外部设置。现在,如果设置了该比较器,添加/删除控件到/从我们的项目列表需要调用items.Sort(comparer)

Using the Code

虽然我们有一个属性可以获取用户控件正在显示的控件列表,但我们不能使用此列表来更改元素,因为点击事件处理程序没有正确设置。它们只在Add(Control)中设置,并且只在Remove(Control)RemoveAt(Index)中移除。

public方法和属性是

//constructor
    public ARBOScrollableListBox()

//properties
    public List Items                       // list of controls to display
    public bool AdaptControlWidth           // make controls width fitting
    public bool AdaptControlHeight          // make controls heights fitting
    public Control CurrentSelected          // the selected control
    public IComparer Comparer               // the comparer to use
    public OrientationEnum Orientation      // orientation of the items

//methods
    public void BeginUpdate()               // stop screen updating
    public void EndUpdate()                 // redo updating
    public void Add( Control Control )      // add a control
    public void Remove( Control Control )   // remove a control
    public void RemoveAt( int Index )       // remove a control at position "index"
    public void Clear()                     // clear list of controls
    public void Rebuild()                   // rebuild the display
    public void SelectControl( Control C )  // manually select a control

关注点

编写这个控件揭示了一些在后台部分解释的有趣点,例如点击事件或加速。

扩展 1:方向

现在,您可以设置ListBox内项目的方向。也就是说,如果您将Orientation设置为OrientationEnum.Vertical,您将按照从上到下的顺序获得项目列表(结合您使用的项目比较器)。否则,您将按照从左到右的顺序获得它们。

如果将AdaptControlWidth属性设置为false,您将获得尽可能多的列(行),以适合控件(参见截图)。

扩展 2:不可见

感谢我的读者,我能够更改控件不可见时的行为。一些滚动条的计算取决于控件中可见的项目数量。当不可见时,这会导致“除以零”错误(已更正)。

谢谢...

... 感谢 Marc Clifton,他在我发布文章之前阅读了这篇文章并给我了一些提示...
如果您想获得好的信息——请阅读他的文章!

历史

  • 2008-05-21:初始发布
  • 2008-06-25:添加了方向和多列/行
  • 2008-06-29:更新了文章
© . All rights reserved.