The Arbel Network
desktop html-based client using mfc 7
part 1 - the dialog box
(sample will be downloadable soon)

So, why would you need a desktop HTML-based client? You already have the same functionality on your web site! Creating such a client for your customers has many advantages:

  • They don't have to open a browser window every time.
  • There's no need to keep hitting the old refresh button to see if something new is out.
  • You get to know your customers' needs better by allowing them to define profiles.
The most common usage scenarios are news updates and search engines.

In this part of the article, I'll show you how to create an HTML-based dialog box very quickly using MFC, handle its DHTML events, and how to create a tray icon and attach a popup menu to it.

Creating the HTML Dialog

MFC 7.0 ships with Visual Studio .NET  and brings a lot of new functions and easier ways to do familiar tasks. In this article, I'm going to focus now on the CDHtmlDialog class, which allows you to create dialog boxes that use HTML rather than dialog resources to implement their user interface. CDHtmlDialog can also tap into HTML controls to exchange data and handle events. Another class available is the CMultiPageDHtmlDialog which gives the same functionality, and enables you to use multiple HTML pages sequentially. This is especially useful for creating wizard-based dialogs.

Visual Studio's wizard would do most of the dialog creation for you:

  1. From Visual Studio .NET's menu select File, New Project.
  2. Select Visual C++ Projects and MFC Application. Type in a name for the project (we'll use HTMLClient) and click OK.
  3. On Application Type choose Dialog Based and check the Use HTML dialog option.
  4. Explore other features you may want to set on the User Interface Features and Advanced Features.
  5. If you're targeting this application for Windows XP, be sure to check the Common Control Manifest, which will instruct the application to link to the Comctl32.dll version 6, and make it Windows XP theme-enabled. This setting has no effect on older operating systems. For more information see Using Windows XP Visual Styles  on MSDN.
  6. Click Finish to create the project.
The wizard has created an HTML dialog box with 2 buttons: OK and Cancel. You may start editing the HTML file in the Resource Editor. If you look at the HTMLClientDlg.cpp file, you will see the following code:

BEGIN_DHTML_EVENT_MAP(CHTMLClientDlg)
	DHTML_EVENT_ONCLICK(_T("btnOK"), OnButtonOK)
	DHTML_EVENT_ONCLICK(_T("btnCancel"), OnButtonCancel)
END_DHTML_EVENT_MAP()

It attaches the DHTML click event for the 2 buttons to 2 functions in your application. Note that it uses the HTML id property to recognize the buttons, so be careful when you edit the HTML file. The DHTML_EVENT_ONCLICK is an MFC macro. There are macros for many DHTML events; check them out at the MFC documentation.

You might need to gain access to the WebBrowser Control. This is done using the m_pBrowserApp pointer. I added the following code in the OnInitDialog function, to modify the behavior of the control:

m_pBrowserApp->put_RegisterAsBrowser(false);
m_pBrowserApp->put_RegisterAsDropTarget(false);

The first setting prevents link requests from other applications being opened by your application. The second one prevents dragging a URL shortcut into your application window.

Adding a Tray Icon

A tray icon is a crucial tool in applications such as this. Users want it to be available all the time, but not always as an active window. We'll need to create two functions - one for creating the icon and one for removing it when the application exits. Windows 2000 (and higher) offer more options with version 5 of shell32.dll, such as using a balloon ToolTip instead of the standard one. To enable this, you must set value of the _WIN32_IE constant to 0x0500 (this is in the stdafx.h file). In this example, I'll rely on version 4 only.

First, import an icon (16x16 pixels) into your resource file, and set its ID to IDR_TRAY. In the dialog's header file add the following constant (this is our custom message):

#define WM_TASKBAR WM_APP + 450

Then, add the following functions to the dialog's file:

void CHTMLClientDlg::CreateNotifyIcon() 
{
	HICON hIcon = AfxGetApp()->LoadIcon(IDR_TRAY);

	NOTIFYICONDATA tnid;
	tnid.cbSize = sizeof(NOTIFYICONDATA);
	tnid.hWnd = m_hWnd;
	tnid.uID = IDR_TRAY;
	tnid.dwInfoFlags = NIIF_NONE;
	_tcscpy(tnid.szTip, _T("My HTML Client"));
	tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
	tnid.uCallbackMessage = WM_TASKBAR;
	tnid.hIcon = hIcon;

	Shell_NotifyIcon(NIM_ADD, &tnid);

	if(hIcon) 
		DestroyIcon(hIcon);
}


void CHTMLClientDlg::RemoveNotifyIcon()
{
	NOTIFYICONDATA tnid;
	tnid.cbSize = sizeof(NOTIFYICONDATA);
	tnid.hWnd = m_hWnd;
	tnid.uID = IDR_TRAY;

	Shell_NotifyIcon(NIM_DELETE, &tnid);
}

Call these functions from CHTMLClientDlg::OnInitDialog() and CHTMLClientDlg::OnDestroy() respectively.

Last thing you may want to do is add a popup menu for the icon. Add the following line to the BEGIN_MESSAGE_MAP section (note that it might appear more than once, for example, if you chose to add an About Box. You need the main dialog's message map).

ON_MESSAGE(WM_TASKBAR, OnTaskbar)

Add an CMenu variable to the class and name it g_hMenu. Also add a menu resource and set its ID to IDR_PUMENU. Put two menu items in it (one for hiding the window and one for closing; name them ID_TRAY_SHOW and ID_TRAY_CLOSE). Next, create the OnTaskbar function to handle the message, and the OnCommand function to handle the popup menu:

LRESULT	CHTMLClientDlg::OnTaskbar(WPARAM wParam, LPARAM lParam)
{
	UINT uMouseMsg = (UINT) lParam;

	switch(uMouseMsg)
	{
		case WM_LBUTTONDOWN:
			if(IsWindowVisible())
				ShowWindow(SW_HIDE);
			else
				ShowWindow(SW_SHOWNORMAL);
			break;
		case WM_RBUTTONDOWN:
			POINT p;
			GetCursorPos(&p);
			SetForegroundWindow();
			if(g_hMenu)
				DestroyMenu(g_hMenu);
			CMenu menu;
			VERIFY(menu.LoadMenu(IDR_PUMENU));
			if(IsWindowVisible())
				menu.CheckMenuItem(ID_TRAY_SHOW, MF_CHECKED | MF_BYCOMMAND);
			else
				menu.CheckMenuItem(ID_TRAY_SHOW, MF_UNCHECKED | MF_BYCOMMAND);
			g_hMenu = GetSubMenu(menu, 0);
			TrackPopupMenu(g_hMenu, TPM_LEFTBUTTON, p.x, p.y, 0, m_hWnd, NULL);
			break;
	}

	return 0;
}

BOOL CHTMLClientDlg::OnCommand(WPARAM wParam, LPARAM lParam)
{
	switch(wParam)
	{
		case ID_TRAY_SHOW:
			if(IsWindowVisible())
				ShowWindow(SW_HIDE);
			else
				ShowWindow(SW_SHOWNORMAL);
			break;
		case ID_TRAY_CLOSE:
			OnOK();
			break;
		default:
			break;
	}

	return 0;
}

If you want the window to hide and not close when you click the "X" button, modify the CHTMLClientDlg::OnSysCommand function as follows:

if(nID == SC_CLOSE)
{
	ShowWindow(SW_HIDE);
}
else
{
	CDHtmlDialog::OnSysCommand(nID, lParam);
}

And you're done!

The next part will deal with data synchronization using XML Web Services, and I'll also show you how to easily create an automatic update for the application.

ælij arbel

aelij arbel

eli arbel

אלי ארבל