在VC++中綁定網頁元素的事件(如onclick),通常會使用HTMLElementEvents2事件介面進行。大致如下:
void CSomeObject:ConnectElement(IHTMLElement* pElement)
{
//取得連接點容器
CComQIPtr<IConnectionPointContainer> pCPC=pElement;
//尋找連接點
CComPtr<IConnectionPoint> pCP;
pCPC->FindConnectionPoint( DIID_HTMLElementEvents2, &pCP);
//串連事件介面
IUnknownPtr pUnk=this; //由CSomeObject實現HTMLElementEvents2事件介面
DWORD dwCookie;
pCP->Advise( pUnk, &dwCookie);
}
而在javascript中使用attachEvent綁定網頁元素事件的方式卻顯得更自然,實現也更直接,我們是否也可以用VC++實現呢?
- function fn_onclick()
- {
- alert("Hello world!");
- }
- document.all("button1").attachEvent("onclick",fn_onclick);
- document.all("button1").detachEvent("onclick",fn_onclick);
先看看在PIMShell中用VC++是怎樣實現以上功能的。
- class ATL_NO_VTABLE CVCEventSinkDemo:
- public IDispatchImpl<IVCEventSinkDemo>
- {
- public:
- CVCEventSinkDemo()
- {
- }
- DECLARE_PROTECT_FINAL_CONSTRUCT()
- HRESULT FinalConstruct()
- {
- return S_OK;
- }
- void FinalRelease()
- {
- }
- private:
- //代理對象
- PIMShellCore::IAjaxDelegatePtr m_pDelegate_onevents;
- //回呼函數
- static HRESULT CALLBACK Sink_onevents( VARIANT vEvent, VARIANT vContext, IUnknown* pInstance, VARIANT* pvarResult);
- //綁定事件或取消綁定
- void __attachEvents(bool bAttach);
- };
- ////////////////////////////////////////////////////
- void CVCEventSinkDemo::__attachEvents(bool bAttach)
- {
- if(bAttach)
- {
- //產生一個代理對象,傳入this指標和回呼函數
- IUnknownPtr pThis=this;
- m_pDelegate_onevents = o->Sys2->CreateDelegateVC1( pThis, (LONGLONG)&Sink_onevents);
- //綁定事件
- o->Control->AttachEvent(L"onclick", m_pDelegate_onevents);
- }
- else
- {
- if(m_pDelegate_onevents!=NULL)
- {
- //取消綁定
- o->Control->DetachEvent( L"onclick", m_pDelegate_onevents);
- //
- m_pDelegate_onevents=NULL;
- }
- }
- }
- //當事件發生時,調用此函數
- HRESULT CVCEventSinkDemo::Sink_onevents( VARIANT vEvent, VARIANT vContext, IUnknown* pInstance, VARIANT* pvarResult)
- {
- __SAFECALL_BEGIN;
- //取得this指標
- CVCEventSinkDemo* pThis=dynamic_cast<CVCEventSinkDemo*>(pInstance);
- //取得event對象
- MSHTML::IHTMLEventObjPtr e=vEvent.pdispVal;
- //event.type
- CString sType=e->type;
- //
- if(sType==L"click")
- {
- //執行響應代碼
- }
- __SAFECALL_END;
- }
1、先產生一個代理對象,這個對象記錄CVCEventSinkDemo的執行個體指標和回呼函數,與C#中delegate的設計理念一致。
2、將代理對象傳入attachEvent實現對onclick事件的綁定。
3、當事件發生時,系統調用代理對象的0方法,
4、在代理對象的0方法中,調用先前記錄的回呼函數,並將CVCEventSinkDemo的執行個體指標傳入。
我們接下來看看CreateDelegateVC1是如何組建代理程式對象的。
- HRESULT CreateDelegateVC1( IUnknown* pInstance, LONGLONG lnCallback, IDispatch** ppDelegate)
- {
- //組建代理程式對象
- CComObject<CVCDelegateDemo>* pDelegate;
- HRESULT hr=CComObject<CVCDelegateDemo>::CreateInstance(&pDelegate);
- if(FAILED(hr))
- return hr;
- //記錄執行個體指標和回呼函數
- pDelegate->__record(pInstance,lnCallback);
- //ok
- return pDelegate->QueryInterface(IID_IDispatch,(void**)ppDelegate);
- }
接下來,我們看CVCDelegateDemo的0方法是如何?的
- class ATL_NO_VTABLE CVCDelegateDemo:
- public IDispatchImpl<CVCDelegateDemo>
- {
- public:
- CVCDelegateDemo()
- {
- }
- DECLARE_PROTECT_FINAL_CONSTRUCT()
- HRESULT FinalConstruct()
- {
- return S_OK;
- }
- void FinalRelease()
- {
- }
- //聲明回呼函數的格式
- typedef HRESULT (CALLBACK* _delegateVC1)( VARIANT vParam1, VARIANT vContext, IUnknown* pInstance, VARIANT* pvarResult);
- private:
- IUnknownPtr m_pInstance;
- _delegateVC1 m_pCallback1;
- public:
-
- //record
- void __record(IUnknown* pInstance,LONGLONG lnCallback)
- {
- //記錄執行個體指標和回呼函數
- this->m_pInstance=pInstance;
- this->m_pCallback1=(_delegateVC1)lnCallback;
- }
- public:
- //實現0方法,並調用回呼函數
- STDMETHOD(Invoke)( DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
- DISPPARAMS* pDispParams, VARIANT* pvarResult,
- EXCEPINFO* pExcepInfo, UINT* puArgErr)
- {
- //只支援0方法
- if(dispidMember!=0)
- return E_INVALIDARG;
- //如果是通過attachEvent綁定網頁元素的事件,那麼傳入的第一個參數就是event對象
- _variant_t vEvent=pDispParams->rgvarg[0];
- //調用回呼函數,傳入event對象和執行個體指標
- (*m_pCallback1)(vEvent,vtMissing,m_pInstance,pvarResult);
- //ok
- return S_OK;
- }
- };