用MFC實現WebGUI--(CDHtmlDialog)


自從去年年底一次棘手的界面,開始研究用web做界面到現在大約1年,這一年間不是局限在實現層面,也並非一直研究這一個問題,有很多問題其實不是問題,只是自己沒有想清楚或者思想沒放開。對於一個界面開發人員,想必拉的對話框不少於100個,膩味不必說,光是對話框大小改變導致控件跟着變化都需要一番功夫,加上界面美觀,界面的風格統一,界面的靈活多變......,頭痛。在對話框里面加載位圖,加載gif,超鏈接......,啊,沒法控制了吧!在考慮遠點,現在.net3.0技術已經完全打破應用和桌面的界限,我們的界面html資源完全可以放在一個web站點上,這樣界面是完全動態的。

其間寫過2篇這方面的文章,基於vc6實現,繞彎很大。在vc7.1、vc8里面要簡單很多,主要是把幾個以前為公開的類公開了,最重要的是在CWnd里面加入了一個虛函數CreateControlSite使得有機會改變控件站點以修改控件行為。在mfc類層次上,CHTMLView和CDHtmlDialog為開發者提供了創建webgui的一系列基礎設施,包括事件機制、窗口行為、以及對html文檔操縱接口。我們在此基礎上實現webgui很簡單,然而仍然困惑我很久,經理也催過我幾次我一直未肯決定最終方案。在我腦袋里一直琢磨是要應用程序完全操縱html文檔,還是html訪問應用獲取信息,其實就是它們之間的通信模式。一直到昨天我才定下方案,應用通過IWebBrowser2接口操縱html元素,html通過vbscript、javascript腳本響應本身事件,訪問應用。主要是考慮通信自然暢通,而以前我一味想通過應用指令完全控制html元素,導致去解析html文檔,費力不討好。下面開始我的想法:

寫一個dll,封裝CDHtmlDialog,提供一個類似html容器的對話框,功能就是加載html網頁,以及創建與html呼應的com組件。它本身不包含與應用功能有關代碼,應用有關的部分是html頁面和對於的com功能組件。這里需要對CDHtmlDialog進行了適當的改造以適合自己的目標:

首先從CDHtmlDialog派生一個類CHTMLContainerDlg,默認情況下會生成一個網頁資源,這個網頁是這個對話框創建時加載的,我們需要的其實是一個容器而不是一個具體的對話框,所以刪除網頁資源,修改對話框頭文件:

enum { IDD = IDD_HTMLCONTAINERDLG, IDH = 0 };

這里把IDH修改為0,因為我們刪除了網頁資源。然而在對話框創建后會加載該資源,在CDHtmlDialog的OnInitDialog函數里面我們可以看到:

if (m_nHtmlResID)
        LoadFromResource(m_nHtmlResID);
    else if (m_szHtmlResID)
        LoadFromResource(m_szHtmlResID);
    else if (m_strCurrentUrl)
        Navigate(m_strCurrentUrl);

結果就是對話框一出現就會出現加載一個無效地址的頁面,出現無法打開鏈接的頁面,為了避免這個問題,需要重載OnInitDialog函數。其實就是拷貝mfc代碼然后去掉上面那段代碼就ok,強制不加載頁面。那么為了加載指定頁面,需要一個函數:

void CHTMLContainerDlg::SetHtmlAndCom(CString strURL, CString strProg)
{
    HRESULT        hr        = NOERROR;
    m_strURL = strURL;
    hr = m_spComDisp.CoCreateInstance(strProg);
    if(FAILED(hr))
    {
        TRACE(_T("Some error when create com object\n"));
    }
    SetExternalDispatch(m_spComDisp);
}

指定html的url和對應功能組件的progid,這樣在網頁里面可以通過腳本window.external訪問該com組件。
這樣就可以加載html網頁,但是html頁面里面的元素風格卻是2k風格(至少在ie7以下版本是如此),這個怕是沒起到一點美觀作用,為之我考慮了半天,問過做web的人是否有辦法,最終還是靈感光臨,誤撞上了。重載GetHostInfo函數:

STDMETHODIMP CHTMLContainerDlg::GetHostInfo(DOCHOSTUIINFO* pInfo)
{
    pInfo->dwFlags = DOCHOSTUIFLAG_THEME;
    return S_OK;
}

這個多得不說,^_^。
下面就可以演示了,在vs2005里面找個向導來show一下:

CHTMLContainerDlg    dlg;
    TCHAR                szPath[MAX_PATH] 
=   0 } ;
    CString                strPath;
    GetCurrentDirectory(MAX_PATH, szPath);
    strPath 
=  szPath;
    strPath 
+=  _T( " \\Default.htm " );
    dlg.SetHtmlAndCom(strPath, _T(
" TestWebCom.WebComCtrl " ));
    dlg.DoModal();



對話框標題其實可以通過解析html文檔獲取title標題設置,目前還未處理。下面看看html與應用交互的組件。
生成一個atl工程,TestWebCom,添加一個com組件WebComCtrl,添加方法處理上面那個帶...的按鈕(文件夾瀏覽按鈕):

STDMETHODIMP CWebComCtrl::ShowFolderBrowser( void )
{
    AFX_MANAGE_STATE(AfxGetStaticModuleState());

    
// TODO: 在此添加實現代碼
    AfxMessageBox(_T("In Com, you can show folder select dialog"));
    
return S_OK;
}


這里不作具體處理,只是象征性彈出一個對話框。好了,上面我們在對話框里面已經設置了com組件的progid,這里可以把html和組件關聯上了,通過腳本可以訪問com組件方法:

< BUTTON  CLASS ="buttonClass3Custom"  ID ="BrowseBtn"  TYPE ="BUTTON"  TITLE ="瀏覽頭文件。"  onClick ="OnBrowseHeaderFile();" > </ BUTTON >

腳本如下:

function  OnBrowseHeaderFile()
{
    window.external.ShowFolderBrowser();
}

下面運行試一試,按下選擇文件夾按鈕會出現如下詢問組件是否安全的對話框:

這個很惱人,用戶可沒有耐心忍受每次多彈出這個對話框詢問組件是否安全。我開始打算將組件實現安全接口解決掉此問題,不過不知道緣何,沒有成功,網上搜索一下好像說在ie7里面無效,沒辦法還是看mfc源碼來解決問題。
CDHtmlDialog類獲取external代碼如下:

STDMETHODIMP CDHtmlDialog::GetExternal(IDispatch  ** ppDispatch)
{
    
if(ppDispatch == NULL)
        
return E_POINTER;
        
    
*ppDispatch = NULL;
    
if (m_spExternalDisp.p && CanAccessExternal())
    
{
        m_spExternalDisp.p
->AddRef();
        
*ppDispatch = m_spExternalDisp.p;
        
return S_OK;
    }

    
return E_NOTIMPL;
}

看到CanAccessExternal函數,肯定就是驗證安全性的代碼,找到函數聲明,幸好是虛函數,重載直接返回TRUE:

BOOL CHTMLContainerDlg::CanAccessExternal()
{
    
// we trust all com object (haha, you can make virus)
    return TRUE;
}

有興趣的朋友可以看下內部實現。
這下就好了,按下網頁選擇文件夾按鈕,彈出對話框:

一套流程完備,方案個人覺得不錯,各司其職,通信自然暢通,一個html配對一個com功能組件,功能組件化不僅使代碼封裝性好,而且可以用於多種語言。

由於此技術不用於公司開發,今整理提供下載

 

from:http://www.cppblog.com/wlwlxj/archive/2006/12/15/16495.aspx


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM