“浏览文件夹”对话框及源代码






4.60/5 (5投票s)
2001年1月22日

117226

1745
使用的Shell接口。IShellFolder, IEnumIDList, 等。
引言
此代码演示了Windows Shell接口的使用。
使用的Shell接口和函数
接口是
IShellFolder
IEnumIDList
Shell函数是
SHGetMalloc
SHGetDesktopFolder
SHGetFileInfo
SHGetSpecialFolderLocation
背后的想法
我以前从未接触过Windows Shell API,这是我第一次尝试。我想通过使用提供的和文档化的Shell接口来实现一种通用的检索Shell命名空间的方法。
这个示例是SHBrowseForFolder
Shell函数的替代品。这段代码并没有实现SHBrowseForFolder
的所有功能,但它提供了导航命名空间和选择所需路径的基本功能。
源代码被分成了Shell扩展DLL BFF{D}.DLL 和一个示例应用程序 App{D}.exe。DLL包含了你所需要的一切。示例应用程序展示了如何使用DLL的功能。
如何使用
当您需要调用"浏览文件夹"对话框时,请在源代码中加入以下内容:
CBrowseForFolderDlg::BFFINFO info; memset(&info, 0, sizeof(info)); info.m_pParent = this; strcpy(info.m_szTitle, "Browse For Folder"); strcpy(info.m_szMsg, "Select the Voice Mail Folder"); info.m_pidlRoot = m_pidlMainPath; /* Line 1 */ info.m_pidlSelected = m_pidlSelected; info.m_nFlags |= BFF_IDL_ROOT | BFF_IDL_SELECTED; /* Line 2 */ info.m_nFlags |= (m_bIncFiles) ? BFF_INCLUDE_FILES : info.m_nFlags; info.m_nFlags |= (m_bSysOnly) ? BFF_SYSTEM_ONLY : info.m_nFlags; CBrowseForFolderDlg dlg(&info); if (dlg.DoModal() == IDCANCEL) return; // release memory for the previous default SHFree(m_pidlSelected); // allocate and copy newly selected m_pidlSelected = dlg.GetTreeSelectedFullIdl();
上面的代码假设您已经预先分配并赋值了m_pidlMainPath
和m_pidlSelected
。如果您希望使用以null结尾的字符串,而不是ITEMIDLIST
,您可以如下修改第1和第2行:
strcpy(info.m_szRoot, "c:\\windows\\system");
info.m_nFlags |= BFF_CHAR_ROOT;
请注意,您也可以指定网络路径。
strcpy(info.m_szRoot, "\\\\ABERRESFORD\\views");
info.m_nFlags |= BFF_CHAR_ROOT;
或者CSIDL值。
info.m_pidlRoot = CSIDL_DESKTOP; info.m_nFlags |= BFF_CSIDL_ROOT;
CBrowseForFolderDlg::BFFINFO
结构中的m_pidlSelected
和m_szSelected
成员变量也适用同样的规则。只需确保所选路径是您基础根目录的子路径,否则您将收到指定的路径不存在的消息。
我在对话框中添加了一个"设置为根目录"按钮,并为了调试目的将其设为不可见。您可以在演示应用程序中使其可见并动态设置根目录。
您还必须将BFF{D}.LIB库添加到您的链接列表中。"D"代表您正在链接的是DLL的调试版本。
就是这样。您应该能够运行您的应用程序并使用CBrowseForFolderDlg
类了。
数据与视图分离
我在这段示例中应用了“访问者”设计模式。我引入了一个抽象的ITreeNode
接口,并将其所有成员变量设置为虚拟。ITreeNode
代表"数据"。大部分功能存在于CShellTreeNode
类中。CShellTreeNode
使用Shell API来访问Shell对象。"视图"是MFC的CTreeCtrl
类,而"访问者"是CTreeVisitor
类,它有一个可派生的CUITreeVisitor
。CUITreeVisitor
不知道CTreeCtrl
的哪些方法需要处理才能使其工作并反映基于ITreeNode
构建的层次结构。CUITreeVisitor
不了解具体的CShellTreeNode
,只处理ITreeNode
公开的方法,因此您可以轻松地更改此接口的实现。
CShellHelper
类由CShellTreeNode
类使用,并且是所有"静态"数据的管理者。也就是说,在应用程序的生命周期内不会改变的数据。此类附加到全局系统图像列表(在WinNT上我相信,每个进程都有自己的系统图像列表副本),并允许其他人读/写此列表。
数据检索
我只提取选定节点的直接子节点(如果存在)。您可以通过使用ITreeNode::SetFindDepth()
方法指定搜索深度。深度<1>表示只获取直接子节点。显示树
为了实现树的高速显示,我使用了一种广为人知的显示技术。其核心思想是,您只提供当前可见的信息。CTreeCtrl
在需要每个可见节点的文本和图标时会发送一个消息 - CTreeCtrl::OnGetdispinfo
。TVITEM::lParam
成员指向ITreeNode
的一个实例。只有这时,我才为树控件提供图像列表中的文本和图标ID。根据我上面描述的数据检索方法,每当用户展开一个节点时,我将请求ITreeNode
去获取其直接子节点。然后,我将请求CUITreeVisitor
去访问(显示)在树控件中找到的节点。这通过CTreeCtrl
每次展开一个带子节点的节点时调用的方法CTreeCtrl::OnItemexpanding()
来完成。因此,无论用户只展开一层,还是点击了数字小键盘上的"*"按钮,相同的算法都能完成工作。排序
Shell对象以降序显示。系统文件夹的优先级高于文件。这是使用微软的标准方法完成的。有一个您需要设置的回调函数,当微软算法需要比较正在排序列表中的两个项目时会调用它。在我们的例子中,这是静态的CFileTreeCtrl::CompFunc()
方法。更新
我已更新源代码和图片。尽情享用!如果您发现错误或有任何改进建议,请告诉我。再次更新... (2001/01/30)
我修复了几个错误。其中一个相当棘手。因此,列表控件在WinNT上无法正常工作。现在都已修复。现在您可以将Shell命名空间中的任何节点指定为Shell浏览器的基础根目录。CBrowseForFolderDlg::BFFINFO
结构已扩展以支持任意根目录。您可以指定ITEMIDLIST
、CSIDL
或char*
形式的基础根目录。默认选择也一样。您可以输入网络路径,同步按钮应该可以正常工作。