在有些情况下,窗口内嵌的浏览器控件不能及时重绘。例如,我们建立一个"消息提示窗",采用浏览器控件显示消息的内容。当我们采用AnimateWindow进行动画显示时,浏览器控件会显示一片空白。下面我们探讨浏览器控件的重绘问题。
- 重现场景。
// 生成一个窗口(略)
HWND hwnd = 窗口handle.
// 内嵌浏览器控件。
::AtlAxCreateControlEx(L“about:blank”,hwnd,NULL,NULL,(IUnknown ** ) & pUnkControl);
...
// 窗口动画。
// 为了程序的兼容性,当AnimateWindow失败时,要调用传统的ShowWindow。
if ( ! ::AnimateWindow(hwnd, 200 ,AW_SLIDE | AW_VER_NEGATIVE))
... {
::ShowWindow(hwnd,SW_SHOW);
}
- 重绘的目标。
一个浏览器控件的窗口层次如下:
--Shell Embedding
----Shell DocObject View
------Internet Explorer_Server
我们需要重绘的目标是类名为“Internet Explorer_Server”的窗口句柄。为了支持最新的vista系统,不能简单地通过IWebBrowser2->get_HWND取得窗口句柄。下面,我们通过AtlAxCreateControlEx返回的pUnkControl,取得“Shell Embedding”,再取得“Internet Explorer_Server”。
取得“Shell Embedding”:
HWND hwnd;
if (pUnkControl != NULL)
... {
CComPtr<IWebBrowser2> pWebBrowser2;
pUnkControl->QueryInterface(IID_IWebBrowser2,(void**)&pWebBrowser2);
if(pWebBrowser2!=NULL)
...{
/**/////这句代码在vista下会失败。
//hr=pWebBrowser2->get_HWND((SHANDLE_PTR*)&hwnd);
/**/////这一段代码可直接取得HWND,只是没有正式的文档说明,网上也没见相关的讨论。
//CComPtr<IOleWindow> pow;
//pWebBrowser2->QueryInterface(IID_IOleWindow,(void**)&pow);
//if(pow!=NULL)
//{
// pow->GetWindow(&hwnd);
//}
//采用通行的方法。
CComPtr<IServiceProvider> psp;
pWebBrowser2->QueryInterface(IID_IServiceProvider,(void**)&psp);
if(psp!=NULL)
...{
CComPtr<IOleWindow> pow;
psp->QueryService(SID_SShellBrowser,IID_IOleWindow, (void**)&pow);
if(pow!=NULL)
...{
//this is “Shell Embedding”.
pow->GetWindow(&hwnd);
}
}
}
}
取得“Internet Explorer_Server”:HWND __getWebBrowserHwnd(HWND hwndShell)
... {
//
if(hwndShell==NULL)
return NULL;
//loop
TCHAR szClassName[MAX_PATH];
HWND hwnd=hwndShell;
while(true)
...{
//
::GetClassName(hwnd,szClassName,MAX_PATH);
if(::_wcsicmp(L"Internet Explorer_Server",szClassName)==0)
...{
return hwnd;
}
//
hwnd=::GetWindow(hwnd,GW_CHILD);
if(hwnd==NULL)
break;
}
return NULL;
}
- 重绘的时机。
窗口动画将执行一段指定的时间。用户要看到的是最后的页面内容,因此不必在动画过程中启动重绘,而是在动画完成之后进行。那么怎样才知道AnimateWindow结束了呢?幸运的是,AnimateWindow采用的是同步返回(与AJAX中流行的异步调用思想是不同的。)。也就是说,当动画执行完毕后,AnimateWindow才返回。因此,重绘的时机是在AnimateWindow之后。
// redraw the web page.
HWND hwnd = __getWebBrowserHwnd(hwndShell);
//
if (hwnd != NULL)
... {
::InvalidateRect(hwnd,NULL,TRUE);
::UpdateWindow(hwnd);
}