最近在寫一個小軟體,想在MFC對話方塊中顯示協助文檔,都是html格式的,上網找了好多資料,用一個CHtmlCtrl類的東西,但是網上找的這個類放在VS2005下編譯不過去,老是提示錯誤。於是幾經修改,可以使用,下面貼代碼(環境:Win7 + VS2005 SP1):
標頭檔:
#pragma once<br />#include <afxhtml.h><br />#include <atlsafe.h><br />////////////////////////////////////////////////////////////////<br />// 該結構在命令映射中定義一個入口,這個映射將文本串映射到命令IDs,<br />// 如果命令映射中有一個映射到 ID_APP_ABOUT 的入口 “about”,並且<br />// HTML 有一個連結錨 ,那麼單擊該連結時將執行<br />struct HTMLCMDMAP<br />{<br />LPCTSTR name;<br />int nID;<br />};// command name used in "app:name" HREF in<br />//這個類將 CHtmlView 轉換為普通的能在對話方塊和架構中使用的控制<br />class CHtmlCtrl : public CHtmlView<br />{<br />DECLARE_DYNCREATE(CHtmlCtrl)<br />public:<br />CHtmlCtrl():m_bHideMenu(FALSE), m_cmdmap(NULL){}; // protected constructor used by dynamic creation<br />virtual ~CHtmlCtrl(){};<br />HTMLCMDMAP* m_cmdmap;// command map<br />BOOL m_bHideMenu;// hide context menu<br />// get/set HideContextMenu property<br />BOOL GetHideContextMenu(){ return m_bHideMenu; }<br />void SetHideContextMenu(BOOL val){ m_bHideMenu=val; }<br />// Set doc contents from string<br />HRESULT SetHTML(LPCTSTR strHTML);<br />// set command map<br />void SetCmdMap(HTMLCMDMAP* val){ m_cmdmap = val; }<br />// create control in same place as static control<br />BOOL CreateFromStatic(UINT nID, CWnd* pParent);<br />// create control from scratch<br />BOOL Create(const RECT& rc, CWnd* pParent, UINT nID,DWORD dwStyle = WS_CHILD|WS_VISIBLE,CCreateContext* pContext = NULL)<br />{<br />return CHtmlView::Create(NULL, NULL, dwStyle, rc, pParent,nID, pContext);<br />}<br />// 重寫該函數可以截獲子視窗訊息,從而禁用操作功能表。<br />virtual BOOL PreTranslateMessage(MSG* pMsg);<br />// 通常,CHtmlView 自己是在 PostNcDestroy 銷毀的,但對於一個介面控制來說<br />// 我們不想那樣做,因為控制一般都是作為另一個視窗對象的成員實現的。<br />virtual void PostNcDestroy(){}<br />// 重寫以便旁路掉對 MFC doc/view 架構的依賴,CHtmView 僅僅在這裡依附於架構。<br />afx_msg void OnDestroy();<br />afx_msg int OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest,UINT msg);<br />// 重寫以便截獲 "app:" 偽協議<br />virtual void OnBeforeNavigate2( LPCTSTR lpszURL,DWORD nFlags,LPCTSTR lpszTargetFrameName,CByteArray& baPostedData,LPCTSTR lpszHeaders,BOOL* pbCancel );</p><p>// 你可以重寫處理 "app:" 命令的代碼。注意只是在不使用命令映射機制時才需要重寫<br />virtual void OnAppCmd(LPCTSTR lpszCmd);<br />DECLARE_MESSAGE_MAP();<br />};
源檔案:
////////////////////////////////////////////////////////////////// CHtmlCtrl 實現 -- 控制項中的 網頁瀏覽器,只要改寫 CHtmlVie// 你就可以擺脫架構 - 從而將此控制用於對話方塊和其它任何視窗。//// 特性s:// - SetCmdMap 用於設定“app:command”連結的命令映射。.// - SetHTML 用於將字串轉換為 HTML 文檔。.#include "StdAfx.h"#include "HtmlCtrl.h"#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;#endif// macro to declare a typedef for ATL smart poitners; eg SPIHTMLDocument2#define DECLARE_SMARTPTR(ifacename) typedef CComQIPtr<ifacename> SP##ifacename;// smart pointer to IHTMLDocument2DECLARE_SMARTPTR(IHTMLDocument2)// useful macro for checking HRESULTs#define HRCHECK(x) hr = x; if (!SUCCEEDED(hr)) { /TRACE(_T("hr=%p/n"),hr);/return hr;/}IMPLEMENT_DYNAMIC(CHtmlCtrl, CHtmlView)BEGIN_MESSAGE_MAP(CHtmlCtrl, CHtmlView)ON_WM_DESTROY()ON_WM_MOUSEACTIVATE()END_MESSAGE_MAP()//////////////////// Create control in same position as an existing static control with given// the same ID (could be any kind of control, really)//BOOL CHtmlCtrl::CreateFromStatic(UINT nID, CWnd* pParent){CStatic wndStatic;if (!wndStatic.SubclassDlgItem(nID, pParent))return FALSE;// Get static control rect, convert to parent's client coords.CRect rc;wndStatic.GetWindowRect(&rc);pParent->ScreenToClient(&rc);wndStatic.DestroyWindow();return Create(rc, pParent, nID);}////////////////// Override to avoid CView stuff that assumes a frame.//void CHtmlCtrl::OnDestroy(){m_pBrowserApp = NULL; // will call ReleaseCWnd::OnDestroy(); // bypass CView doc/frame stuff}////////////////// Override to avoid CView stuff that assumes a frame.//int CHtmlCtrl::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT msg){// bypass CView doc/frame stuffreturn CWnd::OnMouseActivate(pDesktopWnd, nHitTest, msg);}// Return TRUE iff hwnd is internet explorer window.inline BOOL IsIEWindow(HWND hwnd){static LPCSTR IEWNDCLASSNAME = "Internet Explorer_Server";char classname[32]; // always char, never TCHARGetClassName(hwnd, (LPSTR)classname, sizeof(classname));return strcmp(classname, IEWNDCLASSNAME)==0;}//////////////////// Override to trap "Internet Explorer_Server" window context menu messages.//BOOL CHtmlCtrl::PreTranslateMessage(MSG* pMsg){if (m_bHideMenu) {switch (pMsg->message){case WM_CONTEXTMENU:case WM_RBUTTONUP:case WM_RBUTTONDOWN:case WM_RBUTTONDBLCLK:if (IsIEWindow(pMsg->hwnd)) {if (pMsg->message==WM_RBUTTONUP)// let parent handle context menuGetParent()->SendMessage(WM_CONTEXTMENU, pMsg->wParam, pMsg->lParam);return TRUE; // eat it}}}return CHtmlView::PreTranslateMessage(pMsg);}//////////////////// Override to pass "app:" links to virtual fn instead of browser.//void CHtmlCtrl::OnBeforeNavigate2( LPCTSTR lpszURL, DWORD nFlags, LPCTSTR lpszTargetFrameName, CByteArray& baPostedData, LPCTSTR lpszHeaders, BOOL* pbCancel ){LPCTSTR APP_PROTOCOL = _T("app:");int len = (int)_tcslen(APP_PROTOCOL);if (_tcsnicmp(lpszURL, APP_PROTOCOL, len)==0){OnAppCmd(lpszURL + len); // call virtual handler fn*pbCancel = TRUE; // cancel navigation}}//////////////////// Called when the browser attempts to navigate to "app:foo". Default handler// searches command map for "foo" command, and sends parent a WM_COMMAND// message with the ID if found. Call SetCmdMap to set the command map. Only// override OnAppCmd if you want to do something more complex.//void CHtmlCtrl::OnAppCmd(LPCTSTR lpszCmd){if (m_cmdmap) {for (int i=0; m_cmdmap[i].name; i++) {if (_tcsicmp(lpszCmd, m_cmdmap[i].name) == 0)// Use PostMessage to avoid problems with exit command. (Let// browser finish navigation before issuing command.)GetParent()->PostMessage(WM_COMMAND, m_cmdmap[i].nID);}}}//////////////////// Set document contents from string//HRESULT CHtmlCtrl::SetHTML(LPCTSTR strHTML){HRESULT hr;// Get document objectSPIHTMLDocument2 doc = GetHtmlDocument();// Create string as one-element BSTR safe array for IHTMLDocument2::write.CComSafeArray<VARIANT> sar;sar.Create(1,0);sar[0] = CComBSTR(strHTML);// open doc and writeLPDISPATCH lpdRet;HRCHECK(doc->open(CComBSTR("text/html"),CComVariant(CComBSTR("_self")),CComVariant(CComBSTR("")),CComVariant((bool)1),&lpdRet));HRCHECK(doc->write(sar)); // write contents to docHRCHECK(doc->close()); // closelpdRet->Release(); // release IDispatch returnedreturn S_OK;}
使用方法,在自己的對話方塊上拖進來一個static,然後修改一下ID為IDC_STATIC_HELP,然後在對話方塊的初始化函數,也就是OnInitDialog()中添加下面代碼,注意下面的m_html是一個類成員變數,定義在標頭檔中
,CHtmlCtrl m_html;
:
BOOL CHelpDialog::OnInitDialog(){ CDialog::OnInitDialog(); m_html.SetHideContextMenu(TRUE); m_html.CreateFromStatic(IDC_STATIC_HELP,this); m_html.Navigate(m_strUrl); //CString m_Text = " 李民錄 "; //m_html.SetHTML(m_strUrl); return TRUE; // 除非將焦點設定到控制項,否則返回TRUE}
至此,調用你的對話方塊,就可以看到效果了。
如果你想直接顯示html文本的話,可以這樣寫:
m_html.SetHideContextMenu(TRUE);m_html.CreateFromStatic(IDC_STATIC_HELP,this);m_html.Navigate("about:blank"); //這句話一定要寫CString m_Text = 李民錄liminlu0314@163.com> ";m_html.SetHTML(m_Text );"
運行
HTML文檔