Click here to Skip to main content
15,860,972 members
Articles / Desktop Programming / MFC
Article

Resize/Reposition the Controls in a Dialog at your Pleasure

Rate me:
Please Sign up or sign in to vote.
4.55/5 (38 votes)
1 Mar 2006CPOL7 min read 236.9K   8.8K   65   83
You can resize or reposition the controls in your dialog derived from CSizingDialog to anywhere you wish just by specifying some simple strings or numbers. In addition, most kinds of controls almost don't flicker when moving, which is often a problem in some other solutions.
Sample Image - maximum width is 600 pixels
1. Original Dialog


Sample Image - maximum width is 600 pixels
2. Dialog after being resized

Introduction

A dialog is a simple but also important user interface which can be used as the complementarities in a SDI or MDI project. In many cases, it's also used as the main window of the project. Unfortunately the MFC architecture doesn't provide methods for resizing the controls in a dialog. When you change the size of a resizable dialog, all the controls stay still in the top-left corner.

Many people put forward several kinds of solutions, but they hardly meet my need. Some solutions use very complex techniques that are hard to understand and so it's hard to improve and extend. Some solutions are too simple to meet even a slightly complex need. Some are not very convenient for secondary developers to use. And most of them move the controls to the destination positions without thinking of the flicker of controls.

The following class, CSizingDialog, gives you some simple but powerful methods to change the situation in a way which can make your dialogs more usable and professional. I thank many people for their intelligent work of giving me many good elicitations.

Background

The movement of controls in a dialog can be simply regarded as the movement of the four borders of controls, which make the whole control resize and reposition in a dialog. The class CSizingDialog is just based on such a simple fact and is encapsulated for better use.

Using the Code

Let's start with how to use the class. The details will be discussed later:

  1. Change the base class of your dialog class (for example CYourDlg) from CDialog to baseCYourDialog where CDialog occurs as the base class of CYourDlg. Add the following code to header file YourDlg.h:

    C++
    #include "SizingDialog.h"
    #ifndef baseCYourDialog
    #define baseCYourDialog CSizingDialog
    #endif

    Of course, you can use CSizingDialog to take the place of CDialog directly.

  2. Copy files SizingDialog.h and SizingDialog.cpp to your project directory, and add SizingDialog.cpp to your project.
  3. Now you can specify the controls you want to move or size in the function CYourDlg::OnInitDialog() just as the following examples do. The functions will be explained below.

    C++
    this->AddResizableCtrl(IDC_EDIT1, _T("(-0.5)C"));
    this->AddResizableCtrl(IDC_STATIC1, _T("X+CY"));
    this->AddResizableCtrlArray(IDC_CHECK1, IDC_CHECK3, 0.0, 1.0, FALSE);

That's all to use the code.

Functions to Define the Moving Way of Controls

  1. Add a single control specified by nID.

    C++
    void AddResizableCtrl(UINT nID, LPCTSTR lpszString = NULL);
    void AddResizableCtrl(UINT nID,
            double dRateLeft, double dRateRight,
            double dRateTop, double dRateBottom);

    lpszString and dRateLeft, dRateRight, dRateTop, dRateBottom are parameters to describe the moving way of controls. They will be discussed in details below.

    Tips: If nID == NULL, all the controls in the dialog will be added using the same parameters.

  2. Add (or modify) a control-range where IDs range from nIDStart to nIDEnd or from nIDEnd to nIDStart.

    C++
    void AddResizableCtrlRange(UINT nIDStart, UINT nIDEnd,
            LPCTSTR lpszString = NULL);
    void ModifyResizableCtrlRange(UINT nIDStart, UINT nIDEnd,
            LPCTSTR lpszString = NULL);
    void AddResizableCtrlRange(UINT nIDStart, UINT nIDEnd,
            double dRateLeft, double dRateRight,
            double dRateTop, double dRateBottom);
  3. Add a control-range where IDs range from nIDStart to nIDEnd or from nIDEnd to nIDStart.

    C++
    void AddResizableCtrlArray(UINT nIDStart, UINT nIDEnd,
            double dSelfGain = 0.0, double dBorderIntervalRate = 1.0,
                BOOL bHori = TRUE);

The difference between this control-set definition method and the former methods is that in the former cases, all controls use the same moving parameters so they all behave the same while in the last case the moving parameters are not given directly and have to be calculated from other parameters. The typical result is that the controls' moving parameters are gradually changed from the first control to the last control.

The parameters of this function will be discussed separately below. You may also refer to moving parameters explanation.

dSelfGainThe size gain of a control relative to the dialog window size gain in the dimension specified by bHori.
dBorderIntervalRateThe proportion between the "border" size, offset between the first control's left border and its original position, and the interval size between two controls in horizontal direction. The case of vertical direction is similar.
bHoriThe direction in which the controls are rearranged.

Tips:

  • Set dBorderIntervalRate = 0.0 to keep Ctrls near the border Close To Border
  • Set dBorderIntervalRate = 1.0 to keep Ctrls Rearranged Uniformly
  • Set dBorderIntervalRate = 1.0e20 to keep Intervals between Ctrls Constant

Moving Parameters Explanation

The moving offset of the right/bottom border of the dialog window relative to its original position is regarded as 1.0 in the follow definition.

  1. Defined using numbers:

    dRateLeftThe moving rate of a control's left border to the right
    dRateRightThe moving rate of a control's right border to the right

    The vertical case is similar.

  2. Defined using strings:

    A right defined string can be divided into different sections which are connected by the character '+' (other characters can also be specified). Each section is made up of a coefficient and a key word such as 0.5X, (-0.3)C, (-15.5RXY). If the coefficient is not specified, it is set to 1.0. The string is not sensitive to upper/lower characters.

    As you can guess, in a key word, X represents the horizontal dimension, while Y represents the vertical dimension. And if there is no X or Y, the definition is applied to both the horizontal dimension and vertical dimension.

    X, Y, XYThe whole control moves following the dialog's right/bottom border.
    C, CX, CY, CXYOnly the right/bottom border of the control moves following the dialog's right/bottom border. The left/upper border of the control stays still when the dialog is resizing.
    R, RX, RY, RXYThe whole control will keep constant proportion to the dialog's client rectangle when the dialog is resizing.

Now, all the resizing and repositioning problems are discussed. Note that if a control's moving way in some direction is defined more than once, only the last definition can work.

Guess what the following sentences can bring about?

C++
this->AddResizableCtrl(NULL);

or

C++
this->AddResizableCtrl(NULL, _T("R"));

If you are not sure, add one of them to your CYourDlg::OnInitDialog() just before the sentence return TRUE; to see the effect. Of course, the first two steps in using the code are required to compile correctly.

Points Of Interest

Now I will discuss something about how to avoid control flicker when moving the dialog as far as possible. Anyone who just wants to use the class can jump over this section.

The first improvement is to set the redraw flag to FALSE before moving controls and restore it to TRUE after moving the controls as follows:

C++
pCtrl->SetRedraw(FALSE);
pCtrl->MoveWindow(&rcCtrl);
pCtrl->SetRedraw(TRUE);

(The original code is different since the visible state of the control is considered.)

The second improvement is in the way of erasing the old rectangle (for instance, rcCtrlOld) of a control. Instead of using this->InvalidateRect(&rcCtrlOld), we just update the area which belongs to rcCtrlOld and at the same time DOESN'T belong to rcCtrl where the control stays currently. This is implemented by the following function:

C++
void CSizingDialog::InvalidateCtrlBorder(     // XR: erase lpRectOld - lpRectNew
        LPCRECT lpRectOld, LPCRECT lpRectNew, BOOL bErase /*= TRUE*/)

There are still problems when you move a groupbox control. The previous two steps can't solve the problems. There are some old traces which remain IN the new groupbox control. But if you don't apply these two steps, the groupbox control and all the other controls in the groupbox control will flicker severely.

My solution is to divide the groupbox control's area into 9 parts. Each part is an individual rectangle. The work is done by the following function:

C++
void GetGroupBoxRgn(const CWnd* pGBox, const CWnd* pParent, CRect* pRc)

pRc points to a CRect array with 9 elements. The meaning of pRc's elements in a GroupBox is illustrated as follows, 0 means whole rectangle.

//        ______________9____________
//       |                           |
//       7                           8
//       |_____5_____[ 4 ]_____6_____|
//       |                           |
//       |                           |
//       |                           |
//       |                           |
//       2                           3
//       |                           |
//       |                           |
//       |                           |
//       |                           |
//       |_____________1_____________|

Note that 7th, 8th and 9th part of the groupbox control do not exist in a groupbox with the default style.

So a Groupbox can be regarded as 9 "controls". Each of them can be applied the first two steps and the problem is solved. But I don't consider it as a good solution.

Of the 24 kinds of typical controls, the following kinds of controls still flicker more or less when moving even after applying the two steps. They are:

  • Combobox
  • Listbox
  • Slider control
  • List control
  • Tree control
  • Tab control

More effort is needed to solve this problem.

Revision History

  • 8th October, 2005 - V1.2: A critical problem has been fixed when the class is used in a modeless dialog.
  • 7th September, 2005 - V1.1: Four statements which are not supported by VC6 were modified, so the class now can be used in a project developed by VC6.
  • 5th September, 2005 - V1.0

License

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


Written By
Software Developer
China China
_____________________________
Xia Xiongjun loves this site. Smile | :)

Comments and Discussions

 
GeneralLink Errors Pin
RahulOP9-Mar-06 21:36
RahulOP9-Mar-06 21:36 
GeneralRe: Link Errors Pin
Xia Xiongjun9-Mar-06 23:35
Xia Xiongjun9-Mar-06 23:35 
GeneralRe: Link Errors Pin
RahulOP10-Mar-06 0:29
RahulOP10-Mar-06 0:29 
GeneralRe: Link Errors Pin
Xia Xiongjun10-Mar-06 2:13
Xia Xiongjun10-Mar-06 2:13 
GeneralVery nice. Pin
Cui Sheng1-Mar-06 14:08
Cui Sheng1-Mar-06 14:08 
GeneralRe: Very nice. Pin
Xia Xiongjun1-Mar-06 14:43
Xia Xiongjun1-Mar-06 14:43 
Questionmiran Pin
Member 112048811-Oct-05 19:51
Member 112048811-Oct-05 19:51 
AnswerRe: miran Pin
Xia Xiongjun13-Oct-05 8:13
Xia Xiongjun13-Oct-05 8:13 
I will think about it later.
I think you need to derive your own DialogBar class from CDialogBar, and then to add WM_SIZE message handler to the new class. As to how to resize the controls, your may refer to the function void ResizeControls() in the CSizingDialog.
Best wishes.

_____________________________
Xia Xiongjun loves this site. Smile | :)
Generala little sly trick to avoid flicker when dragging… Pin
vfilll11-Oct-05 9:10
vfilll11-Oct-05 9:10 
GeneralBug on modifying a control options that already added [and its solution :)] Pin
Behzad Ebrahimi9-Oct-05 22:11
Behzad Ebrahimi9-Oct-05 22:11 
AnswerRe: Bug on modifying a control options that already added [and its solution :)] Pin
Xia Xiongjun10-Oct-05 0:10
Xia Xiongjun10-Oct-05 0:10 
GeneralNot working for Modeless Dialog, pls help Pin
kirankumar4-Oct-05 23:48
kirankumar4-Oct-05 23:48 
AnswerRe: Not working for Modeless Dialog, pls help Pin
Xia Xiongjun7-Oct-05 6:27
Xia Xiongjun7-Oct-05 6:27 
AnswerRe: Not working for Modeless Dialog, pls help Pin
Xia Xiongjun7-Oct-05 9:02
Xia Xiongjun7-Oct-05 9:02 
QuestionWhat about DOCK option !? Pin
Behzad Ebrahimi3-Oct-05 5:26
Behzad Ebrahimi3-Oct-05 5:26 
AnswerRe: What about DOCK option !? Pin
Xia Xiongjun3-Oct-05 6:25
Xia Xiongjun3-Oct-05 6:25 
GeneralVery Nice Article! The first resizable class that support AutoFit. Pin
Behzad Ebrahimi21-Sep-05 0:51
Behzad Ebrahimi21-Sep-05 0:51 
GeneralRe: Very Nice Article! The first resizable class that support AutoFit. Pin
Xia Xiongjun21-Sep-05 2:57
Xia Xiongjun21-Sep-05 2:57 
GeneralAnother idea Pin
andrewtruckle7-Sep-05 9:41
andrewtruckle7-Sep-05 9:41 
GeneralRe: Another idea Pin
andrewtruckle7-Sep-05 9:52
andrewtruckle7-Sep-05 9:52 
GeneralRe: Another idea Pin
Xia Xiongjun7-Sep-05 9:58
Xia Xiongjun7-Sep-05 9:58 
GeneralRe: Another idea Pin
Xia Xiongjun7-Sep-05 9:54
Xia Xiongjun7-Sep-05 9:54 
GeneralRe: Another idea Pin
andrewtruckle7-Sep-05 10:02
andrewtruckle7-Sep-05 10:02 
GeneralRe: Another idea Pin
andrewtruckle7-Sep-05 10:21
andrewtruckle7-Sep-05 10:21 
GeneralRe: Another idea Pin
Xia Xiongjun7-Sep-05 10:38
Xia Xiongjun7-Sep-05 10:38 

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.