Windows程序设计与架构
上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 程序运行结果(二)