Click here to Skip to main content
15,867,488 members
Articles / Desktop Programming / MFC
Article

CMagDialog

Rate me:
Please Sign up or sign in to vote.
4.64/5 (12 votes)
9 Nov 2005CPOL3 min read 75.5K   3.4K   55   11
CMagDialog: a class to enable dialogs to dock side by side.

Screenshot of demo application

Introduction

I started to write this class because I'm often in need to popup additional dialogs around the main one. Often these dialogs can give some trouble to the user; for example, he must move/close them one by one... A solution that could give the application a more solid aspect and that could make the management of the various windows easier could be, to dock all dialogs side by side (like Winamp does, for example).

Background

First of all: when I talk about parent dialog I mean the main dialog and the children are dialogs that can dock to the parent. I know that this isn't exactly the meaning that MS gives to these words but I can't think anything better ;) The class code is really simple. All my work was focused on two points: "the magnetic field" management that enables dialogs to dock, and avoid flickering when windows are forced to dock (i.e. you are inside the field ;)). The first point wasn't a real problem: my solution was to build around the parent dialog four rectangles (one for each side); if the center point of the correspondent child window's edge is inside the rectangle the child is docked.

Here is a snapshot of the code:

//m_pMagParentDlg: pointer to parent window
//pRect: pointer to child window rect
//m_dwMagType: Type of dock (left,right,top,bottom or any side...)

m_pMagParentDlg->GetWindowRect(rectParent);

// Magnetic fields!
rectRight = CRect(rectParent.right-rHight,rectParent.top-rWidth,
     rectParent.right+rHight,rectParent.bottom+rWidth);
//Right magnetic field of parent dialog

curPl = CPoint(pRect->left, 
        pRect->top+(pRect->bottom-pRect->top)/2);
//center point of child's left edge

if ((m_dwMagType == DKDLG_RIGHT || m_dwMagType == DKDLG_ANY) && 

     rectRight.PtInRect(curPl))
{
   m_pMagParentDlg->DockMagneticDialog(this,DKDLG_RIGHT);
   nEdge        = DKDLG_RIGHT;    
   m_bDocked    = TRUE;
}

I spent some time to find a solution to the second problem (avoid flickering). I wanted to reproduce the same effect of Winamp dialogs: childrent that go inside the "magnetic field" of the parent will be pushed to dock. The code behind this effect is simple (a MoveWindow is enough...). The real problem is: where to put the code? Using the ON_WM_MOVING message is bad because this message is cast after the actual window movement so the resulting effect is really ugly (more than a simple flicker!). After some research, I found the right message to catch: ON_WM_WINDOWPOSCHANGING.

The prototype of this function is:

void CMagDialog::OnWindowPosChanging(WINDOWPOS* lpwndpos)

where lpwndpos is a structure that contain the coordinates to which the window will be moved. Filtering these coordinates according to my need is the trick.

void CMagDialog::OnWindowPosChanging(WINDOWPOS* lpwndpos)
{
    if ((m_bDocked) &  // This is a fix on movement... 
        (m_pMagParentDlg != NULL) &&
         // I need it only if the windows is docked
         // and I'm trying to move it:
        (m_bDisablePosFix == FALSE))
        // when this window is dragged
        // by parent window I don't need this fix
    {
      CRect tmpRect,rectParent;
      GetWindowRect(tmpRect);
      m_pMagParentDlg->GetWindowRect(rectParent);
      if (nEdge == DKDLG_RIGHT)
      {
        tmpRect.MoveToXY(rectParent.right,lpwndpos->y);
    
      }
    }

    lpwndpos->x  = tmpRect.left;
    lpwndpos->y  = tmpRect.top;
    lpwndpos->cx = tmpRect.Width();
    lpwndpos->cy = tmpRect.Height();

    CDialog::OnWindowPosChanging(lpwndpos);
    //commit changes
}

Using the code

To use magnetic dialogs in your project, simply derive both the parent dialog and the children (see the Background section about the definition of parent and children) from the CMagDialog class. The children must be all modeless dialogs and should be defined inside the parent dialog class (the same thing happens when you are using dockable windows in a DOC/View architecture). Then you must call AddMagneticDialog and EnableMagnetic methods in the parent dialog for each child.

Let's say that m_MyMagDlg is a modeless dialog declared in theparent dialog class. The OnInitDialog will look like:

BOOL CCMagTestDlg::OnInitDialog()
{
   //this child will pop up docked on right side of parent window
   AddMagneticDialog(&m_MyMagDlg,TRUE,DKDLG_RIGHT);
   //and can dock to any side
   m_MyMagDlg.EnableMagnetic(DKDLG_ANY,this);
   m_MyMagDlg.Create(IDD_DIALOG_LIBPREVIEW,this);
   return TRUE;
}

As you can see in the demo, the class also remembers the relative position between the children and the parent so, if you close one child and move around the parent, when you will show the child again, it will placed in the same relative position that it had before.

Overview

These are the definitions of the two public methods of CMagDialog:

void AddMagneticDialog(CMagDialog* pDialog, 
         BOOL bDocked = FALSE,DWORD dwMagWhere =0);
  • PDialog - to child dialog.
  • bDocked - if true, the child window popup is already docked.
  • dwMagWhere - where the dialog docks; can have the following values:
    • DKDLG_ANY
    • DKDLG_LEFT
    • DKDLG_RIGHT
    • DKDLG_TOP
    • DKDLG_BOTTOM
void EnableMagnetic(DWORD dwMagType,CMagDialog* pMagParentDlg);
  • dwMagType - where the dialog can dock; values are the same as dwMagWhere described previously.
  • pMagParentDlg - pointer to the parent dialog window.

Remarks:

dwMagWhere refers to the parent's window side. So DKDLG_LEFT means that children will docked to the left side of the parent window.

History

2005/11/07

  • First public release.

License

This program/code is free and provided "as is" without any expressed or implied warranty. Use at your own risk!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Engineer
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
SuggestionConvert to template class Pin
Vachaun226-Jun-12 3:01
Vachaun226-Jun-12 3:01 
GeneralRe: Convert to template class Pin
Francesco Aruta10-Jun-12 0:55
Francesco Aruta10-Jun-12 0:55 
GeneralInteresting idea... Pin
Destiny77725-May-10 11:35
Destiny77725-May-10 11:35 
GeneralA small bug Pin
bluewater020923-Oct-06 16:48
bluewater020923-Oct-06 16:48 
GeneralRe: A small bug Pin
Francesco Aruta24-Oct-06 9:29
Francesco Aruta24-Oct-06 9:29 
QuestionChild Dialogs? Pin
peterdrozd5-Oct-06 11:14
peterdrozd5-Oct-06 11:14 
Generalthis way Pin
p3_1410-Feb-06 4:24
p3_1410-Feb-06 4:24 
GeneralDoes it work with VC 6.0 Pin
Varchas8-Jan-06 23:46
Varchas8-Jan-06 23:46 
I tried to put it on my code that is VC 6.0. It did not compile saying MoveToXY() is not a member function in CRect. I tried CDC because it has a MoveTo method. But then it is obvious that CDC cannot replicate what your code has done with CREct. How should I go about it? Please help me
GeneralRe: Does it work with VC 6.0 Pin
Francesco Aruta9-Jan-06 21:54
Francesco Aruta9-Jan-06 21:54 
GeneralRe: Does it work with VC 6.0 Pin
kbomb9875-Apr-06 16:55
kbomb9875-Apr-06 16:55 
GeneralRe: Does it work with VC 6.0 Pin
Francesco Aruta6-Apr-06 11:52
Francesco Aruta6-Apr-06 11:52 

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.