Click here to Skip to main content
15,881,248 members
Articles / Desktop Programming / MFC
Article

Docking CSizingControlBar objects inside ActiveX containers

Rate me:
Please Sign up or sign in to vote.
4.90/5 (17 votes)
14 Jun 20013 min read 175.9K   2.5K   72   22
A solution that allows you to dock Cristi Posea's Docking Window class inside ActiveX controls.

Image 1

Introduction

A client recently asked me for a component that could be used in a Win32 application, as well as embedded in a web page. My solution was to write an ActiveX control that I could embed in a CDialog for the Win32 app and use the <OBJECT> tag in IE to embed the control in a web page. When they asked me if I could do docking windows like in Visual Studio, that lead me to The Code Project, where I found Cristi Posea's excellent docking window code.

There was one problem, though - the docking code relied on having a CFrameWnd to dock to. I made the assumption that CFrameWnd could only be a top-level window with a title bar, but that turns out not to be true. CFrameWnd can be created with the WS_CHILD style, rather than WS_OVERLAPPEDWINDOW (the default) and you can make it a child of any other window, including an ActiveX control!

The result is a control that allows you to have docking windows, even inside Internet Explorer. I've included and HTML file called index.htm to illustrate this, which you can open in IE after building the control.

Additionally, I have also figured out how to embed ActiveX controls in the docking windows themselves, so you can now have ActiveX controls inside ActiveX controls, etc. My example code uses the ActiveMovie control.

Here’s what you need to do:

  1. derive your own class from CFrameWnd (ie. CEmbeddedFrame)

  2. derive a class from the docking window code (I used Cristi Posea’s CMyBar as a basis). I called it CDockingWnd.

  3. create a member variable in the control of your frame class
  4. CEmbeddedFrame m_Frame;
  5. call Create() on the frame in the OnCreate() override of your control.
    int CDockCtrlCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct) 
    {
        if (COleControl::OnCreate(lpCreateStruct) == -1)
            return -1;
    
        
        CRect r( 0, 0, 100, 100 );
        if( !m_Frame.Create( NULL,                 /// use CFrameWnd default
                             _T("Embedded Frame"),
                             WS_CHILD | WS_VISIBLE | 
                             WS_CLIPCHILDREN,  /// NOT WS_OVERLAPPEDWINDOW
                             r,
                             this,
                             NULL,
                             NULL,
                             NULL ) )
        {
            TRACE( "**** Creation of embedded frame failed\n" );
            return -1;
        }
    
        return 0;
    }
  6. Add member variables to the frame for the client area object and the docking window
    CEdit 	         m_wndClient;
    CDockingWnd       m_wndDockingWnd;
  7. In OnCreate() of the frame control, create the docking window and any other client-area controls you need, then dock the docking windows as you see fit.
    int CEmbeddedFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 
    {
        if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
             return -1;
    
        //////////////////////////////////////////////////////////////////////
        /// The regular CMainFrame in the doc-view MFC
        /// apps receive WM_IDLEUPDATECMDUI messages from the
        /// CWinThread::OnIdle(), and calls RecalcLayout if the idleLayout
        /// flag was set by a previous call to DelayRecalcLayout(). Since an
        /// ActiveX control doesn't have this mechanism, I have to fake it
        /// (my thanks to Cristi for this fix).
        //////////////////////////////////////////////////////////////////////
        SetTimer(0, 100, NULL);
        
        //////////////////////////////////////////////////////////////////////
        /// create an edit control within the frame so there's something 
        /// interesting in there...
        //////////////////////////////////////////////////////////////////////
        m_wndClient.Create( WS_VISIBLE|WS_CHILD, 
                            CRect(0,0,0,0), 
                            this, 
                            AFX_IDW_PANE_FIRST );
        m_wndClient.ModifyStyleEx(0, WS_EX_CLIENTEDGE);
    
        //////////////////////////////////////////////////////////////////////
        /// older versions of Windows* (NT 3.51 for instance)
        /// fail with DEFAULT_GUI_FONT
        //////////////////////////////////////////////////////////////////////
        if (!m_font.CreateStockObject(DEFAULT_GUI_FONT))
        if (!m_font.CreatePointFont(80, "MS Sans Serif"))
            return -1;
    
        m_wndClient.SetFont(&m_font);
    
        m_wndClient.SetWindowText(
            _T( "This window is a child of the frame." ) );
        
        //////////////////////////////////////////////////////////////////////
        /// Create the docking window and dock it into the frame...
        //////////////////////////////////////////////////////////////////////
        if (!m_wndDockingWnd.Create(_T("Docking Window"), this,
            IDC_DOCKING_WND))
        {
            TRACE0( "Failed to create Docking Windown" );
            return -1;      // fail to create
        }
        //////////////////////////////////////////////////////////////////////
        /// styles suggested by Cristi Posea.
        //////////////////////////////////////////////////////////////////////
        m_wndDockingWnd.SetBarStyle(m_wndDockingWnd.GetBarStyle() |
            CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
    
    
        //////////////////////////////////////////////////////////////////////
        /// Use CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT to only dock on sides,
        /// CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM to only dock on top and bottom
        //////////////////////////////////////////////////////////////////////
        EnableDocking(CBRS_ALIGN_ANY);
        
        //////////////////////////////////////////////////////////////////////
        /// from Cristi Posea's documentation
        //////////////////////////////////////////////////////////////////////
    #ifdef _SCB_REPLACE_MINIFRAME
        m_pFloatingFrameClass = RUNTIME_CLASS(CSCBMiniDockFrameWnd);
    #endif //_SCB_REPLACE_MINIFRAME
    
    
        //////////////////////////////////////////////////////////////////////
        /// Use CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT to only dock on sides,
        /// CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM to only dock on top and bottom
        //////////////////////////////////////////////////////////////////////
        m_wndDockingWnd.EnableDocking(CBRS_ALIGN_ANY);
    
        //////////////////////////////////////////////////////////////////////
        /// Actually dock it into the frame, otherwise it'll float.
        //////////////////////////////////////////////////////////////////////
        DockControlBar(&m_wndDockingWnd, AFX_IDW_DOCKBAR_LEFT);
        
        return 0;
    }
  8. In the OnCreate() handler of your docking window, create the control(s) you want to be children of the docking window. My sample code shows how to create a simple edit control or how to embed ActiveX controls in your docking window.
    int CDockingWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) 
    {
        if (baseCDockingWnd::OnCreate(lpCreateStruct) == -1)
            return -1;
    
        SetSCBStyle(GetSCBStyle() | SCBS_SHOWEDGES | SCBS_SIZECHILD);
            
    #ifdef _USE_EDIT_CTRL_  
    
        if (!m_wndChild.Create( WS_CHILD|WS_VISIBLE|
                                ES_MULTILINE|ES_WANTRETURN|ES_AUTOVSCROLL,
                                CRect(0,0,0,0), 
                                this,
                                IDC_FRAME_EDIT) )
            return -1;
    
        m_wndChild.ModifyStyleEx(0, WS_EX_CLIENTEDGE);
    
        //////////////////////////////////////////////////////////////////////
        /// older versions of Windows* (NT 3.51 for instance)
        /// fail with DEFAULT_GUI_FONT
        //////////////////////////////////////////////////////////////////////
        if (!m_font.CreateStockObject(DEFAULT_GUI_FONT))
            if (!m_font.CreatePointFont(80, "MS Sans Serif"))
                return -1;
    
        m_wndChild.SetFont(&m_font);
    
        m_wndChild.SetWindowText(
            _T( "This docking window is embedded in the control" ) );
    
    #else  //  _USE_ACTIVE_MOVIE_CTRL_
    
        //////////////////////////////////////////////////////////////////////
        /// You need the following call to allow your control to contain 
        /// other controls, like the ActiveMovie control. This is the secret
        /// to getting the docking windows to contain ActiveX controls. The
        /// rest is easy...
        //////////////////////////////////////////////////////////////////////
        AfxEnableControlContainer();
    
    
        //////////////////////////////////////////////////////////////////////
        /// Actually get the CWnd object to instantiate an active movie
        /// control for you
        //////////////////////////////////////////////////////////////////////
        if( !m_AMControl.CreateControl( _T("AMOVIE.ActiveMovie Control.2"), 
                                        _T("Active Movie Ctrl"), 
                                        WS_CHILD | WS_VISIBLE, 
                                        CRect(0,0, 50, 50),
                                        this, 
                                        IDC_ACTIVE_MOVIE_CTRL ) )
        {
            TRACE( "Unable to create Active Movie control. "
                    "GetLastError() == %dn", GetLastError() );
    
            return -1;
        }
    
        //////////////////////////////////////////////////////////////////////
        /// Get the IUnknown of the control so I can QI the IMediaPlayer
        /// interface. See MSDXM.TLH for the interface definition.
        //////////////////////////////////////////////////////////////////////
        IUnknownPtr pUnk = m_AMControl.GetControlUnknown();
        if( pUnk )
        {
            pUnk->QueryInterface( IID_IMediaPlayer,
                                  (void**)&m_pMediaPlayer );
    
            //////////////////////////////////////////////////////////////////
            /// If the QI worked
            //////////////////////////////////////////////////////////////////
            if( m_pMediaPlayer )
        {
            //////////////////////////////////////////////////////////////////
            /// Try to open a sample file. This could be a URL on the web.
            /// This particular clip is a picture of my little girl, Brianna
            //////////////////////////////////////////////////////////////////
            /*
            if( SUCCEEDED( m_pMediaPlayer->Open( L".\brianna.mpg" ) ) )
            {         
                m_pMediaPlayer->put_EnableContextMenu( TRUE );
            }
            */
        }
    }
    
    #endif  // _USE_ACTIVE_MOVIE_CTRL_
    
    
        return 0;
    }
  9. In your control’s OnSize() handler, make CEmbeddedFrame as big as the window (of course, you don’t have to, but I will make this assumption here).
  10. You also have to simulate the WM_IDLEUPDATECMDUI behavior that the MFC doc-view architecture gives you. This is the mechanism in doc-view that updates the state of toolbar buttons and deletes temporary objects from memory when the app isn't busy. The docking window code uses a lot of calls to DelayRecalcLayout(), where the RecalcLayout() call for the CMiniFrameWnd of the docking window is deferred until idle processing. If you don't do this, the frame and the control will not get updated properly. In the ActiveX control, we fake it with the timer we set in CEmbeddedFrame::OnCreate(). Again, I wish to thank Cristi for his help on this behavior.
    void CEmbeddedFrame::OnTimer(UINT nIDEvent)
    { 
        CFrameWnd::OnTimer(nIDEvent);
    
        if (nIDEvent != 0)
            return;
    
        if (m_hWnd != NULL)
        { 
            if (m_nShowDelay == SW_HIDE)
                ShowWindow(m_nShowDelay); 
            if (IsWindowVisible() || m_nShowDelay >= 0) 
            { 
                AfxCallWndProc(this, m_hWnd,
                               WM_IDLEUPDATECMDUI, (WPARAM)TRUE, 0); 
                SendMessageToDescendants( WM_IDLEUPDATECMDUI, 
                                          (WPARAM)TRUE, 
    	                              0, 
                                          TRUE, 
                                          TRUE); 
            } 
            
            if (m_nShowDelay > SW_HIDE) 
               ShowWindow(m_nShowDelay); 
            m_nShowDelay = -1; 
    
            //////////////////////////////////////////////////////////////////
            // send WM_IDLEUPDATECMDUI to the floating miniframes 
            //////////////////////////////////////////////////////////////////
            POSITION pos = m_listControlBars.GetHeadPosition(); 
            while (pos != NULL) 
            { 
                CControlBar* pBar =
                    (CControlBar*) m_listControlBars.GetNext(pos); 
                ASSERT(pBar != NULL); 
    
                //////////////////////////////////////////////////////////////
                // skip if not created yet or if it is not a floating CDockBar
                //////////////////////////////////////////////////////////////
                if (pBar->m_hWnd == NULL ||
                    pBar->GetDlgCtrlID() != AFX_IDW_DOCKBAR_FLOAT) 
                    continue; 
    
                CFrameWnd* pFrameWnd = pBar->GetParentFrame(); 
                if (pFrameWnd->m_hWnd != NULL && pFrameWnd != this) 
                { 
                    if (pFrameWnd->m_nShowDelay == SW_HIDE) 
                        pFrameWnd->ShowWindow(pFrameWnd->m_nShowDelay); 
    
                    if (pFrameWnd->IsWindowVisible() 
                          || pFrameWnd->m_nShowDelay >= 0) 
                    { 
                        AfxCallWndProc(pFrameWnd, pFrameWnd->m_hWnd, 
                                       WM_IDLEUPDATECMDUI, (WPARAM)TRUE, 0); 
                        pFrameWnd->SendMessageToDescendants(
                            WM_IDLEUPDATECMDUI, 
                            (WPARAM)TRUE, 0, 
                            TRUE, TRUE); 
                    } 
                    if (pFrameWnd->m_nShowDelay > SW_HIDE) 
                        pFrameWnd->ShowWindow(pFrameWnd->m_nShowDelay); 
                
                    pFrameWnd->m_nShowDelay  = -1; 
                } 
            } 
            //////////////////////////////////////////////////////////////////
            // find if the top level parent is the active window 
            //////////////////////////////////////////////////////////////////
            bool bActive = (GetTopLevelParent() == GetForegroundWindow()); 
            if (bActive != m_bActive) 
            { 
                //////////////////////////////////////////////////////////////
                // notify the floating miniframes of state change 
                //////////////////////////////////////////////////////////////
                NotifyFloatingWindows(bActive ? FS_ACTIVATE : FS_DEACTIVATE); 
                m_bActive = bActive; 
            } 
        } 
    }

If you have any questions on embedding docking windows inside ActiveX controls, please don’t hesitate to email me at docking_windows@intelligentsystems.net. Any problems with the behavior of the docking windows should be directed to Cristi Posea (cristi@datamekanix.com).

Greg Winkler
Intelligent Systems Inc.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
Greg is a Microsoft Certified developer and runs Intelligent Systems, Inc, a consulting company specializing in user interface design and progamming in Visual C++.

Comments and Discussions

 
Question3ks,another problem,how can i add tooltips for my toolbar? Pin
CodeTwinkle13-Jun-07 15:25
CodeTwinkle13-Jun-07 15:25 
AnswerRe: 3ks,another problem,how can i add tooltips for my toolbar? Pin
CodeTwinkle13-Jun-07 15:51
CodeTwinkle13-Jun-07 15:51 
QuestiononMouseWheel() Pin
CodeTwinkle28-Mar-07 17:26
CodeTwinkle28-Mar-07 17:26 
AnswerRe: onMouseWheel() Pin
Greg Winkler9-Apr-07 22:32
Greg Winkler9-Apr-07 22:32 
GeneralRun executable into activeX Pin
Gabriel Simoes17-May-04 15:57
Gabriel Simoes17-May-04 15:57 
GeneralRe: Run executable into activeX Pin
gwinkler18-May-04 3:54
gwinkler18-May-04 3:54 
Generalnever works Pin
deepak2190310-Aug-03 0:47
deepak2190310-Aug-03 0:47 
GeneralRe: never works Pin
Greg Winkler10-Aug-03 4:18
Greg Winkler10-Aug-03 4:18 
GeneralPerfect work Pin
young_bin20-May-03 16:11
young_bin20-May-03 16:11 
GeneralRe: Perfect work Pin
Greg Winkler20-May-03 16:33
Greg Winkler20-May-03 16:33 
Thanks for the feedback. Could you clarify your question again?

Greg

Greg Winkler
http://www.intelligentsystems.net
GeneralRe: Perfect work Pin
young_bin21-May-03 3:04
young_bin21-May-03 3:04 
GeneralRe: Perfect work Pin
Greg Winkler10-Aug-03 4:26
Greg Winkler10-Aug-03 4:26 
GeneralTwo compile errors. Pin
WREY8-May-03 0:32
WREY8-May-03 0:32 
GeneralRe: Two compile errors. Pin
Greg Winkler8-May-03 3:18
Greg Winkler8-May-03 3:18 
GeneralRe: Two compile errors. Pin
Anonymous6-Aug-04 10:28
Anonymous6-Aug-04 10:28 
GeneralOCX and DLL Pin
Talex31-Jul-02 22:51
Talex31-Jul-02 22:51 
QuestionIs it a problem of time? Pin
Paul Selormey14-Jun-01 14:26
Paul Selormey14-Jun-01 14:26 
AnswerRe: Is it a problem of time? Pin
Greg Winkler14-Jun-01 17:51
Greg Winkler14-Jun-01 17:51 
AnswerRe: Is it a problem of time? Pin
Greg Winkler15-Jun-01 9:58
Greg Winkler15-Jun-01 9:58 
GeneralRe: Is it a problem of time? Pin
Paul Selormey16-Jun-01 1:02
Paul Selormey16-Jun-01 1:02 
AnswerI want to add a menu to it , how..? Pin
10-Jul-01 20:34
suss10-Jul-01 20:34 
GeneralRe: I want to add a menu to it , how..? Pin
Greg Winkler18-Jul-01 6:57
Greg Winkler18-Jul-01 6:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.