上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人
2.10 案例分析10 列表框示例
2.10.1 案例描述
本节介绍如何创作自己的复合控件,完成高效的程序设计工作。
2.10.2 案例分析
复合控件的创建和使用。
2.10.3 案例实现与技巧
① 首先,运行Visual Studio 2005,新建一个Windows应用程序,并命名为ListBoxctl。
② 在设计视图中按照下图的布局向Form1中添加控件,按照下面表格中数据修改相应控件的属性,如图2-30所示。
图2-30 控件布局与设计
续表
③ 向窗体拖曳一个ImageList控件,修改其Images属性,使它包含4个图片。
④ 向窗体拖曳一个ColorDialog控件,修改其Name属性为“LBColorDialog”。
⑤ 在窗口类中添加下列成员变量,如下所示:
private Color[] listBoxColors = new System.Drawing.Color[] {Color.Green, Color.Red,Color.Magenta,Color.Yellow}; private SampleImagePanel imgPanel;
⑥ 在窗口类的构造函数里添加以下代码。
public ListBoxCtl() : base() { // 此调用是支持“Windows 窗体”窗体设计器所必需的 InitializeComponent(); ResourceManager resources = new ResourceManager(typeof(ListBoxCtl)); //添加显示图像的简单自定义控件 //该图像是在列表框中选定的 imgPanel = new SampleImagePanel(); imgPanel.Location = new System.Drawing.Point(64,90); imgPanel.Size = new System.Drawing.Size(64,168); imgPanel.TabIndex = 12; this.Controls.Add(imgPanel); // 对项 Height 的选择只对 OWNERDRAW 模式有影响 integralHeightCheckBox.Enabled = false; // 初始化组合框 borderStyleComboBox.SelectedIndex = 0; selectionModeComboBox.SelectedIndex = 2; drawModeComboBox.SelectedIndex = 0; }
⑦ 添加如下的成员方法:
public new void Dispose() { base.Dispose(); components.Dispose(); }
⑧ 将事件方法添加到窗口类中,如下所示:
private void sortedCheckBox_Click(object sender,EventArgs e) { /* * 如果列表框已经填充,设置 * Sorted 属性将排序该列表 * 以后添加到该列表框的项将 * 被插入经过排序的位置上 * 如果未设置 Sorted 属性(设置为 false) * 则添加到列表的项将出现在该列表的末尾 * 下面的else分支演示这一点 * 在InitializeComponent方法中故意使用 * 这种方法,而不使用最有效的setItems方法 */ if (sortedCheckBox.CheckState == CheckState.Checked) { listBox1.Sorted = true; } else { listBox1.Items.Clear(); listBox1.Items.Add("第一个图像"); listBox1.Sorted = false; listBox1.Items.Add("第二个图像"); listBox1.Items.Add("第三个图像"); listBox1.Items.Add("第四个图像"); } ClearSelections(); imgPanel.ClearImages(); } private void borderStyleComboBox_SelectedIndexChanged(object sender,EventArgs e) { int index = borderStyleComboBox.SelectedIndex; if (index == 0) { listBox1.BorderStyle = System.Windows.Forms.BorderStyle. Fixed3D; } else if (index == 1) { listBox1.BorderStyle = System.Windows.Forms.BorderStyle. FixedSingle; } else { listBox1.BorderStyle = System.Windows.Forms.BorderStyle. None; } } private void integralHeightCheckBox_Click(object sender,EventArgs e) { if (integralHeightCheckBox.CheckState == CheckState.Checked) { listBox1.ItemHeight = 25; } else { listBox1.ItemHeight = 15; } } private void FGColorButton_Click(object sender,EventArgs e) { if (LBColorDialog.ShowDialog() == DialogResult.OK) { listBox1.ForeColor = LBColorDialog.Color; } } private void BGColorButton_Click(object sender,EventArgs e) { if (LBColorDialog.ShowDialog() == DialogResult.OK) { listBox1.BackColor = LBColorDialog.Color; } } private void multiColumnCheckBox_Click(object sender,EventArgs e) { int selection = drawModeComboBox.SelectedIndex; drawModeComboBox.Items.Clear(); if (multiColumnCheckBox.CheckState == CheckState.Checked) { // 禁用 OWNERDRAWVARIABLE drawModeComboBox.Items.AddRange(new object[] { "正常", "OwnerDrawFixed"}); /* * 为了显示多列,我们调整列表框的大小以显示三行 * 列表中的最后一项出现在第二列中 */ listBox1.MultiColumn = true; listBox1.ColumnWidth = 70; } else { // 启用 OWNERDRAWVARIABLE drawModeComboBox.Items.AddRange(new object[] { "正常", "OwnerDrawFixed", "OwnerDrawVariable"}); listBox1.MultiColumn = false; listBox1.ColumnWidth = 144; } drawModeComboBox.SelectedIndex = selection; } private void selectionModeComboBox_SelectedIndexChanged(object sender,EventArgs e) { int index = selectionModeComboBox.SelectedIndex; if (index == 0) { listBox1.SelectionMode = SelectionMode.None; } else if (index == 1) { listBox1.SelectionMode = SelectionMode.One; } else if (index == 2) { listBox1.SelectionMode = SelectionMode.MultiSimple; } else { listBox1.SelectionMode = SelectionMode.MultiExtended; } /* * 更改选择模式后,最好移除所有选定内容 * 使选定内容无效也可以。 */ ClearSelections(); imgPanel.ClearImages(); } private void ClearSelections() { int index = selectionModeComboBox.SelectedIndex; if (index != 0) { /* * SelectionMode 为"无"的情况下调用 SetSelected 将 * 引发异常 */ for (int selectIndex = 0; selectIndex<listBox1.Items.Count; selectIndex++) { listBox1.SetSelected(selectIndex,false); } } imgPanel.Invalidate(); } private void listBox1_DrawItem(object sender,DrawItemEventArgs e) { /* * 如果当前选定该项,则应调用方法 e.drawFocusRect() * 来填写快捷焦点矩形。此调用 * 应该在所有其他绘图完成后再 * 进行,否则它将被改写 * 下面的方法通常应在绘图前调用 * 此处它实际上是多余的,因为后面的 * 绘图将完全覆盖此处关注的区域 */ e.DrawBackground(); DrawMode mode = listBox1.DrawMode; if (mode == DrawMode.Normal) { // NORMAL Draw:该控件处理绘图 return; } else if (mode == DrawMode.OwnerDrawFixed ||mode == DrawMode.OwnerDrawVariable) { /* * 在每种所有者描述模式中,系统都提供 * 所有者自定义绘制要求的图形所在的上下文 * 要在其中进行绘制的上下文是 e.graphics * 要绘制的项的索引是 e.index * 绘制应该在 e.rect 描述的区域内完成 */ Brush brush = new SolidBrush(listBoxColors[e.Index]); e.Graphics.FillRectangle(brush,e.Bounds); e.Graphics.DrawRectangle(SystemPens.WindowText,e.Bounds); bool selected = ((e.State & DrawItemState.Selected) == DrawItemState. Selected) ? true : false; /* * 文本边界框的左上角放在 drawString * 调用中指定的点处 * 这些"+1"是使背景边框与上面所绘制的一样 */ string selText = selected ? "SEL. " : ""; e.Graphics.DrawString("Bmp #" + e.Index.ToString() + " " + selText,Font,new SolidBrush(Color.Black),e.Bounds.X + 1,e.Bounds.Y + 1); /* * 完成所有其他绘图后,下面的方法 * 将添加一个矩形,指示此项是否具有 * 焦点 */ e.DrawFocusRectangle(); } } private void drawModeComboBox_SelectedIndexChanged(object sender,EventArgs e) { int index = drawModeComboBox.SelectedIndex; if (index == 0) { listBox1.DrawMode = DrawMode.Normal; integralHeightCheckBox.Enabled = false; multiColumnCheckBox.Enabled = true; } else if (index == 1) { listBox1.DrawMode = DrawMode.OwnerDrawFixed; integralHeightCheckBox.Enabled = true; multiColumnCheckBox.Enabled = true; } else { // 如果选中 OWNERDRAWVARIABLE,则不允许使用多列 multiColumnCheckBox.Enabled = false; multiColumnCheckBox.CheckState = CheckState.Unchecked; listBox1.MultiColumn = false; listBox1.DrawMode = DrawMode.OwnerDrawVariable; integralHeightCheckBox.Enabled = true; } listBox1.ItemHeight = (int)itemHeightUpDown.Value; } private void listBox1_SelectedIndexChanged(object sender,EventArgs e) { imgPanel.ClearImages(); int[] selected = new int[listBox1.SelectedIndices.Count]; listBox1.SelectedIndices.CopyTo(selected,0); for (int i=0; i<selected.Length; i++) { int index = selected[i]; object item = listBox1.Items[index]; string str = item.ToString(); Image img = GetImage(str); imgPanel.AddImage(img); } /* * 为了更新(绘画)图像区域,调用 invalidate 方法 * 此操作导致调用该控件的 onPaint 方法 */ imgPanel.Invalidate(); } private void itemHeightUpDown_ValueChanged(Object sender,EventArgs e) { listBox1.ItemHeight = (int)itemHeightUpDown.Value; } private void listBox1_MeasureItem(object sender,MeasureItemEvent Args mie) { int nominalHeight = listBox1.ItemHeight; mie.ItemHeight = 2 * (mie.Index % 5) + nominalHeight; }
⑨ 增加获得图标图像的方法,如下所示:
private Image GetImage(string bmpName) { if (bmpName.Equals("第一个图像")) { return imageList1.Images[0]; } else if (bmpName.Equals("第二个图像")) { return imageList1.Images[1]; } else if (bmpName.Equals("第三个图像")) { return imageList1.Images[2]; } else if (bmpName.Equals("第四个图像")) { return imageList1.Images[3]; } else { return null; } }
⑩ 按照下表的对应关系对事件进行注册,以保证程序能及时响应用户的请求。
⑪ 选择Visual Studio 2005 IDE主菜单“调试”|“开始执行(不调试)”菜单项来运行应用程序。程序运行结果如图2-31所示。
⑫ 选择不同的外观选项,列表框呈现的外观会相应发生变化,如图2-32所示。
图2-31 程序运行结果(一)
图2-32 程序运行结果(二)