Pop-up controls anywhere

Sample Image

Environment: VC6, 95/98/NT4

The CPopup class

This class allows you to display controls as popup.
A Popup control is visible only when it has the focus and it can be displayed outside its own frame.

For example, you can display a treectrl under a button. See the PopupSample application to see other uses of this class.

Implementation

The CPopup class derives from CMiniFrameWnd. This is the same-based frame than floating toolbar. This class allows a CFrameWnd or a CMDIFrameWnd to keep activated when a CMiniFrameWnd becomes active. It seems to have 2 active frames at the same time.
Unlike CMiniFrameWnd, CPopup has no title and no border. It's just a frame that contains controls called 'popped' controls.

The simplified class declaration


    class CPopup : public CMiniFrameWnd
    {
    public:
        bool Display (CWnd* pControl, CWnd* pCaller, CRect& rcCaller, IPopupCtrlEvent* pEvents = NULL);

    protected:
        CPopup ();
        virtual ~CPopup () {};

        void EndPopup (bool bAbort = false, bool bSetFocus = true);

        DECLARE_DYNCREATE(CPopup)
    };

How to use this

You have to create dynamic instances because destruction is automatic.
The Display method is the unique public method of the class.
After creating a CPopup object (by the CreateObject runtime method), just call this method to pop-up your control.

    bool CPopup::Display (CWnd* pControl,
                          CWnd* pCaller,
                          CRect& rcCaller,
                          IPopupCtrlEvent* pEvents = NULL);
The first parameter is the control you want to popup. You can specify listbox, listctrl, treectrl, formview, activex, ...

The second parameter allows you to specify a 'caller'. It's used to determine the parent frame of the popup. This object will also be used at the end of popup operation to set focus on it. You can omit it by specifying NULL.

The CRect parameter is the rectangle that specifies the position of the popup in screen coordinates. It may be the rectangle of the caller (as pushbutton) or the rectangle of an item (as toolbar button). It's not just a point because the code applies a little algorithm for best placement of the popup :
By default, the popup is at left and bottom corner of the rectangle, but if there is not enough place to display entirely the control, it can be align at the right side and/or the top side. See the comments into the code.
The size of the popup is the control size before the Display call; it's not the rectangle size.

The last parameter is a pointer to an interface, which defines some callbacks. See description below.

The method returns a Boolean value that indicates if the popup control is correctly displayed or not. So you may just do a VERIFY() with it.

The IPopupCtrlEvent interface


    interface IPopupCtrlEvent
    {
        virtual bool OnInit () = 0;
        virtual void OnShow () = 0;
        virtual void OnHide (bool bAbort) = 0;
        virtual int OnKeyDown (UINT nChar, UINT nRepCnt, UINT nFlags) = 0;
        virtual int OnLButtonDown (UINT nFlags, CPoint point) = 0;
        virtual int OnLButtonUp (UINT nFlags, CPoint point) = 0;
        virtual IPopupCtrlEvent* GetInterfaceOf (HWND hWnd) = 0;
    };
Most of controls you want to popup have to implement this interface (which is not a COM interface !). It's not absolutely necessary, but strongly recommended for correct behaviors.
This interface defines a set of callbacks called by CPopup and implemented by each 'popped' controls.

The OnInit callback is called at the beginning of CPopup::Display method. It allows control to modify itself before being popped.

The OnShow callback is called just after the control becomes visible. Controls can set focus on it (it's not automatic)

The OnHide callback is called when the popup is finished but before it become invisible. The bAbort parameter informs control if the selection was cancelled (by <Escape> key for example)

The three next callbacks are called when some key and mouse events occur on the popped control. They are used to determine when the popup action is finished (by clicking on list item, by pressing <Return> key, ...). See the sample application for some examples of implementation.

The last callback : GetInterfaceOf is used when you pop-up several controls at the same time (In the sample application see the CMySplitterWnd class).
With the CPopup Display method, you can declare only one control to be popped. In the case you popup a dialog (via CFormView), all controls can act to close the popup. So the GetInterfaceOf callback gives to the master popped control (the declared control) the possibility to dispatch actions to child controls.

Downloads

Download demo project - 42 Kb

Download source - 4 Kb

History



Comments

  • Set font

    Posted by Legacy on 02/21/2003 12:00am

    Originally posted by: Manuel D. Jim�nez

    Hello,

    I used this control with CListBox, deriving my class CPopupList from it. I did not create this control from resource editor but with Create function.

    The problem is that I can not set the control's font. I tried to use SetFont in XPopupCtrlEvent::OnInit, in CPopupList::OnCreate and in the caller, after RUNTIME_CLASS(CPopup)->CreateObject(), but it does not work.

    How (or where) must I set the font when resource editor is not used?

    Thanks, and congratullations for the control.

    Reply
  • Possible crash during EndPopup

    Posted by Legacy on 02/28/2002 12:00am

    Originally posted by: Doug Sorensen

    Very helpful class!  I have found that a crash can occur in
    
    the following test in CPopup::OnActivate:

    if ( nState == WA_INACTIVE && m_bDestroy == false )

    The code following the if can fail if the popup has been
    destroyed. EndPopup needs to set m_bDestroy early to
    prevent getting into the above if. I changed the code as
    follows:

    void
    CPopup::EndPopup( bool bAbort, bool bSetFocus )
    {
    bool bDestroy = m_bDestroy; // change
    m_bDestroy = true; // change

    if ( m_pEvents )
    m_pEvents->OnHide( bAbort );

    ASSERT( m_pControl->m_hWnd );
    m_pControl->ShowWindow( SW_HIDE );
    ASSERT( m_hPrevParentCtrl );
    ::SetParent( m_pControl->m_hWnd, m_hPrevParentCtrl );

    // Do a local copy of the pointer because DestroyWindow deletes the
    // object and m_pCaller will not be valid after the delete.
    CWnd *pCaller = m_pCaller;

    if ( bDestroy == false ) //change
    DestroyWindow( );

    if ( bSetFocus && pCaller )
    pCaller->SetFocus( );
    }

    Reply
  • How do i use CFormView derived Class

    Posted by Legacy on 10/01/2001 12:00am

    Originally posted by: Mohit Jain

    I want to use a CFormView derived class as a popup window, but am getting an ASSERTION FAILURE at :
    
    

    bool CPopup::Display (CWnd* pControl, CWnd* pCaller, CRect& rcCaller, IPopupCtrlEvent* pEvents)
    {
    ...
    ...
    m_hPrevParentCtrl = m_pControl->SetParent (this)->GetSafeHwnd();
    ...
    ...
    }

    The assertion is due to the fact the Hwnd of m_pControl is NULL which will be the case for RUNTIME classes.
    I also tried using the overloaded method from the earlier comments but still i get the same error.

    I also tried my own way by instantiating my class as:

    CMyFormView pMotor = (CMyFormView *)RUNTIME_CLASS(CMyFormView )->CreateObject();


    Kindly help and advise.
    THANX in advance

    Reply
  • Useful addition to the class...

    Posted by Legacy on 11/14/1999 12:00am

    Originally posted by: Andrew Forget

    One nice addition to the class is a second Display() method:
    
    

    bool Display( CRuntimeClass* pControlClass,
    CWnd* pCaller,
    CRect& in_rcCaller,
    IPopupCtrlEvent* in_pEvents = NULL ) ;

    which is defined like this:

    bool CPopup::Display(
    CRuntimeClass* pControlClass,
    CWnd* pCaller,
    CRect& rcCaller,
    IPopupCtrlEvent* pEvents )
    {
    if( pControlClass == NULL )
    {
    delete this ;
    return false ;
    }

    if( ! pControlClass->IsDerivedFrom( RUNTIME_CLASS( CWnd ) ) )
    {
    delete this ;
    return false ;
    }

    CWnd* pControl = ( CWnd* )in_pControlClass->CreateObject() ;

    if( ! Display( pControl, pCaller, rcCaller, pEvents ) )
    {
    delete l_pControl ;

    return false ;
    }

    return true ;
    }

    This allows your to have other classes (such as CPopupButton) which take a CRuntimeClass too.

    ~Andy

    Reply
  • VC++ 5 Support Available?

    Posted by Legacy on 10/28/1999 12:00am

    Originally posted by: Robert Pearmain

    It will not compile in VC5 because of the lack of support for the Interface declaration despite me having the lastest SDK.

    Are there any mods I can make to function in VC++ 5.

    Reply
  • fantastic

    Posted by Legacy on 10/27/1999 12:00am

    Originally posted by: Franz Brunner

    Great!

    Reply
  • fantastic

    Posted by Legacy on 10/27/1999 12:00am

    Originally posted by: Franz Brunner

    Great!

    Reply
  • fantastic

    Posted by Legacy on 10/27/1999 12:00am

    Originally posted by: Franz Brunner

    Great!

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

Most Popular Programming Stories

More for Developers

RSS Feeds