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:
- Override COleControl::OnSetObjectRects.
- Copy the code.
- 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
- In Microsoft Visual Studio .NET, create a default MFC
ActiveX control.
- 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);
}
- Build the control. Registration will automatically
occur.
- 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. - 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.
- 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.