65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.60/5 (5投票s)

2001年1月22日

viewsIcon

117226

downloadIcon

1745

使用的Shell接口。IShellFolder, IEnumIDList, 等。

Sample Image - BFF_new.jpg

引言

此代码演示了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_pidlMainPathm_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_pidlSelectedm_szSelected成员变量也适用同样的规则。只需确保所选路径是您基础根目录的子路径,否则您将收到指定的路径不存在的消息。

我在对话框中添加了一个"设置为根目录"按钮,并为了调试目的将其设为不可见。您可以在演示应用程序中使其可见并动态设置根目录。

您还必须将BFF{D}.LIB库添加到您的链接列表中。"D"代表您正在链接的是DLL的调试版本。

就是这样。您应该能够运行您的应用程序并使用CBrowseForFolderDlg类了。

数据与视图分离

我在这段示例中应用了“访问者”设计模式。我引入了一个抽象的ITreeNode接口,并将其所有成员变量设置为虚拟。ITreeNode代表"数据"。大部分功能存在于CShellTreeNode类中。CShellTreeNode使用Shell API来访问Shell对象。"视图"是MFC的CTreeCtrl类,而"访问者"是CTreeVisitor类,它有一个可派生的CUITreeVisitorCUITreeVisitor不知道CTreeCtrl的哪些方法需要处理才能使其工作并反映基于ITreeNode构建的层次结构。CUITreeVisitor不了解具体的CShellTreeNode,只处理ITreeNode公开的方法,因此您可以轻松地更改此接口的实现。

CShellHelper类由CShellTreeNode类使用,并且是所有"静态"数据的管理者。也就是说,在应用程序的生命周期内不会改变的数据。此类附加到全局系统图像列表(在WinNT上我相信,每个进程都有自己的系统图像列表副本),并允许其他人读/写此列表。

数据检索

我只提取选定节点的直接子节点(如果存在)。您可以通过使用ITreeNode::SetFindDepth()方法指定搜索深度。深度<1>表示只获取直接子节点。

显示树

为了实现树的高速显示,我使用了一种广为人知的显示技术。其核心思想是,您只提供当前可见的信息。CTreeCtrl在需要每个可见节点的文本和图标时会发送一个消息 - CTreeCtrl::OnGetdispinfoTVITEM::lParam成员指向ITreeNode的一个实例。只有这时,我才为树控件提供图像列表中的文本和图标ID。根据我上面描述的数据检索方法,每当用户展开一个节点时,我将请求ITreeNode去获取其直接子节点。然后,我将请求CUITreeVisitor去访问(显示)在树控件中找到的节点。这通过CTreeCtrl每次展开一个带子节点的节点时调用的方法CTreeCtrl::OnItemexpanding()来完成。因此,无论用户只展开一层,还是点击了数字小键盘上的"*"按钮,相同的算法都能完成工作。

排序

Shell对象以降序显示。系统文件夹的优先级高于文件。这是使用微软的标准方法完成的。有一个您需要设置的回调函数,当微软算法需要比较正在排序列表中的两个项目时会调用它。在我们的例子中,这是静态的CFileTreeCtrl::CompFunc()方法。

更新

我已更新源代码和图片。尽情享用!如果您发现错误或有任何改进建议,请告诉我。

再次更新... (2001/01/30)

我修复了几个错误。其中一个相当棘手。因此,列表控件在WinNT上无法正常工作。现在都已修复。现在您可以将Shell命名空间中的任何节点指定为Shell浏览器的基础根目录。CBrowseForFolderDlg::BFFINFO结构已扩展以支持任意根目录。您可以指定ITEMIDLISTCSIDLchar*形式的基础根目录。默认选择也一样。您可以输入网络路径,同步按钮应该可以正常工作。
© . All rights reserved.