WINFORM控件开发总结(一)

WinForm控件开发系列文章均转自:http://www.cnblogs.com/guanjinke/archive/2006/12/04/582084.html

我本人不是专业的控件开发人员,只是在平常的工作中,需要自己开发一些控件。在自己开发WinForm 控件的时候,没有太多可以借鉴的资料,只能盯着 MSDN 使劲看,还好总算有些收获。现在我会把这些经验陆陆续续的总结出来,写成一系列方章,希望对看到的朋友有所帮助。今天我来开个头。
      其实开发 WinForm 控件并不是很复杂, .NET 为我们提供了丰富的底层支持。如果你有 MFC或者API图形界面 的开发经验,那么学会 WinForm 控件可能只需要很短的时间 就够了。
      自己开发的 WinForm 控件通常有三种类型:复合控件( Composite Controls ),扩展控件( Extended Controls ),自定义控件( Custom Controls )。    
      复合控件 :将现有的各种控件组合起来,形成一个新的控件,将集中控件的功能集中起来。
      扩展控件 :在现有控件的控件的基础上派生出一个新的控件,为原有控件增加新的功能或者修改原有控件的控能。
      自定义控件 :直接从 System.Windows.Forms.Control 类派生出来。 Control 类提供控件所需要的所有基本功能,包括键盘和鼠标的事件处理。自定义控件是最灵活最强大的方法,但是对开发者的要求也比较高,你必须为 Control 类的 OnPaint 事件写代码,你也可以重写 Control 类的 WndProc 方法,处理更底层的 Windows 消息,所以你应该了解 GDI +和 Windows API 。    
      本系列文章主要介绍自定义控件的开发方法。
      控件(可视化的)的基本特征:
      1.       可视化。
      2.       可以与用户进行交互,比如通过键盘和鼠标。
      3.       暴露出一组属性和方法供开发人员使用。
      4.       暴露出一组事件供开发人员使用。
      5.       控件属性的可持久化。
      6.       可发布和可重用。
      这些特征是我自己总结出来,不一定准确,或者还有遗漏,但是基本上概括了控件的主要方面。
      接下来我们做一个简单的控件来增强一下感性认识。首先启动 VS2005 创建一个 ClassLibrary 工程,命名为 CustomControlSample , VS 会自动为我们创建一个 solution 与这个工程同名,然后删掉自动生成的 Class1.cs 文件,最后在 Solution explorer 里右键点击 CustomControlSample 工程选择 Add->Classes… 添加一个新类,将文件的名称命名为 FirstControl 。下边是代码:
      

using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.Windows.Forms;
using  System.ComponentModel;
using  System.Drawing;

namespace  CustomControlSample
{
    
public   class  FirstControl : Control
    
{

        
public  FirstControl()
        
{

        }


        
//  ContentAlignment is an enumeration defined in the System.Drawing
        
//  namespace that specifies the alignment of content on a drawing 
        
//  surface.
         private  ContentAlignment alignmentValue  =  ContentAlignment.MiddleLeft;

        [
        Category(
" Alignment " ),
        Description(
" Specifies the alignment of text. " )
        ]
        
public  ContentAlignment TextAlignment
        
{

            
get
            
{
                
return  alignmentValue;
            }

            
set
            
{
                alignmentValue 
=  value;

                
//  The Invalidate method invokes the OnPaint method described 
                
//  in step 3.
                Invalidate();
            }

        }



        
protected   override   void  OnPaint(PaintEventArgs e)
        
{
            
base .OnPaint(e);
            StringFormat style 
=   new  StringFormat();
            style.Alignment 
=  StringAlignment.Near;
            
switch  (alignmentValue)
            
{
                
case  ContentAlignment.MiddleLeft:
                    style.Alignment 
=  StringAlignment.Near;
                    
break ;
                
case  ContentAlignment.MiddleRight:
                    style.Alignment 
=  StringAlignment.Far;
                    
break ;
                
case  ContentAlignment.MiddleCenter:
                    style.Alignment 
=  StringAlignment.Center;
                    
break ;
            }


            
//  Call the DrawString method of the System.Drawing class to write   
            
//  text. Text and ClientRectangle are properties inherited from
            
//  Control.
            e.Graphics.DrawString(
                Text,
                Font,
                
new  SolidBrush(ForeColor),
                ClientRectangle, style);

        }

    }

}

  

在同一个solution 里添加一个 Windows Application 工程(在 Solution Explorer 里右键点击 CustomControlSample solution 选择 Add->New Project… ),命名为 TestControl 。 VS 会为你自动生成一个 Form ,文件名为 Form1.cs 。在 Solution Explorer 里双击 Form1.cs 文件进入到 Form 设计界面。现在我们将 FirstControl 控件添加到工具箱( ToolBox )里,在 Toolbox 上右键点击,在弹出的菜单中选择 Choose Items… ,在出现的 Choose Toolbox Items 对话框中点击 Browse… 按钮,在 Open 对话框中选择我们的控件工程生成的 dll (我的 dll 在 F:/Programs/C#/CustomControlSample/CustomControlSample/bin/Debug 目录下,你可以根据实际情况去找)。完成这一步,在 Toolbox 就会出现我们设计的控件,图标是一个蓝色的齿轮(默认的都是这个,当然你也可以修改,后边的文章我会介绍),名称是 FirstControl 。
      现在我们在 Toolbox 中选中 FirstControl ,在 form 设计器上左键点击,或者按住鼠标拖放。我们制作的控件出现在了 Form 设计器上,在 Form 设计器上选中这个控件,然后在属性浏览器中将 Text 属性设为 Hello World ,现在我们的控件上的文字变成了 Hello World 。接下来我们要运行测试的工程,看看实际的效果。在运行之前,将测试工程设为启动工程,具体做法是,在 solution explorer 中右键点击 TestControl 工程,选择“ Set as Startup Project ”。点击工具栏里的运行按钮,或者按键盘的 F5 功能键。实际效果如下图所示:
      
      你可以根据自己的需要设置断点调试代码。

 

这个类是直接从 Control 类派生出来的,自定义控件都是直接从 Control 类派生出来的。这个类定义了一个属性 TextAlignment ,用来控制文本在控件中显示的位置:
             

        [
        Category(
" Alignment " ),
        Description(
" Specifies the alignment of text. " )
        ]
        
public  ContentAlignment TextAlignment
        
{

            
get
            
{
                
return  alignmentValue;
            }

            
set
            
{
                alignmentValue 
=  value;

                
//  The Invalidate method invokes the OnPaint method described 
                
//  in step 3.
                Invalidate();
            }

        }

 

   在这个属性之上有两个 Attribute ,这两个 attribute 描述了控件在设计时所表现出来的特征。我们来看看在控件设计中有哪些主要用到的设计时 Attribute 。 
   BrowsableAttribute :描述是否一个属性或事件应该被显示在属性浏览器里。
   CategoryAttribute :描述一个属性或事件的类别,当使用类别的时候,属性浏览器按类别将属性分组。
   DescriptionAttribute :当用户在属性浏览器里选择属性的时候, description 里指定的文本会显示在属性浏览器的下边,向用户显示属性的功能。
   BindableAttribute :描述是否一个属性倾向于被绑定。
   DefaultPropertyAttribute :为组件指定一个默认的属性,当用户在 Form 设计器上选择一个控件的时候,默认属性会在属性浏览器里被选中。    
   DefaultValueAttribute :为一个简单类型的属性设置一个默认值。
   EditorAttribute :为属性指定一个特殊的编辑器。
   LocalizableAttribute :指示一个属性是否能被本地化,任何有这个 Attribute 的属性将会被持久化到资源文件里。    
   DesignerSerializationVisibilityAttribute :指示一个属性是否或者如何持久化到代码里。
   TypeConverterAttribute :为属性指定一个类型转换器,类型转换器能将属性的值转化成其它的数据类型。
   DefaultEventAttribute :为组件指定一个默认的事件,当用户在 form 设计其中选择一个控件的时候,在属性浏览器中这个事件被选中。
   这些设计时的 Attribute 时很重要的,如果使用的好,将会对用户的使用带来很大的便利。

 

其中BrowsableAttribute , CategoryAttribute , DescriptionAttribute , DefaultPropertyAttribute , DefaultEventAttribute 都是比较简单的,也是可有可无,但是为了提供更好的用户体验这些 Attribute 最好不要省掉,如果你对这些Attribute还不熟悉,可以参考我前一篇文章的描述或者查看MSDN,这里我就不在赘述了。
       下来我们主要介绍一下 DesignerSerializationVisibilityAttribute 和 TypeConverterAttribute 。
       DesignerSerializationVisibilityAttribute 的功能是指示一个属性是否串行化和如何串行化,它的值是一个枚举,一共有三种类型 Content , Hidden , Visible 。 Content 指示代码生成器为对象包含的内容生成代码,而不是为对象本身, Hidden 指示代码生成器不为对象生成代码, visible 指示代码生成器为对象生成代码。假如你的控件有一个集合属性,又想在设计时自动将集合属性的内容生成代码,那么就使用这个 Attribute ,并将值设为 DesignerSerializationVisibility.Content 。
      TypeConverterAttribute 的作用就更大一些,也稍微复杂一些。 TypeConverterAttribute 主要的目的是为属性指定一个类型转换器,这个转化器可以将属性的值转换城其它的类型。 .NET 框架已经为大部分常用的类型都提供了类型转换器,比如 Color 就有 ColorConverter ,枚举类型就有 EnumConverter ,等等,所以一般情况下你没有必要写类型转换器,如果你的属性的特殊的类型或者自定义的类型那么就必须要写了。类型转换器都是从 System.ComponentModel.TypeConverter 派 生出来的,你需要重写其中的一些方法来达到转换的目的,在我们开发的过程中,其实只关心属性的值如何转换成字符串(因为属性的值需要在属性浏览器里显示出 来,属性浏览器里显示的都是字符串)和源代码(需要自动为属性的值生成源代码以实现持久化),当然反过来,也要将字符串和源代码转换成属性的值。另外使用 TypeConverter 也可以实现子属性,让属性的子属性也显示在属性浏览器里,并且可以折叠。
       接下来我就写一个简单的控件来演示一下这个控件。代码如下:
      

using  System;
using  System.Collections.Generic;
using  System.Text;
using  System.Windows.Forms;
using  System.Drawing;
using  System.ComponentModel;
using  System.Collections;

namespace  CustomControlSample
{
    
public   class  MyListControl:System.Windows.Forms.Control
    
{
        
private  List < Int32 >  _list  =   new  List < Int32 > ();

        
public  MyListControl()
        
{

        }


        [Browsable(
true )]
        
public  List < Int32 >  Item
        
{
            
get
            
{
                
return  _list;
            }

            
set
            
{
                _list 
=  value;
            }

        }


        
protected   override   void  OnPaint(PaintEventArgs e)
        
{
            
base .OnPaint(e);

            Graphics g 
=  e.Graphics;
            
// 绘制控件的边框

            g.DrawRectangle(Pens.Black,
new  Rectangle(Point.Empty, new  Size(Size.Width - 1 ,Size.Height - 1 )));
   
            
for  (Int32 i  =   0 ; i  <  _list.Count; i ++ )
            
{
                g.DrawString(_list[i].ToString(), Font, Brushes.Black,
1 , i  *  FontHeight);
            }

        }

    }

}


      我创建了一个简单的 List 控件,将用户输入的数据显示在控件中,效果图如下:
   
     在这个控件中,我声明了一个集合属性 Item 供用户输入要显示的整型数值。我们按照 WinForm 控件制作教程(二)中的方法将控件加到 ToolBox 里,然后拖到 Form 设计器中,然后选中控件,在属性浏览中查看控件的属性,属性中有一个 Item 的属性,属性右边的值显示为 Collection ,当你点击这个值的时候,值的右边出现一个小按钮,点击这个小按钮,就会出现弹出一个 Collection Editor 窗口,你可以在在这个编辑器里添加你想显示的整型值,如图:
       
      添加完以后,关闭 Collection Editor 。现在我们看看 Form 设计器为我们生成了什么代码。对于用户在 Form 设计器中设计的内容,设计器的代码生成器会将代码生成到窗口类的 I nitializeComponent ()方法中,对于 vs2005 来说,这个方法位于 ***.Designer.cs 文件中,在我当前的工程中位于 Form1.Designer.cs 文件中。在 solution 浏览器中双击打开这个文件,看看 Form 设计器为我们生成了什么代码:
      

             //  
            
//  myListControl1
            
//  
             this .myListControl1.BackColor  =  System.Drawing.SystemColors.ActiveCaptionText;
            
this .myListControl1.Item  =  ((System.Collections.Generic.List < int > )(resources.GetObject( " myListControl1.Item " )));
            
this .myListControl1.Location  =   new  System.Drawing.Point( 12 34 );
            
this .myListControl1.Name  =   " myListControl1 " ;
            
this .myListControl1.Size  =   new  System.Drawing.Size( 220 180 );
            
this .myListControl1.TabIndex  =   1 ;
            
this .myListControl1.Text  =   " myListControl1 " ;

 

      设计器将 Item 的内容串行化到了资源文件里。现在我们修改控件的代码,让设计器将 Item 的内容串行化到源代码里。我们为 Item 属性添加 DesignerSerializationVisibilityAttribute ,代码片断如下:

 

[Browsable( true )]
        [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Content)]
        
public  List < Int32 >  Item
        
{
            
get
            
{
                
return  _list;
            }

            
set
            
{
                _list 
=  value;
            }

        }

      编辑完以后, Build 控件工程,回到测试工程里,将 Item 属性里的值,删掉重新添加,添加完以后,我们再来看看设计器生成的代码:
      

//  
            
//  myListControl1
            
//  
             this .myListControl1.BackColor  =  System.Drawing.SystemColors.ActiveCaptionText;
            
this .myListControl1.Item.Add( 1 );
            
this .myListControl1.Item.Add( 2 );
            
this .myListControl1.Item.Add( 3 );
            
this .myListControl1.Item.Add( 6 );
            
this .myListControl1.Item.Add( 8 );
            
this .myListControl1.Item.Add( 9 );
            
this .myListControl1.Location  =   new  System.Drawing.Point( 12 34 );
            
this .myListControl1.Name  =   " myListControl1 " ;
            
this .myListControl1.Size  =   new  System.Drawing.Size( 220 180 );
            
this .myListControl1.TabIndex  =   1 ;
            
this .myListControl1.Text  =   " myListControl1 " ;

      现在设计器将 Item 的内容串行化到源代码里了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值