Using the CodeGuru HTML View Automation Classes
Eric Hartwell - December 1999
Microsoft's CHtmlView class provides the functionality of the WebBrowser
control within the context of MFC's document/view architecture. The WebBrowser
control is a window in which the user can browse sites on the World Wide Web, as
well as folders in the local file system and on a network. This effectively
makes the application a web browser.
The functionality of CHtmlView is designed for applications that access the
Web (and/or HTML documents). For HTML-based applications, however, we need a
tighter integration between the view class and the actual application. For
example,
- Set HTML text directly from a string, rather than a URL
- Access the HTML DOM directly from the application's CDocument class
- Capture arbitrary events within the browser
- Interact with scripting code running on the browser
V. Rama Krishna (mailto:ramakrsna@hotmail.com)
has published an excellent series of articles about automating the HTML view on
the CodeGuru web site (see references).
This article explains how his code is extended and implemented for MyApp
Studio.
CHTMLPage Classes
Rama explains the architecture of his CHTMLPage classes (excerpts):
A traditional MFC multiple form-based
application has a class derived from some base class like CFormView, for each
dialog resource (or form). In that derived class the controls are mapped to
variables through DDX_ functions. The events are mapped through Message Map
macros. I wished to have a similar architecture for working with DHTML
pages.
Therefore,
the class CHTMLPage serves as a base class from which
you derive any other class to represent
a particular page. For example, to convert a form-based application to DHTML,
all you have to do is to derive a class from CHTMLPage
and add your specialized processing for the events occurring in the page. You
can also map HTML events to a variable of the class CHTMLElement or
you can derive your own class from CHTMLElement for special elements.
CHTMLEventSink, derived from IDisptach,
serves to sink events from any HTML element through connection points. The
class CHTMLEventTarget is the class where ultimately the event is
handled. When you advise a connection you advise pass the target as an object
of CHTMLEventSink which has a member m_pTraget where it is
supposed to pass the event to. CHTMLPage and CHTMLEvent have
both been derived from CHTMLEventTarget. This gives me the
flexibility to develop certain elements that have respond to events in a
particular way.
The CHTMLElementCollection collection
is just a wrapper class for IHTMLElementCollection . CHTMLPage
is also derived from CHTMLElementCollection, since the CHTMLElementCollection
part of CHTMLPage mainatins the 'all' collection. CHTMLPage
also serves to wrap around the interfaces IHTMLDocument2, which
represents the HTML document, and IHTMLWindow2, which represents the
"window" scripting object.
-
Programming
Dynamic HTML through VC++
Once the CHtmlView has been initialized, you can get a dispatch
pointer to the document and connect it to the CHTMLPage-derived class. There's
a catch - the document object isn't valid until after the browser has finished
navigating to the first page. So, before you can do anything, you have to
navigate to a page. The browser fires a NavigateComplete event when the initial
document is loaded, so this is done in the OnNavigateComplete or OnDocumentComplete
methods:
CDerivedHTMLPage* pPage = new CDerivedHTMLPage();
LPDISPATCH pDisp1 = GetHtmlDocument();
pPage->SetDocument(pDisp1);
pDisp1->Release();
The CHTMLPage code gets the document interface, initializes the
element classes, creates and attaches an event sink, and builds the customized
event and variable maps. CHTMLPage auto deletes itself, all
connections, and all associated classes, when the unload event is fired by the
window object.
CHtmlView2 Class
CHtmlView2 was originally developed for displaying HTML screens in
BenWin32. Basically, this class adds the NavigateText method, which lets
you specify the screen as an HTML characters string instead of a URL (see
Exoware Tech Note: Setting
HTML View Text Directly From a String).
void CHtmlView2::NavigateText(const char *pszHtmlText)
If the browser has already navigated to a page, the HTML is loaded directly.
However, if it hasn't, the method caches the text, navigates to the internal
page "about:blank" to initialize the document object, then loads the
HTML.
CMyAppHtmlView Class
CMyAppHtmlView was developed for displaying HTML screens in
BenWin32. It builds on the CHtmlView2 foundation to add event sourcing
and sinking (see Q181845),
printing (see Q156732),
and the ability to execute a script within the browser (see Exoware Tech Note: Issuing
Commands to the HTML View).
Printing support is simply a matter of sending a print command to the browser
control. (It's actually more complicated in BenWin32, since the user may choose
to print a screen that isn't currently displayed).
void CMyAppHtmlView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
{
// Let the HTML control print the current screen
ExecWB(pInfo->m_bPreview ? OLECMDID_PRINTPREVIEW : OLECMDID_PRINT,
OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL);
}
Implementation
- Create a document/view pair, where the view class is derived from
CHtmlView or CHtmlView2, and the document class is derived from
CMyAppStudioDoc.
- Add printing support to the view class:
void CnewView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
{
// Let the HTML control print the current screen
ExecWB(pInfo->m_bPreview ? OLECMDID_PRINTPREVIEW : OLECMDID_PRINT,
OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL);
}
- Add support for the clipboard [Edit - Cut, Copy, Paste, Select All] to the
document/view's menu and the actual view class (see How
to add clipboard use to CHtmlView):
void CnewView::OnEditCut() { ExecWB(OLECMDID_CUT, OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL); }
void CnewView::OnEditCopy() { ExecWB(OLECMDID_COPY, OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL); }
void CnewView::OnEditPaste() { ExecWB(OLECMDID_PASTE, OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL); }
void CnewView::OnEditSelectall() { ExecWB(OLECMDID_SELECTALL, OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL); }
- Edit the document class declaration so it derives from CHTMLPage as well
as CMyAppStudioDoc:
class CnewDoc : public CMyAppStudioDoc, public CHTMLPage
It is important to clear the HTML autodelete flag so the document isn't
deleted by the HTML routines:
CnewDoc::CnewDoc()
{
CHTMLPage::m_bAutoDelete = FALSE;
. . . .
- Override the view's OnInitialUpdate method to load an empty
document (blank screen):
void CnewView::OnInitialUpdate()
{
CHtmlView::OnInitialUpdate();
Navigate2("about:blank"); // Initialize system to a blank screen
}
- Override the view's OnDocumentComplete method to attach the
document class to the browser:
void CnewView::OnDocumentComplete(LPCTSTR lpszURL)
{
LPDISPATCH pDisp = GetHtmlDocument(); // Get the HTML COM interface
((CnewDoc *)GetDocument())->SetDocument(pDisp); // Set document does the rest for us
pDisp->Release(); // Finished with this copy of COM interface
CHtmlView::OnDocumentComplete(lpszURL); // Default processing
}
- You now have full control of the HTML DOM directly through the document
class. The CHTMLPage code calls the OnInitPage function as soon as the DOM
is ready. This is first called after the blank screen has been loaded; you
can set the HTML text or directly manipulate the DOM at this point. Note
that the navigate, open, write, reload, and other methods trigger the
onbeforeunload event, which causes it to delete the attached event sink.
- The document class can also implement the MapVars and MapEvents
functions to map HTML elements and events as desired.
Resources:
MSDN, Microsoft Knowledge Base, Site Builder Workshop, Platform SDK, etc.
Revisions:
- December, 1999 - Description for MyApp implementation.
|