Introduction
This article describes ways of creating a Web-style GUI in an MFC application. Under the formula application with Web-style GUI I mean that the user interface or a part of it is made on HTML basis. There is an example of Web-style dialog in the picture.
In an attempt to use such an interface in applications, I faced the following problems:
- Processing web interface events (getting events from DHTML to MFC code).
- Interactions with the Web elements (modifying DHTML from MFC code).
In order to solve these questions, MSDN library advices to use DHTML
COM interfaces (see also Handling HTML Element Events). It seemed to me
that this is a bad alternative to the simple interaction with GUI model in MFC: event maps and direct handling with control objects.
This article gives an explanation of some work methods which make
easier the creation of web-interface and the work with it from the
application. Namely:
- Receiving events from the web-interface through
OnBeforeNavigate2()
event.
- The interaction with the web GUI through the HTML script functions in the HTML code.
In this article, I describe CHtmlDialog
, CHtmlScript
, CHtmlCtrl
classes for the creation of Web style GUI in this article. An archive including ChtmlDialog
class and examples of its use is attached to the article (read readme.txt before use).
Examples of Web-style GUI implementations
The web-style GUI for applications was certainly used for the first time in Microsoft Applications which are part of Windows OS.
Web-interface in Windows XP help window.
Web-interface in Norton-AntiVirus program.
Web-GUI implements Inductive User Interface in Windows XP "User Accounts" dialog.
HTML display using CHtmlView class
We need to change the HTML code for the application to look different from Internet Explorer.
Displaying HTML in an MFC application is quite simple. All you have to do is to use CHtmlView
class. To try it, you have to start a new project with the help of ?MFC
AppWizard (exe)?. Choose ?Single Document? type and turn on Document
View architecture support. And on the last page of the **View class
wizard, set ?CHtmlView? as a base class. Next, add the HTML page into
application resources.
In order to make our program to look more like an application than an
Internet Explorer window, it is necessary to change something in the
HTML page code:
- To set the standard background color for Windows Applications as background color.
- To forbid Internet Explorer context menu to be displayed (except the context menu above the EditBox fields).
- To forbid the mouse selection for the HTML content. Allow selection only for the text in the EditBox fields.
- Forbid the mouse cursor change over the static text. In IE, the
cursor placed over the text becomes an edit cursor - ?I? (as in EditBox
fields) so as the user to be able to mark and copy the text from the
HTML page. In Windows applications, the text cursor usually appears only
in EditBoxes.
The following HTML code executes these changes. These actions are possible due to DHTML technology.
Collapse
<SCRIPT LANGUAGE="JScript">
// Forbid user?s mouse selecting for the content
// (allow text selection in EditBox only)
function onSelect1(){
if ( window.event.srcElement.tagName !="INPUT" ) {
window.event.returnValue = false;
window.event.cancelBubble = true;
}
}
// Forbid IE context menu
// (allow in EditBox only)
// (if the real context menu must be shown - Advanceв Hosting
// Interfaces must be used)
function onContextMenu(){
if ( window.event.srcElement.tagName !="INPUT" ) {
window.event.returnValue = false;
window.event.cancelBubble = true;
return false;
}
}
// Install Context Menu and Mark handlers on HTML loading.
//
function onLoad()
{
// forbid cursor change (except "INPUT"
// entry box and "A" hyperlink) for HTML text.
var Objs = document.all;
for (i=0; i< Objs.length; i++)
// "INPUT" entry box and "A" hyperlink
if (Objs(i).tagName!="INPUT" && Objs(i).tagName!="A")
Objs(i).style.cursor = "default";
// event handler ? content selection
document.onselectstart = onSelect1;
// event handler ? context menu
document.oncontextmenu = onContextMenu;
}
</SCRIPT>
<BODY onload="onLoad();"
leftmargin=0 topmargin=0 rightmargin=0 bottommargin=0
style = "background-color: buttonface;" >
// the HTML background color will be as in Windows Applications
?
In such a way, we have created an application that displays HTML as
its interface. The Menu, Toolbar and status line remained, but this is
normal, because we aren?t forced to give up using traditional controls,
and in this way gain a certain flexibility.
At this step, we faced the DHTML technology due to which everything
here is possible. The DHTML technology itself is possible due to DOM
(Document Object Model) technology. The DOM represents the HTML document
as objects [MSDN Library].
HTML window event processing
What is missing in CHtmlView?
A typical script working with interface elements presumes receiving
events from them (buttons for example) and data placing (for example,
into a text field). Particularly in our case, it is necessary to
organize an interaction of the HTML window with the MFC code. This in
not a complicate thing to do ? the idea is to transmit the event from
HTML to MFC code using the function OnBeforeNavigate2
of the CHtmlView
class [Thomas Aust].
In HTML, or being more specific Dynamic HTML, also exists the
conception of event. The possibilities of the event model in DHTML are
very large, for example, an event can be stopped at a certain step of
its processing.
At the occurrence of the event in HTML, it can be transformed into a window.navigate(%line%)
call. The MFC code will receive such an event as a OnBeforeNavigate2
call with the %line%
parameter. It is possible to transmit any HTML parameters in %line%
,
and MFC code will be able to process the user?s action. The
transmission of event "click OK button", at the same time the text from txtBox
is transmitted:
Collapse
.
.
.
<SCRIPT LANGUAGE="JScript">
function onBtnOk(){
var Txt = txtBox.value; // the line from TextBox
window.navigate("app:1005@" + Txt);
// "app:1005@" ? this is the MFC code command prefix.
// Txt ? data can be transmitted along with the event.
}
</SCRIPT>;
<BODY>
.
.
.
<input type=text style="width:50" id=txtBox >
<input type=BUTTON value="Ok" onClick="onBtnOk()" style="width:45%">
// the button has an event handler ? the onBtnOk() script function
.
.
.
</BODY>
</HTML>
The MFC code which will process the event:
Collapse
void CHtmlCtrl::OnBeforeNavigate2( LPCTSTR lpszURL,
DWORD nFlags,
LPCTSTR lpszTargetFrameName,
CByteArray& baPostedData,
LPCTSTR lpszHeaders,
BOOL* pbCancel )
{
const char APP_PROTOCOL[] = "app:";
int len = _tcslen(APP_PROTOCOL);
if (_tcsnicmp(lpszURL, APP_PROTOCOL, len)==0) {
// there is a specific Application?s reaction there.
OnAppCmd(lpszURL + len);
// Event cancellation, otherwise an error will occur.
*pbCancel = TRUE;
}
CHtmlView::OnBeforeNavigate2(lpszURL, nFlags,
lpszTargetFrameName, baPostedData,
lpszHeaders, pbCancel);
}
As not only the HTML but also the MFC code can be the event source,
it is necessary for the MFC code to be able to transmit data into HTML.
In order to make it possible, we can call scripts (Jscript\VBScript) in
HTML and transmit data as script-functions? parameters. The idea and
realization of this method belong to [Eugene Khodakovsky]. The
acquisition of ?Script? object from HTML:
Collapse
void CHtmlCtrl::OnDocumentComplete(LPCTSTR lpszURL)
{
.
.
.
HRESULT hr;
hr = GetHtmlDocument()->QueryInterface(IID_IHTMLDocument,
(void**) &m_pDocument);
if (!SUCCEEDED(hr)) {
m_pDocument= NULL;
return;
}
IDispatch *disp;
m_pDocument->get_Script( &disp); // get script object
.
.
.
}
Next, the MFC code which calls the specific script function with the parameters:
Collapse
.
.
.
CStringArray strArray;
strArray.Add("Parameter 1");
strArray.Add("Parameter 2");
strArray.Add("Parameter 3");
// the call of "SetParameters" function
// from the script, (passing array of strings)
m_HtmlCtrl.CallJScript2("SetParameters", strArray);
// inside the CallJScript2 function:
// GetIDsOfNames() ? get the ID number of the script function
// Invoke() ? call the script-function by the number
.
.
.
The scripts in HTML are very powerful and easy tools. It is possible
to write a whole program with it which will handle the HTML content and
react to users? actions. The DHTML object model makes this programming
quite easy.
In this way, the part of the MFC code which works with the interface
and user?s actions can be moved to the HTML script. If the HTML pages
are kept apart from the application, it is possible to change the
interface?s logic without recompiling the EXE file.
CHtmlDialog ? the HTML-based dialog window
Why it is necessary to use Advanced Hosting Interfaces?
Every application has dialogs besides the main window. These dialogs
can have a quite complicated user interface. I mean not only the design,
but also the reaction to user?s actions. So, it makes sense to use
DHTML here too. The problem "How to place a class derived from CView
on a dialog" is solved in the article by [Paul DiLascia, MSDN]. After the changes in CHtmlView
, we have a CHtmlCtrl
class (derived from CView
) which can be placed on a dialog. The CHtmlDialog
class solves two problems ? setting the dialog window name and its size. These parameters are indicated on the HTML page. The CHtmlDialog
class use:
- Insert the dialog into resources. Place the STATIC element on it
(STATIC will be replaced with the HTML control item). Next, create an
MFC based class on this dialog (inherited from
CDialog
).
- Change the inheritance class to
ChtmlDialog
in the header file (Dlg4.h for example).
Collapse
// inheriting the class from CHtmlDialog
class CDlg4 : public CHtmlDialog
{
// Construction
public:
- Add the
CHtmlDialog
constructor call in the Dlg4
class constructor in the CPP file (Dlg4.cpp for example).
Collapse
CDlg4::CDlg4(CWnd* pParent /*=NULL*/)
:CHtmlDialog(CDlg4::IDD,pParent, IDR_HTML4,
IDC_STATIC1) // the HTML page resource transmission
{
//{{AFX_DATA_INIT(CDlg4)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
}
The ChtmlDialog
class also allow to change dialog size from HTML. (See _onHtmlCmd
in CHtmlDialog
).
Dialog window screenshots.
The hereby given Dialog example is taken from real life: it is
necessary to give the user the possibility to add data blocks of a
certain type on the smart-card. Although, it can be not a smart-card,
but the file database of this card. So, this dialog must be logical:
show the necessary icon ? of a document or a smart-card (in the top left
corner), use the text ?Key? instead of ?Document?. In general, the
dialog must be adaptable. But this is not all.
Next, the things from Usability area come ? the Dialog is displayed
in a shorten variant at first, because the user has no need to know how
many free space is available. The program was developed for
Administrators and Developers. For example, the Administrator doesn?t
have to know and see every time the free space number and the message
that new data block size can be changed. The information about free
space is given to the user at the very beginning, here it is needed to
the Developer mostly (testing while developing smart-card applications).
If there is no free space on the smart-card, the Dialog will react ?
show the ?Question? icon, the explanatory text and will disable the ?OK?
button. Next, the Dialog does user?s work ? chooses the block which
does not exist on the smart-card (or in the document) at the opening. If
the user chooses an existing block , the Dialog will react ? show the
?Question? icon, the explanatory text, and will disable the ?OK? button.
The main program calls this dialog at any time ? even if the
smart-card is full. In this situation, the dialog appears in an extended
variant with the ?Question? icon, explanatory text, and blocked ?OK?
button. Here the Dialog plays an informational role, otherwise the error
message ?there left no free space? must be shown, and instead a known
dialog will appear ? as a result, the user?s memory is not so loaded. As
a result, such ?interface logic? takes a lot of C++ code. And imagine
that the application has 5 such dialogs.
Everything is almost finished, but there still are two problems:
- The window which displays HTML in our application (ActiveX element)
will always have a ?pressed in? border and this cannot be changed. The
explicit window options setting through
SetWindowLong( GWL_STYLE)
doesn?t work. This isn?t looking very good.
- If the user changes the HTML display options (Internet Explorer
Properties-> General Tab, Colors, Fonts,.. Buttons), it will
influence the HTML appearance in our application, in other words, the
fonts and colors will change. This doesn?t suit anyone. The application
user may simply not recognize it when it is launched next time.
If the first problem can be tolerated, the second one is very
serious. It is possible such a situation will occur when the application
will look different depending on user?s IE settings.
Here is the way an HTML dialog looks if the user changes display
settings in IE options. To solve this problem, it is necessary to
provide the Advanced Hosting Interfaces support. Advanced Hosting
Interfaces (AHI) are such COM interfaces which the WebBrowser?s ActiveX
element has (starting with version 4.0). They help to take full control
over the WebBrowser element [MSDN Library]. The realization of AHI in
applications and this interfaces? advantages are well demonstrated in
[Ethan Akhgari]?s examples.
The problems described here are solved by OnGetHostInfo
and OnGetOptionKeyPath
event redefinition (see Html_Host_Handlers.cpp). I?ll stop at this point. Thank you for your attention.
Usage
Please take a look at the projects in the source files archive.
Revision history
- 2003 December, Initial public release at Russian dev site.
- 2004 July, translation into English.