鍵字:HTML Element, Sink
1、概述
實(shí)現(xiàn)了對(duì)Webbrowser的resuing之后我們便會(huì)發(fā)現(xiàn)有時(shí)候我們還需要處理瀏覽器中的元素(HTML Element)。這種處理包括主動(dòng)和被動(dòng)兩個(gè)方面,像《FAQ:如何訪問WebBrowser的滾動(dòng)條》、《FAQ:操縱下拉列表》、《FAQ:兩種方法訪問多層嵌套的frame》等
文章所演示的就是主動(dòng)的處理。通常我們從Webbrowser獲得一個(gè)Web文檔接口(IHTMLDocumentx),從它出發(fā)便可訪問到瀏覽器所包含
的一切HTML元素。而被動(dòng)的處理則是在COM技術(shù)中稱為Sink的技術(shù),我更喜歡的說法是事件通知。當(dāng)文檔的下載進(jìn)度發(fā)生變化時(shí),我們可以獲得
ProgressChange通知,當(dāng)Webbrowser下載完HTML文檔時(shí),我們可以獲得DocumentComplete的通知,而當(dāng)鏈接被點(diǎn)
擊,或圖片被拖動(dòng)時(shí),我們?nèi)绾潍@得通知呢?本文希望能夠給出部分的答案。
2、HtmlObj Template
如何Sink一個(gè)HTML Element并不是本文的重點(diǎn),其理論我不是太了解,也懶得去搞透徹,所以使用現(xiàn)成的庫來實(shí)現(xiàn)。CodeProject上的一篇文章《CHtmlObj Template》給出的一個(gè)模板類CHtmlObj就非常好用。下面的例子是針對(duì)Html Anchor Element的一個(gè)實(shí)例化。
#include "HtmlObj.h"
class CHtmlAnchorElement : public CHtmlObj<IHTMLAnchorElement,
&DIID_HTMLAnchorEvents>
{
public:
CHtmlAnchorElement(CHtmlDocument2* pParentDoc2);
virtual ~CHtmlAnchorElement();
virtual HRESULT OnInvoke(DISPID dispidMember,REFIID riid,
LCID lcid, WORD wFlags,
DISPPARAMS * pdispparams, VARIANT * pvarResult,
EXCEPINFO * pexcepinfo,
UINT * puArgErr);
};
HRESULT CHtmlAnchorElement::OnInvoke(DISPID dispidMember,
REFIID riid, LCID lcid, WORD wFlags,
DISPPARAMS * pdispparams, VARIANT * pvarResult,
EXCEPINFO * pexcepinfo,
UINT * puArgErr)
{
HRESULT hr = E_NOTIMPL;
switch(dispidMember)
{
case DISPID_HTMLELEMENTEVENTS_ONMOUSEOVER :
{//當(dāng)鼠標(biāo)經(jīng)過鏈接時(shí),我們?cè)谶@里獲得通知
hr = S_OK;
// TODO: add code to handle on mouse over events
break;
}
case DISPID_HTMLELEMENTEVENTS_ONMOUSEOUT :
{//當(dāng)鼠標(biāo)從鏈接上移開時(shí),我們?cè)谶@里獲得通知,其它的Dispatch ID可根據(jù)需要添加
hr = S_OK;
// TODO: add code to handle on mouse out events
break;
}
default:
{
break;
}
}
return hr;
}
當(dāng)
我們得到某個(gè)鏈接的HTML接口指針,便可調(diào)用CHtmlAnchorElement繼承自CHtmlObj的SetSite(IUnknown
*pUnkSite)成員函數(shù)傳入該接口指針。在CHtmlObj類內(nèi)部用一個(gè)智能指針m_spHtmlObj來保存相應(yīng)的HTML
Element接口指針,所以當(dāng)上面的ONMOUSEHOVER和ONMOUSEOUT兩個(gè)事件通知到達(dá)時(shí),從m_spHtmlObj就可以訪問
IHTMLAnchorElement的所有成員,如從href獲得鏈接的Url等,此處不再贅述。
3、CHtmlElements類
有
了CHtmlObj之后我們又會(huì)發(fā)現(xiàn)實(shí)踐中常常會(huì)需要多個(gè)相同類型的CHtmlObj。比如包含F(xiàn)rame的網(wǎng)頁中每個(gè)Frame的HTML
Document都需要一個(gè)CHtmlObj來Sink其事件。所以我們還需要有效地管理這些相同類型的CHtmlObj。下面是我寫的一個(gè)簡單的模板類
CHtmlElements,它通過CMap來管理多個(gè)CHtmlObj對(duì)象。
template<class THtmlElement> class CHtmlElements
{
typedef CMap<LPDISPATCH, LPDISPATCH, THtmlElement*, THtmlElement*> CMapDispToHtmlElement;
CMapDispToHtmlElement m_htmlElements;
BOOL IsSiteConnected( LPDISPATCH pDisp )
{
THtmlElement *pElement;
return m_htmlElements.Lookup( pDisp, pElement );
}
public :
CHtmlElements( void )
{
}
~CHtmlElements( void )
{
}
public :
void SetSite( LPDISPATCH pDisp )
{
if ( IsSiteConnected( pDisp ) ) //檢查以避免多余的Sink
{
return ;
}
THtmlElement *pElement = new THtmlElement; //通過模板類型創(chuàng)建相應(yīng)的類的實(shí)例進(jìn)行連接
pElement->SetSite( pDisp );
m_htmlElements.SetAt( pDisp, pElement );
}
//在合適的地方調(diào)用Clear釋放所管理的內(nèi)存
void Clear(void)
{
POSITION pos = m_htmlElements.GetStartPosition();
THtmlElement *pElement = NULL;
LPDISPATCH pDisp = NULL;
while (pos != NULL)
{
m_htmlElements.GetNextAssoc( pos, pDisp, pElement );
m_htmlElements.RemoveKey( pDisp );
delete pElement;
}
}
};
假設(shè)我們有一個(gè)象CHtmlAnchorElement那樣派生自CHtmlObj的類CHtmlDocument2,使用CHtmlElements時(shí)這樣聲明:
typedef CHtmlElements<CHtmlDocument2> CHtmlDocuments;
typedef CHtmlElements<CHtmlAnchorElement> CHtmlAnchors;
class CMyView : public CHtmlView
{
private :
CHtmlDocuments m_htmlDocs;
CHtmlAnchors m_htmlAnchors;
}
在DocumentComplete時(shí)就可以這樣連接到瀏覽器的文檔對(duì)象:
void CMyView ::OnDocumentComplete(LPDISPATCH pDisp, LPCTSTR lpszURL)
{
m_htmlDocs.SetSite(pDisp);
}
如
果想一次性連接上文檔中所有的Anchor
Element,可以通過IHTMLDocument2::get_anchors獲得包含所有IHTMLAnchorElement接口指針的
IHTMLElementCollection,再遍歷其中的每個(gè)元素,分別調(diào)用m_htmlAnchors.SetSite即可。當(dāng)然,一次性的
Sink全部鏈接可能并不是個(gè)好注意,我更愿意在CHtmlDocument2中響應(yīng)事件再通過其它手段來訪問當(dāng)前位置的HTML Element。
4、結(jié)論
響
應(yīng)HTML
Element的事件通知對(duì)于瀏覽器編程來說是一個(gè)非常強(qiáng)大的手段,它可以更深入細(xì)化地控制瀏覽器中的文檔及其HTML元素,實(shí)現(xiàn)更為高級(jí)的功能,比如所
謂的“超級(jí)拖放”(許多多窗口瀏覽器都提供了該功能,但實(shí)際上沒有哪個(gè)瀏覽器完美地實(shí)現(xiàn)了對(duì)URL、文字及圖片的拖放)。
5、參考資料
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=621961