скрыть

скрыть

  Форум  

Delphi FAQ - Часто задаваемые вопросы

| Базы данных | Графика и Игры | Интернет и Сети | Компоненты и Классы | Мультимедиа |
| ОС и Железо | Программа и Интерфейс | Рабочий стол | Синтаксис | Технологии | Файловая система |



Google  
 

Меню в WEB-браузере




- Будьте добры позавите пожалуйста Ваню.
- Вани нет, он покинул этот мир.
- Он, что умер?
- Нет, к интернету подключился.

В конференции я часто натыкался на вопросы типа – "Как добавить свой пункт меню в контекстное меню IE, как это делает ReGet", "Как запретить появление контекстного меню в TwebBrowser” или "Как показать свое меню вместо стандартного". А вот ответов в большинстве случаев не было, или они советовали попробовать другие компоненты. Но когда мне самому понадобилось в рамках одного проекта сразу, и запретить появление меню, и вставить свой пункт в стандартное меню IE, я решил покопать в этом направлении. И, конечно, MSDN выручила меня в этих поисках. Так что не бойтесь, меню и TwebBrowser – очень даже дружны между собой и то, что с легкостью делают ребята с ReGet Software, не такая уже и неприступная магия…

Запрещение появления меню в TwebBrowser

Хотя в инспекторе и есть такое свойство для этого компонента – PopurMenu, но его использование очень ограничено. Давайте для примера создадим PopurMenu с двумя произвольными пунктами и присвоим свойству PopurMenu TwebBrowser значение PopurMenu1. Запускаем приложение. Щелчок правой кнопкой мыши – ура, меню наше исправно отображаеться. Но радоваться рано. Загружаем любую страницу в браузер, снова щелкае мышкой – вместо нашего меню появляеться стандартное контекстное меню IE. Почему же так?

Компонент TwebBrowser всего лишь оболочка для COM объектов IE, а пока никакая страница не загружена – все сообщения передаются непосредственно вашей программе и, обрабатывая их, программа воспринимает TwebBrowser как обычный VCL-компонент. Поэтому наше меню и появлялось. Когда же вызван метод Navigate, управление идет уже через СОМ интерфейсы, поэтому сообщения обрабатываються не оконным компонентом, а кодом "под оболочкой".

Вообще запретить появление меню можно. Вот некоторые способы:


...
private
...
procedure WMMouseActivate(var Msg: TMessage); message WM_MOUSEACTIVATE;
end;
...

Ставим обработчик для сообщения WM_MOUSEACTIVATE на уровне головной формы приложения.

Потом пишем процедуру:


procedure TMainForm.WMMouseActivate(var Msg: TMessage);
begin
  try
    inherited;
    //Анализируем, какая кнопка мыши нажата
    if Msg.LParamHi = 516 then // если правая
      // показываем свое меню
      PopupMenu1.Popup(Mouse.CursorPos.x, Mouse.CursorPos.y);
    Msg.Result := 0;
  except
  end;
end;

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

Что бы просто запретить появление меню можно преобразовать процедуру так:


procedure TForm1.WMMouseActivate(var Msg: TMessage);
begin
  try
    inherited;
    if Msg.LParamHi = 516 then
      Msg.Result:= MA_NOACTIVATEANDEAT;
  except
  end;
end;

Значение Msg.LparamHi показывает, какая кнопка нажата. 513 - нажата левая, 516 – нажата правая.

Все, что сказано было про координаты, справедливо и здесь. Все эти решения имеют один недостаток – процедура срабатывает на каждое нажатие кнопки мыши в пределах всего приложения.

И еще один недостаток/особенность – полностью, со 100% надежностью перекрыть меню IE своим таким способом почему-то не удается, а вот просто запретить появление – да.

А можно ли управлять отображением меню на уровне самого WebBrowser? Да, отвечает MSDN.

Для этого нужно сперва получить доступ к интерфейсу IDocHostUIHandler и вызвать один из его методов – ShowContextMenu.

Учтите, версия IE – не ниже 4.0. (В С/С++ он описан в файлах Mshtmhst.h; Mshtmhst.idl )

Получить этот интерфейс можно вызывая QueryInterface с параметром IID_IDocHostUIHandler. Он предназначен для управления панелями, меню и контекстными меню WebBrowser-a.

Нас интересует пока только метод ShowContextMenu. Вот его обьявление:


function ShowContextMenu(const dwID: DWORD; const ppt: PPOINT; const
  pcmdtReserved: IUnknown; const pdispReserved: IDispatch): HRESULT; stdcall;

dwID
идентификатор меню, которое будет отображаться
ppt
указатель на структуру, которая указывает на координаты, где нужно отобразить меню.
pcmdTarget
ссылка на IOleCommandTarget интерфейс, который используется для запроса статуса и команд, которые должны выполняться меню.
рdispObject
ссылка на IDispatch интерфейс объекта, для того, что бы вызывать различные меню для различных объектов.

Метод возвращает:

S_OK
Отображается стандартное меню.
S_FALSE
Отображается другое, определенное программой меню.
DOCHOST_E_UNKNOWN
Идентификатор меню неизвестен.

В Internet Explorer 4.0 параметр pdispObject не используется, но в IЕ 5 и позже параметр содержит адрес IDispatch интерфейса. Таким способом можно выборочно запрещать появление контекстных меню.

Некоторые другие интересные методы IDocHostUIHandler:


function HideUI: HRESULT; stdcall;

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


function ShowUI(const dwID: DWORD; const pActiveObject: IOleInPlaceActiveObject;
  const pCommandTarget: IOleCommandTarget; const pFrame: IOleInPlaceFrame; const
  pDoc: IOleInPlaceUIWindow): HRESULT; stdcall;

Вызиваеться, когда приложение заменяет меню или панель инструментов.

Добавление пункта в стандартное меню

Иногда, если вы пишете какое-то приложение, которое взаимодействует с браузером, вам необходимо вызвать его непосредственно из IE. Но как добавить свой пункт в меню?

Пункт меню должен быть связан через URL с файлом, который содержит текст сценария, например, на JavaScript. Для того, что бы добавить пункт меню, откройте программно в реестре ключ:


HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\MenuExt

В этом разделе создайте подраздел, который будет иметь название такое же, как и пункт меню. Значение по умолчанию содержит URL, по которому находиться скрипт. Для подчеркивания вставьте в название перед нужной буквой символ &.

Скрипт будет загружен и выполнен в скрытом окне. В его свойстве external.menuArguments будет содержаться объект window того окна, где был выполнен скрипт меню.

Пример. Вставляет пункт с названием "Демо" в стандартное меню. При нажатии выполняется скрипт, который содержится в файле "С:\demo_script.htm".


HKEY_CURRENT_USER
Software
Microsoft
Internet Explorer
MenuExt
Open in new window = "file://c:\demo_script.htm"

В файл впишите следующее


<SCRIPT LANGUAGE="JavaScript" defer>
open(external.menuArguments.location.href);
</SCRIPT>

Действие скрипта заключаеться в следующем:

Он открывает новое окно браузера, и загружает в него документ, который определен в external.menuArguments.location.href – окне, в котором было вызвано меню.

Дополнительные ключи.

Под ключом, где содержится URL скрипта, есть еще несколько величин. Одна из них определяет, в каком из доступных контекстных меню появиться этот новый пункт. Вторая определяет, что сценарий должен выполняться как dialog box.

Ключ "Contexts" имеет тип DWORD, и задает контексты, в которых будет появляться ваше меню. Определяется как применение операции логического ИЛИ над следующими константами:


(0x1 << CONTEXT_MENU_DEFAULT) (evaluates to 0x1) 
(0x1 << CONTEXT_MENU_IMAGE) (evaluates to 0x2) 
(0x1 << CONTEXT_MENU_CONTROL) (evaluates to 0x4) 
(0x1 << CONTEXT_MENU_TABLE) (evaluates to 0x8) 
(0x1 << CONTEXT_MENU_TEXTSELECT) (evaluates to 0x10) 
(0x1 << CONTEXT_MENU_ANCHOR) (evaluates to 0x20) 
(0x1 << CONTEXT_MENU_UNKNOWN) (evaluates to 0x40)

Так, к примеру, вам нужно, что бы ваше меню появлялось только когда есть выделенный текст. Тогда запишите значение 0x10 (CONTEXT_MENU_TEXTSELECT)

Второй ключ - flag с типом DWORD. Если первый бит установлен в 0x1, то сценарий выполняется так, если бы он был вызван методом showModalDialog. Окно, в котором выполняеться скрипт не скрываеться, и не закрываеться после выполнения сценария.

Как реализовать все это в Дельфи?

  1. Поскольку метод вставки пунктов меню позволяет вставить только ссылку на файл со скриптом, то нужно писать скрипт который будет вызывать вашу программу и передавать ей нужные значения. Для этого нужно еще знать VBScript или JavaScript.
  2. Большая проблема состоит в том, что описания интерфейса IDocHostUIHandler нет в файлах.

Немножко поразбиравшись, я пришел к таким результатам:

Интерфейс не поддерживаеться стандартным TWebBrowser. Попытка перенести описание интерфейса с EmbeddedWB ник чему не приводит. Я понял с исходников, что при вызове IUnknown(WebBrowser1) as IDocHostUIHandler происходит обращение к DefaultInterface, а он у TWebBrowser IWebBrowser2. А он не знает о нужном нам интерфейсе.

Может, эта статья и не ответила на все вопросы, а только создала новые – не знаю. Это всегда так – как только с чем-то начинаешь разбираться, сразу к старым вопросам прибавляются новые…






Copyright © 2004-2016 "Delphi Sources". Delphi World FAQ




Группа ВКонтакте   Ссылка на Twitter   Группа на Facebook