Is it not possible to add our own menu items on the CHtmlView context menu?

北慕城南 提交于 2020-03-04 20:50:04

问题


So I keep coming back to this article on CodeProject:

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

I then realised this statement at the top of the article:

The revised sample projects are using a new, much better customization approach that is going to be comprehensively discussed in the next update of this article, which will hopefully be ready in a couple of weeks. I am publishing this semi-documented and not fully-tested code, because I am having indications that some developers may need to have this code much sooner than the day of my next update. For each revised sample there is also a Readme.htm file that briefly describes how the sample works.

I thought I was struggling to understand the code in the article snippets vs the downloaded source! So I read the readme and it stated:

In MFC 7 CHtmlView has embedded support for IDocHostUIHandler, thus I simply override the CHtmlView::OnShowContextMenu method and afterwards I call the ::CustomShowContextMenu() function, (inside CustomMenus.cpp) which works like described in the section 5 of my original article.

So, I decided to add my own function override in my project:

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

And I added the similar custom menu function:

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;
}

Finally, I added a menu resource:

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

The menu ID values are based on the IDM_ versions and they all work.

I then tried to add my own menu item into that list with my own event handler and it shows as disabled.

Is it not possible to add our own menu items on the CHtmlView context menu?

I wanted to add my own menu item "Print Preview" which in turn simply posted a message to my parent "Editor" to simulate clicking "Print Preview" there. But it seems that any custom item that is added to this menu is always greyed out.

If I add a "Print Preview" menu item and give it a value of 2003(IDM_PRINTPREVIEW) it just triggers the original print preview mechanism. And I can't add my own event handler for the same to my CChristianLifeMinistryHtmlView class as it is not honoured.

I found this article which mentions:

Should you choose to replace the standard menu with your own, you can still append menu extensions to your custom menu. Simply include a blank IDM_MENUEXT_PLACEHOLDER menu option in your menu definition to indicate where the custom commands are to be inserted. Menu extensions are inserted just before this placeholder. You can also add your own custom command to the standard menu by inserting the menu option before IDM_MENUEXT_PLACEHOLDER, as shown in the following example.

#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); }

The menu IDs for extensions fall between IDM_MENUEXT_FIRST__ and IDM_MENUEXT_LAST__ for a maximum of 33 custom commands.

I know I didn't design it right but I added a menu item for the place holder and then another for Print Preview with a menu item id of IDM_MENUEXT_FIRST__. I then added a menu handler to it. The menu item is no longer disabled so that is good. But clicking it does nothing.


This question relates to:

  • CHtmlView and Print Preview and Context menu

Update

I think I have found a solution and will provide an answer shortly.


回答1:


I have got to the bottom of this. There were a few key things that I learned along the way.

Concept 1

I was not away of the CHtmlView::OnShowContextMenu function which I needed to implement:

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

In my defence, the IDE in Visual Studio did not offer it in the list as a possibly override. But it existed non-the-less.

Concept 2

The menu IDs for all custom menu items fall between IDM_MENUEXT_FIRST__ and IDM_MENUEXT_LAST__ for a maximum of 33 custom commands. In my case I manually create some #define values in my resource.h file:

#define CUSTOM_MENU_PRINT_PREVIEW       3700
#define CUSTOM_MENU_EXPORT              3701

Note that I am not happy using literal values. I wish I could use IDM_MENU_EXT_FIRST + 1 etc. for my definitions but I do not know how to do that. Possible?

Concept 3

When you design your custom menu you can infill existing commands with the IDM_XXX values too:

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

The same note still applies though. I can't work out how to assign the IDM_* constants to my own #defines rather than using literal values.

Your custom menu items will be greyed out if you don't use the right ID values.

Concept 4

Then you have to create the context menu (see original question for that code).

Concept 5

This is closely related to item 4. TrackMenuPopup needed to be adjusted in my situation as follows:

// 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);
    }
}

The key was testing the return value of TrackMenuPopup and doing custom handling. In-fact, whilst writing this answer I now realise that my "Print Preview" menu item can be defined as the value of IDM_PRINTPREVIEW and I test again that, like this:

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);
    }
}

So my only gripe is that my own #define are values are using literal numbers and I would prefer them to be based on the IDM constants if possible. But either way, it all works fine!



来源:https://stackoverflow.com/questions/60505381/is-it-not-possible-to-add-our-own-menu-items-on-the-chtmlview-context-menu

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!