Extender Provider 简化文件/文件夹选择






4.71/5 (17投票s)
让用户选择文件或文件夹的常见任务,封装在 IExtenderProvider 实现中。
引言
你是否曾经编写过一个配置对话框,要求用户为你的应用程序输入一个文件名或路径来执行某些操作?
如果你写过,那么你可能已经添加了一个按钮来浏览文件或文件夹,并编写了一些相当琐碎的 Click
事件处理程序:实例化相应的 FileDialog
或 FolderBrowserDialog
,设置其属性,调用 ShowDialog()
,如果 DialogResult
是 DialogResult.OK
,则用对话框中的文件名或文件夹填充关联的 TextBox
。
没什么大不了的,你可能会说。
你说得对。
这是一个标准的过程,一点也不难,但为了让配置对话框更用户友好,你必须这样做。当你有很多配置对话框或需要配置多个文件路径/文件夹时,你会发现自己一遍又一遍地编写相同的琐碎代码。
所以我想到可以稍微简化这个重复性的任务,并重新温习一下我对 extender provider 的知识...
File/FolderBrowserExtenderProvider 组件
基于 IExtenderProvider
的方法看起来很合适:将两个控件绑定在一起,一个充当“浏览”按钮,另一个接收选择的文件或文件夹。
我选择的方法是让 extender provider 扩展接收所选文件/文件夹的控件,并提供用于启动浏览的按钮。
编写 extender provider
要开发这样的组件,你需要继承 IExtenderProvider
,并在 ProvidePropertyAttribute
中指定要提供的属性的名称,如下所示:
[ProvideProperty("BrowseButton", typeof(Control))]
public class FolderBrowserExtenderProvider : Component, IExtenderProvider
这样,我就指定了我的 extender provider 将为某些组件添加一个名为“BrowseButton
”的附加属性,并且该属性是一个 Control
。
下一步是说明哪些组件可以被新的 extender provider 扩展。
这通过实现 CanExtend()
来完成。给定一个可扩展对象(即将被扩展的对象),你的 extender provider 必须告知它是否能为该对象提供给定的属性。
起初,如果可扩展对象派生自 TextBoxBase
,我就会返回 true
(通常你需要:在 TextBox
中输入文件名/文件夹),但过了一段时间,我想到不必将开发者限制于这种类型。任何控件都有一个 Text
属性,可以接收所选的文件/文件夹名称,所以现在这个函数对 Control
s 返回 true
。
接下来,你需要处理新属性的设置和检索。
extender provider 实际上并没有以一种你可以实际写的方式向可扩展对象添加新属性:
myTextBox.BrowseButton = myButton;
相反,它的职责是维护一个可扩展对象及其分配属性的列表。这样的列表条目是通过调用来创建的:
Set<PropertyName>(extendee, property value)
并通过调用来查询:
Get<PropertyName>()
在我的例子中,这些方法的签名如下:
public Control GetBrowseButton(Control extendee);
public void SetBrowseButton(Control extendee, Control browseButton);
我认为大多数 extender provider 会使用 HashTable
来维护分配,这样很容易跟踪。我的组件就是这样做的。
为 extender provider 添加功能
到目前为止,我们的 extender provider 除了记住哪个控件被分配为 BrowseButton
给哪个其他控件之外,并没有做任何事情。为了在点击分配的 BrowseButton
时实际开始浏览,一旦分配了 BrowseButton
,我们就必须为其添加一个 Click
事件处理程序。
虽然你很少会删除或重新分配可扩展对象,但在 BrowseButton
被重新分配之前移除事件处理程序是个好主意,否则你会发现多个对话框弹出。
Click
事件处理程序最终负责显示相应的 File
或 FolderBrowserDialog
。为了能够可视化设计这个对话框,每个 extender provider 都有一个公共只读属性 FileDialog
和 FolderBrowserDialog
。
这里有一个小细节:为了让设计器正确地序列化对话框的属性,我必须将 DesignerSerializationVisibilityAttribute
设置为 DesignerSerializationVisibility.Content
,否则会序列化对话框本身的引用而不是对话框的属性。
使用 extender providers
要在你的窗体中使用 extender providers,你应该将它们添加到你的工具箱中。然后将它们拖到你的窗体上,你的每个控件都会显示一个额外的属性 BrowseButton
,默认值为 None
。
只需选择你希望点击的控件以显示相应的浏览对话框,并将选择的文件夹/文件传输到第一个控件的文本中。
一个基本场景是在你的 Form
上有一个 TextBox
(textBox1
)和一个 Button
(button1
)。
你将一个 FolderBrowserExtenderProvider
添加到 Form
,并将 textBox1
的 BrowseButton
属性设置为 button1
,这样就完成了。
顺便说一句,你可以将一个控件分配为自己的 BrowseButton
。不幸的是,由于 VS 代码生成中的一个 bug,你无法在视觉上做到这一点,否则你会得到错误的 C# 代码。由于至今没有解决方法或修复,我在设计器中尝试进行此类分配时会抛出异常。
但是,你可以在代码中毫无问题地分配 BrowseButton
。
如果你不确定,只需查看源代码中包含的示例应用程序。
修改历史
- 2004.01.05 - 首次发布。