文档章节

.Net开发笔记(七)使用组件编程

IT周见智
 IT周见智
发布于 2015/06/05 17:17
字数 2870
阅读 6
收藏 0
点赞 0
评论 0

本文主要说到以下内容:

  • 什么是.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~

© 著作权归作者所有

共有 人打赏支持
IT周见智

IT周见智

粉丝 10
博文 61
码字总数 185891
作品 0
西青
读书笔记之《Java并发编程的艺术》-线程池和Executor的子孙们

读书笔记部分内容来源书出版书,版权归本书作者,如有错误,请指正。 欢迎star、fork,读书笔记系列会同步更新 git https://github.com/xuminwlt/j360-jdk module j360-jdk-thread/me.j360....

Hi徐敏 ⋅ 2015/11/11 ⋅ 1

react-native 入门之 helloworld

简介 在网上找了一篇很不错的例子,推荐给大家边学边做 helloworld 首先,我们先创建一个项目,它的原始代码如下,可以看出来主要分为样式和功能两部分,没什么值得留意的 显示效果是这样的 ...

邪人君子 ⋅ 01/20 ⋅ 0

14.1-全栈Java笔记: Java语言中GUI到底是神马鬼?| AWT | Swing

到目前为止,我们在Java中编写的基本都是基于控制台的程序;而Java编程语言之所以如此流行的一个主要原因之一,就是因为它支持图形用户界面功能,即GUI(Graphical User Interface)。 GUI(...

全栈Java ⋅ 2017/08/09 ⋅ 0

Oracle笔记 目录索引

Oracle笔记 一、oracle的安装、sqlplus的使用 Oracle笔记 二、常用dba命令行 Oracle笔记 三、function 、selectOracle笔记 四、增删改、事务 Oracle笔记 五、创建表、约束、视图、索引、序列...

ibm_hoojo ⋅ 2011/05/03 ⋅ 0

大前端 Android 开发日记七:MPAndroidChat 填坑笔记

大前端 Android 开发日记七:MPAndroidChat 填坑笔记 Blog | Phodal Geek's Life2017-12-241 阅读 Android 继续上一天的 MPAndroidChat 填坑记录。 MPAndroidChat 自定义 Marker 首先,是自定...

Blog | Phodal Geek's Life ⋅ 2017/12/24 ⋅ 0

作为一名Java开发工程师需要掌握的专业技能

在学习Java编程完之后,学员们面临的就是就业问题。作为一名Java开发工程师,企业在招聘的时候,也是有一定的标准的。 为了帮助大家更好的找到适合自己的工作,小编在这里分享了作为一名Jav...

IT范儿 ⋅ 2017/11/21 ⋅ 0

UNIX网络编程卷2进程间通信读书笔记汇总

UNIX网络编程卷2进程间通信读书笔记(一)—概述 http://blog.chinaunix.net/u/22935/article527112.html UNIX网络编程卷2进程间通信读书笔记(二)—管道 (1) http://blog.chinaunix.net/...

长平狐 ⋅ 2012/09/03 ⋅ 0

免费的编程中文书籍索引

免费的编程中文书籍索引,欢迎投稿。 国外程序员在 stackoverflow 推荐的程序员必读书籍,中文版。 stackoverflow 上的程序员应该阅读的非编程类书籍有哪些? 中文版 github 上的一个流行的编...

modernizr ⋅ 2014/04/08 ⋅ 24

作为一名Java开发工程师需要掌握的专业技能

在学习Java编程完之后,学员们面临的就是就业问题。作为一名Java开发工程师,企业在招聘的时候,也是有一定的标准的。 为了帮助大家更好的找到适合自己的工作,小编在这里分享了作为一名Jav...

嘿你好夏天 ⋅ 2017/11/23 ⋅ 0

免费的计算机编程类中文书籍

免费的编程中文书籍索引,欢迎投稿。 国外程序员在 stackoverflow 推荐的程序员必读书籍,中文版。 stackoverflow 上的程序员应该阅读的非编程类书籍有哪些? 中文版 github 上的一个流行的编...

justjavac ⋅ 2014/08/13 ⋅ 10

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Spring表达式语言(SpEL)

1、SpEL引用 Spring EL在bean创建时执行其中的表达式。此外,所有的Spring表达式都可以通过XML或注解的方式实现。下面将使用Spring表达式语言(SpEL),注入字符串,整数,Bean到属性。 SpEL的...

霍淇滨 ⋅ 22分钟前 ⋅ 0

Gradle使用阿里云镜像

gradle 生命周期中有一个初始化( Initialization )的过程,这个过程运行在 build script 之前,我们可以在这个地方做一点系统全局的设置,如配置仓库地址。 你可以在以下几个位置实现仓库地址...

明MikeWoo ⋅ 30分钟前 ⋅ 0

appium+python3.6

1.安装jdk1.8(不知道为啥只识别1.8,1.10不识别,所以为了少折腾,迁就安装1.8) http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 配置 JAVA_HOME:...

Kampfer ⋅ 49分钟前 ⋅ 0

详解Apache 日志分割教程

一、日志切割 安装cronolog CentOS 5.3中编译安装Apache日志默认是不切割的,需要用用工具Cronnolog进行日志切割。 1.下载及安装 wget http://cronolog.org/download/cronolog-1.6.2.tar.gz ...

dragon_tech ⋅ 51分钟前 ⋅ 0

Keepalived介绍

负载均衡器(Load Balancer, LB )是一组能够将IP数据流以负载均衡形式转发到多台物理服务器的集成软件。有硬件负载均衡器和软件负载均衡器之分,硬件负载均衡器主要是在访问网络和服务器之间...

寰宇01 ⋅ 51分钟前 ⋅ 0

java8-Collections and Streams

stream和集合的区别是什么? 1.在计算的时候处理不同, 2.every element should be computed in the memory and then to be part of collections stream Stream apis filter with a predica......

writeademo ⋅ 56分钟前 ⋅ 0

Confluence 6 重新获得附件指南

每一个文件在恢复上传到 Confluence 的时候必须单独重命名,你可以通过下面说明的 3 个方法中选择一个进行操作: 选择 A - 通过文件名恢复附件 如果你知道你需要恢复的每一个文件名,尤其是你...

honeymose ⋅ 今天 ⋅ 0

【每天一个JQuery特效】根据状态确定是否滑入或滑出被选元素

主要效果: 本文主要采用slideToggle()方法实现以一行代码同时实现以展开或收缩的方式显示或隐藏被选元素。 主要代码如下: <!DOCTYPE html><html><head><meta charset="UTF-8">...

Rhymo-Wu ⋅ 今天 ⋅ 0

度量.net framework 迁移到.net core的工作量

把现有的.net framework程序迁移到.net core上,是一个非常复杂的工作,特别是一些API在两个平台上还不能同时支持。两个类库的差异性,通过人工很难识别全。好在微软的工程师们考虑到了我们顾...

李朝强 ⋅ 今天 ⋅ 0

请不要在“微服务”的狂热中迷失自我!

微服务在过去几年一直是一个非常热门的话题(附录1)。何为“微服务的疯狂”,举个例子: 众所周知,Netflix在DevOps上的表现非常棒。Netfix可以做微服务。因此:如果我做微服务,我也将非常...

harries ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部