实现文件夹视图

Windows Shell 提供文件夹视图的默认实现(俗称 DefView),因此你可以避免实现自己的命名空间扩展的大部分工作。 由于某些视图功能无法通过自定义视图实现,因此通常建议使用默认的系统文件夹视图对象来代替自定义视图。 有关详细信息,请参阅 SHCreateShellFolderView。 本主题的其余部分讨论如何实现不支持较新视图功能的自定义文件夹视图。

与树视图不同,Windows 资源管理器不管理文件夹视图的内容。 相反,文件夹视图窗口承载文件夹对象提供的子窗口。 文件夹对象负责创建文件夹视图对象,以在子窗口中显示文件夹的内容。

实现文件夹视图对象的关键是 IShellView 接口。 Windows 资源管理器使用此接口与文件夹视图对象通信。 在显示文件夹视图之前,Windows 资源管理器调用文件夹对象的 IShellFolder::CreateViewObject 方法,并将 riid 设置为 IID_IShellView。 创建文件夹视图对象并返回其 IShellView 接口。 然后,文件夹视图对象必须创建文件夹视图窗口的子窗口,并使用子窗口显示有关文件夹内容的信息。

除了控制数据的显示方式外,扩展还可以自定义 Windows 资源管理器的一些功能。 例如,扩展可以向工具栏或菜单栏添加项,或在状态栏上显示信息。

文件夹视图对象

文件夹视图对象是公开 IShellView 接口的 COM) 对象的组件对象模型 (。 此对象负责:

  • 创建文件夹视图窗口的子窗口,并使用它来显示文件夹的内容。
  • 处理与 Windows 资源管理器的通信。
  • 将特定于文件夹的命令添加到 Windows 资源管理器菜单栏和工具栏,并在选中这些命令时处理这些命令。
  • 管理用户与文件夹视图窗口的交互,例如显示快捷菜单或处理拖放操作。

本文档讨论前三个主题。 由于用户与文件夹视图的交互发生在子窗口中,因此文件夹视图对象负责处理它,就像处理任何其他窗口一样。

Windows 资源管理器通过调用文件夹对象的 IShellFolder::CreateViewObject 请求文件夹视图对象,并且 riid 设置为 IID_IShellView。 创建文件夹视图的过程为:

  1. 文件夹对象创建文件夹视图对象的新实例,并返回指向其 IShellView 接口的指针。
  2. Windows 资源管理器通过调用 IShellView::CreateViewWindow 方法初始化文件夹视图对象。 创建文件夹视图窗口的子窗口,并将其句柄返回到 Windows 资源管理器。
  3. 文件夹视图对象使用 Windows 资源管理器IShellBrowser 接口自定义 Windows 资源管理器工具栏、菜单栏和状态栏。
  4. 文件夹视图对象在子窗口中显示文件夹的内容。
  5. 文件夹视图对象处理用户与文件夹视图以及任何特定于文件夹的工具栏或菜单栏项的交互。

初始化文件夹视图对象

Windows 资源管理器通过调用 IShellView::CreateViewWindow 方法初始化文件夹视图对象。 方法的参数为文件夹视图对象提供创建子窗口所需的信息,该窗口将用于显示文件夹的内容:

  • 指向上一个文件夹视图对象的 IShellView 接口的指针。 此参数可以设置为 NULL
  • 包含上一个文件夹视图设置的 FOLDERSETTINGS 结构。
  • 指向 Windows 资源管理器IShellBrowser 接口的 指针。
  • 具有文件夹视图窗口维度的 RECT 结构。

在销毁上一个文件夹视图对象之前调用 IShellView::CreateViewWindow 方法。 因此, IShellView 接口指针允许你与上一个文件夹视图对象通信。 如果上一个文件夹属于你的扩展并且使用相同的显示方案,则此接口主要有用。 如果是这样,则可以与上一个文件夹视图对象进行通信,以便交换专用设置。

确定 IShellView 指针是否属于扩展的一种简单方法是让所有文件夹视图对象公开一个专用接口。 调用 IShellView::QueryInterface 请求专用接口,并检查返回值以确定文件夹视图对象是否为你的对象之一。 然后,可以在该接口上使用专用方法来交换信息。

FOLDERSETTINGS 结构包含上一个文件夹视图的显示设置。 主要设置是查看模式:大图标、小图标、列表或详细信息。 还有一个标志,其中包含各种显示选项的设置,例如视图是否应左对齐。 文件夹显示应尽可能遵循这些设置,以便在用户从一个文件夹转到另一个文件夹时提供一致的体验。 应存储此结构,并在设置更改时更新它。 Windows 资源管理器调用 IShellView::GetCurrentInfo 以获取此结构的最新值,然后再打开下一个文件夹视图。

IShellBrowser 接口由 Windows 资源管理器公开。 此接口是 IShellView 的配套接口,允许文件夹视图对象与 Windows 资源管理器通信。 没有其他方法来检索此接口指针,因此文件夹视图对象应存储该指针以供以后使用。 具体而言,需要调用 IShellBrowser::GetWindow 来检索将用于创建子窗口的父文件夹视图窗口。 请注意,IShellBrowser::GetWindow 方法不包含在 IShellBrowser 文档中,因为它继承自 IOleWindow。 存储接口指针后,请记住调用 IShellBrowser::AddRef 以递增接口的引用计数。 如果不再需要 接口,请调用 IShellBrowser::Release

创建子窗口:

  1. 检查 FOLDERSETTINGSRECT 结构。
  2. 如果需要,请从上一个文件夹视图对象获取专用设置。
  3. 调用 IShellBrowser::GetWindow 以检索父文件夹视图窗口。
  4. 创建在上一步中获取的文件夹视图窗口的子级,并将其返回到 Windows 资源管理器。

显示文件夹视图

创建子窗口并将其返回到 Windows 资源管理器后,即可显示文件夹的内容。 通常,扩展通过让子窗口托管 列表视图控件WebBrowser 控件来显示其信息。 列表视图控件允许复制 Windows 资源管理器 经典视图。 WebBrowser 控件允许使用动态 HTML (DHTML) 文档来显示信息,这与 Windows 资源管理器 Web 视图非常类似。 有关详细信息,请参阅这些控件的文档。

文件夹视图窗口始终存在,即使它没有焦点。 因此,应为子窗口保持三种状态:

  • 使用焦点激活。 文件夹视图已创建并具有焦点。 设置适合焦点状态的菜单栏或工具栏项。
  • 激活时没有焦点。 文件夹视图已创建并处于活动状态,但它没有焦点。 设置适合非焦点状态的菜单栏或工具栏项。 省略适用于文件夹视图中项目选择的任何项目。
  • 关闭。 文件夹视图即将被销毁。 删除所有特定于文件夹的菜单项。

Windows 资源管理器通过调用 IShellView::UIActivate 在窗口状态更改时通知文件夹视图对象。 反过来,当用户通过调用 IShellBrowser::OnViewWindowActive 激活文件夹视图窗口时,文件夹视图对象应通知 Windows 资源管理器。 有关此接口的进一步讨论,请参阅 使用 IShellBrowser 与 Windows 资源管理器通信

文件夹视图处于活动状态时,需要处理属于子窗口的任何 Windows 邮件,例如 WM_SIZE。 窗口过程还将接收已添加到 Windows 资源管理器菜单栏或工具栏的任何项 的WM_COMMAND 消息。

创建文件夹视图后,Windows 资源管理器调用文件夹视图对象的 IShellView 接口,以向其传递各种信息。 对象必须相应地修改其显示。 当文件夹视图即将被销毁时,Windows 资源管理器会通过调用其 IShellView::D estroyViewWindow 方法通知文件夹视图对象。

实现 IShellView

创建文件夹对象后,Windows 资源管理器调用各种 IShellView 方法以请求信息或通知对象 Windows 资源管理器的状态发生更改。 本部分概述了如何实现这些 IShellView 方法。 其余方法用于更专用的用途,例如“文件打开”常用对话框。 有关详细信息,请参阅 IShellView 文档。

AddPropertySheetPages

当用户从 Windows 资源管理器“工具”菜单中选择“文件夹选项”时,将显示一个属性表,允许用户修改文件夹选项。 Windows 资源管理器调用文件夹视图对象的 IShellView::AddPropertySheetPages 方法,以便向此属性表添加页面。

Windows 资源管理器将指针传递到 IShellView::AddPropertySheetPages 的 lpfn 参数中的 AddPropSheetPageProc 回调函数。 调用 CreatePropertySheetPage 创建页面,然后调用 AddPropSheetPageProc 将其添加到属性表。 有关如何处理属性表的进一步讨论,请参阅 属性表

GetCurrentInfo

当用户从一个文件夹切换到另一个文件夹时,Windows 资源管理器会尝试维护类似的文件夹视图。 例如,如果上一个文件夹视图显示大图标,则下一个文件夹视图也应显示大图标。 当 Windows 资源管理器调用 IShellView::CreateViewWindow 来初始化文件夹视图对象时,该方法将接收 FOLDERSETTINGS 结构。 此结构包含使文件夹视图显示与上一个文件夹视图一致的信息。

若要帮助确保下一个视图与当前视图一致,请存储 FOLDERSETTINGS 结构。 如果视图更改(例如从大图标更改为小图标),请相应地更新结构。 在切换视图之前,Windows 资源管理器将调用 IShellView::GetCurrentInfo 来请求当前的 FOLDERSETTINGS 值,以将它们传递给下一个文件夹视图对象。

刷新

用户可以通过从“视图”菜单中选择刷新”或按 F5 键,确保文件夹视图反映文件夹的当前状态。 当用户执行此操作时,Windows 资源管理器将调用 IShellView::Refresh 方法。 文件夹视图对象应立即更新文件夹视图显示。

SaveViewState

Windows 资源管理器调用 IShellView::SaveViewState 方法来提示文件夹视图对象保存其视图状态。 然后,可以在下次查看文件夹时恢复状态。 保存视图状态的首选方法是调用 IShellBrowser::GetViewStateStream 方法。 此方法返回文件夹视图对象可用于保存其状态的 IStream 接口。 创建另一个文件夹视图时,可以调用同一 IShellBrowser::GetViewStateStream 方法以获取 IStream 指针,该指针允许读取以前文件夹视图保存的设置。

TranslateAcelerator

当用户按快捷键时,Windows 资源管理器通过调用 IShellView::TranslateAccelerator 将邮件传递到文件夹视图对象。 返回S_FALSE让 Windows 资源管理器处理消息。 如果文件夹视图对象已处理邮件,则返回S_OK。

当文件夹视图窗口具有焦点时,Windows 资源管理器会在处理消息之前调用 IShellView::TranslateAccelerator 。 由于大多数邮件通常与 Windows 资源管理器菜单栏或工具栏命令相关联,因此文件夹视图对象通常应返回S_FALSE。 然后,Windows 资源管理器可以正常处理消息。 仅当邮件特定于文件夹且你不希望 Windows 资源管理器执行任何进一步处理时,才返回S_OK。 如果文件夹视图没有焦点,Windows 资源管理器将首先处理邮件。 仅当它不处理消息时,它才调用 IShellBrowser::TranslateAcceleratorSB

使用 IShellBrowser 与 Windows 资源管理器通信

文件夹视图对象使用 IShellBrowser 接口与 Windows 资源管理器进行通信,以实现各种目的,包括:

修改 Windows 资源管理器菜单栏

Windows 资源管理器菜单栏允许用户启动各种命令。 默认情况下,菜单栏仅支持特定于 Windows 资源管理器的命令。 相关 WM_COMMAND 邮件由 Windows 资源管理器处理,不会传递到文件夹视图对象。 但是,可以通过 IShellBrowser 修改菜单栏,以包含一个或多个特定于文件夹的菜单项。 Windows 资源管理器将这些项目的关联WM_COMMAND消息传递给文件夹对象的窗口过程进行处理。 还可以删除或禁用不适用于应用程序的任何标准命令。

文件夹视图对象通常在首次显示文件夹视图之前修改菜单栏。 文件夹视图被销毁时,它们必须将菜单栏返回到其原始状态。 每次文件夹视图获得或失去焦点时,可能还需要修改菜单栏。

由于每次文件夹视图窗口的状态更改时,Windows 资源管理器都会调用 IShellView::UIActivate ,因此菜单栏修改通常包含在该方法的实现中。 修改 Windows 资源管理器菜单栏的基本过程是:

  1. 调用 CreateMenu 以创建菜单句柄。
  2. 通过调用 IShellBrowser::InsertMenusSB 将该菜单句柄传递给 Windows 资源管理器。 Windows 资源管理器将填写其菜单信息。
  3. 根据需要修改菜单。
  4. 调用 IShellBrowser::SetMenuSB ,让 Windows 资源管理器显示修改后的菜单栏。

Windows 资源管理器的菜单栏上有六个弹出菜单。 与所有 OLE 容器一样,Windows 资源管理器菜单分为六个组:文件、编辑、容器、对象、窗口和帮助。 下表列出了 Windows 资源管理器弹出菜单及其关联的组,以及菜单 ID。

弹出菜单 ID Group
文件 FCIDM_MENU_FILE 文件
编辑 FCIDM_MENU_EDIT 文件
视图 FCIDM_MENU_VIEW 容器
收藏夹 FCIDM_MENU_FAVORITES 容器
工具 FCIDM_MENU_TOOLS 容器
帮助 FCIDM_MENU_HELP 窗口

 

通过调用 IShellBrowser::InsertMenusSB 将菜单句柄传递给 Windows 资源管理器时,还必须将指针传递给成员已初始化为零的 OLEMENUGROUPWIDTHS 结构。

当 IShellBrowser::InsertMenusSB 返回时,Windows 资源管理器将添加其菜单项。 然后,可以将返回的菜单句柄与标准 Windows 菜单函数(如 InsertMenuItem) 配合使用,以便:

  • 将项添加到 Windows 资源管理器弹出菜单。
  • 修改或删除 Windows 资源管理器弹出菜单中的现有项。
  • 添加新的弹出菜单。

使用表中列出的 ID 标识各种 Windows 资源管理器弹出菜单。

注意

为了避免与 Windows 资源管理器命令 ID 冲突,添加的任何菜单项的 ID 都必须位于 FCIDM_SHVIEWFIRST 和 FCIDM_SHVIEWLAST 之间。 这两个值在 Shlobj.h 中定义。

 

修改完菜单后,调用 IShellBrowser::SetMenuSB 让 Windows 资源管理器显示新的菜单栏。

最初显示文件夹视图后,每次文件夹视图获得或失去焦点时,Windows 资源管理器都会调用 IShellView::UIActivate 。 如果有任何对文件夹视图状态敏感的菜单项,则应在每次状态更改时相应地修改菜单。 例如,你可能有一个菜单项,该菜单项作用于用户选择的文件夹视图中的某个项。 当文件夹视图失去焦点时,应禁用或删除此菜单项。

当 Windows 资源管理器调用 IShellView::UIActivate 以指示正在停用文件夹视图时,请通过调用 IShellBrowser::RemoveMenusSB 将菜单栏还原为其原始状态。

修改 Windows 资源管理器工具栏

除了修改 Windows 资源管理器菜单栏外,还可以向工具栏添加按钮。 与菜单栏一样,工具栏修改通常是 IShellView::UIActivate 实现的一部分。 将按钮添加到 Windows 资源管理器工具栏的过程如下:

  1. 将按钮的位图添加到工具栏的图像列表。
  2. 定义按钮的文本字符串。
  3. 将按钮添加到工具栏。

若要将位图添加到工具栏的图像列表,请通过调用 IShellBrowser::SendControlMsg 向工具栏发送TB_ADDBITMAP消息。 若要指定工具栏控件,请将方法的 id 参数设置为 FCW_TOOLBAR。 将 wParam 设置为位图中的按钮图像数,将 lParam 设置为 TBADDBITMAP 结构的地址。 图像索引在 pret 参数中返回。

有两种方法可以定义按钮的字符串:

  • 将字符串分配给按钮的 TBBUTTON 结构的 iString 成员。
  • 调用 IShellBrowser::SendControlMsg 向工具栏控件发送 TB_ADDSTRING 消息。 wParam 参数应设置为零,将 lParam 参数设置为字符串。 字符串索引在 pret 参数中返回。

若要将按钮添加到工具栏,请填写 TBBUTTON 结构并将其传递给 IShellBrowser::SetToolbarItems。 与菜单一样,命令 ID 必须介于 FCIDM_SHVIEWFIRST 和 FCIDM_SHVIEWLAST 之间。 然后,工具栏会将按钮追加到现有按钮的右侧。

例如,以下代码片段将一个标准按钮添加到 Windows 资源管理器工具栏,命令 ID 为 IDB_MYBUTTON。

TBADDBITMAP tbadbm;
int iButtonIndex;
TBBUTTON tbb;

tbadbm.hInst = g_hInstance;    // The module's instance handle
tbadbm.nID = IDB_BUTTONIMAGE;  // The bitmap's resource ID

psb->SendControlMsg(FCW_TOOLBAR, TB_ADDBITMAP, 1,
                    reinterpret_cast<LPARAM>(&tbadbm), &iButtonIndex);

psb->SendControlMsg(FCW_TOOLBAR, TB_ADDSTRING, NULL,
                    reinterpret_cast<LPARAM>(szLabel), &iStringIndex);

ZeroMemory(&tbb, sizeof(TBBUTTON));
tbb.iBitmap = iButtonIndex;
tbb.iCommand = IDB_MYBUTTON;
tbb.iString = iStringIndex;
tbb.fsState = TBSTATE_ENABLED;
tbb.fsStyle = TBSTYLE_BUTTON;

psb->SetToolbarItems(&tbb, 1, FCT_MERGE);

有关如何处理工具栏控件的进一步讨论,请参阅 工具栏控件

修改 Windows 资源管理器状态栏

可以使用 Windows 资源管理器状态栏显示各种有用的信息。 有两种方法可以使用状态栏:

第一种方法很简单,但足以满足多种目的。 为了获得更大的灵活性,可以通过调用 IShellBrowser::SendControlMsg 并将 id 参数设置为 FCW_STATUS,将消息直接发送到状态栏控件。 有关状态栏控件的进一步讨论,请参阅 状态栏

存储特定于视图的信息

当视图即将被销毁时,存储信息(如视图的状态或设置)通常很有用。 Windows 资源管理器通过调用 IShellView::SaveViewState 提示你执行此任务。 保存特定于视图的信息的首选方法是调用 IShellBrowser::GetViewStateStream。 此方法提供可用于存储信息的 IStream 接口。 你可以随意使用任何合适的数据格式。