详解WinForm通用速选组件的构建

开发 后端
在这里我们要讨论的是WinForm通用速选组件的构建,希望对大家了解WinForm组件有所帮助。

对于WinForm组件,大家并不陌生。在这里我们将为大家讲解WinForm通用速选组件的构建,这也是通往WinForm开发的必经之路。

用户界面中,需要用户进行多项选择时,我们通常会提供一组快速选择(以下简称速选)按钮:全选、反选、清空,以方便用户操作。本文章将会构建一个通用速选组件来简化操作,使用之后,您不需要编写任何代码,只需设置两个属性便可让一个控件拥有速选的功能。

WinForm中常见的几种多选形式如下图:

SelectComponent 

图1

我们暂且将用于显示选项的控件叫做“选项控件”,全选、反选、清空叫做“速选按钮”。

四种形式采用了不同的控件用作选项控件、速选按钮:

  选项控件 速选按钮
形式一 CheckedListBox Label
形式二 CheckBox LinkLabel
形式三 TreeView Button
形式四 DataGridView PictureBox

实际使用中还可能会有其它形式的选项控件和速选按钮,根据使用场合不同,选项控件和速选按钮可以任意组合。这给我们编程带来了麻烦,选项控件没有统一的访问接口,换一个选项控件就要编写不同的选择代码。

想要将全选、反选、清空的选择逻辑提取出来还真不容易。于是很多程序员就选择了针对实际应用的组合(如形式一,CheckedListBox + Label)进行直接编码。这样导致大量相似的代码充斥在项目之中,会带来以下问题:

  1. 重复的编码、调试、测试(主要是界面)工作;
  2. 一旦选项控件发生改变,就必须修改代码,修改后还要测试;(可能性比较大,可能客户不喜欢形式一,非要你修改成形式二)

这样的直接编码实际上已经违反了DRY原则,我们应该纠正。

面向对象要求我们封装变化,我们这里不变的是速选的逻辑,变化的是选项控件和速选按钮,变化是两个方向的,有点接近桥模式的应用场景了。不过我对设计模式了解不深,不敢冒用,而且感觉这里速选按钮的变化是比较简单的,至少它们都有一个Click事件,也可以认为是不变的。

 

#T#

我们采用另外一种途径,.Net其实已经给我们提供了一种扩展控件功能的方式(也正体现了面向对象的OCP原则),可以让我们给控件赋予额外的功能。我们来看一个重要的接口:IExtenderProvider 接口,位于System.ComponentModel命名空间下。实现了这个接口的组件有ToolTip、ErrorProvider等,ToolTip、ErrorProvider这两个组件可以向其它组件(控件是组件的一种)提供额外的功能,ToolStip能让其它控件在用户鼠标悬停时弹出一个小框显示一些提示信息,ErrorProvider则能让控件显式错误信息。

我们要做的就是创建一个新的组件,实现IExtenderProvider接口,向控件(Button、Label等)提供速选的功能。这个组件我已经完成了,名字叫FastSelect,先不考虑实现原理、如何实现,我们先看下如何使用吧:

FastSelect是一个组件,会自动显示在工具箱中,将其拖入窗体,将会显示在设计器下方。

FastSelect组件

图2

选中全选按钮(label1),属性显示窗口如下图:

FastSelect2

图3

分组中出现了一个新的分组:速选,其中有如上图两个属性,***个属性用于选择选项控件,第二个属性用于确定选择方式(全选还是清空)。设置两个属性的值如上图,完成后label1就可以全选分类中的所有选项了。

简单说来,只需要向窗体置入一个控件(Button、Label、LinkLabel、Picture),简单设置两个属性,这个控件就自动具有了速选功能,不需要任何代码。当然得借助FastSelect组件。

 

这么神奇,是如何实现的呢?要从IExtenderProvider接口说起,这个接口可以向其他组件提供属性,如上图中的两个属性。有这里有两点要说明一下:

  1. 向其它组件提供属性,这个可以限定,比如仅只向Button提供。
  2. 提供属性并不是真正给其他组件加上新的属性,只是在WinForm设计时,在PropertyGrid中显示额外属性(后面会详说)。

我们来看下FastSelect是如何实现的: 

  1. public enum SelectionType  
  2.   {  
  3.       清空,  
  4.       反选,  
  5.       全选,  
  6.   }  
  7.  
  8.   [ProvideProperty("SelectionSource", typeof(Control))]  
  9.   [ProvideProperty("SelectionType", typeof(Control))]  
  10.   public partial class FastSelect : Component, IExtenderProvider  
  11.   {  
  12.       
  13.      public bool CanExtend(object extendee)  
  14.       {  
  15.           if (extendee == null) return false;  
  16.          if (extendee is Button || extendee is Label || extendee is PictureBox) return true;  
  17.          return false;  
  18.       }  
  19.  
  20.      [Category("速选"), Description("速选源控件"), Localizable(true)]  
  21.       public Control GetSelectionSource(Control control)  
  22.       {  
  23.           ...  
  24.       }  
  25.  
  26.      public void SetSelectionSource(Control control, Control selectionSource)  
  27.       {  
  28.           ...  
  29.       }  
  30.  
  31.  
  32.       [Category("速选"), Description("速选方式"), DefaultValue(SelectionType.清空), Localizable(true)]  
  33.       public SelectionType GetSelectionType(Control control)  
  34.       {             
  35.      }  
  36.      public void SetSelectionType(Control control, SelectionType selectionType)  
  37.      {      
  38.      ...  
  39.     }  
  40.      ...  
  41.       }  

SelectionType枚举不必多说,我们来看FastSelect,它继承至Component,实现了IExtenderProvider接口。

CanExtend是IExtenderProvider接口的***成员,它用来标识可以给那些组件进行扩展,上面的代码中我们限定了只可以给Button、Label、PictureBox进行扩展,也就是说其它类型的控件在属性窗口中是看不到速选属性的。CanExtend中没有LinkLabel,是因为LinkLabel是Label的子类,Label有的它也会自动拥有。

Get(Set) SelectionSource、Get(Set)SelectionType有点类似属性的get/set吧,只不过多了一个参数(***个参数control),传入这个参数的是要进行属性扩展的控件,还是让我们看一下WinForm生成的代码吧(在Form1.generated.cs中)

  1. this.fastSelect.SetSelectionSource(this.label1, this.categoriesListBox);  
  2.  this.fastSelect.SetSelectionType(this.label1, SelectionType.全选); 

对照图三,应该明白“提供属性”的真正含义了吧。

我们再来看是如何实现选择的,先看代码片段:

  1. public partial class FastSelect : Component, IExtenderProvider  
  2.     {  
  3.         private Dictionary sourceControlsDict;  
  4.         private Dictionary typeDict;  
  5.  
  6.       public void SetSelectionSource(Control control, Control selectionSource)  
  7.       {  
  8.               
  9.            sourceControlsDict.Add(control, selectionSource);  
  10.              control.Click += new EventHandler(control_Click);  
  11.              
  12.        }  
  13.  
  14.        public void SetSelectionType(Control control, SelectionType selectionType)  
  15.       {  
  16.             
  17.            typeDict.Add(control, selectionType);  
  18.              
  19.         }  
  20.  
  21.         void control_Click(object sender, EventArgs e)  
  22.          {  
  23.             Control control = sender as Control;  
  24.            Control selectionSource = sourceControlsDict[control];  
  25.             SelectionType selectType = typeDict[control];  
  26.  
  27.              if (selectionSource is DataGridView)  
  28.             {  
  29.                  DataGridView dataGridView = selectionSource as DataGridView;  
  30.                  foreach (DataGridViewRow row in dataGridView.Rows)  
  31.                      row.Selected = ChangeSelected(row.Selected, selectType);  
  32.              }  
  33.               
  34.          }  
  35.    
  36.         private bool ChangeSelected(bool isSelected, SelectionType type)  
  37.          {  
  38.            if (type == SelectionType.清空) return false;  
  39.             else if (type == SelectionType.全选) return true;  
  40.            else if (type == SelectionType.反选) return !isSelected;  
  41.             else throw new NotImplementedException();  
  42.          }  
  43.            
  44.     } 

一个窗体上只需要一个FastSelect组件,但它要为其它多个组件提供属性,我们这里使用Dictionary保存这些属性。

设置控件SelectionSource属性时,其实是调用了SetSelectionSource方法,其中我们为控件注册了Click事件,control_Click中根据不同的选项控件的类型(DataGridView、CheckedListBox等)和选择类型进行相应的处理。这样应该明白了吧。

代码比较长,就不发在文章中了,此处下载。(大部分时间花在文章上了,代码没太测试,可能存在问题,如发现请告知,谢谢了)。

原文标题:构建WinForm 通用速选(全选、反选、清空)组件

链接:http://www.cnblogs.com/ldp615/archive/2009/11/29/WinForm_FastSelect_Component.html

责任编辑:彭凡 来源: 博客园
相关推荐

2022-02-08 15:55:00

Vue组件库Vue Demi

2009-10-13 11:32:19

Winform假框架

2012-12-11 10:15:02

Winform开发框架

2010-06-13 09:15:16

WinForm窗体

2010-08-18 09:58:33

WinFormASP.NET

2021-06-22 09:44:56

鸿蒙HarmonyOS应用

2012-07-11 15:54:59

canvas

2024-10-24 17:13:55

WinformUI多线程

2009-04-13 09:23:41

.NET 2.0Winform经验

2013-07-04 09:33:26

BGP选路

2020-02-21 11:08:24

浏览器HTML设计

2009-11-26 14:37:37

Visual Stud

2009-10-10 14:54:44

treeView1控件

2010-02-03 15:59:08

Android组件

2017-03-13 16:30:50

React Route构建JavaScript

2021-04-23 16:08:08

鸿蒙HarmonyOS应用

2010-01-07 09:53:09

Winform多线程编

2012-03-27 11:08:23

Java

2009-09-17 16:34:24

C#组件类

2009-09-16 13:05:32

C#组件开发
点赞
收藏

51CTO技术栈公众号