Разве нельзя добавить наши собственные пункты меню в контекстное меню CHtmlView? - PullRequest
0 голосов
/ 03 марта 2020

Так что я продолжаю возвращаться к этой статье о CodeProject:

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

Затем я понял это утверждение в начале статьи:

В пересмотренных примерах проектов используется новый , гораздо лучший подход к настройке, который будет всесторонне обсужден в следующем обновлении этой статьи, которое, мы надеемся, будет готово через пару недель. , Я публикую этот полу-документированный и не полностью протестированный код, потому что у меня есть признаки того, что некоторым разработчикам этот код может понадобиться гораздо раньше, чем в день моего следующего обновления. Для каждого пересмотренного образца есть также файл Readme.htm, в котором кратко описывается, как работает этот пример.

Я думал, что изо всех сил пытался понять код фрагментов статьи против загруженного источника! Поэтому я прочитал readme и там было написано:

В MF C 7 CHtmlView встроена поддержка IDocHostUIHandler, поэтому я просто переопределяю метод CHtmlView::OnShowContextMenu, а затем вызываю * Функция 1022 * (внутри 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

Значения идентификатора меню основаны на IDM_ версий, и все они работают.

Затем я попытался добавить свой собственный пункт меню в этот список с помощью моего собственного обработчика событий, и он показывается как отключенный.

Невозможно добавить наши собственные пункты меню в контекстном меню CHtmlView?

Я хотел добавить свой собственный пункт меню «Предварительный просмотр», который, в свою очередь, просто отправлял сообщение моему родительскому «Редактору», чтобы имитировать нажатие там «Предварительного просмотра». Но кажется, что любой пользовательский элемент, который добавляется в это меню, всегда отображается серым цветом.

Если я добавлю пункт меню «Предварительный просмотр» и присвоу ему значение 2003 (IDM_PRINTPREVIEW), он просто вызовет оригинальный механизм предварительного просмотра. И я не могу добавить свой собственный обработчик событий для того же класса в мой CChristianLifeMinistryHtmlView класс, поскольку это не соблюдается.

Я нашел эту статью , в которой упоминается:

Если вы решите заменить стандартное меню своим собственным, вы все равно можете добавить расширения меню в свое пользовательское меню. Просто включите пустую опцию меню 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); }

Идентификаторы меню для расширений находятся в диапазоне от IDM_MENUEXT_FIRST__ до IDM_MENUEXT_LAST__ для максимум 33 пользовательских команд.

Я знаю, что я не разработал это правильно, но я добавил пункт меню для заполнителя, а затем еще один для предварительного просмотра с идентификатором пункта меню из IDM_MENUEXT_FIRST__. Затем я добавил обработчик меню. Пункт меню больше не отключен, так что это хорошо. Но щелчок по нему ничего не дает.


Этот вопрос относится к:

Обновление

Я думаю, что нашел решение и вскоре дам ответ.

1 Ответ

0 голосов
/ 03 марта 2020

Я дошел до сути этого. На этом пути я узнал несколько ключевых вещей.

Концепция 1

Я не был в стороне от функции CHtmlView::OnShowContextMenu, которую мне нужно было реализовать:

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

В мою защиту IDE в Visual Studio не предлагал его в списке в качестве возможного переопределения. Но, тем не менее, оно существовало.

Концепция 2

Идентификаторы меню для всех пользовательских пунктов меню находятся в диапазоне от IDM_MENUEXT_FIRST__ до IDM_MENUEXT_LAST__ максимум до 33 пользовательских команды. В моем случае я вручную создаю некоторые значения #define в своем файле resource.h:

#define CUSTOM_MENU_PRINT_PREVIEW       3700
#define CUSTOM_MENU_EXPORT              3701

Обратите внимание, что я недоволен использованием литеральных значений. Я мог бы sh Я мог бы использовать IDM_MENU_EXT_FIRST + 1 и c. для моих определений, но я не знаю, как это сделать. Возможно?

Концепция 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 вместо использования литеральных значений.

Ваши пользовательские пункты меню будут выделены серым, если вы не используете правильные значения идентификаторов.

Concept 4

Затем необходимо создать контекстное меню (см. Оригинальный вопрос для этого кода).

Concept 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, если это возможно. Но в любом случае, все отлично работает!

...