Click here to Skip to main content
Click here to Skip to main content

Resize/Reposition the Controls in a Dialog at your Pleasure

, 1 Mar 2006
Rate this:
Please Sign up or sign in to vote.
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:

    #include <span class="code-string">"SizingDialog.h"</span>
    #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.

    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.

    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.

    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.

    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.

dSelfGain The size gain of a control relative to the dialog window size gain in the dimension specified by bHori.
dBorderIntervalRate The 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.
bHori The 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:

    dRateLeft The moving rate of a control's left border to the right
    dRateRight The 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, XY The whole control moves following the dialog's right/bottom border.
    C, CX, CY, CXY Only 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, RXY The 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?

this->AddResizableCtrl(NULL);

or

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:

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:

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:

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)

Share

About the Author

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

Comments and Discussions

 
GeneralRe: Fix for Visual C++ 6.0! PinmemberXia Xiongjun21-Mar-07 7:34 
GeneralRe: Fix for Visual C++ 6.0! Pinmembercristitomi21-Mar-07 21:56 
QuestionProblem with Combo box control Pinmember123rajesh25-Oct-06 20:03 
AnswerRe: Problem with Combo box control Pinmemberandrewtruckle21-Mar-07 5:34 
GeneralRestoreWindowPosition Pinmemberandrewtruckle26-Sep-06 1:15 
QuestionWhat about SDI - Applications? Pinmemberenne8717-Sep-06 1:45 
AnswerRe: What about SDI - Applications? PinmemberXia Xiongjun17-Sep-06 2:26 
GeneralRe: What about SDI - Applications? Pinmemberenne8717-Sep-06 11:35 
QuestionHow can i create a resizeable dialog in my existing project Pinmemberpremkamalg28-Jun-06 21:29 
GeneralWonderful, bravo ! Added a little correction for VC8 Pinmemberandré_k14-Jun-06 2:03 
GeneralRe: Wonderful, bravo ! Added a little correction for VC8 Pinmemberandrewtruckle28-Feb-08 23:37 
GeneralRe: Wonderful, bravo ! Added a little correction for VC8 PinmemberPriya_Sundar20-Jul-08 22:57 
GeneralHi, works fine. NEED ONE MORE THING Pinmemberkevikev202030-May-06 0:05 
GeneralRe: Hi, works fine. NEED ONE MORE THING Pinmemberkevikev202030-May-06 0:34 
GeneralRe: Hi, works fine. NEED ONE MORE THING PinmemberXia Xiongjun30-May-06 0:43 
GeneralRe: Hi, works fine. NEED ONE MORE THING PinmemberWernight15-Jun-08 8:40 
GeneralNice, but a few caveat Pinmemberpierrebai13-Mar-06 10:43 
GeneralRe: Nice, but a few caveat PinmemberXia Xiongjun15-Mar-06 0:28 
GeneralRe: Nice, but a few caveat Pinmemberpierrebai16-Mar-06 8:53 
GeneralRe: Nice, but a few caveat PinmemberXia Xiongjun16-Mar-06 16:16 
GeneralRe: Nice, but a few caveat Pinmemberzuwena28-Nov-07 1:51 
GeneralLink Errors PinmemberRahulOP9-Mar-06 21:36 
GeneralRe: Link Errors PinmemberXia Xiongjun9-Mar-06 23:35 
GeneralRe: Link Errors PinmemberRahulOP10-Mar-06 0:29 
GeneralRe: Link Errors PinmemberXia Xiongjun10-Mar-06 2:13 

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

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

| Advertise | Privacy | Mobile
Web01 | 2.8.140827.1 | Last Updated 1 Mar 2006
Article Copyright 2005 by Xia Xiongjun
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid