• .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~

  • 相关阅读:
    清除富文本样式
    jquery--cookie应用
    Log4j 配置详解
    判断请求是否为ajax
    日期工具类
    Windows Server2012 KB2919355 补丁无法安装
    安装系统步骤:
    大白菜u盘启动盘制作工具取消赞助商方法详解
    视频使用教程
    检查网络是否正常的几种命令
  • 原文地址:https://www.cnblogs.com/xiaozhi_5638/p/2939209.html
Copyright © 2020-2023  润新知