zoukankan      html  css  js  c++  java
  • .Net开发笔记(七)使用组件编程

    本文主要说到以下内容:

    • 什么是.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<Control, MirrorItem> _controllist = new Dictionary<Control, MirrorItem>();
     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             else
    24             {
    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             else
    39             {
    40                 return false;
    41             }
    42         }
    43 
    44         #region IExtenderProvider 成员
    45         public bool CanExtend(object extendee)
    46         {
    47             return (extendee is Control);
    48         }
    49         #endregion
    50     }
    51     class MirrorItem
    52     {
    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         /// <summary>
     24         /// 目标控件发生变化时,镜子中的内容需要重绘
     25         /// </summary>
     26         /// <param name="sender"></param>
     27         /// <param name="e"></param>
     28         void _target_Paint(object sender, PaintEventArgs e)
     29         {
     30             if (!isSnapshotNow)
     31             {
     32                 Invalidate();
     33             }
     34         }
     35         /// <summary>
     36         /// 目标控件改变父控件时,初始化镜子
     37         /// </summary>
     38         /// <param name="sender"></param>
     39         /// <param name="e"></param>
     40         void _target_ParentChanged(object sender, EventArgs e)
     41         {
     42             Init();
     43         }
     44         /// <summary>
     45         /// 目标控件位置变化时,初始化镜子
     46         /// </summary>
     47         /// <param name="sender"></param>
     48         /// <param name="e"></param>
     49         void _target_LocationChanged(object sender, EventArgs e)
     50         {
     51             Init();
     52         }
     53         /// <summary>
     54         /// 目标控件显示或隐藏时,初始化镜子
     55         /// </summary>
     56         /// <param name="sender"></param>
     57         /// <param name="e"></param>
     58         void _target_VisibleChanged(object sender, EventArgs e)
     59         {
     60             Init();
     61         }
     62         /// <summary>
     63         /// 初始化镜子
     64         /// </summary>
     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         /// <summary>
     83         /// 镜子位置
     84         /// </summary>
     85         void InitPadding()
     86         {
     87             Padding = new Padding(0, 0, 0, 20);
     88         }
     89         /// <summary>
     90         /// 获取倒影
     91         /// </summary>
     92         /// <returns></returns>
     93         Bitmap OnNonLinearTransfromNeeded()
     94         {
     95             Bitmap bmp = null;
     96             if (CtrlBmp == null)
     97                 return null;
     98 
     99             try
    100             {
    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             catch
    120             {
    121             }
    122 
    123             return bmp;
    124         }
    125         /// <summary>
    126         /// 转化成字节数组
    127         /// </summary>
    128         /// <param name="bmp"></param>
    129         /// <returns></returns>
    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         /// <summary>
    145         /// 获取控件截图
    146         /// </summary>
    147         /// <param name="ctrl"></param>
    148         /// <returns></returns>
    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         /// <summary>
    164         /// 重绘时,画出目标控件的倒影
    165         /// </summary>
    166         /// <param name="e"></param>
    167         protected override void OnPaint(PaintEventArgs e)
    168         {
    169             try
    170             {
    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             catch
    184             {
    185 
    186             }
    187             base.OnPaint(e);
    188         }
    189         /// <summary>
    190         /// 计算倒影
    191         /// </summary>
    192         /// <param name="e"></param>
    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 : EventArgs
    223     {
    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~

  • 相关阅读:
    Windows性能计数器应用
    Azure Oracle Linux VNC 配置
    Azure 配置管理系列 Oracle Linux (PART6)
    Azure 配置管理系列 Oracle Linux (PART5)
    Azure 配置管理系列 Oracle Linux (PART4)
    Azure 配置管理系列 Oracle Linux (PART3)
    Azure 配置管理系列 Oracle Linux (PART2)
    vagrant多节点配置
    docker基本操作
    LINUX开启允许对外访问的网络端口命令
  • 原文地址:https://www.cnblogs.com/xiaozhi_5638/p/2939209.html
Copyright © 2011-2022 走看看