.Net Winform开发笔记(三) 谈谈自制控件

末日这天写篇博客吧,既然没来,那就纪念一下。

这次谈谈自制控件,也就是自定义控件,先上图,再说

1.扩展OpenFileDialog,在OpenFileDialog中添加各种文件(.txt,.jpg,.excel等等)的预览功能

.Net Winform开发笔记(三) 谈谈自制控件

2.重写ListBox,增加折叠、鼠标背影、分类等功能

-----------------------------分割线--------------------------------------------------------------

一、扩展OpenFileDialog

许多软件的打开对话框都有预览功能,最常见的就是图片预览,用鼠标选择一个图片文件后,右侧或者下侧就会有该图片的缩略图(photoshop中属于后者)。在winform编程中,有专门的打开文件对话框的类OpenFileDialog,但是他不提供文件预览功能,封装得实在太好。看看它公开那些接口

.Net Winform开发笔记(三) 谈谈自制控件

提到扩展,很多人可能想到继承它就可以扩展它,可惜OpenFileDialog声明为sealed,不允许从他继承。稍微底层一点的,想到可以通过Win32 API来修改它的显示方式,只可惜,如你所见,它根本没提供Handle属性,更别说HandleCreated、HandleDestroyed等事件了。那么怎么样子搞呢?其实答案还是通过Win32 API,只是我们取得它的句柄的方式要复杂一点而且调用API的时机隐晦了一点。

提示:

1.Win32 API操作窗体需要知道窗体的句柄;

2.不熟悉Win32编程的同学可以先上网查查资料,特别是不知道SetParent、SetWindowPos等API是干嘛的,我觉得以下的看不懂。

为什么说取得它的句柄复杂了一点?难道不是用“FindWindow”、“FindWindowEx”、“ EnumChildWindows”等获取OpenFileDialog的句柄,再用“SetParent”、“SetWindowPos”等API将.net控件(本例中是DataGridView控件,当然可以使其他任何一种)添加到OpenFileDialog中去?没错,以上列举出来的API都是基本要用到的,“只是用在什么地方、什么时候用”是个比较麻烦的问题,原因如下:

1)我们知道OpenfileDialog显示的是模式对话框,也就是说,一旦它ShowDialog(),它以下的代码是不会再执行的,具体原因是什么(我以后的博客会专门讲为什么),你现在可以理解为OpenFileDialog()方法会阻塞调用线程,既然阻塞了调用线程,那么我们再无法控制程序了(直到它返回),根本谈不上再调用API获取OpenFileDialog的句柄然后去操作它。如果有人会说,“我可以另开辟线程去取OpenFileDialog得句柄再操作它”,恩,我不否定这个方法,只是我想说,如果你真的按照这个方法去试,那么肯定会陷入泥潭。因为你不仅要取它的句柄,你还要监视OpenFIleDialog的一举一动,移动、缩放、用户鼠标点击选择文件、更改目录等,然后再操作.net控件(本例中是DataGridView控件,下同),让.net控件去适应OpenFileDialog的大小等等,你会发现你忙死了,甚至有的你根本监视不了,比如用户点击选择文件、更改目录。

2)就算我们能够在OpenFIleDialog显示之后,取得它的句柄,那么什么时候再调用其他API呢?比如什么时候调用SetWindowPos,让.net控件适应OpenFileDialog的大小变化?什么时候知道用户选择文件发生了变化?

所以,API方法什么时候用?用在什么地方?就是接下来要讨论的东西。

我不知道各位在使用各种框架的时候,对“框架”的理解到什么程度,我觉得可以总结成一句话“跟个2b似地注册一些事件,然后苦逼地去写好每一个回调方法,我们却不知道为啥要这样写”,不是么?既然这样,那么我们的API方法只要写在了正确的回调方法中,我们就能到达想要的目的了。考虑几个问题:

1)OpenFileDialog显示,我们向其中添加.net控件。我们什么时候知道它显示?

2)OpenFileDialog大小发生变化时,我们要更新.net控件以适应新的大小。我们什么时候知道OpenFileDialog的大小发生了变化?

3)OpenFileDialog中用户选择的文件发生了变化,我们需要知道新选择的文件路径,用来显示在.net控件中。我们怎么知道选择了什么文件?(这里选择文件指用户用鼠标在OpenFileDialog中单击选取,不是点击“确定”后。)

以上所有的问题,其实在一个地方都可以知道,那就是监听OpenFileDialog窗体的Windows消息,因为一个窗体的任何一个动作都伴随着一系列的Windows消息(这个可以用Spy++查看)。既然这样,那么我们可以在窗体处理Windows消息的回调方法中调用API方法了,也就是窗体的Control.WndProc方法中。之前已经说过了,OpenFileDialog声明为Sealed,提供的接口少之又少,我们几乎根本不可能接触到OpenFileDialog的WndProc,更谈不上监听Windows消息,然后调用API去操作它。这时候,NativeWindow该出场了,先来引用一下MSDN上对NativeWindow的解释:

“提供窗口句柄和窗口过程的低级封装。”

说了像没说一样,其实就是说,将一个窗口句柄与NativeWindow对象绑定后,该NativeWindow对象就能接收到这个窗体的所有消息。说到Windows消息,我想说一下Windows桌面应用程序的运行流程,其实如果看了我前一篇博客的同学应该有些了解,.Net Winform开发笔记(二)中基本提到了一些。为了配合本次讲解,我再次画了一张图

.Net Winform开发笔记(三) 谈谈自制控件

上图中,虚线框可以看做是一个.net中的Control类对象(或者其派生类,下同,控件即窗体、窗体即控件),正常情况下,Winform程序会按照1->2->3的步骤运行,当我们将Control类对象的Handle(就是我们常说的窗口句柄,做了一下封装)与一个NativeWIndow对象绑定后,程序不再按照1->2->3这样的顺序运行了,他会按照1->2-1->2-2->3这样运行,也就是说,NativeWindow对象可以拦截Control类对象的WIndows消息,我们完全可以在NativeWIndow中重写他的WndProc方法,像处理自己的Windows消息一样去处理Control类对象的消息。所以,我们就可以在NativeWindow对象的WndProc中调用我们的API方法。

接下来,上代码(代码只提供大概思路)

1.扩展对话框

.Net Winform开发笔记(三) 谈谈自制控件View Code

 

2.监听Dialog的NativeWindow

.Net Winform开发笔记(三) 谈谈自制控件View Code

 

3.监听子控件的NativeWindow

.Net Winform开发笔记(三) 谈谈自制控件View Code

 

4.中间过渡窗体,用来获取OpenFileDialog的句柄

.Net Winform开发笔记(三) 谈谈自制控件View Code

 

5.访问Excel文件的类

.Net Winform开发笔记(三) 谈谈自制控件View Code

 

6.几个Win32结构体、枚举类型以及API声明(本代码参考CodeProject上的一篇文章)

.Net Winform开发笔记(三) 谈谈自制控件View Code

 

补充一下

1.代码只提供思路,不能拿来继承一下,就能实现自己想要的功能。

2.可以自己将代码中DialogNativeWindow类的addControl替换为其他控件,比如PictureBox用来预览图片、TextBox用来预览txt文件、RichTextBox用来预览代码文件等等,还可*组合。

3.可以自己将代码中DialogNativeWindow类的两个事件(SelectedFileChanged、SelectPathChanged)移到MultiOpenFileDialog中,并使其对外开放,外部程序可以监听该事件。

4.如果想做成通用类,拿过来继承一下就可以实现自己想要的效果,建议使MultiOpenFileDialog从UserControl,并将addControl设为自己。也就是说将自己添加到OpenFileDialog中去,MultiOpenFileDialog的派生类就可以在VS中设计自己想要的效果。

5.一个窗体新建显示时,它的拥有者会接收许多消息,包括WM_ACTIVATE、WM_IDLE等等,并且Lparam参数为新建窗体的句柄。

.Net Winform开发笔记(三) 谈谈自制控件View Code

6.Windows中窗体和所谓的控件(button、textbox)本质上没有区别,任务栏与QQ聊天框或者Chrome浏览器的地址栏对我们程序员来讲,是同一个东西。

7.与窗体有关的Win32 API基本都需要窗体句柄,其实任何一个API几乎都需要知道操作对象的句柄(不一定是窗体)。

8.Windows中任何一个窗体(控件)理论上都是平级的,不管是否同一进程,也就是说,我的winform应用程序只要知道了Chrome浏览器窗体的句柄,就可以控制Chrome浏览器,监听Chrome窗体的Windows消息(除非Chrome程序本身禁止了此操作)。

9.Windows桌面应用程序开发中,(部分平台、语言)理解四个东西,即进程、线程 、窗体(已经说了,是广义上的窗体)、消息。

10.查看系统中以上四个东西,可以使用Spy++工具。

完了,剩下那个下次再写了,太多了。希望有帮助~

 

作者:周见智 
出处:http://www.cnblogs.com/xiaozhi_5638/ 
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

标签: c#Winform

本文转自周见智博客博客园博客,原文链接:http://www.cnblogs.com/xiaozhi_5638/archive/2012/12/21/2828376.html,如需转载请自行联系原作者