Практикум прикладного программирования на MFC и C++ в среде VS.NET

Встраивание возможностей броузера в приложение

Разбить на страницы
Показывать лекцию целиком
Файлы к данной лабораторной работе, Вы можете скачать здесь.

Одной из возможностей, которые Microsoft предоставила в своих средствах разработки - использование функциональностей Internet Explorer в качестве интегрированной части любого приложения.

Цель лабораторной работы - познакомиться с вопросами:

  • Как объектная модель компонентов ActiveX для Internet Explorer ( Internet Explorer ActiveX Object Model ) позволяет интегрировать все компоненты в приложение
  • Каким образом класс CHtmlView библиотеки MFC инкапсулирует практически все функциональности Internet Explorer
  • Как обращаться к различным интерфейсам модели компонентных объектов Microsoft ( COM -интерфейсам), доступным в объектной модели ActiveX для Internet Explorer
  • Как с помощью Visual C++ создать свои интерфейсные классы для COM -интерфейсов, т.е. как вызывать функции COM -интерфейсов из своего приложения
  • Как создать простой браузер, используя класс CHtmlView и Internet Explorer
  • Класс CHtmlView

    Класс CHtmlView, будучи использован в качестве базового вместо класса CView, позволяет создавать однодокументные или многодокументные приложения с возможность просмотра HTML -документов и другими функциональностями Web -броузера. Класс содержит множество функций, в том числе и навигационных

    Некоторые функции класса CHtmlView
    Функция Назначение
    GoBack() Направляет пользователя на предыдущую Web - страницу
    GoForward() Направляет пользователя на последующую Web - страницу
    GoHome() Направляет пользователя на начальную страницу броузера Internet Explorer
    GoSearch() Направляет пользователя на страницу поиска по Internet
    Navigate(LPCTSTR URL) Направляет пользователя на страницу в Internet с указанным адресом URL
    Refresh() Заставляет браузер перезагрузить текущую Web - страницу
    Stop() Останавливает текущий процесс загрузки
    GetLocationURL() Возвращает URL текущей просматриваемой браузером Web - страницы
    GetBusy() Возвращает логическое значение, информирующее, занят ли в данный момент браузер загрузкой Web - страницы

    Создание приложения с возможностями броузера

    Создадим SDI -приложение на базе класса CHtmlView. Добавим в него меню с функциями навигации назад и вперед. Добавим диалог для возможности вводить URL.

    Чтобы приложение заработало, необходимо, чтобы на компьютере был установлен Web -браузер Internet Explorer. Обычно при установки IDE Visual C++ на компьютере устанавливается самая последняя версия Internet Explorer. Если разработанное приложение планируется запускать на других компьютерах, то там Internet Explorer также должен быть установлен для успешной работы нашего приложения.

    Создание заготовки приложения

  • Выберите папку для размещения проекта
  • Создайте новый проект с именем
  • Заполните вкладки мастера создания приложений так

    Настройки остальных вкладок мастера создания приложений оставьте без изменения. Проследите, чтобы в последней вкладке Generated Classes (Генерируемые классы) для создаваемого мастером класса CWebBrowseView в качестве базового в поле Base class был выбран класс CHtmlView.

  • После нажатия кнопки Finish мастер создаст заготовку приложения
  • Установите режим сборки проекта
  • Постройте проект и убедитесь, что мастер построил заготовку броузера, которую нам предстоит наделить функциональностью
  • Создание поддержки ввода URL

    URL (Uniform Resource Locator) - унифицированный указатель ресурса служит для адресации к документам в информационном гиперпространстве Internet. Обратите внимание, что мастер создал заготовку дополнительной панели управления со статической надписью " TODO: layout dialog bar ", указывающей на необходимость ее оформить.

    Редактирование ресурса панели управления

    Панель управления является гибридом панели инструментов и диалогового окна. Она представляет собой панель инструментов со средствами управления диалогом. Мастер MFC Application Wizard расположил ее не в разделе Toolbar, где и должны находиться панели инструментов, а в разделе Dialog, где располагаются ресурсы диалоговых окно.

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

  • Вызовите на редактирование ресурс диалогового окна IDR_MAINFRAME
  • Измените содержимое текстовой метки на Address и измените свойство Align Text метки на значение Right
  • Дополните ресурс полем редактирования Edit Control
  • Присвойте свойству ID поля редактирования значение IDC_EADDRESS
  • Передача URL броузеру

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

    Воспользуемся тем, что когда пользователь нажимает клавишу Enter после завершения ввода в поле редактирования, этот элемент управления Edit Control посылает фреймовому окну (класс CMainFrame ) идентификатор команды IDOK. Чтобы перехватить и обработать эту команду, нам только требуется добавить обработчик команды в карту сообщений класса CMainFrame (в файл MainFrame.cpp ).

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

  • В панели
  • Заполните мастер добавления функций так
  • Заполните код функции так
    Функция передачи URL броузеру в файле MainFrm.cpp
    // Функция передачи адреса URL
    void CMainFrame::OnNewAddress(void)
    {
      CString strAddress;
      
      // Получить новый URL
      m_wndDlgBar.GetDlgItem(IDC_EADDRESS)->GetWindowText(strAddress);
      
      // Передать URL в броузер
      ((CWebBrowseView*)GetActiveView())->Navigate(strAddress);
    }
  • Панель управления была зарегистрирована мастером создания приложений в функции OnCreate() файла MainFrm.cpp под именем m_wndDlgBar как экземпляр класса CDialogBar. Панель управления является родительской для элемента редактирования с идентификатором IDC_EADDRESS. Поэтому при получении содержимого URL мы адресуемся к полю редактирования через нее.

    В последней строке кода тип возвращаемого функцией GetActiveView() указателя приводится к указателю на класс CWebBrowseView. Это позволяет вызвать функцию Navigate(), наследуемую представлением от базового класса CHtmlView.

  • Зарегистрируйте созданную функцию как обработчик команды IDOK нажатия клавиши. Для этого в карту сообщений класса CMainFrame вручную внесите следующий код
    Регистрация функции как обработчика сообщения IDOK в карте сообщений класса 
    CMainFrame в файле MainFrm.cpp
    // CMainFrame
      
    IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
      
    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
      ON_WM_CREATE()
      ON_COMMAND(IDOK, OnNewAddress)
    END_MESSAGE_MAP()
    ...................................
  • Добавьте в начало файла MainFrm.cpp директивы подключения описаний классов CWebBrowseView и CWebBrowseDoc
    Директивы подключения заголовочных файлов с описаниями классов документа и 
    Дирпредставления вначале файла MainFrm.cpp
    // MainFrm.cpp : implementation of the CMainFrame class
    //
      
    #include "stdafx.h"
    #include "WebBrowse.h"
      
    #include "MainFrm.h"
    #include ".\mainfrm.h"
      
    #include "WebBrowseDoc.h"
    #include "WebBrowseView.h"
      
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    ..............................................
  • Если скомпилировать и запустить наше приложение, то можно ввести URL. После нажатия клавиши Enter, если в системе имеется соединение с Internet или настроена локальная PHP -машина (система Денвер-2), то в приложение будет загружена HTML -страница как в настоящем броузере. Это может быть примерно такое...

    Отображение текущего значения URL

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

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

  • Начало перехода на новую Web -страницу
  • Начало загрузки Web -страницы
  • Отслеживание процесса загрузки
  • Окончание загрузки
  • Перехватим событие окончания загрузки и переопределим соответствующую функцию OnDocumentComplete.

  • Выберите класс CWebBrowseView на панели Class View
  • В панели
  • Создайте для OnDocumentComplete функцию замещения, которая будет вызываться после окончания загрузки Web -страницы и которую заполните так

    Замещенная функция забора текущего URL в классе представления и вызова функции записи адреса 
    в поле редактирования из класса фрейма, где и находится панель управления с полем адреса
    void CWebBrowseView::OnDocumentComplete(LPCTSTR lpszURL)
    {
      // TODO: Add your specialized code here and/or call the base class
      // Передать текущий URL в класс фрейма для вывода в поле адреса
      ((CMainFrame*)GetParentFrame())->SetAddress(lpszURL);
      
      CHtmlView::OnDocumentComplete(lpszURL);
    }

    Обратите внимание, что в итоге не пришлось вызывать функцию GetLocationURL(), тем более, нам неизвестно было, когда ее нужно вызывать. Искомый URL мы получили как аргумент lpszURL, который сразу передали во фреймовое окно. Теперь в классе фреймового окна нужно этот URL отобразить в поле адреса с идентификатором IDC_EADDRESS.

  • Чтобы в файле WebBrowseView.cpp компилятор видел описание класса CMainFrame, добавьте в начало этого файла директиву подключения файла MainFrm.h
    Подключение к файлу WebBrowseView.cpp директивы с описанием класса CMainFrame
    // WebBrowseView.cpp : implementation of the CWebBrowseView class
    //
      
    #include "stdafx.h"
    #include "WebBrowse.h"
      
    #include "WebBrowseDoc.h"
    #include "WebBrowseView.h"
    #include ".\webbrowseview.h"
      
    #include "MainFrm.h"
      
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
      
    ....................................
  • В панели
  • Заполните мастер добавления метода так
  • Незабудьте нажать кнопку Add перед щелчком по кнопке Finish
  • Заполните заготовку функции SetAddress() следующим кодом (мы помним, что панель управления с полем адреса ассоциирована с переменной m_wndDlgBar )
    Функция класса фреймового окна CMainFrame для заполнения поля адреса 
    текущим значением URL в файле MainFrm.cpp
    // Заполнение поля адреса текущим значением URL
    void CMainFrame::SetAddress(LPCTSTR lpszURL)
    {
      // Сменить значение URL после загрузки новой страницы
      (m_wndDlgBar.GetDlgItem(IDC_EADDRESS))->SetWindowText(lpszURL);
    }
  • Теперь можно построить приложение и убедиться, что при переходе по ссылкам в поле адреса отображается текущий URL страницы.

    Переходы GoBack и GoForward

    Добавим к нашему приложению возможности перехода на предыдущую и последующую Web -страницы. Для этого нужно воспользоваться функциями GoBack() и GoForward(), унаследованными нашим классом представления CWebBrowseView от базового MFC -класса CHtmlView. Вызовы этих функций можно связать с пунктами меню, клавишами быстрого вызова ("горячими клавишами"), или с кнопками панели инструментов.

    Реализация возможностей перехода через меню

  • В панели
  • В панели свойств
  • Вновь в панели
  • Удалите с панели меню категорию Edit все ее пункты и замените эту категорию на Navigate
  • Перетащите эту категорию на новое место перед категорией Help
  • Добавьте два новых пункта в категорию Navigate: один для функции GoBack, а второй - для функции GoForward
  • Установите значения свойств новых пунктов, как показано в таблице
    Значения свойств пунктов меню
    Пункт меню Свойство Значение
    Первый ID ID_NAVIGATE_BACK
    Caption Назад \tCtrl + B
    Prompt Назад на предыдущую Web -страницу \n Назад
    Второй ID ID_NAVIGATE_NEXT
    Caption Вперед \tCtrl + F
    Prompt Вперед на следующую Web -страницу\nВперед
  • Внешний вид категории Navigate в редакторе меню должен выглядеть так

    Для каждого пункта меню необходимо добавить обработчики сообщения о событии COMMAND. Эти сообщения должен принимать и обрабатывать класс представления CWebBrowseView. Обработчики будем создавать не двойным щелчком по соответствующему пункту, а через контекстное меню оболочки для соответствующего пункта нашего проектируемого меню в редакторе. Для меню способ создания обработчика двойным щелчком оболочкой не поддерживается, поскольку неизвестно, какой класс будет принимать и обрабатывать сообщения COMMAND, пришедшие от меню.

  • В редакторе меню щелкните правой кнопкой мыши на первом пункте категории
  • Выберите в списке
  • Нажмите кнопку Add and Edit и заполните созданный мастером обработчик следующим кодом
    Обработчик пункта меню "Назад"
    void CWebBrowseView::OnNavigateBack()
    {
      // TODO: Add your command handler code here
      // Идти к предыдущей странице
      GoBack();
    }
  • Подобным образом создайте обработчик для второго пункта меню и заполните его так
    Обработчик пункта меню "Вперед"
    void CWebBrowseView::OnNavigateNext()
    {
      // TODO: Add your command handler code here
      // Перейти на следующую страницу вперед
      GoForward();
    }
  • Запустите приложение и убедитесь, что пункты спроектированного меню работают
  • Реализация возможностей перехода через акселераторы

    Акселераторы (горячие клавиши) применяются для быстрого доступа к командам меню при нажатии комбинации назначенных клавиш. Мы для наших пунктов меню "Назад" и "Вперед" зарезервировали комбинации клавиш " Ctrl + B " и " Ctrl + F ". Реализуем эти комбинации. Наша задача - закрепить за выбранными комбинациями те же самые идентификаторы ID_NAVIGATE_BACK и ID_NAVIGATE_NEXT, которые мы назначали для соответствующих пунктов меню Navigate.

  • Раскройте узел акселераторов Accelerator в дереве ресурсов панели Resource View и щелкните дважды по ресурсу IDR_MAINFRAME

    Откроется на редактирование таблица акселераторов. Мы видим, что каждый акселератор состоит из идентификатора ID и закрепленной за ним комбинации клавиш.

  • Щелкните правой кнопкой мыши в любом месте таблицы акселераторов и в появившемся контекстном меню выполните команду New Accelerator

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

  • Введите значение ID_NAVIGATE_BACK в поле ID добавленной новой строки (или выберите из раскрывающегося в поле списка, поскольку раньше мы их вводили для пунктов меню), в качестве модификатора ( Modifier ) оставьте Ctrl, а в качестве клавиши ( Key ) введите латинскую B
  • В следующую пустую строку в поле ID введите значение ID_NAVIGATE_NEXT (или выберите из раскрывающегося в поле списка), в качестве модификатора ( Modifier ) оставьте Ctrl, а в качестве клавиши ( Key ) введите F

    Теперь таблица акселераторов после внесенных изменений должна выглядеть так

  • Вернитесь в редактор меню и проверьте, остались ли у идентификаторов пунктов те же значения ID_NAVIGATE_BACK и ID_NAVIGATE_NEXT. Если нет, то в раскрывающемся списке панели свойств Properties восстановите их вновь

    Таким образом, мы назначили для пунктов меню "Назад" и "Вперед" горячие клавиши. Теперь глубокоуважаемый жирный Пользователь (у него много денежек, которые мы должны выманить!!!) нашего шедевра сможет осуществлять переходы на уже просмотренные Web-страницы или использованные ссылки, которые хранятся в кэше броузера, не только через пункты меню, но и по горячим клавишам.

  • Постройте приложение и убедитесь, что все работает
  • Реализация возможностей перехода через кнопки панели управления

    Добавим в панель управления правее строки ввода адреса две кнопки, дублирующие команды перехода категории Navigate главного меню приложения.

  • Во вкладке Resource View вызовите на редактирование диалоговый ресурс панели управления
  • Поместите на нее правее поля ввода адреса две кнопки Button из вкладки Toolbox
  • Сделайте ширину кнопок примерно равной высоте ( Width=16, Height=15 )

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

  • Настройте свойства кнопок в соответствии с таблицей
    Значения свойств кнопок перехода
    Кнопка Свойство Значение
    Левая ID ID_BUTTON_BACK
    Caption BACK
    Prompt Назад на предыдущую Web-страницу\nНазад
    Owner Draw True
    Правая ID ID_BUTTON_NEXT
    Caption NEXT
    Prompt Вперед на следующую Web-страницу\nВперед
    Owner Draw True

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

    Теперь нужно создать обработчики для кнопок. Кнопки находятся в классе CMainFrame и будут подавать сообщения BN_CLICKED при щелчках по ним. Принимать же и обрабатывать сообщения нужно в классе CWebBrowseView. Это точно такая же ситуация, как при создании пунктов меню, которые сами тоже находятся во фреймовом окне CMainFrame, а посылаемые ими команды перехватываются и обрабатываются в окне представления, поддерживаемого классом CWebBrowseView.

  • Выполните правый щелчок мышью на левой кнопке панели управления и в появившемся контекстном меню выполните команду
  • Настройте мастер добавления обработчиков, выделив в качестве целевого класс
  • Нажмите кнопку Add and Edit и мастер создаст заготовку обработчика, которую заполните так
    Обработчик щелчка на левой кнопке ID_BUTTON_BACK
    void CWebBrowseView::OnBnClickedButtonBack()
    {
      // TODO: Add your control notification handler code here
      // Идти к предыдущей странице
      GoBack();
    }
  • Повторите такие же шаги для создания заготовки обработчика щелчка на правой кнопке панели управления. Заготовку заполните следующим кодом
    Обработчик щелчка на правой кнопке ID_BUTTON_NEXT
    void CWebBrowseView::OnBnClickedButtonNext()
    {
      // TODO: Add your control notification handler code here
      // Перейти на следующую страницу вперед
      GoForward();
    }

    Перед тем как построить приложение после введенных кнопок, привяжитесь покрепче к стулу, на котором сидите.

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

    Исчезновение кнопок с панели управления произошло потому, что мы установили для них свойство Owner Draw в значение True. Тем самым мы определили, что кнопки должен рисовать их владелец ( Owner ), а о самом коде рисования внутри владельца мы пока не позаботились. Владельцем (родителем) кнопок, в данном случае, является панель управления m_wndDlgBar как экземпляр класса CDialogBar, объявленная в файле MainFrm.h.

  • Измените свойство Owner Draw на значение False и снова постройте приложение
  • Кнопки появились и ведут себя как и положено, обеспечивая переходы броузера, но невмещающиеся внутри них надписи портят всю картину

    Заменим традиционные текстовые надписи внутри кнопок на содержательные рисунки. Для этого нужно подготовить нужные картинки в формате Bitmap, добавить в них немного соли, густо поперчить и подавать горячими. Студент Жук - разрешаю Вам передохнуть, а остальные и примкнувший к ним Панасюк - продолжайте. До Силиконовой долины или Редмонда осталось совсем немного (почти как до психушки).

    Присоединяемые к кнопкам картинки будут состоять из стрелок и должны быть одинакового размера. Иначе в разных состояниях они начнут скакать по экрану. Одна картинка будет изображать стрелку влево, другая - стрелку вправо. Мы установили для левой кнопки свойство Caption в значение BACK, а для правой - NEXT. В дальнейшем мы воспользуемся этими именами для привязки изображений.

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

  • кнопка нажата - Down (экземпляры BACKD и NEXTD )
  • кнопка отпущена - Up (экземпляры BACKU и NEXTU )
  • (Можно еще: кнопка получила фокус ввода - Focus (экземпляры BACKF и NEXTF кнопка не получила фокус ввода - экземпляры BACKX и NEXTX )

    Картинки в файл ресурсов можно добавить готовыми ( Import ), а можно нарисовать средствами встроенного в оболочку графического редактора ( New ). В любом случае размер картинок должен примерно соответствовать размеру кнопок. Ранее мы определили размеры кнопок примерно такими ( Width=16, Height=15 ). Значит и картинки нужно выполнить такого же размера. Хотя при загрузке картинок в кнопки их размеры автоматически подстроятся под размеры картинок. Но нам важно, чтобы кнопки не вышли по размерам за пределы панели управления. Итак, создаем картинки для кнопок.

  • Щелкните правой кнопкой мыши в любом месте панели Resource View и выполните в появившемся контекстном меню команду Add Resource
  • Установите категорию ресурса
  • В открывшемся графическом редакторе щелкните правой кнопкой мыши рядом с полем рисования и через панель
  • Сохраните ресурс, щелкнув по вкладке правой кнопкой мыши
  • Нарисуйте стрелку влево, при этом для рисования активно используйте панель инструментов
  • Панель Colors обеспечивает настройку инструментов рисования как цветовую пару с текущим значением для левой (передний цвет пары) и правой (задний цвет пары) кнопок мыши. Цветовая пара текущего цвета выбирается в панели Colors щелчками по образцам соответствующими кнопками мыши. Количество доступных цветовых образцов устанавливается в панели Properties. Дополнительные возможности графического редактора заложены в меню Image оболочки.

    Сам графический редактор представлен двумя фреймами. В правом фрейме масштаб поля рисования можно менять через инструмент "Увеличительное стекло"

    В левом фрейме рисунок представлен в натуральную величину.

  • Вновь сохраните готовый рисунок стрелки влево в файле ресурсов. Убедитесь, что в проект добавился соответствующий ресурс

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

  • Закройте окно графического редактора, войдите в панель
  • Выполните команду
  • Сохраните проект и убедитесь, что в панели
  • Вызовите на редактирование ресурс IDB_BITMAP_NEXT и выполните для него две команды поворота, чтобы развернуть стрелку в противоположную сторону

    Должен получиться такой результат

  • В панели
  • Сохраните проект и убедитесь, что у нас теперь есть по две картинки для каждой кнопки
  • Вызовите на редактирование две последних картинки с идентификаторами
  • Теперь переназначьте каждой картинке значение свойства ID как показано в таблице
  • Числовые идентификаторы мы назначали ранее для того, чтобы не запутаться. Не забудьте, что новые имена ресурсов растров нужно обязательно заключать в двойные кавычки.

    Новые значения свойства ID для картинок кнопок
    Старый ID Bitmap Новый ID Назначение
    IDB_BITMAP_BACK " BACKU " Картинка для левой кнопки в отпущенном состоянии. Свойству Caption самой левой кнопки мы ранее присвоили значение BACK
    IDB_BITMAP_BACK_INV " BACKD " Картинка для левой кнопки в нажатом состоянии. Свойству Caption самой левой кнопки мы ранее присвоили значение BACK
    IDB_BITMAP_NEXT " NEXTU " Картинка для правой кнопки в отпущенном состоянии. Свойству Caption самой правой кнопки мы ранее присвоили значение NEXT
    IDB_BITMAP_NEXT_INV " NEXTD " Картинка для правой кнопки в нажатом состоянии. Свойству Caption самой правой кнопки мы ранее присвоили значение NEXT

    Иными словами, если на кнопке текст BACK, то мы для нее создаем два растра с идентификаторами " BACKU " и " BACKD " - соответственно для отжатого и нажатого состояния. Для другой кнопки - то же самое. Этот прием обеспечит нормальную работу картинок на кнопках. Теперь панель ресурсов должна выглядеть так

    Теперь осталось немного для завершения работы с кнопками: создать два экземпляра класса CBitmapButton и обеспечить с их помощью динамическую загрузку изображений в кнопки и управление ими.

  • Откройте через панель Solution Explorer файл MainFrm.h с описанием класса фреймового окна и внесите в конец класса следующий код
    Объявление поддерживающих объектов в файле MainFrm.h
    class CMainFrame : public CFrameWnd
    {
      
    protected: // create from serialization only
      CMainFrame();
      DECLARE_DYNCREATE(CMainFrame)
      
    // Attributes
    public:
      
    // Operations
    public:
      
    // Overrides
    public:
      virtual BOOL PreCreateWindow(CREATESTRUCT cs);
      
    // Implementation
    public:
      virtual ~CMainFrame();
    #ifdef _DEBUG
      virtual void AssertValid() const;
      virtual void Dump(CDumpContext dc) const;
    #endif
      
    protected:  // control bar embedded members
      CStatusBar  m_wndStatusBar;
      CToolBar    m_wndToolBar;
      CReBar      m_wndReBar;
      CDialogBar      m_wndDlgBar;
      
    // Generated message map functions
    protected:
      afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
      DECLARE_MESSAGE_MAP()
    public:
      // Функция передачи адреса URL
      void OnNewAddress(void);
      // Заполнение поля адреса текущим значением URL
      void SetAddress(LPCTSTR lpszURL);
    private:
      CBitmapButton m_Back, m_Next;
    };
  • Откройте файл MainFrm.cpp, найдите в нем функцию OnCreate() и вставьте в конце код загрузки ресурсов в кнопки
    Код загрузки картинок в кнопки
    int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
    .....................................................
      // TODO: Remove this if you don't want tool tips
      m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
        CBRS_TOOLTIPS | CBRS_FLYBY);
      
      m_Back.AutoLoad(ID_BUTTON_BACK, m_wndDlgBar);
      m_Next.AutoLoad(ID_BUTTON_NEXT, m_wndDlgBar);
      
      return 0;
    }
  • Не забудьте установить свойство Owner Draw кнопок в значение True
  • Постройте проект и убедитесь, что изображения кнопок меняются, сами кнопки функционируют правильно и посылают сообщения классу CWebBrowseView, а обработчики для кнопок в файле WebBrowseView.cpp мы построили ранее
  • Теперь наш уважаемый Пользователь имеет возможность перехода на посещенные страницы по командам меню, по горячим клавишам и кнопкам панели управления. Вот сколько много мы для него постарались.

    Дополнительные возможности управления браузером

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

    Если Web-страница большого объема или пропускная способность соединения слабая, то у пользователя может возникнуть необходимость прервать загрузку страницы броузером. Для этого в класса CHtmlView предусмотрена функция Stop().

    Когда пользователь перемещается кнопками "Назад" и "Вперед" по уже посещенным Web-страницам, то он фактически использует информацию, которую броузер хранит в своем внутреннем кэше. Чтобы заставить броузер перегрузить страницу, а не просто отобразить версию из кэша, существует функция Refresh().

    И последнее... Чтобы быстро переместиться на домашнюю страницу броузера, а это как правило стартовая страница с именем Home или Index, можно использовать встроенную в класс CHtmlView функцию GoHome(). При выполнении этой функции внутренний кэш броузера очищается.

    Прежде всего нужно дополнить меню Navigate нашего приложения соответствующими пунктами.

  • Отредактируйте меню приложения, добавив сепаратор и три пункта в соответствии с рисунком
  • Откорректируйте свойства новых пунктов меню, используя информацию из таблицы
    Свойства дополнительных пунктов меню
    Свойство Значение
    ID ID_NAVIGATE_STOP
    Caption Stop
    Prompt Остановить загрузку\nСтоп
    ID ID_NAVIGATE_REFRESH
    Caption Refresh
    Prompt Обновить текущую страницу\nОбновить
    ID ID_NAVIGATE_GOHOME
    Caption GoHome
    Prompt Перейти на начальную страницу\nДомой
  • Создайте обработчики для каждого нового пункта меню через контекстное меню пункта и команду
  • Заполните обработчики следующим кодом
    Обработчики для дополнительных возможностей броузера
    void CWebBrowseView::OnNavigateStop()
    {
      // TODO: Add your command handler code here
      // Остановить текущую загрузку
      Stop();
    }
      
    void CWebBrowseView::OnNavigateRefresh()
    {
      // TODO: Add your command handler code here
      // Перезагрузить текущую страницу
      Refresh();
    }
      
    void CWebBrowseView::OnNavigateGohome()
    {
      // TODO: Add your command handler code here
      // Идти на начальную страницу
      GoHome();
    }
  • Постройте приложение - все должно работать!
  • Отображение заголовка текущей Web-страницы в заголовке окна приложения

    Чтобы получить заголовок текущей Web -страницы и установить его в заголовке окна приложения воспользуемся свойством Title и функцией (средством доступа к свойству) get_title() интерфейса IHTMLDocument2. Для этого выполним несколько пошаговых инструкций.

  • Добавьте к приложению новый класс, выполнив команду Для добавления нового класса в приложение используем корневой узел любой из приведенных панелей
  • В появившемся окне
  • После щелчка по кнопке
  • Для настройки мастера Add Class From TypeLib Wizard выполните следующее:
  • Из раскрывающегося списка Available type libraries выберите значение Microsoft HTML Object Library <4.0>
  • В списке Interfaces найдите строку IHTMLDocument2 и выделите ее
  • Нажмите верхнюю кнопку со значком угловой стрелки ">", чтобы создать в правом списке Generated classes имя создаваемого класса, соответствующее выбранному интерфейсу
  • Щелкните по кнопке Finish для создания класса

    В результате мастер создаст исходный код интерфейсного класса для интерфейса IHTMLDocument2, который полностью разместится в заголовочном файле CHTMLDocument2.h приложения и нам остается только воспользоваться кодом этого класса для решения поставленной задачи загрузки заголовка текущей Web -страницы.

  • В файле WebBrowseView.cpp класса представления CWebBrowseView найдите уже использованную нами ранее функцию OnDocumentComplete() и дополните ее кодом, как показано ниже
    Код, добавленный в функцию OnDocumentComplete() файла WebBrowseView.cpp
    void CWebBrowseView::OnDocumentComplete(LPCTSTR lpszURL)
    {
      // TODO: Add your specialized code here and/or call the base class
      // Передать текущий URL в класс фрейма для вывода в поле адреса
      ((CMainFrame*)GetParentFrame())->SetAddress(lpszURL);
      
      // Создать экземпляр интерфейса класса CHTMLDocument2
      CHTMLDocument2* pHDoc = new CHTMLDocument2(GetHtmlDocument());
      // Получить заголовок текущей страницы
      CString strTitle = pHDoc->get_title();
      // Получить указатель на класс документа
      CWebBrowseDoc* pDoc = GetDocument();
      // Установить заголовок окна приложения
      pDoc->SetTitle(strTitle);
      // Удалить экземпляр интерфейса на куче
      delete pHDoc;
      
      CHtmlView::OnDocumentComplete(lpszURL);
    }
  • Добавьте директиву #include в начало файла WebBrowseView.cpp для подключения описания нового класса CHTMLDocument2
    Подключение класса интерфейса к файлу представления WebBrowseView.cpp
    // WebBrowseView.cpp : implementation of the CWebBrowseView class
    //
      
    #include "stdafx.h"
    #include "WebBrowse.h"
      
    #include "WebBrowseDoc.h"
    #include "WebBrowseView.h"
    #include ".\webbrowseview.h"
      
    #include "MainFrm.h"
      
    #include "CHTMLDocument2.h"
      
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    .........................................
  • Постройте приложение и убедитесь, что заголовок текущей страницы отображается в заголовке окна приложения

    Если при построении приложения Ваш компилятор выдает ошибки в коде добавленного нами файла интерфейса CHTMLDocument2.h (мой выдает!), то сделайте следующее

  • Откройте в оболочке на редактирование файл CHTMLDocument2.h, найдите в нем функции write() и writeln() и внесите в них следующие изменения
    Корректируем "Ошибки Билла"
    ......................................  
      void write(SAFEARRAY * psarray)
      {
        // static BYTE parms[] = VTS_NONE ;
        static BYTE* parms = (BYTE*)VTS_NONE ;
        InvokeHelper(0x41e, DISPATCH_METHOD, VT_EMPTY, NULL, parms, psarray);
      }
      void writeln(SAFEARRAY * psarray)
      {
        // static BYTE parms[] = VTS_NONE ;
        static BYTE* parms = (BYTE*)VTS_NONE ;
        InvokeHelper(0x41f, DISPATCH_METHOD, VT_EMPTY, NULL, parms, psarray);
      }
    ......................................
  • Теперь опять постройте приложение (ошибок больше быть не должно) и убедитесь, что заголовок текущей страницы отображается в заголовке окна приложения
  • Настройка приложения на открытие локальных HTML-документов

    Тот стандартный каркас приложения, который сгенерировал для нас мастер в самом начале, не рассчитан на открытие документов в формате HTML. Заложенные изначально базовые возможности открытия документа реализуются посылкой сообщения ON_FILE_OPEN в ответ на щелчок Пользователя по кнопке Open панели инструментов (значок ) или выполнения одноименной команды меню. Это сообщение перехватывается в классе CWebBrowseApp (карта сообщений файла WebBrowse.cpp ) и обрабатывается функцией базового класса CWinApp::OnFileOpen()

    Карта сообщений для класса CWebBrowseApp в файле WebBrowse.cpp
    // CWebBrowseApp
      
    BEGIN_MESSAGE_MAP(CWebBrowseApp, CWinApp)
      ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
      // Standard file based document commands
      ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
      ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
      // Standard print setup command
      ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
    END_MESSAGE_MAP()

    Для того, чтобы реализовать задуманное, прежде всего нужно убрать выделенный код из карты сообщений класса CWebBrowseApp. Перехватывать это сообщение мы будем во фреймовом окне, в классе которого у нас есть доступ к полю редактирования адреса. В классе фреймового окна мы построим обработчик сообщения ON_FILE_OPEN, в котором создадим и настроим диалог FileOpen, из которого извлечем информацию об имени выбранного Пользователем открываемого файла. После этого имя файла можно передать в поле ввода адреса панели управления и сгенерировать сообщение IDOK, обработчик которого у нас уже создан. Итак, приступим.

  • Закомментируйте код маршрутизации сообщения ON_FILE_OPEN в карте сообщений класса CWebBrowseApp, как показано ниже
    Комментируем код базового сообщения ON_FILE_OPEN для класса CWebBrowseApp в файле WebBrowse.cpp
    // CWebBrowseApp
      
    BEGIN_MESSAGE_MAP(CWebBrowseApp, CWinApp)
      ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
      // Standard file based document commands
      ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
    //  ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
      // Standard print setup command
      ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
    END_MESSAGE_MAP()
  • Постройте приложение и убедитесь, что кнопка и команда меню Open стали недоступными ( Disabled )
  • В панели Class View выделите класс CMainFrame и сразу после этого в панели Properties установите режим Events
  • Найдите сообщение
  • Постройте приложение и убедитесь, что команда и кнопка Open стали доступными, но естественно ничего не делают, так как обработчик пока пустой
  • Внесите в обработчик код, который и решит всю задачу
    Код нового обработчика сообщения ON_FILE_OPEN
    void CMainFrame::OnFileOpen()
    {
      // TODO: Add your command handler code here
      // Создать диалог File Open
      static char szFilter[] = 
        "HTM Files (*.htm)|*.htm|HTML Files (*.html)|*.html|All Files (*.*)|*.*||";
      CFileDialog openFile(TRUE, ".htm", "", 
        OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, szFilter);
      CString name; 
      if(openFile.DoModal() == IDOK)
        name = openFile.GetPathName();// Полное имя с путем
      else
        return;
      
      // Сменить значение поля адреса
      (m_wndDlgBar.GetDlgItem(IDC_EADDRESS))->SetWindowText(name);
      OnNewAddress();// Вызвать нашу функцию
    }
  • Постройте приложение и убедитесь, что все работает как задумали. Отметьте также, что полнофункционально работает и контекстное меню, генерируемое базовым классом
  • Подчистка лишних интерфейсов и оформление About

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

  • Вызовите на редактирование панель инструментов и удалите лишние кнопки

    Кнопки удаляйте перетаскиванием мышью за пределы панели. Сепараторы между кнопками вводятся более широкими отступами по ширине панели.

    Ресурс панели инструментов после редактирования должен выглядеть так

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

    Теперь, когда мы удалили инструменты New и Save, в классе документа CWebBrowseDoc стали ненужными переопределения функций OnNewDocument() и Serialize(). Теперь уже никто их не сможет вызвать. Все ниточки-веревочки для их вызова мы обрезали. Они конечно никому не мешают и код не портят, но место все-таки занимают, да и могут смутить бедного сопровождающего программиста. Поэтому удалим их объявления в файле WebBrowseDoc.h и реализацию в файле WebBrowseDoc.cpp.

  • Откройте файл WebBrowseDoc.h и удалите (или закомментируйте) в объявлении класса CWebBrowseDoc следующий код
    Удаление объявлений функций в классе CWebBrowseDoc
    class CWebBrowseDoc : public CDocument
    {
    ........................................  
    // Overrides
      public:
    //  virtual BOOL OnNewDocument();
    //  virtual void Serialize(CArchive ar);  
    ........................................
    };
  • Откройте файл WebBrowseDoc.cpp и удалите (или закомментируйте) в реализации класса CWebBrowseDoc код функций OnNewDocument() и Serialize(CArchive ar)
    Удаление лишнего кода в реализации класса CWebBrowseDoc
    // WebBrowseDoc.cpp : implementation of the CWebBrowseDoc class
    //
      
    #include "stdafx.h"
    #include "WebBrowse.h"
      
    #include "WebBrowseDoc.h"
      
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
      
    // CWebBrowseDoc
      
    IMPLEMENT_DYNCREATE(CWebBrowseDoc, CDocument)
      
    BEGIN_MESSAGE_MAP(CWebBrowseDoc, CDocument)
    END_MESSAGE_MAP()
      
    // CWebBrowseDoc construction/destruction
      
    CWebBrowseDoc::CWebBrowseDoc()
    {
      // TODO: add one-time construction code here
    
    }
      
    CWebBrowseDoc::~CWebBrowseDoc()
    {
    }
    /*
    BOOL CWebBrowseDoc::OnNewDocument()
    {
      if (!CDocument::OnNewDocument())
        return FALSE;
      
      // TODO: add reinitialization code here
      // (SDI documents will reuse this document)
      
      return TRUE;
    }
      
    // CWebBrowseDoc serialization
      
    void CWebBrowseDoc::Serialize(CArchive ar)
    {
      if (ar.IsStoring())
      {
        // TODO: add storing code here
      }
      else
      {
        // TODO: add loading code here
      }
    }
    */
      
    // CWebBrowseDoc diagnostics
      
    #ifdef _DEBUG
    void CWebBrowseDoc::AssertValid() const
    {
      CDocument::AssertValid();
    }
      
    void CWebBrowseDoc::Dump(CDumpContext dc) const
    {
      CDocument::Dump(dc);
    }
    #endif //_DEBUG
      
    // CWebBrowseDoc commands
  • Откройте файл WebBrowse.cpp и удалите в карте сообщений маршрутизатор сообщения ID_FILE_NEW. Все равно теперь никто в приложении это сообщение не пошлет - мы все стерилизовали
    Корректировка карты приема сообщений класса CWebBrowseApp
    BEGIN_MESSAGE_MAP(CWebBrowseApp, CWinApp)
      ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
      // Standard file based document commands
    //  ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
    //  ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
      // Standard print setup command
      ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
    END_MESSAGE_MAP()
  • Добавьте в категорию
  • Оформите About и смените иконку приложения
  • Запустите и испытайте приложение - работает как настоящий Броузер (почти)
  • Страницы:
    Файлы к данной лабораторной работе, Вы можете скачать здесь.

    Одной из возможностей, которые Microsoft предоставила в своих средствах разработки - использование функциональностей Internet Explorer в качестве интегрированной части любого приложения.

    Цель лабораторной работы - познакомиться с вопросами:

  • Как объектная модель компонентов ActiveX для Internet Explorer ( Internet Explorer ActiveX Object Model ) позволяет интегрировать все компоненты в приложение
  • Каким образом класс CHtmlView библиотеки MFC инкапсулирует практически все функциональности Internet Explorer
  • Как обращаться к различным интерфейсам модели компонентных объектов Microsoft ( COM -интерфейсам), доступным в объектной модели ActiveX для Internet Explorer
  • Как с помощью Visual C++ создать свои интерфейсные классы для COM -интерфейсов, т.е. как вызывать функции COM -интерфейсов из своего приложения
  • Как создать простой браузер, используя класс CHtmlView и Internet Explorer
  • Класс CHtmlView

    Класс CHtmlView, будучи использован в качестве базового вместо класса CView, позволяет создавать однодокументные или многодокументные приложения с возможность просмотра HTML -документов и другими функциональностями Web -броузера. Класс содержит множество функций, в том числе и навигационных

    Некоторые функции класса CHtmlView
    Функция Назначение
    GoBack() Направляет пользователя на предыдущую Web - страницу
    GoForward() Направляет пользователя на последующую Web - страницу
    GoHome() Направляет пользователя на начальную страницу броузера Internet Explorer
    GoSearch() Направляет пользователя на страницу поиска по Internet
    Navigate(LPCTSTR URL) Направляет пользователя на страницу в Internet с указанным адресом URL
    Refresh() Заставляет браузер перезагрузить текущую Web - страницу
    Stop() Останавливает текущий процесс загрузки
    GetLocationURL() Возвращает URL текущей просматриваемой браузером Web - страницы
    GetBusy() Возвращает логическое значение, информирующее, занят ли в данный момент браузер загрузкой Web - страницы

    Создание приложения с возможностями броузера

    Создадим SDI -приложение на базе класса CHtmlView. Добавим в него меню с функциями навигации назад и вперед. Добавим диалог для возможности вводить URL.

    Чтобы приложение заработало, необходимо, чтобы на компьютере был установлен Web -браузер Internet Explorer. Обычно при установки IDE Visual C++ на компьютере устанавливается самая последняя версия Internet Explorer. Если разработанное приложение планируется запускать на других компьютерах, то там Internet Explorer также должен быть установлен для успешной работы нашего приложения.

    Создание заготовки приложения

  • Выберите папку для размещения проекта
  • Создайте новый проект с именем
  • Заполните вкладки мастера создания приложений так

    Настройки остальных вкладок мастера создания приложений оставьте без изменения. Проследите, чтобы в последней вкладке Generated Classes (Генерируемые классы) для создаваемого мастером класса CWebBrowseView в качестве базового в поле Base class был выбран класс CHtmlView.

  • После нажатия кнопки Finish мастер создаст заготовку приложения
  • Установите режим сборки проекта
  • Постройте проект и убедитесь, что мастер построил заготовку броузера, которую нам предстоит наделить функциональностью
  • Создание поддержки ввода URL

    URL (Uniform Resource Locator) - унифицированный указатель ресурса служит для адресации к документам в информационном гиперпространстве Internet. Обратите внимание, что мастер создал заготовку дополнительной панели управления со статической надписью " TODO: layout dialog bar ", указывающей на необходимость ее оформить.

    Редактирование ресурса панели управления

    Панель управления является гибридом панели инструментов и диалогового окна. Она представляет собой панель инструментов со средствами управления диалогом. Мастер MFC Application Wizard расположил ее не в разделе Toolbar, где и должны находиться панели инструментов, а в разделе Dialog, где располагаются ресурсы диалоговых окно.

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

  • Вызовите на редактирование ресурс диалогового окна IDR_MAINFRAME
  • Измените содержимое текстовой метки на Address и измените свойство Align Text метки на значение Right
  • Дополните ресурс полем редактирования Edit Control
  • Присвойте свойству ID поля редактирования значение IDC_EADDRESS
  • Передача URL броузеру

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

    Воспользуемся тем, что когда пользователь нажимает клавишу Enter после завершения ввода в поле редактирования, этот элемент управления Edit Control посылает фреймовому окну (класс CMainFrame ) идентификатор команды IDOK. Чтобы перехватить и обработать эту команду, нам только требуется добавить обработчик команды в карту сообщений класса CMainFrame (в файл MainFrame.cpp ).

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

  • В панели
  • Заполните мастер добавления функций так
  • Заполните код функции так
    Функция передачи URL броузеру в файле MainFrm.cpp
    // Функция передачи адреса URL
    void CMainFrame::OnNewAddress(void)
    {
      CString strAddress;
      
      // Получить новый URL
      m_wndDlgBar.GetDlgItem(IDC_EADDRESS)->GetWindowText(strAddress);
      
      // Передать URL в броузер
      ((CWebBrowseView*)GetActiveView())->Navigate(strAddress);
    }
  • Панель управления была зарегистрирована мастером создания приложений в функции OnCreate() файла MainFrm.cpp под именем m_wndDlgBar как экземпляр класса CDialogBar. Панель управления является родительской для элемента редактирования с идентификатором IDC_EADDRESS. Поэтому при получении содержимого URL мы адресуемся к полю редактирования через нее.

    В последней строке кода тип возвращаемого функцией GetActiveView() указателя приводится к указателю на класс CWebBrowseView. Это позволяет вызвать функцию Navigate(), наследуемую представлением от базового класса CHtmlView.

  • Зарегистрируйте созданную функцию как обработчик команды IDOK нажатия клавиши. Для этого в карту сообщений класса CMainFrame вручную внесите следующий код
    Регистрация функции как обработчика сообщения IDOK в карте сообщений класса 
    CMainFrame в файле MainFrm.cpp
    // CMainFrame
      
    IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)
      
    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
      ON_WM_CREATE()
      ON_COMMAND(IDOK, OnNewAddress)
    END_MESSAGE_MAP()
    ...................................
  • Добавьте в начало файла MainFrm.cpp директивы подключения описаний классов CWebBrowseView и CWebBrowseDoc
    Директивы подключения заголовочных файлов с описаниями классов документа и 
    Дирпредставления вначале файла MainFrm.cpp
    // MainFrm.cpp : implementation of the CMainFrame class
    //
      
    #include "stdafx.h"
    #include "WebBrowse.h"
      
    #include "MainFrm.h"
    #include ".\mainfrm.h"
      
    #include "WebBrowseDoc.h"
    #include "WebBrowseView.h"
      
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    ..............................................
  • Если скомпилировать и запустить наше приложение, то можно ввести URL. После нажатия клавиши Enter, если в системе имеется соединение с Internet или настроена локальная PHP -машина (система Денвер-2), то в приложение будет загружена HTML -страница как в настоящем броузере. Это может быть примерно такое...

    Отображение текущего значения URL

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

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

  • Начало перехода на новую Web -страницу
  • Начало загрузки Web -страницы
  • Отслеживание процесса загрузки
  • Окончание загрузки
  • Перехватим событие окончания загрузки и переопределим соответствующую функцию OnDocumentComplete.

  • Выберите класс CWebBrowseView на панели Class View
  • В панели
  • Создайте для OnDocumentComplete функцию замещения, которая будет вызываться после окончания загрузки Web -страницы и которую заполните так

    Замещенная функция забора текущего URL в классе представления и вызова функции записи адреса 
    в поле редактирования из класса фрейма, где и находится панель управления с полем адреса
    void CWebBrowseView::OnDocumentComplete(LPCTSTR lpszURL)
    {
      // TODO: Add your specialized code here and/or call the base class
      // Передать текущий URL в класс фрейма для вывода в поле адреса
      ((CMainFrame*)GetParentFrame())->SetAddress(lpszURL);
      
      CHtmlView::OnDocumentComplete(lpszURL);
    }

    Обратите внимание, что в итоге не пришлось вызывать функцию GetLocationURL(), тем более, нам неизвестно было, когда ее нужно вызывать. Искомый URL мы получили как аргумент lpszURL, который сразу передали во фреймовое окно. Теперь в классе фреймового окна нужно этот URL отобразить в поле адреса с идентификатором IDC_EADDRESS.

  • Чтобы в файле WebBrowseView.cpp компилятор видел описание класса CMainFrame, добавьте в начало этого файла директиву подключения файла MainFrm.h
    Подключение к файлу WebBrowseView.cpp директивы с описанием класса CMainFrame
    // WebBrowseView.cpp : implementation of the CWebBrowseView class
    //
      
    #include "stdafx.h"
    #include "WebBrowse.h"
      
    #include "WebBrowseDoc.h"
    #include "WebBrowseView.h"
    #include ".\webbrowseview.h"
      
    #include "MainFrm.h"
      
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
      
    ....................................
  • В панели
  • Заполните мастер добавления метода так
  • Незабудьте нажать кнопку Add перед щелчком по кнопке Finish
  • Заполните заготовку функции SetAddress() следующим кодом (мы помним, что панель управления с полем адреса ассоциирована с переменной m_wndDlgBar )
    Функция класса фреймового окна CMainFrame для заполнения поля адреса 
    текущим значением URL в файле MainFrm.cpp
    // Заполнение поля адреса текущим значением URL
    void CMainFrame::SetAddress(LPCTSTR lpszURL)
    {
      // Сменить значение URL после загрузки новой страницы
      (m_wndDlgBar.GetDlgItem(IDC_EADDRESS))->SetWindowText(lpszURL);
    }
  • Теперь можно построить приложение и убедиться, что при переходе по ссылкам в поле адреса отображается текущий URL страницы.

    Переходы GoBack и GoForward

    Добавим к нашему приложению возможности перехода на предыдущую и последующую Web -страницы. Для этого нужно воспользоваться функциями GoBack() и GoForward(), унаследованными нашим классом представления CWebBrowseView от базового MFC -класса CHtmlView. Вызовы этих функций можно связать с пунктами меню, клавишами быстрого вызова ("горячими клавишами"), или с кнопками панели инструментов.

    Реализация возможностей перехода через меню

  • В панели
  • В панели свойств
  • Вновь в панели
  • Удалите с панели меню категорию Edit все ее пункты и замените эту категорию на Navigate
  • Перетащите эту категорию на новое место перед категорией Help
  • Добавьте два новых пункта в категорию Navigate: один для функции GoBack, а второй - для функции GoForward
  • Установите значения свойств новых пунктов, как показано в таблице
    Значения свойств пунктов меню
    Пункт меню Свойство Значение
    Первый ID ID_NAVIGATE_BACK
    Caption Назад \tCtrl + B
    Prompt Назад на предыдущую Web -страницу \n Назад
    Второй ID ID_NAVIGATE_NEXT
    Caption Вперед \tCtrl + F
    Prompt Вперед на следующую Web -страницу\nВперед
  • Внешний вид категории Navigate в редакторе меню должен выглядеть так

    Для каждого пункта меню необходимо добавить обработчики сообщения о событии COMMAND. Эти сообщения должен принимать и обрабатывать класс представления CWebBrowseView. Обработчики будем создавать не двойным щелчком по соответствующему пункту, а через контекстное меню оболочки для соответствующего пункта нашего проектируемого меню в редакторе. Для меню способ создания обработчика двойным щелчком оболочкой не поддерживается, поскольку неизвестно, какой класс будет принимать и обрабатывать сообщения COMMAND, пришедшие от меню.

  • В редакторе меню щелкните правой кнопкой мыши на первом пункте категории
  • Выберите в списке
  • Нажмите кнопку Add and Edit и заполните созданный мастером обработчик следующим кодом
    Обработчик пункта меню "Назад"
    void CWebBrowseView::OnNavigateBack()
    {
      // TODO: Add your command handler code here
      // Идти к предыдущей странице
      GoBack();
    }
  • Подобным образом создайте обработчик для второго пункта меню и заполните его так
    Обработчик пункта меню "Вперед"
    void CWebBrowseView::OnNavigateNext()
    {
      // TODO: Add your command handler code here
      // Перейти на следующую страницу вперед
      GoForward();
    }
  • Запустите приложение и убедитесь, что пункты спроектированного меню работают
  • Реализация возможностей перехода через акселераторы

    Акселераторы (горячие клавиши) применяются для быстрого доступа к командам меню при нажатии комбинации назначенных клавиш. Мы для наших пунктов меню "Назад" и "Вперед" зарезервировали комбинации клавиш " Ctrl + B " и " Ctrl + F ". Реализуем эти комбинации. Наша задача - закрепить за выбранными комбинациями те же самые идентификаторы ID_NAVIGATE_BACK и ID_NAVIGATE_NEXT, которые мы назначали для соответствующих пунктов меню Navigate.

  • Раскройте узел акселераторов Accelerator в дереве ресурсов панели Resource View и щелкните дважды по ресурсу IDR_MAINFRAME

    Откроется на редактирование таблица акселераторов. Мы видим, что каждый акселератор состоит из идентификатора ID и закрепленной за ним комбинации клавиш.

  • Щелкните правой кнопкой мыши в любом месте таблицы акселераторов и в появившемся контекстном меню выполните команду New Accelerator

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

  • Введите значение ID_NAVIGATE_BACK в поле ID добавленной новой строки (или выберите из раскрывающегося в поле списка, поскольку раньше мы их вводили для пунктов меню), в качестве модификатора ( Modifier ) оставьте Ctrl, а в качестве клавиши ( Key ) введите латинскую B
  • В следующую пустую строку в поле ID введите значение ID_NAVIGATE_NEXT (или выберите из раскрывающегося в поле списка), в качестве модификатора ( Modifier ) оставьте Ctrl, а в качестве клавиши ( Key ) введите F

    Теперь таблица акселераторов после внесенных изменений должна выглядеть так

  • Вернитесь в редактор меню и проверьте, остались ли у идентификаторов пунктов те же значения ID_NAVIGATE_BACK и ID_NAVIGATE_NEXT. Если нет, то в раскрывающемся списке панели свойств Properties восстановите их вновь

    Таким образом, мы назначили для пунктов меню "Назад" и "Вперед" горячие клавиши. Теперь глубокоуважаемый жирный Пользователь (у него много денежек, которые мы должны выманить!!!) нашего шедевра сможет осуществлять переходы на уже просмотренные Web-страницы или использованные ссылки, которые хранятся в кэше броузера, не только через пункты меню, но и по горячим клавишам.

  • Постройте приложение и убедитесь, что все работает
  • Реализация возможностей перехода через кнопки панели управления

    Добавим в панель управления правее строки ввода адреса две кнопки, дублирующие команды перехода категории Navigate главного меню приложения.

  • Во вкладке Resource View вызовите на редактирование диалоговый ресурс панели управления
  • Поместите на нее правее поля ввода адреса две кнопки Button из вкладки Toolbox
  • Сделайте ширину кнопок примерно равной высоте ( Width=16, Height=15 )

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

  • Настройте свойства кнопок в соответствии с таблицей
    Значения свойств кнопок перехода
    Кнопка Свойство Значение
    Левая ID ID_BUTTON_BACK
    Caption BACK
    Prompt Назад на предыдущую Web-страницу\nНазад
    Owner Draw True
    Правая ID ID_BUTTON_NEXT
    Caption NEXT
    Prompt Вперед на следующую Web-страницу\nВперед
    Owner Draw True

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

    Теперь нужно создать обработчики для кнопок. Кнопки находятся в классе CMainFrame и будут подавать сообщения BN_CLICKED при щелчках по ним. Принимать же и обрабатывать сообщения нужно в классе CWebBrowseView. Это точно такая же ситуация, как при создании пунктов меню, которые сами тоже находятся во фреймовом окне CMainFrame, а посылаемые ими команды перехватываются и обрабатываются в окне представления, поддерживаемого классом CWebBrowseView.

  • Выполните правый щелчок мышью на левой кнопке панели управления и в появившемся контекстном меню выполните команду
  • Настройте мастер добавления обработчиков, выделив в качестве целевого класс
  • Нажмите кнопку Add and Edit и мастер создаст заготовку обработчика, которую заполните так
    Обработчик щелчка на левой кнопке ID_BUTTON_BACK
    void CWebBrowseView::OnBnClickedButtonBack()
    {
      // TODO: Add your control notification handler code here
      // Идти к предыдущей странице
      GoBack();
    }
  • Повторите такие же шаги для создания заготовки обработчика щелчка на правой кнопке панели управления. Заготовку заполните следующим кодом
    Обработчик щелчка на правой кнопке ID_BUTTON_NEXT
    void CWebBrowseView::OnBnClickedButtonNext()
    {
      // TODO: Add your control notification handler code here
      // Перейти на следующую страницу вперед
      GoForward();
    }

    Перед тем как построить приложение после введенных кнопок, привяжитесь покрепче к стулу, на котором сидите.

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

    Исчезновение кнопок с панели управления произошло потому, что мы установили для них свойство Owner Draw в значение True. Тем самым мы определили, что кнопки должен рисовать их владелец ( Owner ), а о самом коде рисования внутри владельца мы пока не позаботились. Владельцем (родителем) кнопок, в данном случае, является панель управления m_wndDlgBar как экземпляр класса CDialogBar, объявленная в файле MainFrm.h.

  • Измените свойство Owner Draw на значение False и снова постройте приложение
  • Кнопки появились и ведут себя как и положено, обеспечивая переходы броузера, но невмещающиеся внутри них надписи портят всю картину

    Заменим традиционные текстовые надписи внутри кнопок на содержательные рисунки. Для этого нужно подготовить нужные картинки в формате Bitmap, добавить в них немного соли, густо поперчить и подавать горячими. Студент Жук - разрешаю Вам передохнуть, а остальные и примкнувший к ним Панасюк - продолжайте. До Силиконовой долины или Редмонда осталось совсем немного (почти как до психушки).

    Присоединяемые к кнопкам картинки будут состоять из стрелок и должны быть одинакового размера. Иначе в разных состояниях они начнут скакать по экрану. Одна картинка будет изображать стрелку влево, другая - стрелку вправо. Мы установили для левой кнопки свойство Caption в значение BACK, а для правой - NEXT. В дальнейшем мы воспользуемся этими именами для привязки изображений.

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

  • кнопка нажата - Down (экземпляры BACKD и NEXTD )
  • кнопка отпущена - Up (экземпляры BACKU и NEXTU )
  • (Можно еще: кнопка получила фокус ввода - Focus (экземпляры BACKF и NEXTF кнопка не получила фокус ввода - экземпляры BACKX и NEXTX )

    Картинки в файл ресурсов можно добавить готовыми ( Import ), а можно нарисовать средствами встроенного в оболочку графического редактора ( New ). В любом случае размер картинок должен примерно соответствовать размеру кнопок. Ранее мы определили размеры кнопок примерно такими ( Width=16, Height=15 ). Значит и картинки нужно выполнить такого же размера. Хотя при загрузке картинок в кнопки их размеры автоматически подстроятся под размеры картинок. Но нам важно, чтобы кнопки не вышли по размерам за пределы панели управления. Итак, создаем картинки для кнопок.

  • Щелкните правой кнопкой мыши в любом месте панели Resource View и выполните в появившемся контекстном меню команду Add Resource
  • Установите категорию ресурса
  • В открывшемся графическом редакторе щелкните правой кнопкой мыши рядом с полем рисования и через панель
  • Сохраните ресурс, щелкнув по вкладке правой кнопкой мыши
  • Нарисуйте стрелку влево, при этом для рисования активно используйте панель инструментов
  • Панель Colors обеспечивает настройку инструментов рисования как цветовую пару с текущим значением для левой (передний цвет пары) и правой (задний цвет пары) кнопок мыши. Цветовая пара текущего цвета выбирается в панели Colors щелчками по образцам соответствующими кнопками мыши. Количество доступных цветовых образцов устанавливается в панели Properties. Дополнительные возможности графического редактора заложены в меню Image оболочки.

    Сам графический редактор представлен двумя фреймами. В правом фрейме масштаб поля рисования можно менять через инструмент "Увеличительное стекло"

    В левом фрейме рисунок представлен в натуральную величину.

  • Вновь сохраните готовый рисунок стрелки влево в файле ресурсов. Убедитесь, что в проект добавился соответствующий ресурс

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

  • Закройте окно графического редактора, войдите в панель
  • Выполните команду
  • Сохраните проект и убедитесь, что в панели
  • Вызовите на редактирование ресурс IDB_BITMAP_NEXT и выполните для него две команды поворота, чтобы развернуть стрелку в противоположную сторону

    Должен получиться такой результат

  • В панели
  • Сохраните проект и убедитесь, что у нас теперь есть по две картинки для каждой кнопки
  • Вызовите на редактирование две последних картинки с идентификаторами
  • Теперь переназначьте каждой картинке значение свойства ID как показано в таблице
  • Числовые идентификаторы мы назначали ранее для того, чтобы не запутаться. Не забудьте, что новые имена ресурсов растров нужно обязательно заключать в двойные кавычки.

    Новые значения свойства ID для картинок кнопок
    Старый ID Bitmap Новый ID Назначение
    IDB_BITMAP_BACK " BACKU " Картинка для левой кнопки в отпущенном состоянии. Свойству Caption самой левой кнопки мы ранее присвоили значение BACK
    IDB_BITMAP_BACK_INV " BACKD " Картинка для левой кнопки в нажатом состоянии. Свойству Caption самой левой кнопки мы ранее присвоили значение BACK
    IDB_BITMAP_NEXT " NEXTU " Картинка для правой кнопки в отпущенном состоянии. Свойству Caption самой правой кнопки мы ранее присвоили значение NEXT
    IDB_BITMAP_NEXT_INV " NEXTD " Картинка для правой кнопки в нажатом состоянии. Свойству Caption самой правой кнопки мы ранее присвоили значение NEXT

    Иными словами, если на кнопке текст BACK, то мы для нее создаем два растра с идентификаторами " BACKU " и " BACKD " - соответственно для отжатого и нажатого состояния. Для другой кнопки - то же самое. Этот прием обеспечит нормальную работу картинок на кнопках. Теперь панель ресурсов должна выглядеть так

    Теперь осталось немного для завершения работы с кнопками: создать два экземпляра класса CBitmapButton и обеспечить с их помощью динамическую загрузку изображений в кнопки и управление ими.

  • Откройте через панель Solution Explorer файл MainFrm.h с описанием класса фреймового окна и внесите в конец класса следующий код
    Объявление поддерживающих объектов в файле MainFrm.h
    class CMainFrame : public CFrameWnd
    {
      
    protected: // create from serialization only
      CMainFrame();
      DECLARE_DYNCREATE(CMainFrame)
      
    // Attributes
    public:
      
    // Operations
    public:
      
    // Overrides
    public:
      virtual BOOL PreCreateWindow(CREATESTRUCT cs);
      
    // Implementation
    public:
      virtual ~CMainFrame();
    #ifdef _DEBUG
      virtual void AssertValid() const;
      virtual void Dump(CDumpContext dc) const;
    #endif
      
    protected:  // control bar embedded members
      CStatusBar  m_wndStatusBar;
      CToolBar    m_wndToolBar;
      CReBar      m_wndReBar;
      CDialogBar      m_wndDlgBar;
      
    // Generated message map functions
    protected:
      afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
      DECLARE_MESSAGE_MAP()
    public:
      // Функция передачи адреса URL
      void OnNewAddress(void);
      // Заполнение поля адреса текущим значением URL
      void SetAddress(LPCTSTR lpszURL);
    private:
      CBitmapButton m_Back, m_Next;
    };
  • Откройте файл MainFrm.cpp, найдите в нем функцию OnCreate() и вставьте в конце код загрузки ресурсов в кнопки
    Код загрузки картинок в кнопки
    int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
    .....................................................
      // TODO: Remove this if you don't want tool tips
      m_wndToolBar.SetBarStyle(m_wndToolBar.GetBarStyle() |
        CBRS_TOOLTIPS | CBRS_FLYBY);
      
      m_Back.AutoLoad(ID_BUTTON_BACK, m_wndDlgBar);
      m_Next.AutoLoad(ID_BUTTON_NEXT, m_wndDlgBar);
      
      return 0;
    }
  • Не забудьте установить свойство Owner Draw кнопок в значение True
  • Постройте проект и убедитесь, что изображения кнопок меняются, сами кнопки функционируют правильно и посылают сообщения классу CWebBrowseView, а обработчики для кнопок в файле WebBrowseView.cpp мы построили ранее
  • Теперь наш уважаемый Пользователь имеет возможность перехода на посещенные страницы по командам меню, по горячим клавишам и кнопкам панели управления. Вот сколько много мы для него постарались.

    Дополнительные возможности управления браузером

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

    Если Web-страница большого объема или пропускная способность соединения слабая, то у пользователя может возникнуть необходимость прервать загрузку страницы броузером. Для этого в класса CHtmlView предусмотрена функция Stop().

    Когда пользователь перемещается кнопками "Назад" и "Вперед" по уже посещенным Web-страницам, то он фактически использует информацию, которую броузер хранит в своем внутреннем кэше. Чтобы заставить броузер перегрузить страницу, а не просто отобразить версию из кэша, существует функция Refresh().

    И последнее... Чтобы быстро переместиться на домашнюю страницу броузера, а это как правило стартовая страница с именем Home или Index, можно использовать встроенную в класс CHtmlView функцию GoHome(). При выполнении этой функции внутренний кэш броузера очищается.

    Прежде всего нужно дополнить меню Navigate нашего приложения соответствующими пунктами.

  • Отредактируйте меню приложения, добавив сепаратор и три пункта в соответствии с рисунком
  • Откорректируйте свойства новых пунктов меню, используя информацию из таблицы
    Свойства дополнительных пунктов меню
    Свойство Значение
    ID ID_NAVIGATE_STOP
    Caption Stop
    Prompt Остановить загрузку\nСтоп
    ID ID_NAVIGATE_REFRESH
    Caption Refresh
    Prompt Обновить текущую страницу\nОбновить
    ID ID_NAVIGATE_GOHOME
    Caption GoHome
    Prompt Перейти на начальную страницу\nДомой
  • Создайте обработчики для каждого нового пункта меню через контекстное меню пункта и команду
  • Заполните обработчики следующим кодом
    Обработчики для дополнительных возможностей броузера
    void CWebBrowseView::OnNavigateStop()
    {
      // TODO: Add your command handler code here
      // Остановить текущую загрузку
      Stop();
    }
      
    void CWebBrowseView::OnNavigateRefresh()
    {
      // TODO: Add your command handler code here
      // Перезагрузить текущую страницу
      Refresh();
    }
      
    void CWebBrowseView::OnNavigateGohome()
    {
      // TODO: Add your command handler code here
      // Идти на начальную страницу
      GoHome();
    }
  • Постройте приложение - все должно работать!
  • Отображение заголовка текущей Web-страницы в заголовке окна приложения

    Чтобы получить заголовок текущей Web -страницы и установить его в заголовке окна приложения воспользуемся свойством Title и функцией (средством доступа к свойству) get_title() интерфейса IHTMLDocument2. Для этого выполним несколько пошаговых инструкций.

  • Добавьте к приложению новый класс, выполнив команду Для добавления нового класса в приложение используем корневой узел любой из приведенных панелей
  • В появившемся окне
  • После щелчка по кнопке
  • Для настройки мастера Add Class From TypeLib Wizard выполните следующее:
  • Из раскрывающегося списка Available type libraries выберите значение Microsoft HTML Object Library <4.0>
  • В списке Interfaces найдите строку IHTMLDocument2 и выделите ее
  • Нажмите верхнюю кнопку со значком угловой стрелки ">", чтобы создать в правом списке Generated classes имя создаваемого класса, соответствующее выбранному интерфейсу
  • Щелкните по кнопке Finish для создания класса

    В результате мастер создаст исходный код интерфейсного класса для интерфейса IHTMLDocument2, который полностью разместится в заголовочном файле CHTMLDocument2.h приложения и нам остается только воспользоваться кодом этого класса для решения поставленной задачи загрузки заголовка текущей Web -страницы.

  • В файле WebBrowseView.cpp класса представления CWebBrowseView найдите уже использованную нами ранее функцию OnDocumentComplete() и дополните ее кодом, как показано ниже
    Код, добавленный в функцию OnDocumentComplete() файла WebBrowseView.cpp
    void CWebBrowseView::OnDocumentComplete(LPCTSTR lpszURL)
    {
      // TODO: Add your specialized code here and/or call the base class
      // Передать текущий URL в класс фрейма для вывода в поле адреса
      ((CMainFrame*)GetParentFrame())->SetAddress(lpszURL);
      
      // Создать экземпляр интерфейса класса CHTMLDocument2
      CHTMLDocument2* pHDoc = new CHTMLDocument2(GetHtmlDocument());
      // Получить заголовок текущей страницы
      CString strTitle = pHDoc->get_title();
      // Получить указатель на класс документа
      CWebBrowseDoc* pDoc = GetDocument();
      // Установить заголовок окна приложения
      pDoc->SetTitle(strTitle);
      // Удалить экземпляр интерфейса на куче
      delete pHDoc;
      
      CHtmlView::OnDocumentComplete(lpszURL);
    }
  • Добавьте директиву #include в начало файла WebBrowseView.cpp для подключения описания нового класса CHTMLDocument2
    Подключение класса интерфейса к файлу представления WebBrowseView.cpp
    // WebBrowseView.cpp : implementation of the CWebBrowseView class
    //
      
    #include "stdafx.h"
    #include "WebBrowse.h"
      
    #include "WebBrowseDoc.h"
    #include "WebBrowseView.h"
    #include ".\webbrowseview.h"
      
    #include "MainFrm.h"
      
    #include "CHTMLDocument2.h"
      
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    .........................................
  • Постройте приложение и убедитесь, что заголовок текущей страницы отображается в заголовке окна приложения

    Если при построении приложения Ваш компилятор выдает ошибки в коде добавленного нами файла интерфейса CHTMLDocument2.h (мой выдает!), то сделайте следующее

  • Откройте в оболочке на редактирование файл CHTMLDocument2.h, найдите в нем функции write() и writeln() и внесите в них следующие изменения
    Корректируем "Ошибки Билла"
    ......................................  
      void write(SAFEARRAY * psarray)
      {
        // static BYTE parms[] = VTS_NONE ;
        static BYTE* parms = (BYTE*)VTS_NONE ;
        InvokeHelper(0x41e, DISPATCH_METHOD, VT_EMPTY, NULL, parms, psarray);
      }
      void writeln(SAFEARRAY * psarray)
      {
        // static BYTE parms[] = VTS_NONE ;
        static BYTE* parms = (BYTE*)VTS_NONE ;
        InvokeHelper(0x41f, DISPATCH_METHOD, VT_EMPTY, NULL, parms, psarray);
      }
    ......................................
  • Теперь опять постройте приложение (ошибок больше быть не должно) и убедитесь, что заголовок текущей страницы отображается в заголовке окна приложения
  • Настройка приложения на открытие локальных HTML-документов

    Тот стандартный каркас приложения, который сгенерировал для нас мастер в самом начале, не рассчитан на открытие документов в формате HTML. Заложенные изначально базовые возможности открытия документа реализуются посылкой сообщения ON_FILE_OPEN в ответ на щелчок Пользователя по кнопке Open панели инструментов (значок ) или выполнения одноименной команды меню. Это сообщение перехватывается в классе CWebBrowseApp (карта сообщений файла WebBrowse.cpp ) и обрабатывается функцией базового класса CWinApp::OnFileOpen()

    Карта сообщений для класса CWebBrowseApp в файле WebBrowse.cpp
    // CWebBrowseApp
      
    BEGIN_MESSAGE_MAP(CWebBrowseApp, CWinApp)
      ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
      // Standard file based document commands
      ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
      ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
      // Standard print setup command
      ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
    END_MESSAGE_MAP()

    Для того, чтобы реализовать задуманное, прежде всего нужно убрать выделенный код из карты сообщений класса CWebBrowseApp. Перехватывать это сообщение мы будем во фреймовом окне, в классе которого у нас есть доступ к полю редактирования адреса. В классе фреймового окна мы построим обработчик сообщения ON_FILE_OPEN, в котором создадим и настроим диалог FileOpen, из которого извлечем информацию об имени выбранного Пользователем открываемого файла. После этого имя файла можно передать в поле ввода адреса панели управления и сгенерировать сообщение IDOK, обработчик которого у нас уже создан. Итак, приступим.

  • Закомментируйте код маршрутизации сообщения ON_FILE_OPEN в карте сообщений класса CWebBrowseApp, как показано ниже
    Комментируем код базового сообщения ON_FILE_OPEN для класса CWebBrowseApp в файле WebBrowse.cpp
    // CWebBrowseApp
      
    BEGIN_MESSAGE_MAP(CWebBrowseApp, CWinApp)
      ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
      // Standard file based document commands
      ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
    //  ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
      // Standard print setup command
      ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
    END_MESSAGE_MAP()
  • Постройте приложение и убедитесь, что кнопка и команда меню Open стали недоступными ( Disabled )
  • В панели Class View выделите класс CMainFrame и сразу после этого в панели Properties установите режим Events
  • Найдите сообщение
  • Постройте приложение и убедитесь, что команда и кнопка Open стали доступными, но естественно ничего не делают, так как обработчик пока пустой
  • Внесите в обработчик код, который и решит всю задачу
    Код нового обработчика сообщения ON_FILE_OPEN
    void CMainFrame::OnFileOpen()
    {
      // TODO: Add your command handler code here
      // Создать диалог File Open
      static char szFilter[] = 
        "HTM Files (*.htm)|*.htm|HTML Files (*.html)|*.html|All Files (*.*)|*.*||";
      CFileDialog openFile(TRUE, ".htm", "", 
        OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, szFilter);
      CString name; 
      if(openFile.DoModal() == IDOK)
        name = openFile.GetPathName();// Полное имя с путем
      else
        return;
      
      // Сменить значение поля адреса
      (m_wndDlgBar.GetDlgItem(IDC_EADDRESS))->SetWindowText(name);
      OnNewAddress();// Вызвать нашу функцию
    }
  • Постройте приложение и убедитесь, что все работает как задумали. Отметьте также, что полнофункционально работает и контекстное меню, генерируемое базовым классом
  • Подчистка лишних интерфейсов и оформление About

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

  • Вызовите на редактирование панель инструментов и удалите лишние кнопки

    Кнопки удаляйте перетаскиванием мышью за пределы панели. Сепараторы между кнопками вводятся более широкими отступами по ширине панели.

    Ресурс панели инструментов после редактирования должен выглядеть так

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

    Теперь, когда мы удалили инструменты New и Save, в классе документа CWebBrowseDoc стали ненужными переопределения функций OnNewDocument() и Serialize(). Теперь уже никто их не сможет вызвать. Все ниточки-веревочки для их вызова мы обрезали. Они конечно никому не мешают и код не портят, но место все-таки занимают, да и могут смутить бедного сопровождающего программиста. Поэтому удалим их объявления в файле WebBrowseDoc.h и реализацию в файле WebBrowseDoc.cpp.

  • Откройте файл WebBrowseDoc.h и удалите (или закомментируйте) в объявлении класса CWebBrowseDoc следующий код
    Удаление объявлений функций в классе CWebBrowseDoc
    class CWebBrowseDoc : public CDocument
    {
    ........................................  
    // Overrides
      public:
    //  virtual BOOL OnNewDocument();
    //  virtual void Serialize(CArchive ar);  
    ........................................
    };
  • Откройте файл WebBrowseDoc.cpp и удалите (или закомментируйте) в реализации класса CWebBrowseDoc код функций OnNewDocument() и Serialize(CArchive ar)
    Удаление лишнего кода в реализации класса CWebBrowseDoc
    // WebBrowseDoc.cpp : implementation of the CWebBrowseDoc class
    //
      
    #include "stdafx.h"
    #include "WebBrowse.h"
      
    #include "WebBrowseDoc.h"
      
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
      
    // CWebBrowseDoc
      
    IMPLEMENT_DYNCREATE(CWebBrowseDoc, CDocument)
      
    BEGIN_MESSAGE_MAP(CWebBrowseDoc, CDocument)
    END_MESSAGE_MAP()
      
    // CWebBrowseDoc construction/destruction
      
    CWebBrowseDoc::CWebBrowseDoc()
    {
      // TODO: add one-time construction code here
    
    }
      
    CWebBrowseDoc::~CWebBrowseDoc()
    {
    }
    /*
    BOOL CWebBrowseDoc::OnNewDocument()
    {
      if (!CDocument::OnNewDocument())
        return FALSE;
      
      // TODO: add reinitialization code here
      // (SDI documents will reuse this document)
      
      return TRUE;
    }
      
    // CWebBrowseDoc serialization
      
    void CWebBrowseDoc::Serialize(CArchive ar)
    {
      if (ar.IsStoring())
      {
        // TODO: add storing code here
      }
      else
      {
        // TODO: add loading code here
      }
    }
    */
      
    // CWebBrowseDoc diagnostics
      
    #ifdef _DEBUG
    void CWebBrowseDoc::AssertValid() const
    {
      CDocument::AssertValid();
    }
      
    void CWebBrowseDoc::Dump(CDumpContext dc) const
    {
      CDocument::Dump(dc);
    }
    #endif //_DEBUG
      
    // CWebBrowseDoc commands
  • Откройте файл WebBrowse.cpp и удалите в карте сообщений маршрутизатор сообщения ID_FILE_NEW. Все равно теперь никто в приложении это сообщение не пошлет - мы все стерилизовали
    Корректировка карты приема сообщений класса CWebBrowseApp
    BEGIN_MESSAGE_MAP(CWebBrowseApp, CWinApp)
      ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
      // Standard file based document commands
    //  ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
    //  ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
      // Standard print setup command
      ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
    END_MESSAGE_MAP()
  • Добавьте в категорию
  • Оформите About и смените иконку приложения
  • Запустите и испытайте приложение - работает как настоящий Броузер (почти)
  • Вернуться к учебному плану