Link to home
Start Free TrialLog in
Avatar of mco
mco

asked on

ActiveX control inside same ActiveX control

I have an ActiveX Controls created using the AppControl Wizard
provided by Visual C++ 6. I want to create one instance of the control
inside another instance of the same control.

When I
create the internal instance I get an assertion in Wincore.cpp in
CWnd::Attach saying that the window is already mapped (was attached
already).

When inserting a control of another class, there is no problem.
Avatar of SergioL
SergioL

How make a MSFlexGrid Control inside a ActiveX Control in VC++ 6
by SergioL

1) Crear el Nuevo Control (CC)
En la función InitInstance agregar

BOOL CCCApp::InitInstance()
{
      BOOL bInit = COleControlModule::InitInstance();

      if (bInit)
      {
            // TODO: Add your own module initialization code here.
            AfxEnableControlContainer();
      }

      return bInit;
}

en el constructor del Control nuevo:

CCCCtrl::CCCCtrl()
{
      InitializeIIDs(&IID_DCC, &IID_DCCEvents);

      // TODO: Initialize your control's instance data here.
      EnableSimpleFrame();

}

class CCCCtrl : public COleControl
{
      DECLARE_DYNCREATE(CCCCtrl)

// Constructor
public:
      CCCCtrl();
      CMSFlexGrid m_grilla;  
            // en este ejemplo uso una Flex Grid!!
...

en la func. OnCreate (Mapear WM_CREATE)
int CCCCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
      if (COleControl::OnCreate(lpCreateStruct) == -1)
            return -1;
      
      CRect rect;
      m_grilla.Create(0,WS_VISIBLE,rect,this,101);
      return 0;
}


2) Propiedades

en el header del nuevo control y en el codigo de la libreria de tipos(con add property):

protected:
...
// Dispatch maps
      //{{AFX_DISPATCH(CCCCtrl)
      long m_filas;
      afx_msg void OnFilasChanged();  
      //}}AFX_DISPATCH
      DECLARE_DISPATCH_MAP()
...

en la func. DoPropExchange:

void CCCCtrl::DoPropExchange(CPropExchange* pPX)
{
      ExchangeVersion(pPX, MAKELONG(_wVerMinor, _wVerMajor));
      COleControl::DoPropExchange(pPX);

      // TODO: Call PX_ functions for each persistent custom property.
      PX_Long (pPX, _T("Filas"), m_filas, 2);

           //... Por cada propiedad a tratar desde la App Cliente!!
}

en el evento que se produce al modificar la propiedad:
void CCCCtrl::OnFilasChanged()
{
      // TODO: Add notification handler code

      m_grilla.SetRows(m_filas);
      SetModifiedFlag();
}

y si desea que el control ocupe toda el area cliente (del nuevo control)
void CCCCtrl::OnSize(UINT nType, int cx, int cy)
{
      COleControl::OnSize(nType, cx, cy);
      
      m_grilla.SetWindowPos(0,0,0,cx,cy,0);      
}


1) Crear el Nuevo Control (for example : CC)  
En la función InitInstance agregar  
1) Make a new activex control (for example: CC)    
In the function InitInstance add    
 
en el constructor del Control nuevo:  
in the contructor of new control:  
 
CMSFlexGrid m_grilla;    
            // en este ejemplo uso una Flex Grid!!  
CMSFlexGrid m_grilla;      
            // in this example I use the Flex Grid!!  
 
en la func. OnCreate (Mapear WM_CREATE)  
in the func. OnCreate (Map WM_CREATE)    
 
2) Propiedades  
2) Properties (of the ocx)  
 
en el header del nuevo control y en el codigo de la libreria de tipos(con add property):  
in the header of the new control and on the code of types library(with add property):    
 
en la func. DoPropExchange:  
in the func. DoPropExchange:    
 
           //... Por cada propiedad a tratar desde la App Cliente!!  
           //... For each properties to handle from the client application!!    
 
en el evento que se produce al modificar la propiedad:  
in the event that takes place when modifying the property:    
 
y si desea que el control ocupe toda el area cliente (del nuevo control)  
and if you wants that the control occupies the whole area client (of the new control)    
 
I hope this helps you.  
SergioL  

Avatar of mco

ASKER

It seems you did not read the question carefully.
The containing and contained ActiveX control have to be instances
of the SAME control.

How are you creating the internal control?  Did you add a wrapper class to your project or are you using CWnd::CreateWindow?
Avatar of mco

ASKER

CWnd:CreateControl
That's what I meant.  Oops.  I've got a slow day today, so I'm going to fool around with this problem.  I'll let you know what I find.
Can I see the code where you create the control?  I think allocating the CWnd object on the heap is the way to go.
Avatar of mco

ASKER

You can't see the code for a number of reasons, major one being I won't be at work for a few days.
Anyway, it is allocate on the heap.
The problem has to do with the fact that since it is another instance
of the same OCX, the InitInstance of the OCX is called only once.
That is why (indirectly) something is being attached twice.

OK -- I have experienced your pain first-hand.  The problem is that the contained control and the control itself are forced to share the same OleClientSite (perhaps because they are the same window class).

I believe this to be a shortcoming of MFC, but I'm not going to proclaim that to the world as I don't have much to back it up.  I think I've heard of someone doing this in ATL, but I don't know that for sure, so I'll be quiet about it.

A potential work around which you have probably already considered is presented below.

ActiveX Control A - The one you already have developed which is unable to contain a control of it's same type.  Modify it to dynamically create an instance of ActiveX Control B (described below) where you had hoped to create another instance of ActiveX Control A.

ActiveX Control B - A control who's only purpose in life is to wrap ActiveX Control A.  In OnCreate, you do a CWnd::CreateControl of ActiveX ControlA and you destroy it in the OnDestroy method.

It's important to note that as far as the user is concerned, they are seeing an instance of a control within an instance of the same control.  Since it's possible to have one OCX file contain more than one ActiveX control, it looks even more like one control containing itself.

I implemented this solution with success.  Control A is called AXTest.  Control B is called AXContainTest.

AXTest displays a CButton object (top 1/5 of the client area) which when clicked creates an instance of AXContainTest which fills the rest of the client area (bottom 4/5).

AXContainTest is very simple.  OnCreate, OnDestroy, and OnSize, are the only functions I have implemented (you may need more I suppose).  I implemented OnSize simply so I could re-position the contained instance of AXTest.

I used the ActiveX Control Test Container to create an instance of AXTest and clicked the button.  This created AXContaintTest in the remaining client area of the control.  AXContainTest contains another instance of AXTest which fills the entire area of AXContainTest (that is, the bottom 4/5 of the initial instance of AXTest).  I could then repeatedly click the bottom most button and more and more instances of AXContainTest (and AXTest with it) were created.

It should be pointed out that AXContainTest could be generalized to contain any control based on a given ProgID.  The challenge would be communicating that ProgID to AXContainTest from AXTest.  I suppose AXContainTest could be created and it wouldn't create the control to be contained until some method was called that set the ProgID.

Hope this helps.  If you'd like, I could email you the controls I created.

Mark
Avatar of mco

ASKER

mandhjo, great stuff
Post and answer and you shall be rewarded

ASKER CERTIFIED SOLUTION
Avatar of mandhjo
mandhjo
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial