page contents

C#制作WinForm控件

本文讲述了C#制作WinForm控件!具有很好的参考价值,希望对大家有所帮助。一起跟随六星小编过来看看吧,具体如下:

attachments-2022-09-NnMblSZk632bc01a23638.png本文讲述了C#制作WinForm控件!具有很好的参考价值,希望对大家有所帮助。一起跟随六星小编过来看看吧,具体如下:

一 、概述

Windows 窗体控件是可再次使用的组件,它们封装了用户界面功能,并且可以用于客户端 Windows 应用程序。“Windows 窗体”不仅提供了许多现成控件,还提供了自行开发控件的基础结构。可以组合现有控件、扩展现有控件或创作自己的自定义控件。Windows 窗体控件是从 System.Windows.Forms.Control 直接或间接派生的类。以下列表描述了开发 Windows 窗体控件的常见方案:

·  组合现有控件来创作一个复合控件。

复合控件封装有一个可以作为控件重复使用的用户界面。可视化设计器为创建复合控件提供了有力的支持。要创作一个派生自 System.Windows.Forms.UserControl 的复合控件。基类 UserControl 为子控件提供了键盘路由并使子控件可以作为一个组进行工作。

·  扩展现有控件,对其进行自定义或为其添加功能。

可以通过从任何 Windows 窗体控件派生控件并重写或添加属性、方法和事件的方式来自定义 Windows 窗体控件。

· 创作一个不是通过组合或扩展现有控件而形成的控件。

在这种方案中,需从基类 System.Windows.Forms.Control 派生控件。可以添加和重写基类的属性、方法和事件,来制作功能强大,能满足自己需求的控件。

Windows 窗体控件的基类 System.Windows.Forms.Control 为客户端 Windows 应用程序中的外观显示提供了所需的途径。Control 提供了一个窗口句柄,用来处理消息路由并提供鼠标和键盘事件及许多其他用户界面事件。还提供了高级布局,并具有用于外观显示的特定属性,如 ForeColor、BackColor、Height、Width 和许多其他属性。此外,它还提供了安全性、线程支持以及与 ActiveX 控件的交互性。由于基类提供了很多基础结构,使得开发自己的 Windows 窗体控件变得相对简单。

二、编写简单的自定义控件

2.1、简单控件例子


下面的示例创建一个简单控件,该控件通过处理 Paint 事件显示其 Text 属性的值。为了创建此控件和处理事件,必须创建一个从 Control 继承的类,并创建一个重写 OnPaint 方法的方法。


public class HelloWorldControl : Control

  {

 protected override void OnPaint(PaintEventArgs e)

  {

         RectangleF rect = new RectangleF(ClientRectangle.X,

                                          ClientRectangle.Y,

                                          ClientRectangle.Width,

                                          ClientRectangle.Height);

         e.Graphics.DrawString(this.Text, Font, new SolidBrush(ForeColor), rect);

     }

 }

2.2、我们在VS2005中创建自定义控件的步骤:


1. 打开vs2005,文件/新建/项目。


2. 出现“新建项目”对话框。


3. 在“名称”框中,键入项目名称,“位置”框选择要存储的位置。


4. 从“语言”列表中选择要使用的编程语言。


5. 单击“添加”,这时一个自定义控件工程已经建成,生成一下,就制作了一个简单的自定义控件,只不过没有任何功能。


6. 向新用户控件添加任何标记和控件,并为该用户控件添加执行的所有任务(例如,处理控件事件或从数据源读取数据)添加代码。


2.3、检查控件的设计时行为


1. 启动 VS2005。


2. 通过从“文件”菜单单击/新建/项目/Windows应用程序,添加新窗体。


3. 右键“工具箱/选择项…”,在弹出的“选择工具箱项”对话框中点下面的浏览按钮选择要使用控件的 DLL;确定后,该控件出现在工具箱的底部。


4. 选择该控件并将其添加到窗体中。将看到该控件出现在窗体上。


5. 如果从上一个示例添加控件,您将注意到即使如此简单的控件都具有一整套属性和广泛的设计时行为。此默认行为是从 Control 类继承的。


三、为控件添加属性


控件应该定义属性而不是公共字段,因为可视化设计器在属性浏览器中显示属性,而不显示字段。属性就像智能字段。属性通常具有带访问函数的专用数据成员,在语法上属性被作为类的字段进行访问。(虽然属性可以具有不同的访问级别,但此处的讨论将重点放在公共访问这种更加常见的情况上。


属性定义通常由以下两部分组成: 


1、专用数据成员的定义。


   private int number = 0;


2、使用属性声明语法对公共属性进行的定义。


该语法通过 get 和 set 访问函数将专用数据成员和公共属性关联起来。


public int MyNumber 

     {

     get 

        { 

           return number; 

        }    

     set 

        { 

           number = value;

        }

     }

value 这个术语是属性定义语法中的一个关键字。在呼叫代码中,将变量 value 分配给属性。value 的类型必须同它被分配到的属性的声明类型相同。


虽然属性定义中通常包含专用数据成员,但这不是必需的。get 访问器不用访问私有数据成员就可以返回值。get 方法返回系统时间的属性就属于这种情况。属性启用数据隐藏,访问器方法隐藏属性的实现。


定义属性时需考虑以下重要的注意事项:


1、   必须将属性应用于定义的属性。属性用来指定设计器显示属性的方式。


2、   如果改变属性将影响控件的外观显示,请从 set 访问器中调用 Invalidate 方法(从 Control 继承该方法)。Invalidate 随后调用 OnPaint 方法,该方法将重新绘制控件。为提高效率起见,对 Invalidate 的多次调用将产生对 OnPaint 的一次调用。


3、   .NET Framework 类库为常见数据类型(如整数、小数、布尔值和其他数据)提供了类型转换器。类型转换器的目的通常是用来提供字符串到数值的转换(从字符串数据转换为其他数据类型)。常见数据类型与默认类型转换器(将数值转换为字符串,并将字符串转换为相应数据类型)相关联。如果定义了自定义(即,非标准)数据类型的属性,则应用的属性必须将类型转换器指定为与该属性相关联。还可以使用属性使自定义 UI 类型编辑器与某个属性相关联。UI 类型编辑器提供了一个用来编辑属性或数据类型的用户界面。颜色选择器是 UI 类型编辑器的一个示例。


例:首先创建一个名为 DrawingMode 的简单枚举。

public enum DrawingMode

         {

             Happy = 0,

             Sad = 1,

             Angry = 2

         }


接着,向该控件添加 MyDrawingMode 属性。


       private DrawingMode myDrawingMode;

        [Browsable(true), Category("Appearance")]

         public DrawingMode MyDrawingMode

         {

             get

             {

                 return myDrawingMode;

             }

             set

             {

                 myDrawingMode = value;

                 SetColors();

             }

         }

 对 SetColors 方法的调用只是根据 myDrawingMode 的值设置控件的 BackColor 和 ForeColor。向控件添加下面的代码。


        private void SetColors()

         {

             switch (myDrawingMode)

             {

                 case DrawingMode.Happy:

                     this.BackColor = Color.Yellow;

                     this.ForeColor = Color.Green;

                     break;

                 case DrawingMode.Sad:

                     this.BackColor = Color.LightSlateGray;

                     this.ForeColor = Color.White;

                     break;

                 case DrawingMode.Angry:

                     this.BackColor = Color.Red;

                     this.ForeColor = Color.Teal;

                     break;

                 default:

                     this.BackColor = Color.Black;

                     this.ForeColor = Color.White;

                     break;

             }

         }

现在可以向控件的paint方法添加代码,来绘制控件的样式,也可以添加现有的控件来组合实现想要的功能(例子里面有)。


private void UserControl1_Paint(object sender, PaintEventArgs e)

         {

             Graphics curG = e.Graphics;

             Pen curPen = new Pen(Color.Black);

             Rectangle curRect = new Rectangle(0, 0, Width - 2, Height - 3);

             curG.DrawRectangle(curPen, curRect);

             curG.DrawEllipse(curPen, curRect);

         }

四、为控件添加添加事件


事件(Event)


事件是对象发送的消息,以发信号通知操作的发生。操作可能是由用户交互(例如鼠标单击)引起的,也可能是由某些其他的程序逻辑触发的。引发事件的对象称为事件发送方。捕获事件并对其作出响应的对象叫做事件接收方。


在事件通信中,事件发送方类不知道哪个对象或方法将接收到(处理)它引发的事件。所需要的是在源和接收方之间存在一个媒介(或类似指针的机制)。.NET Framework 定义了一个特殊的类型(Delegate),该类型提供函数指针的功能。


代理(delegate)


delegate是C#中的一种类型,它实际上是一个能够持有对某个方法的引用的类。与其它的类不同,delegate类能够拥有一个签名(signature),并且它只能持有与它的签名相匹配的方法的引用。这样,代理就等效于一个类型安全函数指针或一个回调。它允许你传递一个类A的方法m给另一个类B的对象,使得类B的对象能够调用这个方法m。但与函数指针相比,delegate有许多函数指针不具备的优点。首先,函数指针只能指向静态函数,而delegate既可以引用静态函数,又可以引用非静态成员函数。在引用非静态成员函数时,delegate不但保存了对此函数入口指针的引用,而且还保存了调用此函数的类实例的引用。其次,与函数指针相比,delegate是面向对象、类型安全、可靠的受控(managed)对象。也就是说,runtime能够保证delegate指向一个有效的方法,你无须担心delegate会指向无效地址或者越界地址。


实现一个delegate是很简单的,通过以下3个步骤即可实现一个delegate:


1. 声明一个delegate对象,它应当与你想要传递的方法具有相同的参数和返回值类型。


2. 创建delegate对象,并将你想要传递的函数作为参数传入。


3. 在要实现异步调用的地方,通过上一步创建的对象来调用方法。


下面是一个简单的例子:


public class MyDelegateTest 

 { 

     // 步骤1,声明delegate对象 

     public delegate void MyDelegate(string name); 

     // 这是我们欲传递的方法,它与MyDelegate具有相同的参数和返回值类型 

     public static void MyDelegateFunc(string name) 

     { 

         Console.WriteLine("Hello, {0}", name); 

     } 

     public static void Main () 

     { 

         // 步骤2,创建delegate对象 

       MyDelegate md = new MyDelegate(MyDelegateTest.MyDelegateFunc); 

         // 步骤3,调用delegate 

         md("sam1111"); 

     } 

 }

事件处理


C#中的事件处理实际上是一种具有特殊签名的delegate,象下面这个样子:


public delegate void MyEventHandler(object sender, MyEventArgs e);


其中的两个参数,sender代表事件发送者,e是事件参数类。MyEventArgs类用来包含与事件相关的数据,所有的事件参数类都必须从System.EventArgs类派生。当然,如果你的事件不含特别的参数,那么可以直接用System.EventArgs类作为参数。


结合delegate的实现,我们可以将自定义事件的实现归结为以下几步:


1:定义delegate对象类型,它有两个参数,第一个参数是事件发送者对象,第二个参数是事件参数类对象。


2:定义事件参数类,此类应当从System.EventArgs类派生。如果事件不带参数,这一步可以省略。


3:定义事件处理方法,它应当与delegate对象具有相同的参数和返回值类型。


4:用event关键字定义事件对象,它同时也是一个delegate对象。


5:用+=操作符添加事件到事件队列中(-=操作符能够将事件从队列中删除)。


6:在需要触发事件的地方用调用delegate的方式写事件触发方法。一般来说,此方法应为protected访问限制,既不能以public方式调用,但可以被子类继承。名字是可以是OnEventName。


7:在适当的地方调用事件触发方法触发事件。


下面是一个例子,例子模仿容器和控件的模式,由控件触发一个事件,在容器中捕捉并且进行处理。


事件的触发者:


<summary>

 /// 事件的触发者

 /// </summary>

 public class Control

 {

     public delegate void SomeHandler(object sender, System.EventArgs e);

     public event SomeHandler SomeEvent;

     public Control()

     {

         //这里使用的delegate必须与事件中声名的一致

         this.SomeEvent += new SomeHandler(this.ProcessSomeEvent);

     }

     public void RaiseSomeEvent()

     {

         EventArgs e = new EventArgs();

         Console.Write("Please input 'a':");

         string s = Console.ReadLine();

         //在用户输入一个小a的情况下触发事件,否则不触发

         if (s == "a")

         {

             SomeEvent(this, e);

         }

     }

     //事件的触发者自己对事件进行处理,这个方法的参数必须和代理中声名的一致

     private void ProcessSomeEvent(object sender, EventArgs e)

     {

         Console.WriteLine("hello");

     }

 }


事件的接收者:


 <summary>

 /// 事件的接收和处理者

 /// </summary>

 class Container

 {

     private Control ctrl = new Control();

    public Container()

     {

         //这里使用的delegate必须与事件中声名的一致

         ctrl.SomeEvent += new Control.SomeHandler(this.ResponseSomeEvent);

         ctrl.RaiseSomeEvent();

     }

     public static void Main()

     {

         Container pane = new Container();

         Console.ReadLine();

     }

     //这是事件的接受者对事件的响应

     private void ResponseSomeEvent(object sender, EventArgs e)

     {

         Console.WriteLine("Some event occur!");

     }

 }

程序运行的结果如下:


please input 'a':a


hello


Some event occur!

更多相关技术内容咨询欢迎前往并持续关注六星社区了解详情。

想高效系统的学习Python编程语言,推荐大家关注一个微信公众号:Python编程学习圈。每天分享行业资讯、技术干货供大家阅读,关注即可免费领取整套Python入门到进阶的学习资料以及教程,感兴趣的小伙伴赶紧行动起来吧。

attachments-2022-05-rLS4AIF8628ee5f3b7e12.jpg

  • 发表于 2022-09-22 09:53
  • 阅读 ( 268 )
  • 分类:C/C++开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
轩辕小不懂
轩辕小不懂

2403 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. Pack 1131 文章
  3. 小柒 1046 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章