[NET][C#][翻译]自定义的文件打开对话框(源代码+示例)
2008-07-22 09:15
344 查看
[NET][C#][翻译]自定义的文件打开对话框(源代码+示例)
文章来源: http://www.codeproject.com/KB/dialog/OpenFileDialogEx.aspx Customizing OpenFileDialog in .NET By CastorTiu
下载源代码 - 300 Kb
下载示例 - 286 Kb
下载时需要登录【http://www.codeproject.com】
![](http://www.codeproject.com/KB/dialog/OpenFileDialogEx/OpenFileDialogEx.png)
0 && image.height>0){if(image.width>=700){this.width=700;this.height=image.height*700/image.width;}}">
创建了主窗口,我想:“从哪开始呢?”随后我决定建个带有“打开”功能的菜单,并且在打开图标前要有预览功能。
如果你在阅读本文,大概是因为你知道.NET有一个OpenFileDialog类,但它不可定制。我写的这个控件,目的是允许你为.NET的OpenFileDialog类添加些功能。你无法自定义OpenFileDialog类主要是因为这个类被声明为sealed,这意味着你不能继承它。如果转到基类FileDialog,此类允许你从它继承,但它有一个internal abstract方法"RunFileDialog"。因为是“内部”“抽象”的,故此只允许从同一程序集内部继承。
多少次,你需要在OpenFileDialog控件上放置额外控件却无可奈何……
搜索.NET的代码,我找到一些用MFC的,却没有用.NET的。打开文件对话框根本就不是在.NET中实现的,而是利用一个Win32 API函数 "GetOpenFileName"。
MSDN
就此,我有三个选择:
从头开始重写自己的OpenFileDialog。
重新利用资源创建自己的OpenFileDialog (利用API “GetOpenFileName” 并建立自己的模版)。
破解(hack) .NET的OpenFileDialog ,在上面增加我所需要的功能。
选择(a)不适合我,因为这需要大量开发时间而我有大量工作要完成,写完以后可能还需要重新检查。后面一个选择需要建立模版使用Win32 API调用和资源。选择(c)是当前更有前途的选择,不要认为hack就是不好的,基本上hack就是当你想要让控件实现一些扩展功能时,你却必须从其他线程或进程来进行。
因为我喜欢挑战,所以我决定"hack" OpenFileDialog类来创建我的自定义控件。
还要创建某个东东,它不需要对当前项目改变或增加代码,并且无需知道它如何工作就能够增加多个控件。它应该是个单独的控件以便被添加,就像IDE中其他控件一样。
创建的这个控件,我叫它"OpenFileDialogEx"。
你可以原封不动地使用OpenFileDialogEx类,但这毫无意义,因为它不包含扩展功能,就是一个空的UserControl。
所以必须继承OpenFileDialogEx以创建你自定义版的“打开文件对话框”。
在继承OpenFileDialogEx后,你已经建了一个自定义控件,在上面你可以加入任何控件,可以是按钮(Button)、面板(Panel)或者分组框(GroupBox)。基本上它就是个控件容器,随后这个容器就会被“挂”到.Net的OpenFileDialog对象上。
这个控件有与任何UserControl不同的额外3个属性、3个方法和2个事件。
属性:
OpenFileDialog默认是可改变尺寸的,OpenFileDialogEx控件也是如此,可自动伸缩。当用户扩大或缩小窗口时,它根据StartLocation属性产生不同变化。
Bottom:用户控件在水平方向伸缩
None:用户控件在两个方向自由伸缩
基本来说,当你添加诸如Button、Panel、Group Box之类的控件时,需要设置每个控件的Anchor属性,然后你就可以控制当用户改变OpenFileDialog窗口尺寸时各个控件的位置了。
举例来说,要获得图片预览,你可以设置StartLocation为Right,在继承OpenFileDialogEx的控件中加入一个PictureBox,设PictureBox的Anchor属性为Left, Top, Right, Bottom。这样当用户改变尺寸时PictureBox也会随之动态伸缩。
方法:
这几个方法都是虚方法,你要重载它们来与原OpenFileDialog交互。
事件:
两个事件为FileNameChanged与FolderNameChanged,由相应的虚方法"OnFileNameChanged()" 和 "OnFolderNameChaned()"来引发。建议不要直接使用事件,而应重写那些方法,因为事件数据在这层就被处理掉,没有传递给更多层。
想要获得OpenFileDialog的句柄,一种方法是重载窗体的WndProc方法并监视消息。当OpenFileDialog创建后,主窗体会收到一些消息,如WM_IDLE, WM_ACTIVATE, WM_NC_ACTIVATE等等。这些消息会设置参数lParam,此参数包含OpenFileDialog的窗口句柄。
如你所见,这需要重载WndProc方法。一些开发者甚至不知道此方法的存在,我可不想重蹈覆辙,而且我还会去注意一些MDI窗口打开OpenFileDialog时发生的问题。
接下来我主要做的是,当ShowDialog()方法被调用时,在屏幕上创建一个“影子”窗体(Dummy Form)并隐藏,此窗体会接管打开OpenFileDialog并获取其窗口句柄。
起初它监听的是WM_IDLE消息,但问题是当消息发送时已经太迟,窗口已经建立并显示在屏幕上,而你还是可以改变其内容,但用户会看到屏幕上原OpenFileDialog和自定义控件之间一个细微的闪烁。
于是,我们可以采用消息WM_ACTIVATE,它发生在OpenFileDialog显示在屏幕上之前。
至此,它获得所需的句柄,将要被显示出来,然后呢?
它如何改变OpenFileDialog窗口的属性呢?
这里涉及到.NET的NativeWindow类,此类是处理由相关句柄所发送消息的窗口过程的封装。那么可以创建一个NativeWindow,使其与OpenFileDialog的句柄相连。这样每个被发送给OpenFileDialog的消息都会被重定向给我们NativeWindow中的WndProc方法,我们可以对其屏蔽、修改或者原封不动。
在WndProc里,我们处理消息WM_WINDOWPOSCHANGING。如果对话框正在打开,那就根据用户设定的StartLocation属性改变原始的水平或垂直尺寸,即增加将要创建窗口的尺寸。这只在控件打开时发生一次。
还要处理消息WM_SHOWWINDOW。这里,所有原来OpenFileDialog里的控件都已创建,我们要往上面“附加”自己的控件。这由一个Win32 API "SetParent" 来完成,此API可让你改变父窗口。下面主要做的是,根据StartLocation属性值“附加”我们的控件到原OpenFileDialog上所设定的位置。
这样做的好处是我们依然拥有对附加在OpenFileDialog上各控件的完整控制。这意味着我们可以接受事件,调用方法,对这些控件做任何我们所需要的。
同时在初始化时,我们还得到了原OpenFileDialog上每个控件的句柄,这样就允许再次建立.NET NativeWindow对象来处理每个控件的消息。
准备就绪,那当用户在ListView中点击时该如何监视消息呢?
原本,我试着通过建立一个NativeWindow来处理ListView本身所发出的消息,但问题是每次用户改变目录或者在不同的视图中点击,句柄都被销毁,必须重建句柄。
用MS Spy分析中OpenFileDialog所有的窗口,我们注意到OpenFileDialog里还有另一个FileDialog窗口,很可能就是OpenFileDialog的基类窗口。检查MSDN文档,我们看到每个OpenFileDialog发出的动作都会引发一个包含OFNOTIFY结构的WM_NOTIFY消息,此结构包含那个动作的代码。其中两个动作是CDN_SELCHANGE和CDN_FOLDERCHANGE。
MSDN
当用户与文件夹组合框或列表框交互时它们被调用。接下来,我首先获取基类对象FileWindow的句柄,并由这个句柄创建一个NativeWindow对象。这样就可以处理消息WM_NOTIFY来分析OFNOTIFY结构以及处理CDN_SELCHANGE和CDN_FOLDERCHANGE。当这个窗口处理消息时,消息被转送给OpenFileDialogEx控件的方法OnFileNameChanged和OnFolderNameChanged。
另一种方法是当OpenFileDialog窗口关闭时进行拦截。开始时我利用消息WM_CLOSE,而它也工作了,但后来我发现当用户在列表框里双击文件时这个消息没有被调用。查看OpenFileDialog产生的消息,我看到可以用WM_IME_NOTIFY消息,当OpenFileDialog关闭时这个消息会带着一个值为IMN_CLOSESTATUSWINDOW的wParam参数被发送,这样就可以把调用传递给方法OnClosingDialog()。
那么,当用户改变OpenFileDialog尺寸时如何伸缩UserControl呢?这通过处理消息WM_WINDOWPOSCHANGING来完成,在此我们设定控件随着OpenFileDialog的尺寸来改变大小。
作为一个重要细节,当OpenFileDialog关闭时,必须恢复我们打开时的原始尺寸。这是因为OpenFileDialog可能会记录上一次的位置和尺寸,如果没有这么做,每次打开OpenFileDialog,它都会增加尺寸,使得它变得越来越大。
文章来源: http://www.codeproject.com/KB/dialog/OpenFileDialogEx.aspx Customizing OpenFileDialog in .NET By CastorTiu
下载源代码 - 300 Kb
下载示例 - 286 Kb
下载时需要登录【http://www.codeproject.com】
![](http://www.codeproject.com/KB/dialog/OpenFileDialogEx/OpenFileDialogEx.png)
0 && image.height>0){if(image.width>=700){this.width=700;this.height=image.height*700/image.width;}}">
简 介
几天前,我开始用我的 IconLib library 来编写一个图标编辑器程序。创建了主窗口,我想:“从哪开始呢?”随后我决定建个带有“打开”功能的菜单,并且在打开图标前要有预览功能。
如果你在阅读本文,大概是因为你知道.NET有一个OpenFileDialog类,但它不可定制。我写的这个控件,目的是允许你为.NET的OpenFileDialog类添加些功能。你无法自定义OpenFileDialog类主要是因为这个类被声明为sealed,这意味着你不能继承它。如果转到基类FileDialog,此类允许你从它继承,但它有一个internal abstract方法"RunFileDialog"。因为是“内部”“抽象”的,故此只允许从同一程序集内部继承。
多少次,你需要在OpenFileDialog控件上放置额外控件却无可奈何……
搜索.NET的代码,我找到一些用MFC的,却没有用.NET的。打开文件对话框根本就不是在.NET中实现的,而是利用一个Win32 API函数 "GetOpenFileName"。
MSDN
就此,我有三个选择:
从头开始重写自己的OpenFileDialog。
重新利用资源创建自己的OpenFileDialog (利用API “GetOpenFileName” 并建立自己的模版)。
破解(hack) .NET的OpenFileDialog ,在上面增加我所需要的功能。
选择(a)不适合我,因为这需要大量开发时间而我有大量工作要完成,写完以后可能还需要重新检查。后面一个选择需要建立模版使用Win32 API调用和资源。选择(c)是当前更有前途的选择,不要认为hack就是不好的,基本上hack就是当你想要让控件实现一些扩展功能时,你却必须从其他线程或进程来进行。
因为我喜欢挑战,所以我决定"hack" OpenFileDialog类来创建我的自定义控件。
它能帮你做什么?
我本就能破解控件使它做我想要的,而且也这么干过,但我在.NET 1.0中遇到这个问题很多次了,迄今为止没人对此给出个解决办法。因此我决定为这个控件编写一个接口,这样它可以用在不同的程序中。还要创建某个东东,它不需要对当前项目改变或增加代码,并且无需知道它如何工作就能够增加多个控件。它应该是个单独的控件以便被添加,就像IDE中其他控件一样。
创建的这个控件,我叫它"OpenFileDialogEx"。
我怎样用它呢?
想象一下若"OpenFileDialogEx"为抽象类——我不把这个类设为abstract只是因为VS IDE不能创建抽象类的实例,它拒绝绘制屏幕。你可以原封不动地使用OpenFileDialogEx类,但这毫无意义,因为它不包含扩展功能,就是一个空的UserControl。
所以必须继承OpenFileDialogEx以创建你自定义版的“打开文件对话框”。
在继承OpenFileDialogEx后,你已经建了一个自定义控件,在上面你可以加入任何控件,可以是按钮(Button)、面板(Panel)或者分组框(GroupBox)。基本上它就是个控件容器,随后这个容器就会被“挂”到.Net的OpenFileDialog对象上。
这个控件有与任何UserControl不同的额外3个属性、3个方法和2个事件。
属性:
DefaultViewMode:
这个属性让你选择OpenFileDialogEx的初始查看模式,默认使用“详细信息”(译者注:在我系统中默认为“列表”,应与系统设置有关)。这里你可以指定不同的查看模式,例如“图标”、“列表”、“缩略图”、“详细信息”等。StartLocation:
这个属性指示控件应该被挂靠在标准OpenFileDialog的右面、下面还是里面。通常,它被用于水平扩展OpenFileDialog。此外,若你需要往OpenFileDialog里添加额外控件,那可以设为"None",OpenFileDialogEx中的控件会与原来的OpenFileDialog 共享同一个客户区(Client Area)。OpenDialog:
这个属性表示控件中内嵌的OpenFileDialog。你可以在此设置打开文件对话框的标准属性,如InitialDirectory、AddExtension、Filters等等。OpenFileDialog默认是可改变尺寸的,OpenFileDialogEx控件也是如此,可自动伸缩。当用户扩大或缩小窗口时,它根据StartLocation属性产生不同变化。
StartLocation
Right:用户控件在垂直方向伸缩Bottom:用户控件在水平方向伸缩
None:用户控件在两个方向自由伸缩
基本来说,当你添加诸如Button、Panel、Group Box之类的控件时,需要设置每个控件的Anchor属性,然后你就可以控制当用户改变OpenFileDialog窗口尺寸时各个控件的位置了。
举例来说,要获得图片预览,你可以设置StartLocation为Right,在继承OpenFileDialogEx的控件中加入一个PictureBox,设PictureBox的Anchor属性为Left, Top, Right, Bottom。这样当用户改变尺寸时PictureBox也会随之动态伸缩。
方法:
这几个方法都是虚方法,你要重载它们来与原OpenFileDialog交互。
OnFileNameChanged()
当用户点击文件时被调用。OnFolderNameChanged()
当用户改变文件夹时被调用。OnClosing()
当OpenFileDialog准备关闭时被调用,在释放被分配的资源时很有用。事件:
两个事件为FileNameChanged与FolderNameChanged,由相应的虚方法"OnFileNameChanged()" 和 "OnFolderNameChaned()"来引发。建议不要直接使用事件,而应重写那些方法,因为事件数据在这层就被处理掉,没有传递给更多层。
它是如何编写的?
第一个问题是OpenFileDialog为有模式对话框,这就是说你基本上无法获得窗口句柄,因为当你调用ShowDialog()方法时,只要OpenFileDialog是打开的,你就没有程序流程的控制权。想要获得OpenFileDialog的句柄,一种方法是重载窗体的WndProc方法并监视消息。当OpenFileDialog创建后,主窗体会收到一些消息,如WM_IDLE, WM_ACTIVATE, WM_NC_ACTIVATE等等。这些消息会设置参数lParam,此参数包含OpenFileDialog的窗口句柄。
如你所见,这需要重载WndProc方法。一些开发者甚至不知道此方法的存在,我可不想重蹈覆辙,而且我还会去注意一些MDI窗口打开OpenFileDialog时发生的问题。
接下来我主要做的是,当ShowDialog()方法被调用时,在屏幕上创建一个“影子”窗体(Dummy Form)并隐藏,此窗体会接管打开OpenFileDialog并获取其窗口句柄。
起初它监听的是WM_IDLE消息,但问题是当消息发送时已经太迟,窗口已经建立并显示在屏幕上,而你还是可以改变其内容,但用户会看到屏幕上原OpenFileDialog和自定义控件之间一个细微的闪烁。
于是,我们可以采用消息WM_ACTIVATE,它发生在OpenFileDialog显示在屏幕上之前。
至此,它获得所需的句柄,将要被显示出来,然后呢?
它如何改变OpenFileDialog窗口的属性呢?
这里涉及到.NET的NativeWindow类,此类是处理由相关句柄所发送消息的窗口过程的封装。那么可以创建一个NativeWindow,使其与OpenFileDialog的句柄相连。这样每个被发送给OpenFileDialog的消息都会被重定向给我们NativeWindow中的WndProc方法,我们可以对其屏蔽、修改或者原封不动。
在WndProc里,我们处理消息WM_WINDOWPOSCHANGING。如果对话框正在打开,那就根据用户设定的StartLocation属性改变原始的水平或垂直尺寸,即增加将要创建窗口的尺寸。这只在控件打开时发生一次。
还要处理消息WM_SHOWWINDOW。这里,所有原来OpenFileDialog里的控件都已创建,我们要往上面“附加”自己的控件。这由一个Win32 API "SetParent" 来完成,此API可让你改变父窗口。下面主要做的是,根据StartLocation属性值“附加”我们的控件到原OpenFileDialog上所设定的位置。
这样做的好处是我们依然拥有对附加在OpenFileDialog上各控件的完整控制。这意味着我们可以接受事件,调用方法,对这些控件做任何我们所需要的。
同时在初始化时,我们还得到了原OpenFileDialog上每个控件的句柄,这样就允许再次建立.NET NativeWindow对象来处理每个控件的消息。
准备就绪,那当用户在ListView中点击时该如何监视消息呢?
原本,我试着通过建立一个NativeWindow来处理ListView本身所发出的消息,但问题是每次用户改变目录或者在不同的视图中点击,句柄都被销毁,必须重建句柄。
用MS Spy分析中OpenFileDialog所有的窗口,我们注意到OpenFileDialog里还有另一个FileDialog窗口,很可能就是OpenFileDialog的基类窗口。检查MSDN文档,我们看到每个OpenFileDialog发出的动作都会引发一个包含OFNOTIFY结构的WM_NOTIFY消息,此结构包含那个动作的代码。其中两个动作是CDN_SELCHANGE和CDN_FOLDERCHANGE。
MSDN
当用户与文件夹组合框或列表框交互时它们被调用。接下来,我首先获取基类对象FileWindow的句柄,并由这个句柄创建一个NativeWindow对象。这样就可以处理消息WM_NOTIFY来分析OFNOTIFY结构以及处理CDN_SELCHANGE和CDN_FOLDERCHANGE。当这个窗口处理消息时,消息被转送给OpenFileDialogEx控件的方法OnFileNameChanged和OnFolderNameChanged。
另一种方法是当OpenFileDialog窗口关闭时进行拦截。开始时我利用消息WM_CLOSE,而它也工作了,但后来我发现当用户在列表框里双击文件时这个消息没有被调用。查看OpenFileDialog产生的消息,我看到可以用WM_IME_NOTIFY消息,当OpenFileDialog关闭时这个消息会带着一个值为IMN_CLOSESTATUSWINDOW的wParam参数被发送,这样就可以把调用传递给方法OnClosingDialog()。
那么,当用户改变OpenFileDialog尺寸时如何伸缩UserControl呢?这通过处理消息WM_WINDOWPOSCHANGING来完成,在此我们设定控件随着OpenFileDialog的尺寸来改变大小。
作为一个重要细节,当OpenFileDialog关闭时,必须恢复我们打开时的原始尺寸。这是因为OpenFileDialog可能会记录上一次的位置和尺寸,如果没有这么做,每次打开OpenFileDialog,它都会增加尺寸,使得它变得越来越大。
结束语
我在Windows XP上测试运行正常,我还没机会在Windows 2000/2003或Vista等不同操作系统上试验,但应该没问题。我想在Windows 95/98上运行不了,因为我设置struct的size只是适合WinNT系统。如果你有任何建议或者发现了bug,请通知我,我会更新控件的。相关文章推荐
- c# 自定义的打开文件对话框(源代码+示例)
- [C#]自定义的打开文件对话框(源代码+示例)
- ASP.NET (C#版)如何将文件上传至数据库(源代码)
- [导入]在 Visual Studio .NET IDE 内使用“从源代码管理打开”时出现无法读取项目文件的错误
- 使文件下载的自定义连接支持 FlashGet 的断点续传多线程链接下载! C#/ASP.Net 实现!
- C#打开文件对话框(OpenFileDialog)
- C# 打开和保存文件对话框
- 在 Delphi 下自定义通用对话框------自定义打开文件对话框
- C#打开文件对话框
- JAVA 中打开/保存开文件对话框 源代码
- 关于asp.net C# 导出Excel文件打开Excel文件格式与扩展名指定格式不一致的解决办法
- C#.NET 窗口打开shp和dwg格式的文件
- c# 对话框:打开对话框 保存文件对话框 设置字体及颜色对话框
- c# .Net :Excel NPOI导入导出操作教程之数据库表信息数据导出到一个Excel文件并写到磁盘示例分享
- C# 打开以对话框,获取文件夹路径 、文件的路径、文件名
- 【原】c# 获取打开文件对话框的结果
- Asp.net 2.0 用C# 创建 PDF文件[引用] (示例代码下载)
- C#打开文件对话框(OpenFileDialog)
- Asp.net 2.0 用C# 创建 PDF文件[引用] (示例代码下载)
- 使文件下载的自定义连接支持 FlashGet 的断点续传多线程链接下载! C#/ASP.Net 实现!