精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖
服务方向
联系方式
锐英源精品原创,禁止全文或局部转载,禁止任何形式的非法使用,侵权必究。点名“简易百科”和闲暇巴盗用锐英源原创内容。
本文要点在于组合窗口形成一个复杂控件,这方向非常有意思。
最近用WPF,研究了里面有CheckComboBox,这种结合性质的控件是初学者进阶中级程序员的好实践项目,就是初学者实现不了,研究掌握结合点的机制,水平也会增加不少。本文虽然是Winform方向,但是都是ComboBox,有兴趣翻译学习,翻译自codeproject,codeproject看不懂,找锐英源。
介绍
此控件源于我创建CheckedListBox控件的需要,该控件在所选项可见时不会占用窗体上的太多空间。虽然已经有一些使用所有者绘制控件的示例(例如,绘制ComboBox的ListBox部分),但由于一些缺点,我并不完全满意,因此我提出了一个新版本,即CheckedComboBox,它具有以下功能:
只读文本部分,显示选中的项目。
每个选中的项目在文本部分显示为一个字符串,每个字符串由我可以定义的自定义分隔符分隔。
列表部分保持打开,直到用户完成选择。
选中/取消选中所有项目。
通过键盘显示/隐藏列表部分。
尽可能模拟.NET ComboBox。
从VisualStudio调色板中使用它(作为自定义控件)。
实施
CheckedComboBox派生自ComboBox。为了避免在用户完成选择之前列表部分消失的问题,我决定捕捉DropDown事件并显示我自己的列表部分,该列表部分由一个表单组成,该表单的客户端区域完全由一个CheckListBox填充。Form和CheckListBox都是我自己的自定义版本,因为我需要重写一些默认行为。
因此,我的自定义列表部分会出现在DropDown事件中,或者当用户在CheckedComboBox具有焦点时按下向下箭头时。我将OnDropDown()、OnKeyDown(()和OnKeyPress()方法重写为:
显示下拉列表,以及
防止任何键盘输入,以便使文本部分为只读。
protected override void OnDropDown(EventArgs e) { base.OnDropDown(e); DoDropDown(); } private void DoDropDown() { if (!dropdown.Visible) { Rectangle rect = RectangleToScreen(this.ClientRectangle); dropdown.Location = new Point(rect.X, rect.Y + this.Size.Height); int count = dropdown.List.Items.Count; if (count > this.MaxDropDownItems) { count = this.MaxDropDownItems; } else if (count == 0) { count = 1; } dropdown.Size = new Size(this.Size.Width, (dropdown.List.ItemHeight + 1) * count); dropdown.Show(this); } } protected override void OnKeyDown(KeyEventArgs e) { if (e.KeyCode == Keys.Down) { OnDropDown(null); } // Make sure that certain keys or combinations are not blocked. e.Handled = !e.Alt && !(e.KeyCode == Keys.Tab) && !((e.KeyCode == Keys.Left) || (e.KeyCode == Keys.Right) || (e.KeyCode == Keys.Home) || (e.KeyCode == Keys.End)); base.OnKeyDown(e); } protected override void OnKeyPress(KeyPressEventArgs e) { e.Handled = true; base.OnKeyPress(e); }
注:OnKeyPress不处理就没输入。
现在,作为表单的列表部分将保持打开而不关闭(实际上是隐藏的),直到用户按Esc键(Escape按钮)取消任何更改,按Enter键接受所有更改,或用鼠标单击列表之外的任何位置,这是组合框的通常行为。
为了实现这一点,我必须捕捉窗体的Deactivate事件(对于鼠标点击行为)和键盘事件(对于Esc、Enter行为)。此外,如果用户在列表部分具有焦点时按下Del(删除按钮)或Shift+Del,则将分别取消选中/选中所有项目,这将提供一个有用的(对我来说:-)键盘快捷键。请注意如何使用自定义CCBoxEventArgs类来区分Deactivate消息是来自框架(即鼠标点击)还是来自键盘(因为我捕获击键,所以我可以控制键盘)。
protected override void OnDeactivate(EventArgs e) { base.OnDeactivate(e); CCBoxEventArgs ce = e as CCBoxEventArgs; if (ce != null) { CloseDropdown(ce.AssignValues); } else { CloseDropdown(true); } }
使用代码
代码作为一个演示项目提供,您可以运行它并将其作为使用示例。为了在代码中使用CheckedComboBox,您只需要一个文件CheckedComboBox.cs。虽然我还没有将它变成一个功能齐全的Visual Studio控件,它包含了所有的箭头和滚动,但一旦编译了项目,CheckedComboBox应该会出现在Visual Studio工具箱调色板中的组件下,您可以将其拖放到设计器中的窗体上。
或者,您可以通过手动编写如下所示的代码来声明和创建CheckedComboBox的实例,从而非常容易地使用它:
private CheckedComboBox ccb = new CheckedComboBox(); // If more than 5 items, add a scroll bar to the dropdown. ccb.MaxDropDownItems = 5; // Make the "Name" property the one to display, rather than the ToString() // representation of the item. ccb.DisplayMember = "Name"; // Set a separator for how the checked items will appear in the Text portion. ccb.ValueSeparator = ", ";
添加数据:
private string[] coloursArr = { "Red", "Green", "Black", "White", "Orange", "Yellow", "Blue", "Maroon", "Pink", "Purple" }; for (int i = 0; i < coloursArr.Length; i++) { CCBoxItem item = new CCBoxItem(coloursArr[i], i); ccb.Items.Add(item); }
CCBoxItem类如下:
public class CCBoxItem { private int val; public int Value { get { return val; } set { val = value; } } private string name; public string Name { get { return name; } set { name = value; } } public CCBoxItem() { } public CCBoxItem(string name, int val) { this.name = name; this.val = val; } public override string ToString() { return string.Format("name: '{0}', value: {1}", name, val); } }
您还可以通过编程方式检查项目或将其CheckState设置为Checked、Unchecked或Indeterment。请注意,用户无法从用户界面将项状态设置为“不确定”,只能通过编程方式设置(除非您希望处理ItemCheck通知并在其中添加自己的逻辑)。
// Check first item (index == 0) ccb.SetItemChecked(0, true); // Set the CheckState of the 2nd item in the list to Indeterminate. ccb.SetItemCheckState(1, CheckState.Indeterminate);
您可能会在应用程序中捕捉到的最有趣的事件是当下拉部分关闭时(DropDownClosed)和当项选中状态即将更改时(ItemCheck)。您可以注册事件处理程序并照常处理事件。
请注意,DropDownClosed事件可直接从ComboBox获得,但ItemCheck必须从CheckListBox获得。
在上面的代码中,可以通过CheckedItems属性看到选中的项。类似地,CheckedIndices将返回一个已检查索引的集合。这两个集合都对应于.NET CheckedListBox集合,因此这里没有什么不熟悉的。
this.ccb.DropDownClosed += new System.EventHandler(this.ccb_DropDownClosed); ccb.ItemCheck += new System.Windows.Forms.ItemCheckEventHandler(this.ccb_ItemCheck); // Handler implementation private void ccb_DropDownClosed(object sender, EventArgs e) { txtOut.AppendText("DropdownClosed\r\n"); txtOut.AppendText(string.Format("value changed: {0}\r\n", ccb.ValueChanged)); txtOut.AppendText(string.Format("value: {0}\r\n", ccb.Text)); // Display all checked items. StringBuilder sb = new StringBuilder("Items checked: "); // Checked items can be found via the CheckedItems property. foreach (CCBoxItem item in ccb.CheckedItems) { sb.Append(item.Name).Append(ccb.ValueSeparator); } sb.Remove(sb.Length-ccb.ValueSeparator.Length, ccb.ValueSeparator.Length); txtOut.AppendText(sb.ToString()); txtOut.AppendText("\r\n"); } private void ccb_ItemCheck(object sender, ItemCheckEventArgs e) { CCBoxItem item = ccb.Items[e.Index] as CCBoxItem; txtOut.AppendText(string.Format("Item '{0}' is about to be {1}\r\n", item.Name, e.NewValue.ToString())); }