在Visual Studio 6.0中出現(xiàn)了一個新類CHtmlView,利用這個類,我們可以實現(xiàn)在對話框的控制中顯示HTML文件。 要想使用CHtmlView類,對它的定義和實現(xiàn)就必須有全面深入的理解。我們不妨拿CHtmlView和CListView做一個比較,通過比較這兩個類,我們會發(fā)現(xiàn)一些有趣的差別。首先,MFC中CListView有一個對應(yīng)的CListCtrl類,而CHtmlView卻沒有一個CHtmlCtrl類與之對應(yīng);其次,CListView的使用依賴于MFC的文檔/視結(jié)構(gòu),而CHtmlView的實現(xiàn)是基于COM的。通過IWebBrowser2接口來實現(xiàn),而且IWebBrowser2與MFC文檔/視圖結(jié)構(gòu)之間沒有任何關(guān)系。
為了實現(xiàn)在對話框的控制中顯示HTML文件,我們也可以為CHtmlView創(chuàng)建一個對應(yīng)的類CHtmlCtrl。
以下是類CHtmlCtrl程序源代碼:
創(chuàng)建一個靜態(tài)控制(也可以是其他控制),這個控制的ID及大小位置與界面上的控制相同。
BOOL CHtmlCtrl::CreateFromStatic(UINT nID, CWnd* pParent)
{
CStatic wndStatic;
if (!wndStatic.SubclassDlgItem(nID, pParent))
return FALSE;
// 獲取靜態(tài)控制的矩形區(qū)域并轉(zhuǎn)換為父窗口的客戶區(qū)坐標(biāo)
CRect rc;
wndStatic.GetWindowRect(&rc);
pParent->ScreenToClient(&rc);
wndStatic.DestroyWindow();
// 創(chuàng)建 HTML 控制 (CHtmlView)
return
Create(NULL, // 類名
NULL, // 標(biāo)題
(WS_CHILD | WS_VISIBLE ), // 風(fēng)格
rc, // 矩形區(qū)域
pParent, //父窗口
nID, // 控制 ID
NULL); //框架/文檔
}
為了避免主控程序?qū)HtmlView對象看作是文檔/視圖框架,需要重載,CView::OnMouseActivate和CView::OnDestroy。此外,當(dāng)用戶在控制中單擊時,OnMouseActivate要負責(zé)響應(yīng)(WM_MOUSEACTIVATE)。
int CHtmlCtrl::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT msg)
{
//旁路 CView 文檔/框架
return CWnd::OnMouseActivate(pDesktopWnd, nHitTest, msg);
}
void CHtmlCtrl::OnDestroy()
{
if (m_pBrowserApp)
{
m_pBrowserApp->Release();
m_pBrowserApp = NULL;
}
CWnd::OnDestroy(); // 旁路 CView 文檔/框架
}
通常,CHtmlView是在virtual void PostNcDestroy()中釋放空間,但對話框中的控制常常是作為堆棧對象實現(xiàn)的,所以,在PostNcDestroy()中不必在做什么。
virtual void PostNcDestroy() { }
為了實現(xiàn)“app:” 假協(xié)議,重載導(dǎo)航處理器OnBeforeNavigate2()。傳遞“app:”鏈接到一個虛擬協(xié)議處理器。因為app:是假協(xié)議,所以在瀏覽起重要取消掉這個導(dǎo)航。
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)
{
OnAppCmd(lpszURL + len);
*pbCancel = TRUE;
}
}
重載OnAppCmd(),處理app:命令,當(dāng)瀏覽器準備導(dǎo)航到“app:foo”時,這個函數(shù)被調(diào)用,參數(shù)lpszWhere的值為“foo”。
void CHtmlCtrl::OnAppCmd(LPCTSTR lpszWhere){ // default: do nothing}
重載OnMouseActivate, OnDestroy, 和 PostNcDestroy以后,CHtrmlCtrl在對話框中就可以象個控制一樣工作。詳細的使用方法請參見例子程序:AboutHtml。
運行AboutHtml.exe,并打開About對話框……音樂多么美妙!更有趣的是程序所用到的HTML源文件、圖像、聲音等文件都作為資源存儲在EXE文件中:
// in AboutHtml.rc
ABOUT.HTM HTML DISCARDABLE "res\\about.htm"
PD.JPG HTML DISCARDABLE "res\\pd.jpg"
OKUP.GIF HTML DISCARDABLE "res\\okup.gif"
OKDN.GIF HTML DISCARDABLE "res\\okdn.gif"
MOZART.WAV HTML DISCARDABLE "res\\mozart.wav"
注意:用文件的實際名字作為資源名很重要,以便瀏覽器能夠找到他們。在一個普通的Web頁面中,我們使用圖像是用下列語法:
此代碼假設(shè)圖像文件"pd.jpg"存在當(dāng)前目錄(頁面文件所在目錄)中。
如果圖像文件是作為資源存在EXE文件中,我們?nèi)绾我媚??方法一樣,此時,我們必須告訴瀏覽器Web頁面文件的位置。為此要在Web頁面文件的開頭加上如下代碼:
這一行代碼告訴瀏覽器當(dāng)前目錄是“res://AboutHtml.exe”,當(dāng)瀏覽器遇到代碼時,它會按照路徑res://AboutHtml.exe/pd.jpg查找。否則,它會在程序文件的路徑查找。
通常用res://modulename可以訪問動態(tài)庫或可執(zhí)行文件中的資源。這里res:的意思與http:,ftp:,file:,及mailto的意思相同。即:“在這個路徑中的第一個名字是一個文件名,第二個名字是文件中的資源名”。其余的工作由瀏覽器完成。
為了實現(xiàn)About對話框,先建一個對話框類:CAboutDialog,其中聲明一個CHtmlCtrl對象:m_page。CAboutDialog本身的初始化代碼如下:
BOOL CAboutDialog::OnInitDialog()
{
VERIFY(CDialog::OnInitDialog());
VERIFY(m_page.CreateFromStatic(IDC_HTMLVIEW, this));
m_page.LoadFromResource(_T("about.htm"));
return TRUE;
}
CHtmlCtrl::CreateFromStatic是個很簡單的函數(shù),它用于簡化對話框的設(shè)計。因為用插入COM對象的方法太麻煩,所以我在對話框中插入了一個靜態(tài)控件,改變它的缺省ID號。然后調(diào)用CreateFromStatic,以完全相同的ID號、大小、位置創(chuàng)建一個靜態(tài)CStatic對象。然后在調(diào)用DestroyWindow,這個方法很有效。為了加載web頁面,調(diào)用CHtmlCtrl::LoadFromResource函數(shù),它是由CHtmlView繼承而來的。也可以用全路徑res://AboutHtml.exe/about.htm作為參數(shù)。
現(xiàn)在您已經(jīng)知道了CHtmlCtrl是如何在對話框中繞過CView來替代框架;知道了如何創(chuàng)建HTML文件,其中包含文字、圖像和聲音,并把它作為資源在程序中使用。
除此之外,還有一個問題就是:CAboutDialog對話框中“OK”按鈕的處理,其實,它根本就不是一個按鈕,而是一個在HTML文件中嵌入的圖像,用JScript來控制圖像被按下時和彈起時的狀態(tài)。處理“OK”按鈕的技巧主要是解決對話框與主控程序之間的通訊。
利用動態(tài)HTML文檔層(COM)技術(shù)可以處理用戶單擊圖像或鏈接,方法是獲得圖像元素,然后偵聽OnClick事件。但這是一種非常非常麻煩的方法。有沒有更簡單的方法呢……對于編程者來說,懶惰是一種美德。
假設(shè)HTML有如下的圖像鏈接:
當(dāng)用戶單擊它時,瀏覽器顯示這個“OK”文件,但是在顯示之前,控制先執(zhí)行CHtmlCtrl::OnBeforeNavigate2。CHtmlCtrl能夠在這個函數(shù)中做想做的任何事情。
void CMyHtmlCtrl::OnBeforeNavigate2(
LPCTSTR lpszURL,
...,
BOOL* pbCancel)
為了實現(xiàn)在對話框的控制中顯示HTML文件,我們也可以為CHtmlView創(chuàng)建一個對應(yīng)的類CHtmlCtrl。
以下是類CHtmlCtrl程序源代碼:
創(chuàng)建一個靜態(tài)控制(也可以是其他控制),這個控制的ID及大小位置與界面上的控制相同。
BOOL CHtmlCtrl::CreateFromStatic(UINT nID, CWnd* pParent)
{
CStatic wndStatic;
if (!wndStatic.SubclassDlgItem(nID, pParent))
return FALSE;
// 獲取靜態(tài)控制的矩形區(qū)域并轉(zhuǎn)換為父窗口的客戶區(qū)坐標(biāo)
CRect rc;
wndStatic.GetWindowRect(&rc);
pParent->ScreenToClient(&rc);
wndStatic.DestroyWindow();
// 創(chuàng)建 HTML 控制 (CHtmlView)
return
Create(NULL, // 類名
NULL, // 標(biāo)題
(WS_CHILD | WS_VISIBLE ), // 風(fēng)格
rc, // 矩形區(qū)域
pParent, //父窗口
nID, // 控制 ID
NULL); //框架/文檔
}
為了避免主控程序?qū)HtmlView對象看作是文檔/視圖框架,需要重載,CView::OnMouseActivate和CView::OnDestroy。此外,當(dāng)用戶在控制中單擊時,OnMouseActivate要負責(zé)響應(yīng)(WM_MOUSEACTIVATE)。
int CHtmlCtrl::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT msg)
{
//旁路 CView 文檔/框架
return CWnd::OnMouseActivate(pDesktopWnd, nHitTest, msg);
}
void CHtmlCtrl::OnDestroy()
{
if (m_pBrowserApp)
{
m_pBrowserApp->Release();
m_pBrowserApp = NULL;
}
CWnd::OnDestroy(); // 旁路 CView 文檔/框架
}
通常,CHtmlView是在virtual void PostNcDestroy()中釋放空間,但對話框中的控制常常是作為堆棧對象實現(xiàn)的,所以,在PostNcDestroy()中不必在做什么。
virtual void PostNcDestroy() { }
為了實現(xiàn)“app:” 假協(xié)議,重載導(dǎo)航處理器OnBeforeNavigate2()。傳遞“app:”鏈接到一個虛擬協(xié)議處理器。因為app:是假協(xié)議,所以在瀏覽起重要取消掉這個導(dǎo)航。
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)
{
OnAppCmd(lpszURL + len);
*pbCancel = TRUE;
}
}
重載OnAppCmd(),處理app:命令,當(dāng)瀏覽器準備導(dǎo)航到“app:foo”時,這個函數(shù)被調(diào)用,參數(shù)lpszWhere的值為“foo”。
void CHtmlCtrl::OnAppCmd(LPCTSTR lpszWhere){ // default: do nothing}
重載OnMouseActivate, OnDestroy, 和 PostNcDestroy以后,CHtrmlCtrl在對話框中就可以象個控制一樣工作。詳細的使用方法請參見例子程序:AboutHtml。
運行AboutHtml.exe,并打開About對話框……音樂多么美妙!更有趣的是程序所用到的HTML源文件、圖像、聲音等文件都作為資源存儲在EXE文件中:
// in AboutHtml.rc
ABOUT.HTM HTML DISCARDABLE "res\\about.htm"
PD.JPG HTML DISCARDABLE "res\\pd.jpg"
OKUP.GIF HTML DISCARDABLE "res\\okup.gif"
OKDN.GIF HTML DISCARDABLE "res\\okdn.gif"
MOZART.WAV HTML DISCARDABLE "res\\mozart.wav"
注意:用文件的實際名字作為資源名很重要,以便瀏覽器能夠找到他們。在一個普通的Web頁面中,我們使用圖像是用下列語法:
此代碼假設(shè)圖像文件"pd.jpg"存在當(dāng)前目錄(頁面文件所在目錄)中。
如果圖像文件是作為資源存在EXE文件中,我們?nèi)绾我媚??方法一樣,此時,我們必須告訴瀏覽器Web頁面文件的位置。為此要在Web頁面文件的開頭加上如下代碼:
這一行代碼告訴瀏覽器當(dāng)前目錄是“res://AboutHtml.exe”,當(dāng)瀏覽器遇到代碼時,它會按照路徑res://AboutHtml.exe/pd.jpg查找。否則,它會在程序文件的路徑查找。
通常用res://modulename可以訪問動態(tài)庫或可執(zhí)行文件中的資源。這里res:的意思與http:,ftp:,file:,及mailto的意思相同。即:“在這個路徑中的第一個名字是一個文件名,第二個名字是文件中的資源名”。其余的工作由瀏覽器完成。
為了實現(xiàn)About對話框,先建一個對話框類:CAboutDialog,其中聲明一個CHtmlCtrl對象:m_page。CAboutDialog本身的初始化代碼如下:
BOOL CAboutDialog::OnInitDialog()
{
VERIFY(CDialog::OnInitDialog());
VERIFY(m_page.CreateFromStatic(IDC_HTMLVIEW, this));
m_page.LoadFromResource(_T("about.htm"));
return TRUE;
}
CHtmlCtrl::CreateFromStatic是個很簡單的函數(shù),它用于簡化對話框的設(shè)計。因為用插入COM對象的方法太麻煩,所以我在對話框中插入了一個靜態(tài)控件,改變它的缺省ID號。然后調(diào)用CreateFromStatic,以完全相同的ID號、大小、位置創(chuàng)建一個靜態(tài)CStatic對象。然后在調(diào)用DestroyWindow,這個方法很有效。為了加載web頁面,調(diào)用CHtmlCtrl::LoadFromResource函數(shù),它是由CHtmlView繼承而來的。也可以用全路徑res://AboutHtml.exe/about.htm作為參數(shù)。
現(xiàn)在您已經(jīng)知道了CHtmlCtrl是如何在對話框中繞過CView來替代框架;知道了如何創(chuàng)建HTML文件,其中包含文字、圖像和聲音,并把它作為資源在程序中使用。
除此之外,還有一個問題就是:CAboutDialog對話框中“OK”按鈕的處理,其實,它根本就不是一個按鈕,而是一個在HTML文件中嵌入的圖像,用JScript來控制圖像被按下時和彈起時的狀態(tài)。處理“OK”按鈕的技巧主要是解決對話框與主控程序之間的通訊。
利用動態(tài)HTML文檔層(COM)技術(shù)可以處理用戶單擊圖像或鏈接,方法是獲得圖像元素,然后偵聽OnClick事件。但這是一種非常非常麻煩的方法。有沒有更簡單的方法呢……對于編程者來說,懶惰是一種美德。
假設(shè)HTML有如下的圖像鏈接:
當(dāng)用戶單擊它時,瀏覽器顯示這個“OK”文件,但是在顯示之前,控制先執(zhí)行CHtmlCtrl::OnBeforeNavigate2。CHtmlCtrl能夠在這個函數(shù)中做想做的任何事情。
void CMyHtmlCtrl::OnBeforeNavigate2(
LPCTSTR lpszURL,
...,
BOOL* pbCancel)