您的位置:首页 > 其它

扩展 FolderBrowserDialog 控件(二)

2010-12-20 14:38 197 查看
自定义 FolderBrowserDialog 控件要实现的功能有 2 个:

(1)允许指定任意有效的文件夹作为根目录;
(2)允许自定义指定文件夹是否能够选择的逻辑;
在前一篇扩展 FolderBrowserDialog 控件中,已经实现了允许指定任意有效的文件夹作为 FolderBrowserDialog 的根目录,这里就来实现允许自定义选择的文件夹是否能够被选择的逻,辑。

通过 Reflector 查看 FolderBrowserDialog 的代码,发现逻辑功能比较强的函数只有 2 个,一个是 RunDialog,另外一个就是 FolderBrowserDialog_BrowseCallbackProc。

RunDialog 是覆盖的基类函数,功能是实现特定对话框的展示;而 FolderBrowserDialog_BrowseCallbackProc 从名称上可以看出来是一个回调函数,那这个回调函数是被谁回调的呢?

在 RunDialog 函数中发现了这么一行代码:

this.callback = new UnsafeNativeMethods.BrowseCallbackProc(this.FolderBrowserDialog_BrowseCallbackProc);

牵出了一个定义在 UnsafeNativeMethods 中的 BrowseCallbackProc,但这只是一个委托:

public delegate int BrowseCallbackProc(IntPtr hwnd, int msg, IntPtr lParam, IntPtr lpData);

然后,在 RunDialog 中又发现了一行代码:

lpbi.lpfn = this.callback;

而从 上一篇 已经清楚了 lpbi 是一个传输显示文件夹选择对话框用的信息的输入结构,lpfn 是用于存储回调函数指针的成员变量。

好了,现在已经明确,FolderBrowserDialog_BrowseCallbackProc 是文件夹选择对话框生命周期中 Windows 用来通知自定义逻辑当前对话框产生了什么事件的途径。我们要实现自定义那些文件夹能够选择就只能从这个回调函数入手。

要实现自定义逻辑来指定文件夹是否能够被选择的步骤如下:

1、FolderBrowserDialog 获取到新选择的文件夹;

2、回调自定义逻辑,并通知当前新选择的文件夹的路径,自定义逻辑进行检测,并返回是否能够选择;

3、FolderBrowserDialog 根据自定义逻辑的指示设置 OK 按钮的启用或禁用。

显然,自定义逻辑的实现用委托就可以很方便地实现,现在的问题就是

a 如何获取到新选择的文件夹

b 如何启用或禁用 OK 按钮

先来看看 FolderBrowserDialog_BrowseCallbackProc 的代码:

private int FolderBrowserDialog_BrowseCallbackProc(IntPtr hwnd, int msg, IntPtr lParam, IntPtr lpData)
{
switch (msg)
{
case 1:
if (this.selectedPath.Length != 0)
{
UnsafeNativeMethods.SendMessage(new HandleRef(null, hwnd), NativeMethods.BFFM_SETSELECTION, 1, this.selectedPath);
}
break;

case 2:
{
IntPtr pidl = lParam;
if (pidl != IntPtr.Zero)
{
IntPtr pszPath = Marshal.AllocHGlobal((int) (260 * Marshal.SystemDefaultCharSize));
bool flag = UnsafeNativeMethods.Shell32.SHGetPathFromIDList(pidl, pszPath);
Marshal.FreeHGlobal(pszPath);
UnsafeNativeMethods.SendMessage(new HandleRef(null, hwnd), 0x465, 0, flag ? 1 : 0);
}
break;
}
}
return 0;
}

而且 shlobj.h 中有

// message from browse
#define BFFM_INITIALIZED        1
#define BFFM_SELCHANGED         2

// messages to browse
#define BFFM_SETSTATUSTEXT      (WM_USER + 100)
#define BFFM_ENABLEOK           (WM_USER + 101)

可以看出,FolderBrowserDialog_BrowseCallbackProc 处理了对话框初始化完成(BFFM_INITIALIZED)和文件夹选择发生改变(BFFM_SELCHANGED)2个事件。

很明显,要实现文件夹是否能够选择的逻辑,必须要依靠 BFFM_SELCHANGED 这个事件。但是我们怎么才能通过这个事件获取到被选择的文件夹的路径呢?

从 FolderBrowserDialog_BrowseCallbackProc 的实现中可以看到,新选择的文件夹可以通过函数的 lParam 参数获取到,而且 MSDN 中也有相关的描述,就是当 uMsg 是 BFFM_SELCHANGED 的时候,lParam 就是新选择的文件夹的 PIDL 标识。

FolderBrowserDialog_BrowseCallbackProc 的默认实现就是当选择了一个文件夹之后,调用 SHGetPathFromIDList 函数尝试把新选择的文件夹转换为文件系统路径,然后通过向窗体发送 BFFM_ENABLEOK 消息来允许或禁止 OK 按钮的启用或停用(取决于 SHGetPathFromIDList 执行的转换成功与否,也就是函数的返回值)。

由于 SHGetPathFromIDList 函数获取到的文件系统路径值是非托管字符串,所以还需要调用 Marshal 类中的 PtrToStringAuto 函数把非托管字符串填充到托管 string 对象中。

总结

好了,实现自定义逻辑来指定文件夹是否能够被选择详细步骤如下:

1、定义一个签名类似于 bool FolderSelectableDelegate(string folder) 的委托;

2、为 FolderBrowserDialog 新增加一个构造函数,接收一个 FolderSelectableDelegate 类型的参数,并保存到成员变量 _selectable 中;

3、修改 FolderBrowserDialog_BrowseCallbackProc 处理 BFFM_SELCHANGED 事件的实现如下:


3.1 调用 SHGetPathFromIDList 函数获取新选择文件夹的文件系统路径非托管字符串 pszPath;

3.2 调用 Marshal 类的 PtrToStringAuto 函数把 fszPath 的值复制到托管字符串对象 selectedPath 中;

3.3 用 selectedPath 作为参数调用委托 _selectable,并将返回值保存到 canSelectPath;

3.4 调用 SendMessage 函数,根据 canSelectPath 设置最后参数的值来启用或禁用 OK 按钮。SendMessage(new HandleRef(null, hwnd), BFFM_ENABLEOK, 0, canSelectPath ? ENABLE : DISABLE);


至此,扩展 FolderBrowserDialog 控件就全部完成了。

代码:FolderBrowserDialogEx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: