WebBrowser. Введение.

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

Модератор: dr.MIG

dr.MIG
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1441
Зарегистрирован: 18.12.2004 (Сб) 9:53
Откуда: г.Ярославль

WebBrowser. Введение.

Сообщение dr.MIG » 03.10.2007 (Ср) 18:58

Данная статья писалась для книги VBStreets. Но так как эта идея сошла на нет, поэтому и публикую статью здесь.
Идея этой статьи возникла после прочтения очередного вопроса по работе с WebBrowser, а так же после прочтения материалов сайта http://msvb.narod.ru/ (отдельное спасибо автору -- marvan'у).

Использование WebBrowser в программах на VisualBasic

Вступление
Для того что бы использовать элемент управления WebBrowser в своей программе, вам необходимо подключить к проекту файл shdocvw.dll. Для этого выберите меню Project / Components, а затем поставьте галочку напротив "Microsoft Internet Controls". Также необходимо подключить библиотеку Microsoft HTML Object Library, через которую мы будем осуществлять взаимодействие с элементом управления WebBrowser. Для этого выберите меню Project / References и отметьте "Microsoft HTML Object Library" соответствующий файлу mshtml.tlb. Кроме этого от вас потребуется хотя бы минимальное знание HTML и CSS.

Основные области применения элемента управления WebBrowser в приложении:

    • Использование WebBrowser в качестве интерфейса программы
    (Возможности WebBrowser, как контейнера для других элементов значительно превосходят возможности стандартной формы. Помимо привычного абсолютного позиционирования, когда задаются чёткие координаты, есть возможность использовать относительное, когда размещение элемента зависит от размеров других. Это очень важное свойство, позволяющее реализовать автоматическое изменение размеров (и при необходимости расположения) элементов формы при изменении размеров окна. При этом вы можете обрабатывать события HTML-документа, как обычно, - внутри самого документа, используя скрипты на языках VBScript или JavaScript, кроме того у вас появляется возможность делать это из программы на VB.
    Использование каскадно-стилевых таблиц позволяет легко менять стиль оформления интерфейса приложения.
    Использование DHTML позволит применять различные графические фильтры к графике в вашей программе)

    • Генератор отчётов
    (Альтернатива Crystal Reports и ему подобным генераторам отчётов)
    • Просмотр файлов различных форматов (как альтернатива OLE Container)

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

Перед тем как начать
Допустим, у нас имеется некая база данных c именем "db". В нашем случае это база данных Microsoft Access "db.mdb", в ней есть таблица с именем "people", в которой хранится список контактов. Структура этой таблицы следующая:
Имя поля -- Тип данных:
id_people -- Счётчик
Surname -- Текстовый
Name -- Текстовый
PName -- Текстовый
City -- Текстовый
Address -- Текстовый
Telephone -- Текстовый


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

Для этого нам необходимо создать два HTML-документа. Первый, в котором будет отображаться список фамилий, назовём "people.html". Вот его HTML-код:

Код: Выделить всё
<html>
<head>
<base href="">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<link rel="stylesheet" href="style_1.css" type="text/css">
</head>
<body>
<table cellspacing=0 cellpadding=0 align="center" class="bg"><tr><td>
<table cellspacing=1 cellpadding=4 width=310 id="people"><tr><td class="number">№</td><td class="surname">Фамилия</td></tr></table>
</td></tr></table>
</body>
</html>


Разобраться в этом коде не составит труда, единственное, что может остаться непонятным – это тэг <base href="">.
В HTML ссылки на внешние изображения, апплеты, программы для обработки форм, таблицы стилей и т.д. всегда задаются с помощью URI. Относительные URI разрешаются в соответствии с базовым URI, который может определяться из различных источников. Элемент "base" позволяет явно указать базовый URI документа.
Необходимость его использования в нашем случае вы поймёте чуть позже.


Второй HTML-документ, в котором будет отображаться подробная информация о определённом человеке, назовём его "people_info.html", будет иметь следующий HTML-код:

Код: Выделить всё
<html>
<head>
<base href="">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<link rel="stylesheet" href="style_1.css" type="text/css">
</head>
<body>
<table cellspacing=0 cellpadding=0 align="center" class="bg"><tr><td>
<table cellspacing=1 cellpadding=4 width=310 id="people_info"></table>
</td></tr></table>
<table width=320 align="center"><tr><td align="center">
<input type="button" value="Назад" id="btnBack" class="button">
<input type="button" value="Предпросмотр" id="btnPreview" class="button">
<input type="button" value="Печать" id="btnPrint" class="button">
</td></tr></table>
</body>
</html>


Таблица с id="people_info" не имеет ни одной ячейки, т.к. все они будут добавлены из нашей программы.

Необходимо отметить, что в Windows XP, для того чтобы в HTML-документе, который вы выводите через WebBrowser, все элементы управления (кнопки, списки, текстовые поля и т.д.) отображались в стиле XP, необходимо в секцию <head></head> добавить следующий мета-тэг <META HTTP-EQUIV="MSThemeCompatible" CONTENT="Yes">, а для того, что бы отключить стиль XP, <META HTTP-EQUIV="MSThemeCompatible" CONTENT="No">.

Теперь нам необходимо создать файл style_1.css, в котором мы опишем стиль наших html-документов. Перед вами листинг этого файла:

Код: Выделить всё
body {background-color:buttonface;border:none}
table.bg {background-color:#000000; margin-bottom:5}
td.number{background-color:#656ECA; width:20; font-family:Tahoma; font-size:12; font-weight:bold; color:#ffffff; text-align:center}
td.surname {background-color:#747FE9; width:290; font-family:Tahoma; font-size:12; font-weight:bold; color:#ffffff; text-align:center}
td.first_col {background-color:#bbbbbb; font-family:Tahoma; font-size:12}
td.second_col {background-color:#cdcdcd; font-family:Tahoma; font-size:12; color:#555555}
td.second_col_on {background-color:#bbbbbb; font-family:Tahoma; font-size:12; cursor:pointer}
input.button{font-family:Tahoma; font-size:12; color:#555555; width:100; cursor:pointer;border: solid 1 #000000; background-color:#cdcdcd}
input.button_on{font-family:Tahoma; font-size:12; width:100; cursor:pointer;border: solid 1 #000000; background-color:#bbbbbb}


Хотелось бы отметить строку body {background-color:buttonface; border:none}. Значение buttonface – это значение системного цвета, который используется для свойства BackColor формы. Таким образом WebBrowser при любой цветовой схеме не будет выделяться на форме. Запись border:none скрывает границу при отображении документа с этим стилем в элементе управления WebBrowser.
Остальные строки не должны вызвать особых затруднений.

Таких файлов можно создать сколь угодно много, описав в них различные стили оформления, которые можно будет применить к программе. Мы же создадим ещё один файл style_2.css. Примем первый стиль за «Классический», второй за «Современный».

Целесообразность использования нескольких стилей оформления может быть продиктована тем, что ваша программа может запускаться на разных операционных системах, имеющих различное графическое оформление. При этом цвета, используемые в оформлении вашей программы могут абсолютно не сочетаться с цветовым оформлением операционной системы. Вы сможете убедиться в этом на примере нашей программы, так как для Windows 9x больше подходит «Классический» стиль, а для XP - «Современный».

Создание файла ресурсов
Все файлы, только что созданные нами, мы будем хранить в виде ресурсов, внутри исполняемого файла. Конечно, их можно было бы поместить в папку, в которой располагается программа или же в отдельную директорию, но это может создать лишние проблемы, так как если один из файлов будет по какой-либо причине удалён, программа не будет корректно работать.
Данные, загружаемые из ресурсов должны находиться в разделе «HTML». Для создания файла ресурса создадим файл htmlRes.rc следующего содержания:

Код: Выделить всё
#define RT_HTML  23

people.html RT_HTML "people.html"
people_info.html RT_HTML "people_info.html"
style_1.css RT_HTML "style_1.css"
style_2.css RT_HTML "style_2.css"


Теперь нам необходимо создать файл ресурсов htmlRed.res. Для упрощения процесса, создадим в этой же папке файл makeRes.bat с таким содержанием:
"C:\Program Files\Microsoft Visual Studio\VB98\Wizards\RC.EXE" /r /fo htmlRes.res htmlRes.rc

Теперь, после того, как вы запустите файл makeRes.bat, в этой же директории будет создан файл htmlRes.res, который вам надо будет подключить к проекту.

Если файл ресурсов не создался, проверьте правильность пути к утилите rc.exe в файле makeRes.bat, а так же наличие всех файлов, указанных в htmlRes.rc – если хотя бы один из них будет отсутствовать, файл ресурсов не будет создан.

Приступаем к программированию
Создайте новый проект. Подключите необходимые библиотеки (см. «Вступление»), кроме того подключите Microsoft DAO 3.6 Object Library, необходимую для работы с базой данных.
После этого добавьте элемент управления WebBrowser на форму и установите в качестве его свойства Name – «WebBrowser».
Сначала объявим все необходимые нам переменные:

Код: Выделить всё
Private WithEvents mDoc As HTMLDocument
Private mIDoc As IHTMLDocument
Private mTable As HTMLTable
Private mTR As HTMLTableRow
Private mTD As HTMLTableCell
Private mIElement As IHTMLElement
Private WithEvents btnBack As HTMLButtonElement
Private WithEvents btnPrint As HTMLButtonElement
Private WithEvents btnPreview As HTMLButtonElement

Private mReg As Object

Private dbPeople As Database
Private strQuery As String
Private recPeople As Recordset

Private Type REGSETTINGS
    footer As String
    header As String
    Print_Background As String
End Type

Private strRegSettings As REGSETTINGS

Private isInfo As Boolean
Private strHTML As String

Объявления WithEvents необходимы для того, что бы мы смогли получать и обрабатывать события соответствующего класса.

Так же необходимо пояснить отличие класса HTMLDocument от IHTMLDocument. Класс HTMLDocument – это функциональный эквивалент объекту document, используемому в скриптах внутри HTML-страниц, он предоставляет все свойства и методы, необходимые для доступа к содержимому активного документа. Если же в программе используется раннее связывание с объектом HTMLDocument, то сослаться на него можно, используя интерфейсы IHTMLDocument, IHTMLDocument2 и IHTMLDocument3.

При загрузке формы нам необходимо выровнять на ней WebBrowser, соединиться с базой данных и вывести список фамилий.

В нашей программе будет предусмотрена возможность просмотра и печати отчёта. При этом внешний вид документа будет зависеть от настроек InternetExplorer’а, которые храняться в реестре. Так, например, если вы будете использовать вывод картинок в WebBrowser, вам необходимо будет включить их отображение. Для этого необходимо будет сделать соответствующие изменения в реестре. Главное, не забывайте восстанавливать прежние значения после завершения работы программы. В нашей программе мы отключим так называемые «footer» и «header» - подписи вверху и внизу страницы, а так же включим вывод на печать фона страницы.
Большинство настроек находится в разделах реестра:
HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main, HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\PageSetup,
поэтому будет полезно вооружиться редактором реестра и уделить некоторое время изучению содержания данных разделов.
Однако, если вы используете такой способ решения этой проблемы, то учтите, что, если пользователь одновременно с вашей программой запустит InternetExplorer, то он очень удивится... Существует более красивый способ решения, но пока он не реализован автором этой статьи...


Код: Выделить всё
Private Sub Form_Load()
'позиционируем WebBrowser на форме, таким образом, что
'бы не было видно его границ
    'создаём объект для работы с реестром
    Set mReg = CreateObject("WScript.Shell")
   'далее идут процедуры сохранения старых и установка
'новых значений реестра, а так же процедуры соединения с
'базой данных и вывода списка фамилий
    GetRegSettings
    SetRegSettings
    Connect
    LoadPeople
End Sub

'сохраняем старые значения реестра
Private Sub GetRegSettings()
    With strRegSettings
        .footer = mReg.RegRead("HKCU\Software\Microsoft\Internet Explorer\PageSetup\footer")
        .header = mReg.RegRead("HKCU\Software\Microsoft\Internet Explorer\PageSetup\header")
        .Print_Background = mReg.RegRead("HKCU\Software\Microsoft\Internet Explorer\Main\Print_Background")
    End With
End Sub

'устанавливаем новые значения реестра
Private Sub SetRegSettings()
    With mReg
        .RegWrite "HKCU\Software\Microsoft\Internet Explorer\PageSetup\footer", "" 'убираем надпись внизу страницы
        .RegWrite "HKCU\Software\Microsoft\Internet Explorer\PageSetup\header", "" 'убираем надпись вверху страницы
        .RegWrite "HKCU\Software\Microsoft\Internet Explorer\Main\Print_Background", "yes" 'печатать фон
    End With


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

Код: Выделить всё
'восстанавливаем старые значения реестра
Private Sub RestoreRegSettings()
    With mReg
        .RegWrite "HKCU\Software\Microsoft\Internet Explorer\PageSetup\footer", strRegSettings.footer
        .RegWrite "HKCU\Software\Microsoft\Internet Explorer\PageSetup\header", strRegSettings.header
        .RegWrite "HKCU\Software\Microsoft\Internet Explorer\Main\Print_Background", strRegSettings.Print_Background
    End With
End Sub

'устанавливаем соединение с базой данных
Private Sub Connect()
    Set dbPeople = OpenDatabase("db.mdb")
'запрос на получение всех записей отсортированных по
'значениям столбца Surname
    strQuery = "SELECT * FROM people ORDER BY Surname ASC"
'Теперь recPeople содержит все записи из таблицы
    Set recPeople = dbPeople.OpenRecordset(strQuery)
End Sub

'выводим список фамилий в таблицу в WebBrowser
Private Sub LoadPeople()
'загружаем пустую страницу
    WebBrowser.Navigate "res://mshtml.dll/blank.htm"
'ждём полной загрузки страницы
        While WebBrowser.ReadyState <> READYSTATE_COMPLETE
            DoEvents
        Wend
'присваиваем ссылки на объект WebBrowser.Document
    Set mDoc = WebBrowser.Document
    Set mIDoc = WebBrowser.Document
'извлекаем HTML-код страницы people.html из ресурсов
    strHTML = StrConv(LoadResData("people.html", 23), vbUnicode)
'записываем HTML-код в наш документ
    mIDoc.write strHTML
'изменяем базовый URI (см.выше) для корректного отображения страницы
    mIDoc.getElementsByTagName("BASE").Item(0).href = _
        "res://" & App.Path & "\" & App.EXEName & ".exe"
'Активируем текущий стиль оформления
    ActivateStyle
'Получаем ссылку на таблицу, id которой равен «people»,
'для дальнейшей работы с этой таблицей (добавление
'строк, обеспечение её интерактивности)
    Set mTable = mDoc.All.people
'добавляем строки в таблицу (процедура будет рассмотрена
'ниже)
    AddRows
'Переменная, сообщающая нам, что сейчас отображается
'список фамилий, а не информация по конкретному человеку
    isInfo = False
End Sub


Получить ссылку на объект внутри документа можно либо по его уникальному идентификатору (mDoc.getElementById(«id») или mDoc.All.id, где id – уникальный идентификатор), либо по имени (mDoc.getElementsByName(«name»), где name – имя элемента), либо по названию тега элемента (mDoc.getElementsByTagName(«tagName»), где tagName – название тега. При этом мы получим массив объектов. Чтобы обратиться к какому-то конкретному элементу этого массива, необходимо указать его индекс – mDoc.getElementsByTagName(«tagName»).Item(Index)).

Теперь создадим процедуру, которая будет добавлять строки из базы данных в таблицу:

Код: Выделить всё
Private Sub AddRows()
Dim lngNumber As Long 'порядковый номер добавляемой строки
    recPeople.MoveFirst 'переходим на начало записи
    lngNumber = 1
'последовательно добавляем строки
    While Not recPeople.EOF
        Set mTR = mTable.insertRow(-1)
        Set mTD = mTR.insertCell()
            With mTD
                .className = "first_col"
                .innerHTML = lngNumber
            End With
        Set mTD = mTR.insertCell()
            With mTD
                .className = "second_col"
                .innerHTML = recPeople.Fields(1)
                .setAttribute "Pos", lngNumber
            End With
        lngNumber = lngNumber + 1
        recPeople.MoveNext
    Wend
End Sub


Метод insertRow([index as Long=-1]) класса HTMLTable позволяет добавлять строку в таблицу. Единственный необязательный параметр index определяет порядок добавления строки: -1 – новая строка добавляется в конец таблицы, 0 – в начало, 1 – перед строкой, добавленной в последнюю очередь.
Метод insertCell([index as Long=-1]) класса HTMLTableRow добавляет ячейки в строку.
Метод setAtribute(strAttributeName As String, AttributeValue, [lFlags As Long = 1]) позволяет устанавливать для любого атрибута, заданного в strAttributeName, любое значение, заданное в AttributeValue. При этом можно устанавливать значения существующих атрибутов, а так же создавать новые, как и показано в нашем примере. Атрибут «Pos» со значением, равным порядковому номеру добавляемой строки, нам будет необходим для того, чтобы определить по какой строке произошёл щелчок мыши. Это нам необходимо, чтобы узнать, информацию о каком человеке необходимо отобразить.
Свойство innerHTML определяет HTML-код внутри определённого объекта.


Для того, чтобы пользователь мог изменять стиль оформления, создадим меню «Стиль» с пунктами «Классический» и «Современный». Дадим им соответствующие имена – mnuStyleClassic и mnuStyleModern. Процедуры выбора пунктов меню будут следующими:

Код: Выделить всё
Private Sub mnuStyleClassic_Click()
    mnuStyleClassic.Checked = True
    mnuStyleModern.Checked = False
    ActivateStyle
End Sub

Private Sub mnuStyleModern_Click()
    mnuStyleModern.Checked = True
    mnuStyleClassic.Checked = False
    ActivateStyle
End Sub


Непосредственно изменять стиль оформления будет процедура ActivateStyle:

Код: Выделить всё
Private Sub ActivateStyle()
    If mnuStyleClassic.Checked = True Then
'получаем объект, по имени тега и меняем его
'свойство href
        mIDoc.getElementsByTagName("LINK").Item(0).href = "style_1.css"
    ElseIf mnuStyleModern.Checked = True Then
        mIDoc.getElementsByTagName("LINK").Item(0).href = "style_2.css"
    End If
End Sub


Таким образом, при загрузке формы у нас будет появляться таблица со списком фамилий. При этом она всегда будет позиционироваться по центру, независимо от изменения размера формы. Если таблица выйдет за пределы формы автоматически появятся полосы прокрутки.

Чтобы скрыть полосы прокрутки используйте код mDoc.body.Style.overflow = "hidden" или определите в каскадно-стилевых таблицах: body {overflow:hidden}, либо для горизонтальной или вертикально полос отдельно – body {overflow-x:hidden} или body {overflow-y:hidden} соответственно.

Теперь нам необходимо сделать так, чтобы при наведении курсора на ячейку с фамилией, менялся её цвет и тип курсора. Основная работа нами уже сделана – стиль ячейки, на которую наведён курсор уже описан (см. листинг файла style_1.css, стиль для класса second_col_on), осталось только зафиксировать наведение курсора на ячейку и изменить её класс с second_col на second_col_on и обратно, когда курсор уйдёт за пределы ячейки.

Для того чтобы получить элемент документа, к которому относится событие необходимо обратиться к объекту Event следующим образом: mDoc.parentWindow.Event.srcElement. Затем нам надо будет убедиться, является ли этот элемент ячейкой таблицы, в которой содержится фамилия, для этого мы проверим наличие атрибута Pos (который есть только у этих ячеек). Если он окажется, то изменим имя класса этой ячейки. Затем аналогичным образом изменим имя класса ячейки на первоначальное. Таким образом нам надо будет обрабатывать события onmouseover и onmouseout:

Код: Выделить всё
Private Sub mDoc_onmouseover()
        If isInfo = False Then
            Set mIElement = mDoc.parentWindow.event.srcElement
                While IsNull(mIElement.getAttribute("Pos")) = True
                    Exit Sub
                Wend
            lngPos = mIElement.getAttribute("Pos")
            mIElement.className = "second_col_on"
        End If
End Sub

Private Sub mDoc_onmouseout()
    If isInfo = False Then
        Set mIElement = mDoc.parentWindow.event.srcElement
            While IsNull(mIElement.getAttribute("Pos")) = True
                Exit Sub
            Wend
        lngPos = mIElement.getAttribute("Pos")
        mIElement.className = "second_col"
    End If
End Sub


Таким же образом обработаем событие onclick. Только теперь нам надо будет не изменить имя класса для элемента, а получить значение атрибута «Pos», для того, чтобы вывести информацию по конкретному человеку. Это реализуется в следующей процедуре:

Код: Выделить всё
Private Function mDoc_onclick() As Boolean
    Dim lngPos As Long
    If isInfo = False Then
        Set mIElement = mDoc.activeElement
            While IsNull(mIElement.getAttribute("Pos")) = True
                Exit Function
            Wend
        isInfo = True
        lngPos = mIElement.getAttribute("Pos")
        LoadInfo lngPos
    End If
End Function


Затем значение атрибута «Pos» передаётся в процедуру отображения информации по конкретному человеку:

Код: Выделить всё
Private Sub LoadInfo(lngPos As Long)
    WebBrowser.Navigate "res://mshtml.dll/blank.htm"
        While WebBrowser.ReadyState <> READYSTATE_COMPLETE
            DoEvents
        Wend
    strHTML = StrConv(LoadResData("people_info.html", 23), vbUnicode)
    mIDoc.write strHTML
    mIDoc.getElementsByTagName("BASE").Item(0).href = _
        "res://" & App.Path & "\" & App.EXEName & ".exe"
    ActivateStyle
    Set mTable = mDoc.All.people_info
    Set btnBack = mDoc.All.btnBack
    Set btnPreview = mDoc.All.btnPreview
    Set btnPrint = mDoc.All.btnPrint
    recPeople.MoveFirst
    recPeople.Move lngPos - 1
    arrDescription = Array("Фамилия:", "Имя:", "Отчество:", "Город:", "Адрес:", "Телефон:")
        For i = 0 To UBound(arrDescription)
            Set mTR = mTable.insertRow(-1)
            Set mTD = mTR.insertCell()
                With mTD
                    .className = "first_col"
                    .Width = 80
                    .innerHTML = arrDescription(i)
                End With
            Set mTD = mTR.insertCell()
                With mTD
                    .className = "second_col"
                    .innerHTML = recPeople.Fields(i + 1)
                End With
        Next
End Sub


С помощью кода:
Set btnBack = mDoc.All.btnBack
Set btnPreview = mDoc.All.btnPreview
Set btnPrint = mDoc.All.btnPrint

мы получаем ссылки на три кнопки, а так как экземпляры btnBack, btnPreview и btnPrint класса HTMLButtonElement объявлены у нас WithEvents, то мы легко можем обрабатывать их события.


При нажатии на кнопку btnBack, мы снова загружаем список фамилий:

Код: Выделить всё
Private Function btnBack_onclick() As Boolean
     LoadPeople
End Function


При нажатии на кнопку btnPreview, мы откроем стандартное окно Предварительного просмотра страницы:

Код: Выделить всё
Private Function btnPreview_onclick() As Boolean
    HideButton
    Me.WebBrowser.ExecWB OLECMDID_PRINTPREVIEW, OLECMDEXECOPT_DODEFAULT
    ShowButton
End Function


Чтобы в отчёте не было трёх кнопок, их необходимо сначала спрятать, а затем снова отобразить. Для этого используются две соответственные процедуры HideButton и ShowButton:

Код: Выделить всё
'скрываем кнопки
Private Sub HideButton()
    btnBack.Style.visibility = "hidden"
    btnPreview.Style.visibility = "hidden"
    btnPrint.Style.visibility = "hidden"
    mDoc.body.Style.backgroundColor = "#ffffff"
End Sub

'показываем кнопки
Private Sub ShowButton()
    btnBack.Style.visibility = "visible"
    btnPreview.Style.visibility = "visible"
    btnPrint.Style.visibility = "visible"
    mDoc.body.Style.backgroundColor = "buttonface"
End Sub


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

Печать отчёта будет осуществляться при нажатии кнопки btnPrint:

Код: Выделить всё
Private Function btnPrint_onclick() As Boolean
    HideButton
    Me.WebBrowser.ExecWB OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER
    ShowButton
End Function


Теперь необходимо позаботиться о надёжности приложения. Нам необходимо запретить WebBrowser’у реагировать на нажатие клавиш, так как последствия их нажатия могут быть для нас нежелательны (например нажатие F5 приведёт к обновлению страницы, в результате чего всё содержимое её будет потеряно). Для этого создадим следующую процедуру:

Код: Выделить всё
Private Sub mDoc_onkeydown()
    mDoc.parentWindow.event.keyCode = 0
End Sub


Таким образом, при нажатии любой клавиши, будет возвращён keyCode, равный 0, следовательно никакой реакции со стороны WebBrowser не последует.

Кроме того вы можете в зависимости от возвращённого keyCode, т.е. в зависимости от того какая клавиша была нажата, производить определённые действия в вашей программе.

Кроме этого может возникнуть необходимость запрета вызова контекстного меню WebBrowser’а, это осуществляется при помощи следующего кода:

Код: Выделить всё
Private Function mDoc_oncontextmenu() As Boolean
    mDoc_oncontextmenu = False
End Function


Аналогично можно запретить выделение в документе:

Код: Выделить всё
Private Function mDoc_onselectstart() As Boolean
    mDoc_onselectstart = False
End Function


По завершению работы программы нам необходимо закрыть соединение с базой данных и восстановить прежние значения реестра:

Код: Выделить всё
Private Sub Form_Unload(Cancel As Integer)
    dbPeople.Close
    RestoreRegSettings
End Sub


Спасибо всем, кто осилил. Кто не выдержал -- может скачать пример и разобраться на досуге.
У вас нет доступа для просмотра вложений в этом сообщении.
Последний раз редактировалось dr.MIG 25.04.2008 (Пт) 19:34, всего редактировалось 4 раз(а).
Salus populi suprema lex

keks-n
Доктор VB наук
Доктор VB наук
Аватара пользователя
 
Сообщения: 2509
Зарегистрирован: 19.09.2005 (Пн) 17:17
Откуда: г. Москва

Сообщение keks-n » 03.10.2007 (Ср) 21:30

1) Хранить в ресурсах некрасиво и неудобно. Лучше на локалхосте поднять простенький HTTP сервер :)
2) Более красивый пути настройки возможен, при создании и прицеплении контрола руками. Я копаю в этом направлении, но по другим причинам. Когда докопаю, выложу dll, наверное.
Изображение

dr.MIG
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1441
Зарегистрирован: 18.12.2004 (Сб) 9:53
Откуда: г.Ярославль

Сообщение dr.MIG » 03.10.2007 (Ср) 23:39

1) Возможно. Но здесь не принципиально.
2) Знаю, что работаешь. Вот этого я как раз очень жду.
Salus populi suprema lex

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16473
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Сообщение Хакер » 03.10.2007 (Ср) 23:40

dr.MIG
Мне кажется, та часть, которая правит реестр, не будет работать под вистой.

Сделал бы шаблонизатор, и тогда генерация отчётов была бы райским удовольствиеми из-за универсальности. Я всё хотел перевести свой php-шный на vb, но отсутсвие в vb некоторых (многих) вещей отбивает всякое желание.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Source
Постоялец
Постоялец
 
Сообщения: 351
Зарегистрирован: 04.09.2007 (Вт) 11:21

Сообщение Source » 07.10.2007 (Вс) 2:03

а кто-то знает точно, будет ли работать под Вистой запись в реестр?

dr.MIG
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1441
Зарегистрирован: 18.12.2004 (Сб) 9:53
Откуда: г.Ярославль

Сообщение dr.MIG » 07.10.2007 (Вс) 16:27

Source писал(а):а кто-то знает точно, будет ли работать под Вистой запись в реестр?

Если никто не опробует работоспособность примера, касательно реестра, в ближайшие дни не поленюсь заехать к подруге и запустить его на её ноутбуке с Вистой... :)
Salus populi suprema lex

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 08.10.2007 (Пн) 8:16

Код: Выделить всё
Private WithEvents btnBack As HTMLButtonElement
Private WithEvents btnPrint As HTMLButtonElement
Private WithEvents btnPreview As HTMLButtonElement
Вот это имхо лишнее. Нужную кнопку можно получить в document_onclick из document.parentWindow.event.srcElement.
Лучший способ понять что-то самому — объяснить это другому.

dr.MIG
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1441
Зарегистрирован: 18.12.2004 (Сб) 9:53
Откуда: г.Ярославль

Сообщение dr.MIG » 09.10.2007 (Вт) 18:50

Antonariy, ну значит будем считать это просто учебным примером, иллюстрирующим то, как ещё можно работать с отдельными объектами из DOM. :)
А твой способ так же отражён в процедуре обработки mDoc_onmouseover() и mDoc_onmouseout(), только не для кнопки.
Salus populi suprema lex

Source
Постоялец
Постоялец
 
Сообщения: 351
Зарегистрирован: 04.09.2007 (Вт) 11:21

Сообщение Source » 10.10.2007 (Ср) 0:39

dr.MIG, лучше напиши тест записи в реестр Vista и выложи отдельным топом, пусть люди проверят!

Source
Постоялец
Постоялец
 
Сообщения: 351
Зарегистрирован: 04.09.2007 (Вт) 11:21

Сообщение Source » 31.10.2007 (Ср) 11:49

dr.MIG, ну что там, был тест записи в реестр Vista али нет?

dr.MIG
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1441
Зарегистрирован: 18.12.2004 (Сб) 9:53
Откуда: г.Ярославль

Сообщение dr.MIG » 17.11.2007 (Сб) 18:16

Долго отсутствовал по причине большой занятости... Тест не забыт. Скоро будет. Ждите :)

Add: c Vist'ой, на первый взгляд всё ок, никаких проблем не обнаружено. Запись работает.
Последний раз редактировалось dr.MIG 03.03.2008 (Пн) 19:00, всего редактировалось 1 раз.
Salus populi suprema lex

dr.MIG
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1441
Зарегистрирован: 18.12.2004 (Сб) 9:53
Откуда: г.Ярославль

Сообщение dr.MIG » 13.02.2008 (Ср) 21:15

Не знаю боян это или нет... Но для VB.Net (да, к сожалению, не для VB6) решил проблему с общими настройками в реестре для WB и IE (при печати документа). :P
Решил её семью строчками кода, ради которых просидел целый вечер. Сейчас излагать концепцию нет сил... Надеюсь, что завтра до этого руки таки дойдут.
Хотя, может быть до завтра скажут, что это всё-таки боян. :)
Add: всё оказалось не так хорошо, как показалось на первый взгляд... Можно изменить стили отображения страницы, отключить footer'ы и header'ы, отступ. Но включить печать рисунков и фона нельзя. :(
Salus populi suprema lex

dr.MIG
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1441
Зарегистрирован: 18.12.2004 (Сб) 9:53
Откуда: г.Ярославль

Сообщение dr.MIG » 03.03.2008 (Пн) 19:48

Ну так вот. Вроде об этом нигде на этом форуме не упоминалось.
Натолкнулся на довольно интересную вещь. Суть состоит в том, что используя данную технологию можно управлять стилем оформления документа, распечатываемого из э/у WebBrowser'а. Т.е. документ может быть отображён в WB одним стилем, а при выводе на печать, в том числе и в окне предпросмотра -- другим. Понравилось, что можно отключать footer'ы и header'ы, которые часто мешают, а так же есть возможность заменять их на свои.

Для использования данной фичи необходимо создать html-файл с особым содержанием и передать его как параметр в метод ExecWB.

Для тех, кто не хочет читать msdn приведу простой пример. Итак. Сначала создаём файл template.html следующего содержания:
Код: Выделить всё
<HTML XMLNS:IE>
<HEAD>
<?IMPORT NAMESPACE="IE" IMPLEMENTATION="#default">
<STYLE TYPE="text/css">
.contentstyle
{
    width:5.5in;
    height:8in;
    margin:1in;
    background:white;   
    border:1 dashed gray;
}
.masterstyle
{
    width:8.5in;
    height:11in;
    background:#FFFF99;   
   border-left:1 solid black;
   border-top:1 solid black;
   border-right:4 solid black;
   border-bottom:4 solid black;
    margin:10px;
}
</STYLE>
<SCRIPT LANGUAGE="JavaScript">
<!--
invocations = 0;

function CheckIfPrintRequested() {
  invocations++;
  if (invocations > 1) return;
  switch (dialogArguments.__IE_PrintType) {
    case "Prompt":
      if (printer.showPrintDialog()) PrintPrep();
      break;
    case "NoPrompt":
      PrintPrep();
      break;
    case "Preview":
    default:
      break;
  }
}

function PrintPrep() {
  if (layoutrect1.contentDocument.readyState == "complete") {
    PrintNow();
  }
  else {
    layoutrect1.contentDocument.onreadystatechange = PrintWhenContentDocComplete;
  }
}

function PrintWhenContentDocComplete() {
  if (layoutrect1.contentDocument.readyState == "complete")   {
    layoutrect1.contentDocument.onreadystatechange = null;
    PrintNow();
  }
}

function PrintNow() {
  printer.startDoc("Printing from template2.htm");
  printer.printPage(page1);   
  printer.printPage(page2);
  printer.stopDoc();
}
// -->
</SCRIPT>

</HEAD>
<BODY>
<IE:TEMPLATEPRINTER ID="printer"/>

<IE:DEVICERECT ID="page1" CLASS="masterstyle" MEDIA="print">
   <IE:LAYOUTRECT ID="layoutrect1" CONTENTSRC="document" CLASS="contentstyle" NEXTRECT="layoutrect2"/>
</IE:DEVICERECT>

<IE:DEVICERECT ID="page2" CLASS="masterstyle" MEDIA="print">
   <IE:LAYOUTRECT ID="layoutrect2" CLASS="contentstyle" ONLAYOUTCOMPLETE="setTimeout('CheckIfPrintRequested()', 100)"/>
</IE:DEVICERECT>

</BODY>
</HTML>


Далее кладём его в папку со своей программой и используем следующий код:
Код: Выделить всё

WebBrowser.ExecWB OLECMDID_PRINTPREVIEW, OLECMDEXECOPT_PROMPTUSER, App.Path & "\template.html"


Вот казалось бы и всё... Однако нет... При выполнении выскакивает сообщение, дескать "Permission Denied"... Этот баг описан здесь, если кому интересно. Судя из его описания можно прийти к выходу, что лучшее решение -- это не писать на VB 6.0... Там, например, предлагают писать на Visual C++ :)

Далее проверил данный код на Delphi. Там всё Ок. Следующим этапом была попытка задействовать эту технологию в VB.Net. Там оказалось всё немного сложнее по причине необходимости использования интерфейса IWebBrowser2 для реализации метода ExecWB.

Для начала импортируем пространство имён, содержащее нужный нам интерфейс:

Код: Выделить всё
Imports SHDocVw


Не забываем добавить WebBrowser на форму :)

Объявляем:
Код: Выделить всё
Private wb As IWebBrowser2


Далее куда-нибудь (в зависимости от конкретной задачи), например в процедуру обработки загрузки формы, добавим следующий код:
Код: Выделить всё

wb = DirectCast(Me.wbReport.ActiveXInstance, IWebBrowser2)
wb.RegisterAsBrowser = True
wb.Navigate("http://someurl.ru/someprint.html") 'загрузим страничку, которую будем распечатывать


И уже теперь добавляем следующий код, который выведет окно предпросмотра:
Код: Выделить всё

wb.ExecWB(OLECMDID.OLECMDID_PRINTPREVIEW, OLECMDEXECOPT.OLECMDEXECOPT_PROMPTUSER, My.Application.Info.DirectoryPath & "\template.html")


Если вы всё сделали правильно, то у вас появится окно предпросмотра с нестандартно оформленым документом :)
Salus populi suprema lex

SyncM
Новичок
Новичок
Аватара пользователя
 
Сообщения: 32
Зарегистрирован: 04.02.2007 (Вс) 15:48

Сообщение SyncM » 17.04.2008 (Чт) 11:32

dr.MIG
Добрый день
вопрос

добавте пожалуйста в описание
с чем может произайти конфликт
тоесть есть ли разница между Internet Explorer 5.0 | 5.5 | 6.0 | 7.0
и возможно ли взять пару библеотек от того же Internet Explorer 4.0 или 5.0 вставив их в программу и работать только с ними для отображения всего интерфейса. Тем самым предугадать вероятность несовместимости. Вопросы возможно глупые, но всё же.
Удалите комментарии по усмотрению. Спасибо за подробный код...

dr.MIG
Гуру
Гуру
Аватара пользователя
 
Сообщения: 1441
Зарегистрирован: 18.12.2004 (Сб) 9:53
Откуда: г.Ярославль

Сообщение dr.MIG » 17.04.2008 (Чт) 14:29

А какой смысл, даже при наличии такой возможности, таскать за собой библиотеки от старых версий IE? Всё что было в них, осталось и в более новых версиях. Да и вообще в данном случае таскать с собой вряд ли что-то надо -- IE есть у всех. Другое дело, что одна и та же страница в IE разных версий может отображаться по-разному. Но это уже будет касаться не VB, а верстки HTML-страницы.
Salus populi suprema lex


Вернуться в dr.MIG

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 3

    TopList