【问题标题】:Is it not possible to add our own menu items on the CHtmlView context menu?是不是不能在 CHtmlView 上下文菜单中添加我们自己的菜单项?
【发布时间】:2020-03-03 10:40:58
【问题描述】:

所以我会继续回到 CodeProject 上的这篇文章:

https://www.codeproject.com/Articles/4758/How-to-customize-the-context-menus-of-a-WebBrowser

然后我意识到文章顶部的这句话:

修改后的示例项目使用了一种新的更好的自定义方法,本文的下一次更新将全面讨论这种方法,希望在几周内准备就绪。我正在发布这个半文档化且未经过全面测试的代码,因为我有迹象表明一些开发人员可能需要比我下一次更新的那一天更早地拥有这个代码。对于每个修改后的示例,还有一个 Readme.htm 文件,简要描述了示例的工作原理。

我以为我很难理解文章 sn-ps 与下载源代码中的代码!所以我读了自述文件,上面写着:

在 MFC 7 中,CHtmlView 嵌入了对IDocHostUIHandler 的支持,因此我只需重写CHtmlView::OnShowContextMenu 方法,然后调用::CustomShowContextMenu() 函数(在CustomMenus.cpp 中),其工作原理如第5 节所述我原来的文章。

所以,我决定在我的项目中添加自己的函数覆盖:

HRESULT CChristianLifeMinistryHtmlView::OnShowContextMenu(DWORD dwID, LPPOINT ppt,
    LPUNKNOWN pcmdtReserved, LPDISPATCH pdispReserved)
{
    return CustomContextMenu(ppt, pcmdtReserved);
}

并且我添加了类似的自定义菜单功能:

HRESULT CustomContextMenu(POINT* ppt, IUnknown* pcmdtReserved)
{
    IOleWindow* oleWnd = NULL;
    HWND        hwnd = NULL;
    HMENU       hMainMenu = NULL;
    HMENU       hPopupMenu = NULL;
    HRESULT     hr = 0;
    INT         iSelection = 0;

    if ((ppt == NULL) || (pcmdtReserved == NULL))
        goto error;

    hr = pcmdtReserved->QueryInterface(IID_IOleWindow, (void**)&oleWnd);
    if ((hr != S_OK) || (oleWnd == NULL))
        goto error;

    hr = oleWnd->GetWindow(&hwnd);
    if ((hr != S_OK) || (hwnd == NULL))
        goto error;

    hMainMenu = LoadMenu(AfxGetInstanceHandle(),
        MAKEINTRESOURCE(IDR_MENU_HTML_POPUP));
    if (hMainMenu == NULL)
        goto error;

    hPopupMenu = GetSubMenu(hMainMenu, 0);
    if (hPopupMenu == NULL)
        goto error;

    // Show shortcut menu
    iSelection = ::TrackPopupMenu(hPopupMenu,
        TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,
        ppt->x,
        ppt->y,
        0,
        hwnd,
        (RECT*)NULL);

    // Send selected shortcut menu item command to shell
    if (iSelection != 0)
        (void) ::SendMessage(hwnd, WM_COMMAND, iSelection, NULL);

error:

    if (hMainMenu != NULL)
        ::DestroyMenu(hMainMenu);

    return S_OK;
}

最后,我添加了一个菜单资源:

IDR_MENU_HTML_POPUP MENU
BEGIN
    POPUP "CustomPopup"
    BEGIN
        MENUITEM "View Source",                 2139
        MENUITEM SEPARATOR
        MENUITEM "Select All",                  31
    END
END

菜单 ID 值基于 IDM_ 版本,它们都有效。

然后我尝试使用我自己的事件处理程序将我自己的菜单项添加到该列表中,它显示为禁用。

难道不能在CHtmlView上下文菜单中添加我们自己的菜单项吗?

我想添加我自己的菜单项“打印预览”,然后它只是向我的父“编辑器”发布一条消息,以模拟在那里单击“打印预览”。但似乎任何添加到此菜单的自定义项始终是灰色的。

如果我添加一个“打印预览”菜单项并将其值设为 2003(IDM_PRINTPREVIEW),它只会触发原始打印预览机制。而且我不能将我自己的事件处理程序添加到我的CChristianLifeMinistryHtmlView 类中,因为它不被尊重。

我发现这个article 提到:

如果您选择用自己的菜单替换标准菜单,您可以 仍然将菜单扩展附加到您的自定义菜单。只需包括一个 菜单定义中的空白 IDM_MENUEXT_PLACEHOLDER 菜单选项 指示要插入自定义命令的位置。菜单扩展 在此占位符之前插入。您也可以添加自己的 通过插入菜单选项自定义命令到标准菜单 在IDM_MENUEXT_PLACEHOLDER之前,如下例所示。

#define IDM_MENUEXT_PLACEHOLDER  6047

// If the placeholder is gone or was never there, then just exit if
(GetMenuState(hMenu, IDM_MENUEXT_PLACEHOLDER, MF_BYCOMMAND) != (UINT)
  -1) {  InsertMenu(hMenu,                    // The Context Menu
             IDM_MENUEXT_PLACEHOLDER,         // The item to insert before
             MF_BYCOMMAND|MF_STRING,          // by item ID and str value
             IDM_MENUEXT_FIRST__ + nExtCur,   // the command ID
             (LPTSTR)aOpts[nExtCur].pstrText);// The menu command text

// Remove placeholder  DeleteMenu(hMenu, IDM_MENUEXT_PLACEHOLDER,
MF_BYCOMMAND); }

扩展的菜单 ID 介于 IDM_MENUEXT_FIRST__IDM_MENUEXT_LAST__ 之间,最多 33 个自定义 命令。

我知道我设计的不对,但我为占位符添加了一个菜单项,然后为打印预览添加了另一个菜单项 ID 为 IDM_MENUEXT_FIRST__。然后我向它添加了一个菜单处理程序。菜单项不再被禁用,这很好。但是点击它什么也没做。


这个问题涉及到:

更新

我想我已经找到了解决办法,很快就会给出答案。

【问题讨论】:

    标签: c++ mfc webbrowser-control


    【解决方案1】:

    我已经深入了解了。在此过程中,我学到了一些关键的东西。

    概念 1

    我并没有离开我需要实现的CHtmlView::OnShowContextMenu 函数:

    HRESULT CChristianLifeMinistryHtmlView::OnShowContextMenu(DWORD dwID, LPPOINT ppt,
        LPUNKNOWN pcmdtReserved, LPDISPATCH pdispReserved)
    {
        return CustomContextMenu(ppt, pcmdtReserved);
    }
    

    在我的辩护中,Visual Studio 中的 IDE 并未在列表中提供它作为可能的覆盖。但它确实存在。

    概念 2

    所有 自定义 菜单项的菜单 ID 介于 IDM_MENUEXT_FIRST__IDM_MENUEXT_LAST__ 之间,最多有 33 个自定义命令。就我而言,我在 resource.h 文件中手动创建了一些 #define 值:

    #define CUSTOM_MENU_PRINT_PREVIEW       3700
    #define CUSTOM_MENU_EXPORT              3701
    

    请注意,我不喜欢使用文字值。我希望我可以使用IDM_MENU_EXT_FIRST + 1 等作为我的定义,但我不知道该怎么做。可能吗?

    概念 3

    当您设计自定义菜单时,您也可以使用 IDM_XXX 值填充现有命令:

    IDR_MENU_HTML_POPUP MENU
    BEGIN
        POPUP "CustomPopup"
        BEGIN
            MENUITEM "Select All",                  31
            MENUITEM SEPARATOR
            MENUITEM "Export",                      CUSTOM_MENU_EXPORT
            MENUITEM SEPARATOR
            MENUITEM "Page Setup",                  2004
            MENUITEM "Print Preview",               CUSTOM_MENU_PRINT_PREVIEW
            MENUITEM SEPARATOR
            MENUITEM "Refresh",                     2300
            MENUITEM SEPARATOR
            MENUITEM "View Source",                 2139
        END
    END
    

    同样的注释仍然适用。我不知道如何将 IDM_* 常量分配给我自己的 #defines 而不是使用文字值。

    如果您不使用正确的 ID 值,您的自定义菜单项将显示为灰色。

    概念 4

    然后您必须创建上下文菜单(请参阅该代码的原始问题)。

    概念 5

    这与第4项密切相关。TrackMenuPopup在我的情况下需要调整如下:

    // Show shortcut menu
    iSelection = ::TrackPopupMenu(hPopupMenu,
        TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,
        ppt->x,
        ppt->y,
        0,
        hwnd,
        (RECT*)NULL);
    
    // Send selected shortcut menu item command to shell
    if (iSelection != 0)
    {
        if (iSelection == CUSTOM_MENU_PRINT_PREVIEW)
        {
            ::SendMessage(GetParent()->GetSafeHwnd(), WM_COMMAND, ID_FILE_PRINTPREVIEW, NULL);
        }
        else if (iSelection == CUSTOM_MENU_EXPORT)
        {
            ::SendMessage(GetParent()->GetSafeHwnd(), WM_COMMAND, ID_FILE_EXPORT, NULL);
        }
        else
        {
            (void) ::SendMessage(hwnd, WM_COMMAND, iSelection, NULL);
        }
    }
    

    关键是测试TrackMenuPopup 的返回值并进行自定义处理。事实上,在写这个答案时,我现在意识到我的“打印预览”菜单项可以定义为 IDM_PRINTPREVIEW 的值,我再次测试,如下所示:

    if (iSelection != 0)
    {
        if (iSelection == IDM_PRINTPREVIEW)
        {
            ::SendMessage(GetParent()->GetSafeHwnd(), WM_COMMAND, ID_FILE_PRINTPREVIEW, NULL);
        }
        else if (iSelection == CUSTOM_MENU_EXPORT)
        {
            ::SendMessage(GetParent()->GetSafeHwnd(), WM_COMMAND, ID_FILE_EXPORT, NULL);
        }
        else
        {
            (void) ::SendMessage(hwnd, WM_COMMAND, iSelection, NULL);
        }
    }
    

    所以我唯一的抱怨是我自己的#define 是使用文字数字的值,如果可能的话,我希望它们基于 IDM 常量。但不管怎样,一切都很好!

    【讨论】:

      猜你喜欢
      • 2013-12-27
      • 2011-09-21
      • 1970-01-01
      • 2015-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-24
      相关资源
      最近更新 更多