问题
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 forIDocHostUIHandler
, thus I simply override theCHtmlView::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 beforeIDM_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__
andIDM_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/46833760/chtmlview-and-print-preview-and-context-menu