显示(用户)控件的 ListBox






4.55/5 (9投票s)
使用此控件将控件排列在 ListBox 中

引言
不时地,我一直在寻找一种简单的方法来在我的程序中显示信息集合。搜索一番后,我总是以在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:更新了文章