CHtmlView在對話框控制中顯示HTML文件

字號:

在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)