MFC. Использование HTML-интерфейса.

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

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

HTML-интерфейс, наряду со своей простотой, обладает другими свойствами, которые делаю его использование в приложениях для Windows все более привлекательным.

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

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

Интерактивность. Использование JavaScript позволяет организовать взаимодействие с пользователем в стиле столь популярной нынче технологии AJAX. Отображая необходимую информацию только по запросу, что особенно важно при ее больших объемах.

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

Красота. Все попытки придумать синоним для этого слова, потерпели неудачу, а все потому, что именно оно в полной мере отражает тот факт, что web-интерфейс гораздо проще сделать красивым, нежели традиционный. Для того чтобы убедиться в этом достаточно побродить по просторам Интернета и учесть тот факт, что при использовании HTML-интерфейса в собственной программе нет необходимости заботиться о размере загружаемой страницы и изображений.

Пора переходить от слов к делу

Для включения в свою MFC-программу HTML-интерфейса, необходимо использовать класс CHtmlView, который предоставляет функциональность веб-браузера. Теперь разберем, тот минимум функций, который необходимо реализовать для взаимодействия основной программы с интерфейсом. В конце страницы размещена ссылка на архив с примером, а потому я остановлюсь лишь на некоторых интересных моментах.

В примере реализован класс CJSHtmlView унаследованный от вышеупомянутого - CHtmlView, который в дополнение к функциям своего предка реализует две дополнительные возможности: замещение текста HTML-элементов и запуск JavaScript кода.

Первая из них реализуется с помощью функции CJSHtmlView:: SetHtmlElementValue(CString sId, CString sValue), которая ищет объект на HTML странице по его идентификатору и в случае успеха заменяет его содержимое строкой sValue, используя свойство innerHTML.

Думаю, что текст данной функции не вызовет никаких сложностей, а потому перейду ко второй возможности – взаимодействие с JavaScript.

Вызов JavaScript.

Для вызова JavaScript используется функция CJSHtmlView:: CallJavaScript(CString sName, CString sParams), первый параметр которой задает имя используемой функции, а второй список параметров с их значениями. Для разбора переданных значений на стороне JavaScript может быть использована следующая функция, которая разбирает строку, как последовательность пар имя – значение, используя в качестве разделителя двоеточие (при необходимости может быть использован любой другой разделитель).

  1.  
  2. function ParseParams(str)
  3. {
  4. var params = str.split(gSplitter);
  5. var cnt = params.length;
  6. var paramsMap = new Array();
  7.  
  8. for(j = 0; j < cnt; j += 2)
  9. {
  10. if(cnt - j == 1)
  11. {
  12. paramsMap[""] = params[j];
  13. }
  14. else
  15. {
  16. paramsMap[params[j]] = params[j + 1];
  17. }
  18. }
  19. return paramsMap;
  20. }
  21.  
  22.  

Вызов C++ функции из JavaScript

Для вызова функции из С++ кода используется метод CJSHtmlView::OnBeforeNavigate2, который вызывается каждый раз перед загрузкой страницы. Что бы инициировать переход на новую страницу можно использовать JavaScript функцию приведенную ниже.

  1.  
  2. function SendCommandValue(cmd, val)
  3. {
  4. window.navigate("cmd:" + cmd + ":" + val);
  5. }
  6.  
  7.  

Передаваемое сообщение начинается с префикса, по которому обработчик в CJSHtmlView::OnBeforeNavigate2 определяет, что необходимо вызвать callback-функцию. Вслед за префиксом записывается идентификатор команды, а завершается сообщение набором пар имя – значения, которые представляются в виде ассоциированного массива , аналогично тому, как это происходило в JavaScript.

В предыдущем абзаце была упомянута callback-функция, которая должна вызываться в случае прихода сообщения от JavaScript. В примере она имеет следующую реализацию:

  1.  
  2. void CJSHtmlDoc::OnJavaScript(void* pObject)
  3. {
  4. CJSHtmlDoc* pDoc = static_cast<CJSHtmlDoc*>(pObject);
  5. CMDIFrameWnd* pMainWnd = static_cast<CMDIFrameWnd*>(AfxGetMainWnd());
  6. if(pMainWnd != 0)
  7. {
  8. CJSHtmlView* pView = static_cast<CJSHtmlView*>(pMainWnd -> GetActiveView());
  9. pDoc -> HandleJSCommand(atoi((pView->GetParam("cmd")).GetString()), (pView->GetParam("")).GetString());
  10. }
  11. }
  12.  
  13.  

В данном случае преобразование void* к CJSHtmlDoc* безопасно в силу того, что это указатель именно на тот объект, для которого производилась регистрация callback-функции.

  1.  
  2. BOOL CJSHtmlDoc::OnNewDocument()
  3. {
  4. ...
  5. CMDIFrameWnd* pMainWnd = static_cast<CMDIFrameWnd*>(AfxGetMainWnd());
  6. if(pMainWnd != 0)
  7. {
  8. CJSHtmlView* pView = static_cast<CJSHtmlView*>(pMainWnd -> GetActiveView());
  9. pView -> Navigate2(htmlFile);
  10. pView -> SetCallback(OnJavaScript, this);
  11. }
  12. ...
  13. }
  14.  
  15.  

NB: Callback-функция должна быть статической функцией-членом класса!

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

  1.  
  2. void CJSHtmlDoc::HandleJSCommand(int cmd, CString param)
  3. {
  4. CMDIFrameWnd* pMainWnd = static_cast<CMDIFrameWnd*>(AfxGetMainWnd());
  5. if(pMainWnd != 0)
  6. {
  7. CJSHtmlView* pView = static_cast<CJSHtmlView*>(pMainWnd -> GetActiveView());
  8. switch(cmd)
  9. {
  10. /*Java script commands
  11.   var gCmdUnknown = 1000;
  12.   var gCmdGetValue = 1001;
  13.   var gCmdReady = 1002;
  14.   var gCmdSendValue = 1003;
  15.   var gCmdGetValueTest = 1004;
  16.   */
  17. case 1001:
  18. pView -> SetHtmlElementValue("test", "Get value: " + param);
  19. break;
  20. case 1002:
  21. pView -> SetHtmlElementValue("test", "Ready ...");
  22. break;
  23. case 1003:
  24. pView -> SetHtmlElementValue("test", "Received value :" + param);
  25. break;
  26. case 1004:
  27. pView -> CallJavaScript("GetValue", "value");
  28. break;
  29. default:
  30. pView -> SetHtmlElementValue("test", "Unknown command");
  31. break;
  32. }
  33. }
  34. }
  35.  
  36.  

JSHtmlView Test (34Kb).

Примечания: для запуска демонстрационной web-страницы выберите создание нового документа. Если программа сообщает о том, что страница не найдена, запустите файл .\html\refresh.bat из директории проекта.

Буду рад любым комментариям, замечаниям и вопросам, которые вы можете оставить на сайте или отправить мне по электронной почте vladimir.rybakov[at]gmail[dot]com

Читайте в блоге

Повторное экранирование кавычек в PHP
Полезные ссылки

Комментарии:

Войдите на сайт, чтобы оставить комментарий