一个多色、多字体的 CheckedListBox






4.38/5 (10投票s)
一个具有动态前景色、背景色和字体的 CheckedListBox
引言
考虑到大多数 WinForms 控件内置的灵活性,您可能会认为 CheckedListBox
会提供一些基本功能,例如项目背景色。但无论出于何种原因,它曾经并且仍然是一个简单而有限的控件。搜索互联网可以找到大量关于设置颜色的讨论,建议从重写 OnDrawItem
到放弃该控件而改用 ListBox
。这些充其量只是部分解决方案。
背景
在我为一台集成电路晶圆处理机设计的界面中,我需要一个自定义的 CheckedListBox
。二十五个 IC 晶圆被放置在一个塑料托盘中,最多可以加载四个托盘到机器中。界面显示每个晶圆的状态(未处理
、已处理
等),以及某些病态情况,例如错位或双槽晶圆。托盘插槽也可能是空的。背景颜色用于一目了然地轻松确定整体状态。
下图摘自实际界面,尽管 CheckedListBox
的内容是随机生成的测试数据,仅用于验证界面的正确运行。
愿望清单
一个漂亮的 CheckedListBox
控件可以让你
- 动态设置项目背景色
- 动态设置项目前景色
- 动态设置项目字体
- 将项目高度设置为非标准值
- 控制焦点/选择视觉属性
- 与 Visual Studio 集成
意外问题
重写 OnDrawItem
来处理更改前景色、背景色和字体应该不难,我当时是这么想的。DrawItemEventArgs
对象具有 ForeColor
、BackColor
和 Font
属性。只需在新 DrawItemEventArgs e2
中实现你想要设置这三个属性的任何逻辑,然后调用 base.OnDrawItem(e2)
。CheckedListBox
有一个 ItemHeight
属性,所以如果你想要独立于字体的高度,这也很容易管理。没什么难的。除了它不起作用。颜色正确更改,但无论我做什么都无法让控件使用我指定的字体,而项目高度则随心所欲。
卢克,看看源码
好吧,这很烦人。是时候深入研究一下了。CheckedListBox
的源代码可以在 dotnetframework.org 上在线获取。OnDrawItem(DrawItemEventArgs e)
证实了我的猜测:项目是使用 e.ForeColor
和 e.BackColor
绘制的,但它完全忽略了 e.Font
,而是使用了控件的 Font
。项目高度是某些晦涩计算的焦点。
此时,我只想说,试图将此控件的源代码复制到您自己的项目中,然后修改它以实现您想要的功能,注定会失败。但不要害怕,因为所有这些问题都有可行的、尽管不完美的解决方案。
不完美,但有进步
本文介绍了一个名为 CustomCheckedListBox
的 C# 类,它继承自 CheckedListBox
,提供了列表中的所有功能,或多或少。该类和一个测试驱动程序(在 VS2015 解决方案中)可以通过页面顶部的链接下载。让我们从查看委托和属性开始。
public class CustomCheckedListBox : CheckedListBox { public delegate Color GetColorDelegate(CustomCheckedListBox listbox, DrawItemEventArgs e); public delegate Font GetFontDelegate(CustomCheckedListBox listbox, DrawItemEventArgs e); [Description("Supply a foreground color for each item")] public event GetColorDelegate GetForeColor = null; [Description("Supply a background color for each item")] public event GetColorDelegate GetBackColor = null; [Description("Supply a font for each item")] public event GetFontDelegate GetFont = null; [Description("Set this if you don't like the standard selection appearance")] public bool DrawFocusedIndicator { get; set; } public override int ItemHeight { get; set; }
与各种在线解决方案相比,委托是 CustomCheckedListBox
的第一个重大区别。仅仅在控件中设置颜色会使所有项目看起来都一样,也许会因状态而区分。没意思。通过为这些委托提供方法,每个项目都可以根据您选择的任何标准着色,并且颜色可以动态更改。相同的机制可用于提供每个项目的字体。对于我的晶圆分拣机,当分拣过程开始时,项目列表是固定的。背景色会发生变化,以指示每个晶圆的当前状态。
ItemHeight
属性在此处必须被重写,因为基类属性实际上是只读的。DrawFocusedIndicator
将在下面解释。让我们接下来看看构造函数。
public CustomCheckedListBox(GetColorDelegate back = null, GetColorDelegate fore = null, GetFontDelegate font = null) { GetForeColor = fore; GetBackColor = back; GetFont = font; //****************************************************************** // If you want to set the item height to a specific value that can // be independent of the font size, this is the place to do it. //****************************************************************** ItemHeight = 14; }
由于所有委托都为 null
,因此整个类实际上是惰性的,因此在您提供这些委托的方法之前,它将表现得像一个普通的 CheckedListBox
。这里又出现了 ItemHeight
,我们将在下一节讨论。它必须在此处初始化,以防止 Visual Studio 设计器崩溃。默认情况下,ItemHeight
设置为 14
,这似乎与 CheckedListBox
使用的值匹配。让我们继续 OnDrawItem
方法。
protected override void OnDrawItem(DrawItemEventArgs e) { Color foreColor = (GetForeColor != null) ? GetForeColor(this, e) : e.ForeColor; Color backColor = (GetBackColor != null) ? GetBackColor(this, e) : e.BackColor; // // The CheckListBox is going to ignore the font in the event // args and use its own. So, make its own the one we want it // to be. For this to always work right, we will have to shut // down the OnFontChanged method below. // if (GetFont != null) this.Font = GetFont(this, e); // // If desired, draw an item focused, but not selected. // DrawItemState state = (DrawFocusedIndicator) ? ((e.State & DrawItemState.Focus) == DrawItemState.Focus ) ? DrawItemState.Focus : DrawItemState.None : e.State; // // e.Font is going to be ignored. // DrawItemEventArgs e2 = new DrawItemEventArgs(e.Graphics, e.Font, e.Bounds, e.Index, state, foreColor, backColor); base.OnDrawItem(e2); } protected override void OnFontChanged(EventArgs e) { if (GetFont == null) base.OnFontChanged(e); }
基本上,此方法仅修改 DrawItemEventArgs
以反映委托为每个项目从应用程序获取的颜色。if (GetFont != null)
行会更改基类 CheckedListBox
的字体。在某些情况下,base.OnFontChanged()
会导致问题,因此如果我们提供每个项目的字体,则禁用它。
DrawFocusedIndicator
会抑制正常的视觉选择指示器(基本上是蓝色背景上的白色项目文本)。取而代之的是,选定的项目会显示一个焦点指示器,这只是一个虚线边框。您可以根据需要打开或关闭此功能。
一些注意事项
首先,您可以为每个项目提供不同的字体,但它们都需要相同的大小。CheckedListBox
不容易处理多个项目高度。其次,您必须在构造函数中设置一个固定高度,并且无法更改。
Visual Studio 集成
如果您将 CustomCheckedListBox
添加到项目中,并将 using Qodex
添加到您的窗体中,Visual Studio 会将该控件添加到您的工具箱中。您现在可以像其他任何控件一样将该控件拖到窗体上,并访问其所有属性和事件。这与制作自定义控件不同,后者超出了本文的范围,但您可能会发现它正好能满足您的需求。
功劳归功于
此控件的灵感来自 这个 StackOverflow 文章。虽然它仅限于静态颜色控制,但它为我指明了正确的方向。
轮到你了
就这样。我希望您觉得这篇文章很有趣,甚至可能很有用。您可能对现有的 CustomCheckedListBox
感到满意,或者您可能已经在考虑一些您希望看到的增强功能。也许您希望复选框与项目背景色匹配。请告诉我您所做的任何巧妙修改。
历史
- 2015 年 12 月 06 日:初始版本