仅仅用于Internet Explorer的事件
有些是仅仅可用于自动化 Internet Explorer,:
· OnQuit
- OnVisible
- OnToolBar
- OnMenuBar
- OnStatusBar
- OnFullScreen
- OnTheaterMode
大多数这些事件属于浏览器用户接口. 另外一些必须要先是或者关闭Internet Explorer才发生. 一些情形中,这些事件将在你宿主webbrowser空间的时候发生. 举例来讲,当你在你的应用程序设置MenuBar 属性,尽管你的WebBrowser control 并没有菜单条, OnMenuBar 事件将被激发, 但是如果你显示或者隐藏你的应用程序菜单条,OnMenuBar 事件不会激发.为什么?因为你的菜单条由你控制,webbrowser对这些用户接口项一无所知. 很长时间以来,这些相互矛盾的功能是一些混乱的根源。
其中一个事件—OnQuit—将永远不会在你的应用程序中激发.举个例子, 察看表 Table 7-6. 注意到OnQuit 事件当用户关闭 Internet Explorer 或者当Quit 方法被调用时激发.如果你宿主改控件且用户关闭你的应用程序, OnQuit 事件不会激发.它仅仅在你自动化Internet Explorer 且用户手动关闭浏览器时候发生.另外,如果你在宿主一个webbrowser控件时试图调用Quit 方法,一个自动化错误将会发生.
自ActiveX控件中控制Internet Explorer 事件
通过 IWebBrowser2 接口你可以在利用vc++在ActiveX 控件中接受事件.
你可能疑惑为什么要在ActiveX控件中接受 Internet Explorer事件.之前介绍"DocumentComplete," 事件时候,我提到过你不可以在DocumentComplete event 事件被触发前安全存取文档.在Activex控件中获知DocumentComplete 事件被触发的途径是ActiveX 控件接收 Internet Explorer并处理 DocumentComplete 事件.
除了你必须接收Internet Explorer 事件外, 你可以开发一个可导航的类浏览器的应用于公司intranet或者学校网络.你可以在ActiveX control中自动化Internet Explorer并接受其事件。.
当你刷新一个页面, 也许DocumentComplete 事件并不激发. 当DocumentComplete 事件并未触发, ProgressChange 事件被用来控制以检测某页是否完成加载. 载一个简单的web页或者没有嵌入帧时 ProgressChange 工作的很好.
记住 ProgressChange 右两个参数告诉你下载操作的进度.第一个参数当下在完成时候设定为-1, 者可以帮助你检测是否可做类打印等操作
让我们学习一个打印控active控件,为从Internet Explorer接收事件,你必须设置事件接收,意味着你必须通过IWebBrowser2 接口以获得实现,如下实现:
protected: CComPtr<IWebBrowser2> m_spWebBrowser; |
.
接下来覆盖IOleObjectImpl 的SetClientSite方法的实现. SetClientSite 放方法是在Internet Explorer通知气客户区的控件的时候被调用.你可用客户区的site指针 (m_spClientSite) 存取容器并且得到IWebBrowser2 接口指针. 在SetClientSite 实现中, 你必须首先调用其基类版本,就想如下:
IOleObjectImpl<CPrintCtl>::SetClientSite(pClientSite); |
这些带吗看起来可能有些生疏, 但记住 IOleObjectImpl 是一个模版类. 为了调用它的方法, 你必须制定要求的模版参数以指示编译器哪一个类实例在调用SetClientSite 方法时被使用. 现在讲残存的访问容器和IWebBrowser2接口指针的代码从Print方法迁移到SetClientSite 方法Now move the remaining code Print 方法将看起来如下:
STDMETHODIMP CPrintCtl::Print() { ATLASSERT(m_spWebBrowser); HRESULT hr = E_FAIL; if (m_spWebBrowser) { hr = m_spWebBrowser->ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER, NULL, NULL); } return hr; } |
而 SetClientSite 方法将接收事件,SetClientSite 讲看起来如下:
注意
你不能够再FinalConstruct m方法中接收事件因为此时客户站点还未设定。
STDMETHODIMP CPrintCtl::SetClientSite(IOleClientSite* pClientSite) { HRESULT hr = IOleObjectImpl<CPrintCtl>::SetClientSite(pClientSite); if (!pClientSite) { return hr; } CComPtr<IOleContainer> spContainer; m_spClientSite->GetContainer(&spContainer); ATLASSERT(spContainer); if (SUCCEEDED(hr)) { // Set up the event sink. // CComQIPtr<IServiceProvider, &IID_IServiceProvider> spServiceProvider(spContainer); ATLASSERT(spServiceProvider); if (spServiceProvider) { spServiceProvider->QueryService(SID_SInternetExplorer, IID_IWebBrowser2, (void**)&m_spWebBrowser); ATLASSERT(m_spWebBrowser); if (m_spWebBrowser) { AtlAdvise(m_spWebBrowser, GetUnknown(), DIID_DWebBrowserEvents2, &m_dwCookie); } } } return hr; } |
注意到在AtlAdvise 调用时你必须建立protected 或者private DWORD的数据成员以掌握返回自AtlAdvise 方法的cookie. CprintCtl 类的构造函数初始化改成员为0. 尽管我们注意到CPrintCtl::SetClientSite 方法使用IOleObjectImpl::SetClientSite 方法的返回值. 此方法并不检查已被调用的返回值因为CPrintCtl::SetClientSi将 反射客户站点的设定状态.
最好, 我们检查pClientSite 的返回值,输入参数是NULL. 如果这样,我们当Internet Explorer 卸载这些控时, 他调用SetClientSite w设置为NULL. 或者告诉你已经从站点解除, 所以包含一个接口, IWebBrowser2 容器不需要一定执行。.
因为当你完成任务时应当关闭任务的站点, 也包含某个控件被卸载时。检查pClientSite 是否为NULL,以便放置AtlUnadvise 方法. 记住pClientSite在控件被卸载时为 NULL. 看起来如下:
if (!pClientSite) { ATLASSERT(m_spWebBrowser); if (m_spWebBrowser) AtlUnadvise(m_spWebBrowser, DIID_DWebBrowserEvents2, m_dwCookie); return hr; } |
现在你可以使用AtlAdvise接收事件,让我们控制事件.为此你必须覆盖重写IDispatchImpl 的Invoke 方法. 典型的,你将为你的时间建立一个单独的类因为 Internet Explorer 事件的DISPIDs 必须同你的控件的DISPIDs 不同.但在此你可以简单在 CPrintCtl 类中来实现.实现Invoke (入代码所示)以控制ProgressChange 事件.在事件句柄, 如果progres的总数设定为 -1,设定一个标志变量指示已被打印.
STDMETHODIMP CPrintCtl::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pvarResult, EXCEPINFO* pExcepInfo, UINT* puArgErr) { if (riid != IID_NULL) return DISP_E_UNKNOWNINTERFACE; if (!pDispParams) return DISP_E_PARAMNOTOPTIONAL; switch (dispidMember) { // // The parameters for this DISPID: // [0]: Maximum progress - VT_I4 // [1]: Amount of total progress - VT_I4 // case DISPID_PROGRESSCHANGE: if (pDispParams->cArgs != 0) { // Make sure that you access the // correct data member of the rgvarg array. // To do this, check the type of data to // make sure it is correct. // if (pDispParams->cArgs > 1 && pDispParams->rgvarg[1].vt == VT_I4 && pDispParams->rgvarg[0].vt == VT_I4) { if (-1 == pDispParams->rgvarg[1].lVal) m_fCanBePrinted = TRUE; } } break; default: // Call the base class implementation of Invoke // so that IPrintCtl methods and properties will // work correctly. // IDispatchImpl<IPrintCtl, &IID_IPrintCtl, &LIBID_ATLPRINTLib>::Invoke(dispidMember, riid, lcid, wFlags, pDispParams, pvarResult, pExcepInfo, puArgErr); break; } return S_OK; } |
在 ProgressChange 事件处理中,当Progress 参数(pDispParams->rgvarg[1].lVal) 是-1, 我们设置一个变量告诉控件问打光在完成可以打印. FCanBePrinted 就是我们要设定的变量。
现在当用户试图调用Print 方法打印文档,你可以检查变量以确定是否可打印. 此处为 Print 方法的代码:
STDMETHODIMP CPrintCtl::Print() { if (!m_fCanBePrinted) { ::MessageBox(NULL, _T("The page is not ready to be printed."), _T("PrintCtl"), MB_OK); return E_FAIL; } ATLASSERT(m_spWebBrowser); HRESULT hr = E_FAIL; if (m_spWebBrowser) { hr = m_spWebBrowser->ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER, NULL, NULL); } return hr; } |