Перехват и обработка событий.
Способы загрузки содержания обсуждались в предыдущей статье. Здесь речь пойдёт о различных способах обработки событий html-документа. Использование vbscript и javascript не рассматривается по идеологическим соображениям.
Самый простой способ создать реакцию на действие пользователя - использовать событие BefoNavigate элемента управления WebBrowser. Оно возникает перед началом загрузки нового документа, например, при нажатии ссылки в текущем документе. Для того, чтобы определить какой из пунктов выбран - анализируется текст ссылки. В данном примере есть ещё одна особенность. Для удаления полосы прокрутки используется строка 'body {overflow-y: visible;}'.
Private Sub Form_Load()
'загрузка документа
WebBrowser1.Navigate "about:" & _
"<html> <head> <style type=""text/css""> body {overflow-y: vi" & _
"sible;} a.item {color: #003DB2; font-family: Verdana, Arial," & _
" Helvetica, sans-serif; text-decoration: none;} a.item:link," & _
" a.item:visited, a.item:active {background-color: #B9D0F1;} " & _
"a.item:hover {background-color: #DDECFE;} </style> </head> <" & _
"body> <a href=""id1"" class=""item"">пункт 1</a><br> <a href" & _
"=""id2"" class=""item"">пункт 2</a><br> <a href=""id3"" clas" & _
"s=""item"">пункт 3</a> </body> </html>"
End Sub
Private Sub WebBrowser1_BeforeNavigate2(ByVal pDisp As Object, _
URL As Variant, Flags As Variant, TargetFrameName As Variant, _
PostData As Variant, Headers As Variant, Cancel As Boolean)
If Mid$(URL, Len(URL) - 2, 2) = "id" Then
'проверка URL
MsgBox "Выбран пункт " & Right$(URL, 1)
'отмена обработки события
Cancel = True
End If
End Sub
У данного метода есть несколько преимуществ:
Недостаток один, но большой: можно перехватить только два события - нажатие enter или клик мышкой. Для обработки остальных событий следует воспользоваться MSHTML
Чтобы это стало возможным необходимо сделать следующее:
Теперь необходимо выбрать событие и добавить код, его обрабатывающий.
Для того чтобы получить элемент документа, к которому относится событие необходимо обратиться к объекту Event следующим образом: 'mHTMLDocument.parentWindow.Event.srcElement'.
После этого необходимо определить необходимость и способ обработки события для данного элемента. Обычно, количество элементов html - документа гораздо больше, чем элементов управления на обычной форме. Можно предложить несколько путей для выбора нужного элемента.
Если необходимо обработать события для группы элементов - можно воспользоваться свойством 'parentElement', возвращающим элемент - предок. Следующий пример демонстрирует упрощённый способ обработки событий.
'References: Microsoft HTML Object Library
'Controls: Microsoft Internet Controls
'добавить на форму WebBrowser с именем WebBrowser1
Private WithEvents mHTMLDocument As MSHTML.HTMLDocument
Private Sub Form_Load()
Dim strBlankFile As String
strBlankFile = "about:" & _
"<input id=""txt1"" type=""text"" size=""20"" value=""текст""/>" & _
"<input id=""cmd1"" type=""submit"" value=""пуск""/>"
'загрузка документа
WebBrowser1.Navigate strBlankFile
While WebBrowser1.Document Is Nothing
DoEvents
Wend
'присваиваем html документ
Set mHTMLDocument = WebBrowser1.Document
End Sub
Private Function mHTMLDocument_onclick() As Boolean
'обработка событий только для элемента с идентификатором 'cmd1'
If mHTMLDocument.parentWindow.Event.srcElement.Id = "cmd1" Then
MsgBox mHTMLDocument.getElementById("txt1").Value
End If
End Function
Этот пример - более сложен. Демонстрирует загрузку html из переменной, использование объекта 'Event', нестандартных атрибутов и изменение содержания html документа во время работы программы.
'References: Microsoft HTML Object Library
'Controls: Microsoft Internet Controls
'добавить на форму WebBrowser с именем WebBrowser1
Private WithEvents mHTMLDocument As MSHTML.HTMLDocument
Private Sub Form_Load()
Dim mStr As String
Dim mDoc As MSHTML.IHTMLDocument
'загрузка простого документа
WebBrowser1.Navigate "res://mshtml.dll/blank.htm"
While WebBrowser1.Document Is Nothing
'ждём создания документа
DoEvents
Wend
Set mDoc = WebBrowser1.Document
mStr = "<html><body>" & _
" <head><style type=""text/css"">" & _
" body {overflow-y: visible;}" & _
" span {color: #003DB2;}" & _
" li {cursor: hand;}" & _
" </style></head>" & _
" <ul>" & _
" <li id=""1"" sel=""1"">пункт <span>1</span></li>" & _
" <li id=""2"" sel=""0"">пункт <span>2</span> " & _
" (не активен)</li>" & _
" <li id=""3"" sel=""1"">пункт <span>3</span></li>" & _
" </ul>" & _
" <div id=""txtout"">нужно кликнуть по списку</div>" & _
"</body></html>"
'меняем содержание всего документа
mDoc.Write mStr
Set mDoc = Nothing
'присваиваем html документ
'для обработки событий
Set mHTMLDocument = WebBrowser1.Document
End Sub
'нажатие кнопки мыши
Private Sub mHTMLDocument_onmousedown()
Dim objElement As MSHTML.IHTMLElement
Dim mDiv As MSHTML.HTMLDivElement
'получаем элемент, в который будем выводить сообщения
'по его идентификатору
Set mDiv = mHTMLDocument.getElementById("txtout")
'если нажата правая кнопка - отмена события
If mHTMLDocument.parentWindow.Event.button = 2 Then
mHTMLDocument.parentWindow.Event.returnValue = False
Exit Sub
End If
'получить элемент, для которого обрабатывается событие
Set objElement = mHTMLDocument.parentWindow.Event.srcElement
'получить элемент, имеющий атрибут "Sel"
Do While IsNull(objElement.getAttribute("Sel")) = True
Set objElement = objElement.parentElement
'поднялись до вершины иерархии, но такого элемента
'не встретили - выводим сообщение и выход из процедуры
If objElement Is Nothing = True Then
mDiv.innerHTML = "нужно кликнуть по списку"
Exit Sub
End If
Loop
'проверяем значение атрибута
If objElement.Sel = "1" Then
mDiv.innerHTML = "выбран пункт " & objElement.Id
Else
mDiv.innerHTML = "пункт " & objElement.Id & " не активен"
End If
End Sub
К недостаткам данного способа следует отнести невозможность обработки ряда событий, характерных для некоторых элементов. Например, событие 'onchange' для поля со списком или 'onpropertychange' для строки ввода. В этом случае можно воспользоваться следующим способом.
Способ аналогичен предыдущему, но WithEvents объявляется не документ, а конкретный элемент
'References: Microsoft HTML Object Library
'Controls: Microsoft Internet Controls
'добавить на форму WebBrowser с именем WebBrowser1
Private WithEvents mInputElement As MSHTML.HTMLInputElement
Private Sub Form_Load()
Dim strBlankFile As String
Dim mHTMLDocument As MSHTML.IHTMLDocument
strBlankFile = "about:."
'загрузка документа
WebBrowser1.Navigate strBlankFile
While WebBrowser1.Document Is Nothing
DoEvents
Wend
'присваиваем html документ
Set mHTMLDocument = WebBrowser1.Document
mHTMLDocument.Write _
"<input id=""txt1"" type=""text"" size=""20"" value=""""/>"
'получаем элемент, используя коллекцию 'All'
Set mInputElement = mHTMLDocument.All.txt1
mInputElement.Value = "измените текст"
'установка фокуса
mInputElement.setActive
End Sub
Private Sub mInputElement_onpropertychange()
Me.Caption = mInputElement.Value
End Sub
Элементы html документа имеют свойства, соответствующие событиям, которые они регистрируют. В скиптовых языках такому свойству может быть назначено имя функции, и она будет вызвана при возникновении соответствующего события. В Visual Basic этим свойствам может быть присвоен класс и вызываться будет заданная по умолчанию процедура. Этот способ описан статье MSDN Handling Events in Visual Basic Applications.
В примере, также демонстрируется ещё один способ загрузки html. Сначала загружается пустой документ из ресурсов файла 'mshtml.dll'. Это и есть страница 'about.blank'. Дальнейшая работа происходит в событии 'DocumentComplete' которое возникает по окончании загрузки. Метод 'innerHTML' изменяет содержание, записывая html код в тело документа. При таком подходе подключение библиотеки mshtml не требуется. Обратите внимание на ещё один способ избавления от вертикальной полосы прокрутки: 'body.Style.overflow = "hidden"'.
код формы:
'Controls: Microsoft Internet Controls
'добавить на форму WebBrowser с именем WebBrowser1
Private Sub Form_Load()
'загрузка документа 'about:blank' из файла ресурсов
WebBrowser1.Navigate "res://mshtml.dll/blank.htm"
End Sub
Private Sub WebBrowser1_DocumentComplete _
(ByVal pDisp As Object, URL As Variant)
Dim cHtmlEvent As New clsHtmlEvent
With WebBrowser1.Document
'присваиваем html документ
.body.innerHTML = _
"<input id=""cmd1"" type=""submit"" value=""пуск""/>"
'убираем полосу прокрутки
.body.Style.overflow = "hidden"
'задаём обработку события
.All("cmd1").onclick = cHtmlEvent
End With
End Sub
код класса 'clsHtmlEvent'
'класс clsHtmlEvent
Public Sub CmdClick()
'эта процедура должна быть процедурой по умолчанию
'для этого: в меню 'Tools' пункт 'Procedure Attributes'
'нажать кнопку 'Advanced >>'
'в списке 'Procedure ID' выбрать 'Default' и нажать 'OK'
MsgBox "клик по кнопке"
End Sub
В следующей статье речь пойдёт о создании интерфейса программ и использовании скинов.