[Previous] [Next]

HTML Views

One of MFC's most powerful new classes is CHtmlView, which converts the WebBrowser control that's the heart and soul of Microsoft Internet Explorer into a full-fledged MFC view. CHtmlView displays HTML documents. You provide a URL, which can reference a document on the Internet, on an intranet, or even on a local hard disk, and CHtmlView displays the document the same way Internet Explorer displays it. From the underlying WebBrowser control, CHtmlView inherits a treasure trove of added functionality, from the ability to go backward or forward in a history list with a simple function call to the ability to host Dynamic HTML (DHTML) documents. CHtmlView is also an Active Document container, which means it can be used to display documents created by Microsoft Word, Microsoft Excel, and other Active Document servers. It can even display the contents of folders on a hard disk—just like Internet Explorer.

CHtmlView is a complex class because it includes dozens of member functions that provide a C++ interface to the WebBrowser control. Despite its complexity, however, it is an exceedingly easy class to use. With just a handful of member functions, you can build applications that rival Internet Explorer itself for richness and functionality. In fact, you can use CHtmlView and other MFC classes such as CToolBar to build an Internet Explorer knock-off in less than a day. Visual C++ comes with an MFC sample named MFCIE that demonstrates how. If you're willing to forego a few bells and whistles, you can build a basic browser in minutes. Do note that because CHtmlView derives most of its functionality from the WebBrowser control, and because the WebBrowser control is part of Internet Explorer, an application that uses CHtmlView can be run only on systems equipped with Internet Explorer 4.0 or later.

CHtmlView Operations

A good way to begin learning about CHtmlView is to get acquainted with its nonvirtual member functions, or operations. The following table lists the operations that the majority of programmers will find the most useful. For information on the others (and there are many), refer to the MFC documentation.

Key CHtmlView Operations

Function Description
GetBusy Indicates whether a download is in progress
GetLocationName If an HTML page is displayed, retrieves the page's title; if a file or folder is currently displayed, retrieves the file or folder name
GetLocationURL Retrieves the URL of the resource that is currently displayed—for example, http://www.microsoft.com/ or file://C:/HTML Files/Clock.htm
GoBack Goes to the previous item in the history list
GoForward Goes to the next item in the history list
Navigate Displays the resource at the specified URL
Refresh Reloads the resource that is currently displayed
Stop Stops loading a resource

The actions performed by these functions should be obvious to anyone familiar with Internet Explorer. For example, if you were writing a browser, you could wire up the Back, Forward, Refresh, and Stop buttons with these one-line command handlers:

// In CMyView's message map
ON_COMMAND (ID_BACK, OnBack)
ON_COMMAND (ID_FORWARD, OnForward)
ON_COMMAND (ID_REFRESH, OnRefresh)
ON_COMMAND (ID_STOP, OnStop)
    
void CMyView::OnBack ()
{
    GoBack ();
}

void CMyView::OnForward ()
{
    GoForward ();
}

void CMyView::OnRefresh ()
{
    Refresh ();
}

void CMyView::OnStop ()
{
    Stop ();
}

The WebBrowser control exposes huge chunks of its functionality through a COM interface named IWebBrowser2. Most nonvirtual CHtmlView member functions, including the ones shown here, are little more than C++ wrappers around calls to IWebBrowser2 methods.

When the user clicks a hyperlink in an HTML document, CHtmlView automatically jumps to the associated URL. You can go to other URLs programmatically with the Navigate function. The statement

Navigate (_T ("http://www.microsoft.com"));

displays the main page of Microsoft's web site. Navigate also accepts file-based URLs. For example, the statement

Navigate (_T ("file://c:/my documents/budget.xls"));

displays an Excel spreadsheet in an HTML view. It works because Excel is an Active Document server, but it does require that Excel be installed on the host PC. Passing a path name identifying a folder rather than a file works, too:

Navigate (_T ("file://c:/my documents"));

A related CHtmlView function named Navigate2 does everything Navigate does and more. Because it will accept pointers to ITEMIDLIST structures in lieu of path names, Navigate2 can be used to access objects anywhere in the shell's namespace. Navigate, by contrast, is limited to file system objects only.

CHtmlView Overridables

CHtmlView includes several virtual functions that you can override in a derived class to obtain up-to-date information about the state of the WebBrowser control and the resources that it displays. A sampling of these functions appears in the following table.

Key CHtmlView Overridables

Function Description
OnNavigateComplete2 Called after navigating to a new URL
OnBeforeNavigate2 Called before navigating to a new URL
OnProgressChange Called to provide an update on the status of a download
OnDownloadBegin Called to indicate that a download is about to begin
OnDownloadComplete Called to indicate that a download is complete
OnTitleChange Called when the document title changes
OnDocumentComplete Called to indicate that a document was successfully downloaded

Unfortunately, the Visual C++ documentation provides only sketchy information about why or when these functions are called. That's why a transcript can be so revealing. Here's a log of the calls that took place when CHtmlView::Navigate was called to go to home.microsoft.com:

OnBeforeNavigate2 ("http://home.microsoft.com/")
OnDownloadBegin ()
OnProgressChange (100/10000)
OnProgressChange (150/10000)
OnProgressChange (150/10000)
OnProgressChange (200/10000)
OnProgressChange (250/10000)
OnProgressChange (300/10000)
OnProgressChange (350/10000)
OnProgressChange (400/10000)
OnProgressChange (450/10000)
OnProgressChange (500/10000)
OnProgressChange (550/10000)
OnDownloadComplete ()
OnDownloadBegin ()
OnProgressChange (600/10000)
OnProgressChange (650/10000)
OnProgressChange (700/10000)
OnProgressChange (750/10000)
OnProgressChange (800/10000)
OnProgressChange (850/10000)
OnProgressChange (900/10000)
OnProgressChange (950/10000)
OnProgressChange (1000/10000)
OnProgressChange (1050/10000)
OnProgressChange (1100/10000)
OnProgressChange (1150/10000)
OnProgressChange (1200/10000)
OnProgressChange (1250/10000)
OnProgressChange (131400/1000000)
OnTitleChange ("http://home.microsoft.com/")
OnNavigateComplete2 ("http://home.microsoft.com/")
OnTitleChange ("MSN.COM")
OnProgressChange (146500/1000000)
OnTitleChange ("MSN.COM")
OnProgressChange (158200/1000000)
OnProgressChange (286500/1000000)
OnProgressChange (452300/1000000)
OnTitleChange ("MSN.COM")
OnProgressChange (692800/1000000)
OnProgressChange (787000/1000000)
OnTitleChange ("MSN.COM")
      
OnDownloadComplete ()
OnTitleChange ("MSN.COM")
OnDocumentComplete ("http://home.microsoft.com/")
OnProgressChange (0/0)

You can clearly see the call to OnBeforeNavigate2 advertising the WebBrowser control's intent to jump to a new URL, the call to OnNavigateComplete2 after a connection was established, and the call to OnDocumentComplete once the page was fully downloaded. In between, you see calls to OnDownloadBegin and OnDownloadComplete marking the downloading of individual page elements and calls to OnProgressChange noting the progress of those downloads. OnProgressChange receives two parameters: a long specifying the number of bytes downloaded thus far and a long specifying the number of bytes to be downloaded. Dividing the first by the second and multiplying by 100 yields a percentage-done figure that can be displayed in a progress bar or other control. A call to OnProgressChange with a first parameter equal to -1 or a pair of 0 parameters is another indication that a download is complete.

The MFCIE sample shipped with Visual C++ provides one example of how these functions can be used. It uses OnTitleChange to update the document title displayed in its title bar, OnBeforeNavigate2 to begin playing an animation indicating a download is in progress, and OnDocumentComplete to stop the animation and update the URL displayed in its address bar. In essence, it uses OnBeforeNavigate2 and OnDocumentComplete to mark the beginning and end of a document download and OnTitleChange to display the title parsed from the HTML.

Utilizing DHTML in CHtmlView-Based Applications

Writing specialized browsers for in-house use is a fine way to put CHtmlView to work, but CHtmlView has plenty of other uses, too. Some MFC developers find CHtmlView interesting because it can be used to write thin clients. A thin client is an application that derives all or part of its functionality from HTML code, DHTML code, or other web programming media. A full discourse on DHTML is beyond the scope of this book, but a sample will help to demonstrate how CHtmlView and DHTML together can be a potent mix.

Suppose you'd like to write a Windows application that simulates a digital clock. One way to do it is to fire up Visual C++ and write an MFC clock application. An alternate approach is to create a CHtmlView-based application that runs a DHTML script that in turn runs the clock. The chief advantage to the latter technique is that the application's look and feel is defined in an ordinary HTML file. Anyone with access to the HTML file can customize the application's user interface using tools as unsophisticated as Notepad. Modifying the user interface of a compiled executable, by contrast, requires more elaborate measures.

Because DHTML is language-independent, DHTML scripts can be written in any scripting language for which a scripting engine is available. Most DHTML scripts are written in JavaScript, which is a dialect of the Java programming language, or Microsoft Visual Basic, Scripting Edition (VBScript), which comes from Visual Basic. The following HTML file is based on a sample provided on MSDN. It uses DHTML and embedded JavaScript to display a ticking digital clock:

<HTML>
<HEAD><TITLE>DHTML Clock Demo</TITLE></HEAD>
<BODY BGCOLOR="#FF0000">
<H1 STYLE="font-family:comic sans ms" ALIGN=center>DHTML Clock</H1>
<DIV ID=Clock ALIGN=center
STYLE="font-family:arial; font-size:64; color:#FFFFFF">
&nbsp;</DIV>

<SCRIPT LANGUAGE="JavaScript">
<!--
function tick() {
    var hours, minutes, seconds, ampm;
    var today = new Date();
    var h = today.getHours();
    var m = today.getMinutes();
    var s = today.getSeconds();

    if (h < 12) { 
        hours = h + ":";
        ampm = "A.M.";
    }
    else if (h == 12) {
        hours = "12:";
        ampm = "P.M.";
    }
    else {
        h = h - 12;
        hours = h + ":";
        ampm = "P.M.";
    }

    if (m < 10)
        minutes = "0" + m + ":";
    else
        minutes = m + ":";

    if (s < 10)
        seconds = "0" + s + " ";
    else
        seconds = s + " ";

    Clock.innerHTML = hours + minutes + seconds + ampm;
    window.setTimeout("tick();", 100);
}
window.onload = tick;
-->
</SCRIPT>
</BODY>
</HTML>

Figure 10-4 below shows a CHtmlView-based application named HtmlClock that uses this HTML script as the basis for a clock program. The HTML is stored in a file named Clock.htm. When HtmlClock is started, the view's OnInitialUpdate function passes the path to Clock.htm to the Navigate function. (Because of the way the path name is formulated, Clock.htm must be located in the same directory as HtmlClock.exe.) Under the hood, Navigate passes the path name to the WebBrowser control, and the WebBrowser control loads the file, parses the HTML, and executes the script.

Click to view at full size.

Figure 10-4. The HtmlClock window.

The source code for HtmlClock's view class appears in Figure 10-5. To create HtmlClock, I used AppWizard to create an SDI document/view program with a CHtmlView-based view. I modified the AppWizard-supplied OnInitialUpdate function to load Clock.htm, added an OnTitleChange function that displays the page title ("DHTML Clock Demo") in the frame window's title bar, and trimmed most of the AppWizard-generated entries from the application's menu.

HtmlClock merely scratches the surface of what you can do with HTML views. For example, you can run Java applets in HTML views, and you can write C++ code that interacts with DHTML objects. CHtmlView is also the perfect tool for building HTML-based help systems. If HTML remains the industry darling that it is today, CHtmlView can be the ticket that gets you into the ball.

Figure 10-5. The HtmlClock application.

HtmlClockView.h

// HtmlClockView.h : interface of the CHtmlClockView class
//
///////////////////////////////////////////////////////////////////////////

#if !defined(
//     AFX_HTMLCLOCKVIEW_H__D39825ED_99C0_11D2_8E53_006008A82731__INCLUDED_)
#define AFX_HTMLCLOCKVIEW_H__D39825ED_99C0_11D2_8E53_006008A82731__INCLUDED_
#if _MSC_VER > 1000

#pragma once
#endif // _MSC_VER > 1000


class CHtmlClockView : public CHtmlView
{
protected: // create from serialization only
    CHtmlClockView();
    DECLARE_DYNCREATE(CHtmlClockView)

// Attributes
public:
    CHtmlClockDoc* GetDocument();

// Operations
public:

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CHtmlClockView)
    public:
    virtual void OnDraw(CDC* pDC);  // overridden to draw this view
    virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
    virtual void OnTitleChange(LPCTSTR lpszText);
    protected:
    virtual void OnInitialUpdate(); // called first time after construct
    //}}AFX_VIRTUAL

// Implementation
public:
    virtual ~CHtmlClockView();
#ifdef _DEBUG
    virtual void AssertValid() const;
    virtual void Dump(CDumpContext& dc) const;
#endif

protected:

// Generated message map functions
protected:
    //{{AFX_MSG(CHtmlClockView)
        // NOTE - the ClassWizard will add and remove member functions here.
        //    DO NOT EDIT what you see in these blocks of generated code !
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};

#ifndef _DEBUG  // debug version in HtmlClockView.cpp
inline CHtmlClockDoc* CHtmlClockView::GetDocument()
   { return (CHtmlClockDoc*)m_pDocument; }
#endif

///////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately
// before the previous line.

#endif 
// !defined(
//     AFX_HTMLCLOCKVIEW_H__D39825ED_99C0_11D2_8E53_006008A82731__INCLUDED_)

HtmlClockView.cpp

// HtmlClockView.cpp : implementation of the CHtmlClockView class
//

#include "stdafx.h"
#include "HtmlClock.h"

#include "HtmlClockDoc.h"
#include "HtmlClockView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

///////////////////////////////////////////////////////////////////////////
// CHtmlClockView

IMPLEMENT_DYNCREATE(CHtmlClockView, CHtmlView)

BEGIN_MESSAGE_MAP(CHtmlClockView, CHtmlView)
    //{{AFX_MSG_MAP(CHtmlClockView)
        // NOTE - the ClassWizard will add and remove mapping macros here.
        //    DO NOT EDIT what you see in these blocks of generated code!
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

///////////////////////////////////////////////////////////////////////////
// CHtmlClockView construction/destruction

CHtmlClockView::CHtmlClockView()
{
}

CHtmlClockView::~CHtmlClockView()
{
}

BOOL CHtmlClockView::PreCreateWindow(CREATESTRUCT& cs)
{
    return CHtmlView::PreCreateWindow(cs);
}

///////////////////////////////////////////////////////////////////////////
// CHtmlClockView drawing

void CHtmlClockView::OnDraw(CDC* pDC)
{
    CHtmlClockDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
}

void CHtmlClockView::OnInitialUpdate()
{
    CHtmlView::OnInitialUpdate();

    TCHAR szPath[MAX_PATH];
    ::GetModuleFileName (NULL, szPath, sizeof (szPath) / sizeof (TCHAR));

    CString string = szPath;
    int nIndex = string.ReverseFind (_T (`\\'));
    ASSERT (nIndex != -1);
    string = string.Left (nIndex + 1) + _T ("Clock.htm");
    Navigate (string);
}

///////////////////////////////////////////////////////////////////////////
// CHtmlClockView diagnostics

#ifdef _DEBUG
void CHtmlClockView::AssertValid() const
{
    CHtmlView::AssertValid();
}

void CHtmlClockView::Dump(CDumpContext& dc) const
{
    CHtmlView::Dump(dc);
}

CHtmlClockDoc* CHtmlClockView::GetDocument() // non-debug version is inline
{
    ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CHtmlClockDoc)));
    return (CHtmlClockDoc*)m_pDocument;
}
#endif //_DEBUG

///////////////////////////////////////////////////////////////////////////
// CHtmlClockView message handlers

void CHtmlClockView::OnTitleChange(LPCTSTR lpszText) 
{
    CHtmlView::OnTitleChange(lpszText);
    AfxGetMainWnd ()->SetWindowText (lpszText);
}

The CHM file was converted to HTML by chm2web software.