博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
.Net开发笔记(七)使用组件编程
阅读量:7096 次
发布时间:2019-06-28

本文共 13595 字,大约阅读时间需要 45 分钟。

本文主要说到以下内容:

  • 什么是.Net中的组件,组件和类、控件的区别和联系。
  • 组件的特性。
  • 利用IExtenderProvider接口进行组件扩展。
  • “扩展组件”的简单应用——控件倒影。

1. 什么是.Net中的组件,组件和类、控件的区别和联系

必须说的是,“组件”一词在编程中经常遇到,意义众多,当然不管什么意思,从字面上来看就知道它应该有“一个可重复使用的单元”的意思。在.Net中,“组件”就特指实现了System.ComponentModel.IComponent接口的类,或者从实现了该接口的类直接或间接派生的子类。因此,“组件”它属于“类”,满足一定标准的类就是组件,而这个标准就由IComponent接口提供。那么什么是控件?组件跟控件有什么关系?我们知道,所有的控件都从System.Windows.Forms.Control类直接或间接派生(Asp.Net中从System.Web.UI.Control),而再查看System.Windows.Forms.Control源码我们会发现,它继承自System.ComponentModel.Component类,后者又实现了System.ComponentModel.IComponent接口,因此,我们可以说,控件也是一种组件。

综上,我们可以得出结论:

  • 类包含组件,组件包含控件。
  • 类不一定是组件,但组件一定是类,组件不一定是控件,但控件一定是组件。
  • 不管是类、组件还是控件,它们都是“一个可重复使用的代码单元”。
  • 不管是类、组件还是控件,他们都应该符合OO编程规律。

举一个FCL中的例子,来说明问题:

  • System.Windows.Forms.Application是一个类,既不是组件也不是控件。
  • System.Windows.Forms.Timer是一个类,也是一个组件,但不是控件(注意)。
  • System.Windows.Forms.ToolTip是一个类,也是一个扩展组件(后面说明什么是扩展组件),但不是控件。
  • System.Windows.Forms.Button是一个类,也是一个组件,同时也是控件。

上一张图说明关系:

图1

2. 组件的特性

1)假设各位知道Dispose模式,Dispose模式一般用于对象中使用了资源(包括托管资源和非托管资源)的场合,具体就是让定义的类实现IDisposable接口。查看System.ComponentModel.IComponent源码会发现,它实现了IDisposable接口,因此,可以说,所有的组件都具备有效管理资源的特性。

2)如果各位使用VS开发工具,那么,凡是实现了System.ComponentModel.IComponent接口的类,即凡是组件者,都可以将它添加在ToolBox中(工具箱),也就是常说的“可视化支持”。

3)Winfrom开发过程中,在设计窗体时,查看Form1.Desinger.cs文件,Form1类中有private System.ComponentModel.IContainer components数据成员,查看InitializeComponent()方法中会发现,一切类似Timer、ImageList、ToolTip这样的组件在构造时,都会像下面这样:

   this.imageList1 = new System.Windows.Forms.ImageList(this.components);

其中System.ComponentModel.IContainer字面意思可以看出它是一个容器,也就是说,每个组件在自我构造的时候,都会把自己加到一个容器中去。事实上,组件一般都不会单独去使用,都是由容器去管理,容器生命期结束时,容器中的各个组件也会结束自己生命期。

注:容器包含组件只是逻辑包含,跟通常的List、Array等集合包含元素不同。

有关IDE对组件的可视化支持,以及容器与组件之间的详细关系是一个非常复杂的话题,本文暂时不涉及。

3. 利用IExtenderProvider接口进行组件扩展

通常当使用一个组件时,需要在原有的基础上添加新的功能,也就是说需要扩展原有功能,我们做不到修改原有组件的代码,比如我们需要一个圆角按钮,现有的标准按钮(System.Windows.Forms.Button,前面说过它属于组件)是直角,第一想到的办法就是新建一个ButtonEx类继承Button,建立一个新的扩展控件。

以上是一种办法,但当需要给一类组件增加新的功能时,使用以上方法就会导致出现许多的扩展控件,而且有些时候只是新增加一个小功能,并不需要产生新的控件,因此,这时候有必要看看System.ComponentModel.IExtenderProvider这个接口。关于这个接口,我就不引用MSDN上的定义了,描述得太抽象,基本没什么参考价值,我将该接口的功能描述如下:

当需要为其它某个(或某些)组件扩展新的功能,又不能做到直接修改原有组件的源代码时,我们就可以定义一个类,让其实现IExtenderProvider接口。

IExtenderProvider接口源码如下:

View Code
public interface IExtenderProvider{         bool CanExtend(object extendee);}

FCL中已有的例子有很多,前面提到过的ToolTip就属于这种,查看System.Windows.Forms.ToolTip源码会发现,它继承自System.ComponentModel.Component,并且实现了System.ComponentModel.IExtenderProvider接口,大概源码如下:(模仿,非实际)

View Code
1 [ProvideProperty(“ToolTip”,typeof(Control))] 2 Public class ToopTip:Component,IExtenderProvider 3 { 4      Public ToolTip() 5      { 6            7      } 8      Public ToolTip(IContainer cont) 9      {10           Cont.Add(this);11      }12   13   Public string GetToolTip(Control control)14   {15          //具体实现16    }17       Public void SetToolTip(Control control,string caption)18       {19            //具体实现20       }21       //符合扩展的条件22       public bool CanExtend(object target)23 {24          return ((target is Control) && !(target is ToolTip)); //所有控件25 }26 }

解释:[ProvideProperty(“ToolTip”,typeof(Control))]的意思是给所有的Control类及其派生类增加属性“ToolTip”,也就是说原来的标准控件Button现在应该增加了ToolTip属性,在界面设计器中,从工具栏中拖出一个ToolTip组件,那么所有的其他控件在属性栏中增加了一项“toolTip1 上的 ToolTip”,可以设置该新增的属性,比如我给按钮button1设置该属性“it’s a button”,那么设计器生成的代码就是toolTip1.SetToolTip(button1,”it’s a button”);。

注:以上GetToolTip(Control control)和SetToolTip(Control control,string caption)方法具体实现暂没说明,不同情况实现不一样,具体可以参考下面的“控件倒影”demo代码。

4.“扩展组件”的简单应用——控件倒影。

让每一个标准控件都具有倒影效果,这一要求完全符合IExtenderProvider接口的使用范围,第一,需要给组件扩展新的功能;第二,数量之多,单单通过继承创建新的扩展控件麻烦;第三,新加功能很小,只是增加一个倒影效果。先上一张效果图:

图2

为了让任何一个控件都具有“倒影效果”,可以给每个控件扩展一个HasMirror的属性,数据类型为Bool型,另外,需要创建一个倒影类,负责显示倒影。

扩展类:

View Code
1 [ProvideProperty("HasMirror",typeof(Control))] 2     class MirrorExtender : Component, IExtenderProvider 3     { 4         Dictionary
_controllist = new Dictionary
(); 5 public MirrorExtender() 6 { 7 8 } 9 public MirrorExtender(IContainer cont)10 {11 cont.Add(this);12 }13 public void SetHasMirror(Control control, bool hasMirror)14 {15 if (_controllist.ContainsKey(control))16 {17 if (!hasMirror)18 {19 _controllist[control].Close();20 _controllist.Remove(control);21 }22 }23 else24 {25 if (hasMirror)26 {27 MirrorItem i = new MirrorItem() { HasMirror = hasMirror, Mirror = new Mirror(control) };28 _controllist.Add(control, i);29 }30 }31 }32 public bool GetHasMirror(Control control)33 {34 if (_controllist.ContainsKey(control))35 {36 return _controllist[control].HasMirror;37 }38 else39 {40 return false;41 }42 }43 44 #region IExtenderProvider 成员45 public bool CanExtend(object extendee)46 {47 return (extendee is Control);48 }49 #endregion50 }51 class MirrorItem52 {53 public bool HasMirror { get; set; }54 public Mirror Mirror { get; set; }55 public void Close()56 {57 Mirror.Dispose();58 }59 }

倒影类:

View Code
1 class Mirror : Control  2     {  3         Control _target;  4         Padding Padding { get; set; }  5         Bitmap CtrlBmp { get; set; }  6         byte[] CtrlPixels { get; set; }  7         int CtrlStride { get; set; }  8         Bitmap Frame { get; set; }  9         public Mirror(Control target) 10         { 11             _target = target; 12             _target.VisibleChanged += new EventHandler(_target_VisibleChanged); //目标控件“可见”属性发生变化 13             _target.LocationChanged += new EventHandler(_target_LocationChanged);  //目标控件“位置”属性发生变化 14             _target.ParentChanged += new EventHandler(_target_ParentChanged); //目标控件“父控件”属性发生变化 15             _target.Paint += new PaintEventHandler(_target_Paint); //目标控件发生重绘 16  17             SetStyle(ControlStyles.Selectable, false); //镜子应无焦点 18             SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true); 19  20             InitPadding(); 21         } 22  23         ///  24         /// 目标控件发生变化时,镜子中的内容需要重绘 25         ///  26         ///  27         ///  28         void _target_Paint(object sender, PaintEventArgs e) 29         { 30             if (!isSnapshotNow) 31             { 32                 Invalidate(); 33             } 34         } 35         ///  36         /// 目标控件改变父控件时,初始化镜子 37         ///  38         ///  39         ///  40         void _target_ParentChanged(object sender, EventArgs e) 41         { 42             Init(); 43         } 44         ///  45         /// 目标控件位置变化时,初始化镜子 46         ///  47         ///  48         ///  49         void _target_LocationChanged(object sender, EventArgs e) 50         { 51             Init(); 52         } 53         ///  54         /// 目标控件显示或隐藏时,初始化镜子 55         ///  56         ///  57         ///  58         void _target_VisibleChanged(object sender, EventArgs e) 59         { 60             Init(); 61         } 62         ///  63         /// 初始化镜子 64         ///  65         void Init() 66         { 67             this.Parent = _target.Parent; 68             this.Location = _target.Location; 69             this.Visible = _target.Visible; 70             if (Parent != null) 71             { 72                 var i = Parent.Controls.GetChildIndex(_target); 73                 Parent.Controls.SetChildIndex(this, i + 1); 74             } 75  76             var newSize = new Size(_target.Width + Padding.Left + Padding.Right, _target.Height + Padding.Top + Padding.Bottom); 77             if (newSize != Size) 78             { 79                 this.Size = newSize; 80             } 81         } 82         ///  83         /// 镜子位置 84         ///  85         void InitPadding() 86         { 87             Padding = new Padding(0, 0, 0, 20); 88         } 89         ///  90         /// 获取倒影 91         ///  92         /// 
93 Bitmap OnNonLinearTransfromNeeded() 94 { 95 Bitmap bmp = null; 96 if (CtrlBmp == null) 97 return null; 98 99 try100 {101 bmp = new Bitmap(Width, Height);102 103 const int bytesPerPixel = 4;104 PixelFormat pxf = PixelFormat.Format32bppArgb;105 Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);106 BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, pxf);107 IntPtr ptr = bmpData.Scan0;108 int numBytes = bmp.Width * bmp.Height * bytesPerPixel;109 byte[] argbValues = new byte[numBytes];110 111 Marshal.Copy(ptr, argbValues, 0, numBytes);112 var e = new TransfromNeededEventArg() {ClientRectangle = ClientRectangle, Pixels = argbValues, Stride = bmpData.Stride, SourcePixels = CtrlPixels, SourceClientRectangle = new Rectangle(Padding.Left, Padding.Top, _target.Width, _target.Height), SourceStride = CtrlStride };113 114 DoBottomMirror(e);115 116 Marshal.Copy(argbValues, 0, ptr, numBytes);117 bmp.UnlockBits(bmpData);118 }119 catch120 {121 }122 123 return bmp;124 }125 /// 126 /// 转化成字节数组127 /// 128 /// 129 ///
130 byte[] GetPixels(Bitmap bmp)131 {132 const int bytesPerPixel = 4;133 PixelFormat pxf = PixelFormat.Format32bppArgb;134 Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);135 BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, pxf);136 IntPtr ptr = bmpData.Scan0;137 int numBytes = bmp.Width * bmp.Height * bytesPerPixel;138 byte[] argbValues = new byte[numBytes];139 Marshal.Copy(ptr, argbValues, 0, numBytes);140 //Marshal.Copy(argbValues, 0, ptr, numBytes);141 bmp.UnlockBits(bmpData);142 return argbValues;143 }144 /// 145 /// 获取控件截图146 /// 147 /// 148 ///
149 Bitmap GetForeground(Control ctrl)150 {151 Bitmap bmp = new Bitmap(this.Width, this.Height);152 153 if (!ctrl.IsDisposed)154 {155 isSnapshotNow = true;156 ctrl.DrawToBitmap(bmp, new Rectangle(Padding.Left, Padding.Top, ctrl.Width, ctrl.Height));157 isSnapshotNow = false;158 }159 return bmp;160 }161 bool isSnapshotNow = false;162 const int bytesPerPixel = 4;163 /// 164 /// 重绘时,画出目标控件的倒影165 /// 166 /// 167 protected override void OnPaint(PaintEventArgs e)168 {169 try170 {171 CtrlBmp = GetForeground(_target);172 CtrlPixels = GetPixels(CtrlBmp);173 174 if (Frame != null)175 Frame.Dispose();176 Frame = OnNonLinearTransfromNeeded();177 178 if (Frame != null)179 {180 e.Graphics.DrawImage(Frame, Point.Empty);181 }182 }183 catch184 {185 186 }187 base.OnPaint(e);188 }189 /// 190 /// 计算倒影191 /// 192 /// 193 void DoBottomMirror(TransfromNeededEventArg e)194 {195 var source = e.SourcePixels;196 var output = e.Pixels;197 198 var s = e.Stride;199 var dy = 1;200 var beginY = e.SourceClientRectangle.Bottom + dy;201 var sy = e.ClientRectangle.Height;202 var beginX = e.SourceClientRectangle.Left;203 var endX = e.SourceClientRectangle.Right;204 var d = sy - beginY;205 206 for (int x = beginX; x < endX; x++)207 for (int y = beginY; y < sy; y++)208 {209 var sourceY = (int)(beginY - 1 - dy - (y - beginY));210 if (sourceY < 0)211 break;212 var sourceX = x;213 int sourceI = sourceY * s + sourceX * bytesPerPixel;214 int outI = y * s + x * bytesPerPixel;215 output[outI + 0] = source[sourceI + 0];216 output[outI + 1] = source[sourceI + 1];217 output[outI + 2] = source[sourceI + 2];218 output[outI + 3] = (byte)((1 - 1f * (y - beginY) / d) * 90);219 }220 }221 }222 public class TransfromNeededEventArg : EventArgs223 {224 public Rectangle ClientRectangle { get; internal set; }225 public byte[] Pixels { get; internal set; }226 public int Stride { get; internal set; }227 228 public Rectangle SourceClientRectangle { get; internal set; }229 public byte[] SourcePixels { get; internal set; }230 public int SourceStride { get; set; }231 public bool UseDefaultTransform { get; set; }232 }

测试时,从工具箱中拖放一个MirrorExtender组件,窗体设计器中各个控件就会增加一个“mirrorExtender1 上的 HasMirror”属性,设置为true,该控件具有倒影效果,反之,则没有倒影效果。

图3

希望对各位有帮助,O(∩_∩)O~

转载地址:http://rbxql.baihongyu.com/

你可能感兴趣的文章
IIS装好了无法访问localhost
查看>>
Flink Internals
查看>>
java.lang.IllegalArgumentException: already added:
查看>>
基于cucumber接口测试框架的扩展——测试框架总结之cucumber
查看>>
使用jconsole分析内存情况-JVM
查看>>
优秀程序员成功的14个好习惯
查看>>
“除了CTO你还是什么?”黑客、摇滚乐手、开源爱好者、程序员
查看>>
以腾讯云IoT Suite为例 谈谈边缘计算在物联网的实践与实现
查看>>
人工智能新物种天猫精灵魔盒:五大声学黑科技
查看>>
阿里云印度大区1月开服,服务出海中国企业
查看>>
设立科创板并试点注册制方案获批 专家称显示资本市场改革决心
查看>>
华林公司被查官网关闭 但酸碱平产品仍在线上销售
查看>>
中铁成都局通报“领导霸座”:已批评教育涉事人员
查看>>
有趣自由的灵魂,让年近 30 的她活成自己想要的模样
查看>>
新华时评:纸墨年轮 家书万金
查看>>
Nginx动态路由的新姿势:使用Go取代lua
查看>>
图数据库奥秘初探
查看>>
mpvue小程序开发 - 生命周期梳理
查看>>
数据库中间件 MyCAT 源码解析 —— 分片结果合并(一)
查看>>
路由跳转的思考
查看>>