FIX: MFC ActiveX Control May Flicker Excessively When it Is Overlapped By an IFRAME (310384)



The information in this article applies to:

  • Microsoft Internet Explorer (Programming) 4.01 SP1
  • Microsoft Internet Explorer (Programming) 5.5
  • Microsoft Internet Explorer (Programming) 5.5 SP1
  • Microsoft Internet Explorer (Programming) 5.5 SP2

This article was previously published under Q310384

SYMPTOMS

When a Microsoft Foundation Class (MFC) ActiveX control appears in an IFRAME element and then another IFRAME covers it completely along its height and width, if you update other parts of the host page, an unnecessary redraw of the MFC control may result in an unwanted flicker.

CAUSE

This behavior is caused by a bug in the way that Microsoft Internet Explorer updates parented controls.

RESOLUTION

You can override the OnSetObjectRects method to prevent the outer window of the MFC control from resizing to the clip:rect value. MFC controls typically have an extra parent window to handle reflection.

STATUS

Microsoft has confirmed that this is a problem in the Microsoft products that are listed at the beginning of this article.

MORE INFORMATION

You typically create MFC ActiveX controls with a parent window to act as a reflector window. When the IFRAME element overlaps an MFC control completely in one dimension, the parent -- or outer -- window of the MFC control shrinks to the clip:rect (the displayed portion) while the true size of the control -- the child window -- stays the same size. When you update the Web page, Microsoft Internet Explorer incorrectly detects that the outer window is the wrong size and then tries to resize it back to the full size of the control. MFC then resizes it back to the clip:rect. This causes the control to conduct an extra paint operation.
To work around this behavior in MFC, you can prevent MFC from resizing its outer window to the smaller clip size. To do so, follow these steps:
  1. Override COleControl::OnSetObjectRects.
  2. Copy the code.
  3. Modify the code. To do so, tell the outer window to resize to the control size and not to the clip:rect size as shown in the OnSetObjectRects in the following code.
To implement the function, you must copy the _GetClippingCoordinates function from the MFC sources and then include some non-standard MFC include files, such as the following:

Header File


class CflickerCtrl : public COleControl
{
...
public:
	virtual BOOL OnSetObjectRects(LPCRECT lpRectPos, LPCRECT lpRectClip);
				

Implementation File


#include <afxpriv2.h>
#include <..\src\mfc\afximpl.h>
#include <..\src\mfc\ctlimpl.h>

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 CflickerCtrl::OnSetObjectRects(LPCRECT lprcPosRect, LPCRECT lprcClipRect)
{
	ASSERT(lprcPosRect != NULL);

	// Remember the position rectangle for later
	m_rcPos = *lprcPosRect;

	// Calculate complete rectangle including the tracker (if 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 that rectangle as appropriate
	CRect rectClip;

	// CHANGE - call your own copy of _GetClippingCoordinates
	MyGetClippingCoordinates(rectPos, lprcClipRect, rectClip, &m_ptOffset);

	// Move outer window first. then inner window

	if (!m_bInPlaceSiteWndless)
	{
		CWnd* pWndOuter = GetOuterWindow();
		
		//BEGIN CHANGE
		if (pWndOuter != NULL)
		{

		//		::MoveWindow(pWndOuter->m_hWnd, rectClip.left, rectClip.top,
		//			rectClip.Width(), rectClip.Height(), TRUE);
				::MoveWindow(pWndOuter->m_hWnd, rectPos.left, rectPos.top,
					rectPos.Width(), rectPos.Height(), TRUE);
		}
		//END CHANGE								
		if (pWndOuter != this)
			MoveWindow(m_ptOffset.x, m_ptOffset.y, rectPos.Width(), rectPos.Height());
	}

	return TRUE;
} 
				

Steps to Reproduce the Problem

  1. In Microsoft Visual Studio .NET, create a default MFC ActiveX control.
  2. Modify the OnDraw code so that the number of times that you called the code appears. For example:
    void CflickerCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
    {
       static int nCount = 0;
       nCount++;
    
       CPen pen;
       CBrush brush;
       pen.CreatePen(PS_SOLID, 4, RGB(0,0,255));
       brush.CreateSolidBrush(RGB(0,128,128));
       CPen *pOldPen = pdc->SelectObject(&pen);
       CBrush *pOldBrush = pdc->SelectObject(&brush);
    
       pdc->Rectangle(&rcBounds);
       CString strMsg;
       strMsg.Format("WM_PAINT : %d", nCount);
       CRect drawRect = rcBounds;
       pdc->DrawText(strMsg, &drawRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
    
       pdc->SelectObject(pOldPen);
       pdc->SelectObject(pOldBrush);
    }
    						
  3. Build the control. Registration will automatically occur.
  4. Create an HTML file to display the iframe objects that use the following code:
    <html>
    <body>
    <textarea style="width: 100%; height: 10%">You can type in this text area</textarea>
    <iframe src="mfctest.htm" style="top: 300; left: 0; position:absolute"></iframe>
    <iframe src="about:blank" style="top: 300; left: 200; position: absolute"></iframe>
    </body>
    </html>
    						
    Name the newly created HTML file Test.htm, and then put it on the Web server.
  5. Create an HTML file to display the MFC control that uses the following code:
    <html>
    <body>
    <object
    	classid="clsid:9BAA02AE-5877-4261-8F35-8831CE25BDD9"
    	height = "100%"
    	width = "100%">
    </object>
    </body>
    </html>
    						
    Name the newly created HTML file Mfctest.htm, and then put it on the Web server. Remember to change the CLSID value so that you can use the CLSID value from your MFC control.
  6. Start Internet Explorer and locate Test.htm. If you type inside the text area, the MFC control redraws every time you press a key. If you slightly misalign the two iframes, for example, if you set the top attribute for one of the iframes in Test.htm at 305, the problem does not occur.


Modification Type:MajorLast Reviewed:5/12/2003
Keywords:kbbug kbfix kbMSHTML KB310384