|
||||||||||||
Автор: Тимофей Чадов ВведениеВ последнее время путешествуя по форумам и группам новостей все чаще можно встретить вопросы, касающиеся ActiveX-элемента WebBrowser. К сожалению зачастую эти вопросы теряются в общем ворохе сообщений, а материалов касающихся использования функциональности Internet Exploler катастрофически мало. В то же время у этих технологий находиться все больше поклонников. Уж слишком заманчиво выглядит возможность написать собственный броузер, приложив минимум усилий. Например, текущая версия продукта, в разработке которой я принимаю участие, использует эти технологии для отображения информации из документно-ориентированной базы данных. При этом, как показывает практика, интерфейс web-представлений в данном случае, гораздо дружественнее пользователю, чем использование множества диалоговых форм, а чтобы его сменить достаточно подправить пару шаблонов в ресурсах. Предлагаемая вашему вниманию статья посвящена одному из аспектов написания подобных программ. В ней будет рассмотрено как можно подключиться с событиям объектной модели броузера, а значит позволить вашему приложению использовать те преимущества, которые дает DHTML. Я попытаюсь рассказать вам как непосредственно обрабатывать события, возбуждаемые объектной моделью броузера в процессе работы пользователя со страницей. Немного теорииКак и большинство ActiveX элементов WebBrowser является источником событий подключаемых через стандартный механизм Connection Point. К числу таких событий относятся OnBeforeNavigate, OnDocumentComplete и т.п. Несомненно они важны для управления приложением в целом, однако их возможностей явно недостаточно, если мы захотим более тесно познакомиться с DOM DHTML, например, узнать о перемещении мыши над элементами страницы или о нажатии клавиши на клавиатуре или вообще быть в курсе всех событий, которые можно использовать в сценариях DHTML. При использовании объектной модели DHTML из сценариев возможно создавать собственные обработчики событий простым присваиванием наблюдаемого элемента соответствующим свойствам, например:
Возникает вопрос. А можно ли получить нечто подобное из клиента на С++? Конечно, причем похожим образом. Достаточно создать свою функцию обработки и зарегистрировать ее. Теперь поподробнее. Для этого нужно проделать не так уж и много. Необходимо реализовать простой Com-объект для каждой функции обработчика. Этот объект должен реализовывать всего два стандартных интерфейса IUnknown и IDispatch. Далее ссылка на этот объект присваивается соответствующему свойству элемента, событие которого мы хотим наблюдать. При возникновении события броузер просто вызовет IDispatch::Invoke нашего объекта со значением DISPID = DISPID_VALUE (=0). Замечу, что это не единственный способ заставить приложения реагировать на события. Например, можно заставить работать механизм window.external. Тогда соответствующий скрипт будет выглядеть например, так:
Думаю идея понятна. Однако этот способ удобен если мы сами формируем страницу. Сегодня мы пойдем первым путем. Пишем шаблоный классИтак, настало время применить все вышеизложенное на практике. Конечно, можно руками написать COM-объекты для каждой функции, однако представьте, что число обработчиков переваливает за десяток, а каждый раз нужно реализовывать по сути одно и тоже. Думаю, понятно, к чему я клоню :) Самое время вспомнить о шаблонах С++. Итак, напишем простенький шаблонный класс. Чтобы не зависеть от конкретной библиотеки реализует пару IUnknown, IDispatch вручную. Реализация IUnknown вполне стандартна, а из IDispatch необходимо реализовать только функцию Invoke.
Как применять этот класс? Проще простого. Шаг 1. Создаем свою функцию обработчик по прототипу OnEvent(DISPID id, VARIANT* pVarResult). В принципе ее можно разместить где угодно. Я предпочитаю создавать ее в классе представления, наследнике CHtmlView. При этом все обработчики сосредоточены в одном месте и не нужно беспокоится о взаимодействии с классом документа. Шаг 2. Регистрируем ее в качестве обработчика интересующего нас события. Для этого через вызов CHtmlEventObject::CreateObject создаем экземпляр нашего COM-объекта. Передаем в него адрес функции обработчика и собственный идентификатор события. После этого передаем ссылку на него интересующему нас элементу.
Здесь есть одна тонкость. Зарегистрировать обработчик можно только тогда, когда документ уже загружен, иначе GetHtmlDocument() вернет NULL. Для этого можно отслеживать событие OnDocumentComplete. Ну вот собственно и все. Рассмотрим еще раз прототип функции обработчика OnEvent(DISPID id, VARIANT* pVarResult); В качестве id передается значение которое мы указали при регистрации обработчика. Это может пригодиться если мы захотим реализовать обработку нескольких событий в одной функции. Для этого нужно зарегистрировать одну и ту же функцию с разными DISPID, тогда по приходу событий мы сможет их различать. pVarResult нужно, если не требуется обработки по умолчанию.При этом достаточно в pVarResult вернуть VARIANT_FALSE. Итак, когда вызывается наш обработчик никакой дополнительной информации о событии в функцию не передается. А как же тогда поподробнее узнать, что произошло? Для этого необходимо воспользоваться интерфейсом IHTMLEventObj, доступным через объект window текущего документа. Посредством этого интерфейса можно получить подробную информацию о произошедшем событии, например, элемент, послуживший источником событий, состояние клавиш, местоположение курсора мыши и состояние ее кнопок. Вот его краткое описание из MSDN: Методы IHTMLEventObj Стоит заметить, что интерфейс IHTMLEventObj доступен только на время обработки конкретного события. При этом не все свойства в контексте определенного события имеют смысл. Например, значения возвращаемые функциями get_fromElement и get_toElement доступны только при обработке событий мыши onmouseover и onmouseout. В следующем примере в обработчике обределяется нажатая клавиша и выводится соответствующее диалоговое окно. Если была нажата клавиша Enter, то дальнейшая обработка отменяется.
В заключениеЧтобы собрать воедино все фрагменты приведу небольшой пример ( В заключение хочется заметить, что в этой статье я затронул только один небольшой аспект использования элемента WebBrowser. Если Вас заинтересует данная тема, пишите, продолжим. Автор: Тимофей Чадов
|
||||||||||||
![]() |
![]() |