MFC ActiveX 控制元件在IE下閃爍的問題
這周打算髮布公司新版本的OCX,忽然又收到客戶報的一個很老的bug,公司的OCX在IE下使用的時候,拉動滾動條會重疊,而且還有很嚴重的閃爍的現象。上半年的時候已經查過相關資料,沒有找到解決方法,而且老大也說這個是微軟的一個bug,我們無法解決。有些戲劇性的是,林mm發了一個微軟的網頁,稱那篇文章詳細說明了那個bug的問題,讓我看一下,再和客戶解釋我們為什麼不能解決。那個連結是:
認真看了一下,微軟是聲稱那個bug在IE5下是有問題的,可是在IE6下,是已經有辦法解決了。
解決第一步,依葫蘆畫瓢,根據微軟說的方法過載了OnSetObjectRects,但是實現為空的,程式碼如下:
BOOL CmfcaxCtrl::OnSetObjectRects(LPCRECT lpRectPos, LPCRECT lpRectClip)
{
return TRUE;
}
興致勃勃的rebuild all,再用IE測試。發現是不閃爍了,可是初始化時被隱藏的那部分照樣沒重新整理,-_-失敗告終。
衝擊第二步,再認真看了那篇文章,發現有一個注意項:
注意 已更改解決以下 Microsoft 知識庫 (KB) 文章中描述的問題: 在 Internet Explorer 中
307978 FIX: 重疊 IFRAME 中的 MFC 控制元件收到不必要的 WM _ PAINT 訊息
點選進去,第二篇文章講述了OnSetObjectRects
void MyGetClippingCoordinates(LPCRECT pPosRect, LPCRECT pClipRect,
LPRECT pIntersectRect, LPPOINT pOffsetPoint)
{
int clipLeft = 0;
int clipTop = 0;
if ((pClipRect == NULL) || IsRectEmpty(pClipRect))
{
CopyRect(pIntersectRect, pPosRect);
}
else
{
IntersectRect(pIntersectRect, pPosRect, pClipRect);
clipLeft = pClipRect->left;
clipTop = pClipRect->top;
}
pOffsetPoint->x = min(0, pPosRect->left - clipLeft);
pOffsetPoint->y = min(0, pPosRect->top - clipTop);
}
BOOL CTmpCtrl::OnSetObjectRects
{
// return COleControl::OnSetObjectRects(lprcPosRect, lprcClipRect);
ASSERT(lprcPosRect != NULL);
// Remember the position rectangle.
m_rcPos = *lprcPosRect;
// Calculate complete rectangle, include the tracker if it is present.
CRect rectPos = m_rcPos;
if (m_bUIActive && m_pRectTracker != NULL)
{
// Save new clipping rectangle (for DestroyTracker).
if (lprcClipRect != NULL)
m_pRectTracker->m_rectClip = *lprcClipRect;
// Adjust tracker rectangle to new dimensions.
CRect rectTmp = rectPos;
rectTmp.OffsetRect(-rectTmp.left, -rectTmp.top);
m_pRectTracker->m_rect = rectTmp;
// Adjust the "true" rectangle to include handles/hatching.
UINT nHandleSize = m_pRectTracker->m_nHandleSize - 1;
rectPos.InflateRect(nHandleSize, nHandleSize);
}
// Now clip the rectangle as appropriate.
CRect rectClip;
MyGetClippingCoordinates(rectPos, lprcClipRect, rectClip, &m_ptOffset);
// Move the outer window first, and then move the inner window.
if (!m_bInPlaceSiteWndless)
{
CWnd* pWndOuter = GetOuterWindow();
//BEGIN CHANGE.
if (pWndOuter != NULL)
{
static CRect oldClipRect(0, 0, 0, 0);
if (oldClipRect != rectClip)
::MoveWindow(pWndOuter->m_hWnd,
rectClip.left, rectClip.top,
rectClip.Width(), rectClip.Height(),
TRUE);
oldClipRect = rectClip;
}
//END CHANGE.
if (pWndOuter != this)
MoveWindow(m_ptOffset.x, m_ptOffset.y,
rectPos.Width(), rectPos.Height());
}
return TRUE;
}
再rebuild下,發現外框是不會閃了,但是中間那些view視窗還是能感覺到閃爍,那個方法只是對IE下閃爍問題做了一些緩解,效果還不是很理想。繼續再baidu和google,很難找到最有效的解決方法。繼續做其他嘗試,還是毫無結果。
衝擊三,今天無意間又搜到一篇文章,內容和第一篇很像,也是說了OnSetObjectRects的過載方法,程式碼幾乎一模一樣,原先還以為微軟弄了個冗餘的網頁,差點忽略過去。那個網頁地址為:
本著死馬當活馬醫的思想,又試了一下,原以為會和第二次的結果是一樣的,令人驚奇的事情就在意外中發生了。用IE測試的時候,沒有閃爍,最小化,很自然的平鋪過去了。正當我要歡呼的時候,在StatusBar下面出現了令人噁心的重疊。。。。。繼續baidu&&google,無果。。。
在msdn上發現了一個人說把
pOffsetPoint->x = min(0, pPosRect->left - clipLeft);
pOffsetPoint->y = min(0, pPosRect->top - clipTop);
改一下,改成(0,0)的效果更好些。嘗試下,果然,效果非常好。閃爍+重疊的現象都沒發現,頓時感到人生真是美好。
那個bug貌似解決了,等測試結果吧。心情一下子開朗了很多,就一個字,爽