Click here to Skip to main content
15,885,216 members
Articles / Desktop Programming / MFC
Article

Enumerate Controls In a Dialog Box or FormView

Rate me:
Please Sign up or sign in to vote.
3.55/5 (26 votes)
9 Aug 20055 min read 79.8K   1.1K   23   8
Enumerate all controls in a dialog box or form view.

Introduction

Once again, a project I'm working on called for a feature (or characteristic, if you like that word better) which I'd never had to implement - centering the controls in a form view while maintaining their relative positions to one another.

To save time, my first stop was here at CodeProject to see if anyone else had done it before me. Imagine my surprise when I found nothing on this site, or even in MSDN about doing something like this. My search was not what I would call exhaustive, but I spent about 30 minutes looking for something that would serve my purposes. What did I find? Nada. Zero. Zilch. A big old-fashioned goose egg.

So here I am, forcing you to suffer through my bio once more so that I might spread some knowledge amongst those of you looking for code that performs this functionality. For the nit pickers, I even included the source code for a sample project.

How it works

Our project was a SDI kiosk-style application, in that it is full screen with no Titlebar, menu, toolbar, or status bar. It also hides the task bar. The user is forced from screen to screen with no deviation from his intended path. Essentially, each screen is a different CFormView with a different set of controls on each page. One of the things we're not absolutely sure of is what screen resolution the workstations will be configured for.

To address this concern, we simply created the dialog templates used by the form views to be whatever size we needed them to be, and then wrote some code to center the whole shebang on the view when it's displayed. In order to do that, we needed to be able to move each control, regardless of how many there were or what their ID's were (yes, each form had different controls, so this particular aspect was a must-do).

Fortunately, the Windows API has functions for doing exactly what we need to do. What we need to do is enumerate through the child Windows of the form view.

The cool part is that once you have the control's hWnd, you can revert back to MFC and manipulate the controls themselves (including their data). Here's the meat of the code, and is all you need to enumerate through all of the view's (or dialog's) controls:

// get the top-most window in the 
// chain of child windows
HWND hwnd = ::GetTopWindow(this->GetSafeHwnd());
// while we have a valid hwnd, 
// loop through all child windows
while (hwnd)
{
    // do something with the hwnd
    // and get the next child control's hwnd
    hwnd = ::GetNextWindow(hwnd, GW_HWNDNEXT);
}

The sample app provided with this article does a couple of things inside this while loop - turns the anchor frame on and off, changes the text in a static string, and re-centers the controls each time the window is re-sized.

HWND hwnd = ::GetTopWindow(this->GetSafeHwnd());
while (hwnd)
{
    // you can even get the ID of the dialog 
    // control you're moving in order 
    // to perform special processing
    UINT nID = ::GetDlgCtrlID(hwnd);
    // if the anchor frame is visible, 
    // hide it, or if not, show it
    if (nID == nAnchorID)
    {
        GetDlgItem(nID)->ShowWindow(GetDlgItem(nID)->
                   IsWindowVisible()?SW_HIDE:SW_SHOW);
    }
    // change the text to all upper case and all 
    // lower case, depending on current state
    else if (nID == IDC_UPLOW_CASE)
    {
        CString sText;
        GetDlgItem(nID)->GetWindowText(sText);
        sText = (sText == "all lowercase") ? 
               "ALL UPPERCASE" : "all lowercase";
        SetDlgItemText(nID, (LPCTSTR)sText);
    }

    // move the window to it's new position
    CRect rect;
    ::GetWindowRect(hwnd, &rect);
    ScreenToClient(&rect);
    ::MoveWindow(hwnd, rect.left + nXDiff, 
          rect.top + nYDiff, rect.Width(), 
          rect.Height(), TRUE);
    // get the next window in the chain
    hwnd = ::GetNextWindow(hwnd, GW_HWNDNEXT);
} // while (hwnd)

A tangent - the centering code

While the centering code is not supposed to be the real focus of this article, I figured I'd describe it briefly since I'm already typing like a madman. The first thing I did was to create an intentionally small template, and then create a static frame around the edge of the template. This control is my anchor control, and is what the formview will use as a reference for relocating all of the other controls.

The formview uses the anchor frame to calculate how far the frame is moved in order to keep itself centered as the window is re-sized. This is essential for the relocation function to work. Once the anchor frame's new x/y position is determined, I enumerate the controls of the form view and position each one at the offset calculated earlier.

The result is that the controls will be centered as long as the window is larger than the original template. If the view is smaller than the template, scrollbars will be displayed, and the template will be positioned in such a way as to be oriented in the top/left corner of the parent window.

Interesting findings and caveats

  • It's perfectly logical to assume that triggering the relocation code would best be implemented from within OnSize(). However, when I did that, I found that even after waiting for the hWnd to be valid, I was getting a CRect that was essentially a null rectangle (CRect(0,0,0,0)). I found that while initializing a MFC SDI app, the view's OnSize() method is called four times!

    The fourth time was after the call to OnInitialUpdate(), so I put a bool variable (m_bCanBeRelocated, and set to false in the constructor) in the class, and then set it to true at the end of OnInitialUpdate(). At that point, you can be assured that you'll get valid rectangle info when you call GetClientRect().

  • When the view was sized smaller than the template, the scrollbars would be displayed as expected. However, if you scroll one of the scrollbars, and then resize again (while keeping the view smaller than the template), the controls would be incorrectly positioned so that they were partially off the left and/or top sides of the view.

    To resolve this issue, I simply called SetScrollPos() to position the scroller at 0,0. I know it's a hack work-around, but it resolved the issue and this article isn't really about the centering code.

Final words

If you're going to vote this article, leave your politics at home and be a grown-up and vote the article on it's own merit. It's not fair to me or others if you pull some puerile voting bullcrap simply because you don't agree with my political views. If you want to talk politics, we can take up as much room as we need in the soapbox forum here on CodeProject.

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
Software Developer (Senior) Paddedwall Software
United States United States
I've been paid as a programmer since 1982 with experience in Pascal, and C++ (both self-taught), and began writing Windows programs in 1991 using Visual C++ and MFC. In the 2nd half of 2007, I started writing C# Windows Forms and ASP.Net applications, and have since done WPF, Silverlight, WCF, web services, and Windows services.

My weakest point is that my moments of clarity are too brief to hold a meaningful conversation that requires more than 30 seconds to complete. Thankfully, grunts of agreement are all that is required to conduct most discussions without committing to any particular belief system.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Member 1101076619-Feb-15 5:38
Member 1101076619-Feb-15 5:38 
GeneralRotating a dialog 90 degrees Pin
AlexEvans11-Jan-06 10:07
AlexEvans11-Jan-06 10:07 
GeneralRe: Rotating a dialog 90 degrees Pin
#realJSOP3-Aug-06 2:59
mve#realJSOP3-Aug-06 2:59 
GeneralCentering Pin
PJ Arends9-Aug-05 11:38
professionalPJ Arends9-Aug-05 11:38 
GeneralRe: Centering Pin
#realJSOP9-Aug-05 15:31
mve#realJSOP9-Aug-05 15:31 
GeneralRe: Centering Pin
PJ Arends9-Aug-05 16:49
professionalPJ Arends9-Aug-05 16:49 
GeneralRe: Centering Pin
Rob Manderson9-Aug-05 22:58
protectorRob Manderson9-Aug-05 22:58 
GeneralRe: Centering Pin
#realJSOP9-Aug-05 23:42
mve#realJSOP9-Aug-05 23:42 

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.