| bitbucket: C++ MFC ATL WTL Win32 COM ActiveX Samples Tutorials Source Code Controls |
Resize ActiveX Control |
Sample code snippet below |
This article provides an workaround for a problem with the MFC libraries when resizing
a hosted HTMLView ActiveX control and other controls, namely controls written with ATL.
Symptoms: the control seems to resize correctly, but when you click on it, it jumps back
to it's original size.
Closer inspection reveals the host tracks two independent control locations - one for
"activated", ne for "not activated" - but the CWnd::MoveWindow etc. do
only update one of them.
Resize an ActiveX control in MFC At the first look, it is easy. Just resize the CWnd that hosts it, using MoveWindow, or SetWindowPos.
Each ActiveX control is hosted by a CWnd. If you add an ActiveX control to a dialog, you can add a member variable for it using clazz wizard - this will also generate a wrapper class for the control, that is inherited from CWnd. If you are an advanced geek, you might prefer creating your activeX controls yourself - then you know it's CWnd already.
However, when we do this, some strange things happen: The Control is resized, you click into it - and it jumps back to it's original size and position. if you examine closer, it always jumps back when the InPlace Activation state changes. This puzzled me for a long time.. I had to trace through MFC's Control Hosting implementation, and they do a great job of hiding the innards. At one particular "Friday is end of coding", there was some magic time left - so I came up with the followign solution.
Inspecting the controls code revealed that MFC indeed tracks separate positions for whether the control is in-place activated or not. Calling the host CWnd's SetWindowPos did reposition the control correctly, but did only update the position rect for the current state. Since these rects are not accessible from the outside (without changing MFC), I chose to use call the IOleInPlaceSite::OnPosRectChange, that updates both rects correctly. (IOleInPlaceSite must be implemented by the client of an ActiveX control if it should be in-place-activated, MFC implements this interface).
Now for the fun part: Again, there is no legal way to acquire the client site implementation from within the client. (sounds silly? I *love* MFC...). So we have to ask the control for help.
The following code snippet uses _com_ptr_t - smart pointers to implement this:
// ==========>>>> resize ActiveX control CRect newRect(...); // [in]: new size+position of control, in client coordinates CWnd * ctrl = ...; // [in]: CWnd of the activeX control IUnknownPtr unk(m_rgc.GetControlUnknown(), true); // see below IOleObjectPtr oleo = unk; // QI for IOleObject IOleClientSitePtr olecs; if (oleo!=0) oleo->GetClientSite(&olecs); // acquire client site IOleInPlaceSitePtr olips = olecs; // QI for InPlaceSite m_ctrl->MoveWindow(r); // "initial" resize of the control if (olips!=0) olips->OnPosRectChange(&r); // indicate the change
One might expect (or argue) that the control itself is responsible for calling OnPosRectChange even when it#s changed from the outside - but an control implemented in ATL does not do this, although MFC obviously relies on this. As well, calling OnPosRectChange twice will not harm, so we are on the safe side.
Notes:
25.2.2002: brushed up formatting, added some more background
information
bitbucket c++ site © Peter Hauptmann. Questions
& Comments to cherea@cherea.de
page updated 29/02/04