Click here to Skip to main content
15,891,409 members
Articles / Desktop Programming / MFC

Control Positioning and Sizing using a C++ Helper Class

Rate me:
Please Sign up or sign in to vote.
4.88/5 (43 votes)
20 Sep 2005CPOL11 min read 205.3K   4.1K   97  
Add layout management of controls to a CWnd or CDialog using a C++ helper class.
// Filename: TestDefaultDlg.cpp
// 2005-08-04 nschan Initial revision.
// 2005-09-07 nschan In OnSize(), call the scroll helper OnSize() first.
//                   Adjusted positioning of some controls.

#include "stdafx.h"
#include "TestDefaultDlg.h"
#include "LayoutHelper.h"
#include "ScrollHelper.h"
#include "GroupBox.h"
#include "ImageWnd.h"
#include "resource.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// Fixed aspect ratio for image: 4/3 = 1.333
static const int IMAGE_ASPECT_RATIO = 1333;

BEGIN_MESSAGE_MAP(CTestDefaultDlg, CDialog)
    //{{AFX_MSG_MAP(CTestDefaultDlg)
    ON_WM_MOUSEACTIVATE()
    ON_WM_PAINT()
    ON_WM_HSCROLL()
    ON_WM_VSCROLL()
    ON_WM_MOUSEWHEEL()
    ON_WM_SIZE()
    ON_BN_CLICKED(IDC_ASPECT_RATIO_CHECK, OnMaintainAspectRatioClick)
    ON_BN_CLICKED(IDC_CENTER_IMAGE_CHECK, OnCenterImageClick)
    ON_EN_CHANGE(IDC_STEP_SIZE_EDIT, OnStepSizeEditChange)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

CTestDefaultDlg::CTestDefaultDlg(CWnd* pParent)
    : CDialog(IDD_TEST_DEFAULT_DLG, pParent)
{
    // Dialog data.
    m_maintainAspectRatio = FALSE;
    m_centerImage = FALSE;
    m_stepSize = 0;

    // Group boxes.
    m_imageDisplayBox  = new CGroupBox;
    m_layoutOptionsBox = new CGroupBox;
    m_logMessagesBox   = new CGroupBox;

    // Image wnd.
    m_imageWnd = new CImageWnd(IDB_PARK_IMAGE);

    // Create the layout helper and attach it to this dialog.
    m_layoutHelper = new CLayoutHelper;
    m_layoutHelper->SetLayoutStyle(CLayoutHelper::DEFAULT_LAYOUT);
    m_layoutHelper->AttachWnd(this);

    // Create the scroll helper and attach it to this dialog.
    m_scrollHelper = new CScrollHelper;
    m_scrollHelper->AttachWnd(this);

    // Create the dialog.
    Create(IDD_TEST_DEFAULT_DLG, pParent);
}

CTestDefaultDlg::~CTestDefaultDlg()
{
    delete m_imageDisplayBox;
    delete m_layoutOptionsBox;
    delete m_logMessagesBox;

    delete m_imageWnd;

    delete m_layoutHelper;
    delete m_scrollHelper;
}

void CTestDefaultDlg::PostNcDestroy()
{
    m_layoutHelper->DetachWnd();
    m_scrollHelper->DetachWnd();

    // Delete the C++ instance so the parent does not have
    // to worry about it.
    delete this;
}

void CTestDefaultDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);

    //{{AFX_DATA_MAP(CTestDefaultDlg)
    DDX_Check(pDX, IDC_ASPECT_RATIO_CHECK, m_maintainAspectRatio);
    DDX_Check(pDX, IDC_CENTER_IMAGE_CHECK, m_centerImage);
    DDX_Text(pDX, IDC_STEP_SIZE_EDIT, m_stepSize);
    DDV_MinMaxInt(pDX, m_stepSize, 0, 50);
    //}}AFX_DATA_MAP
}

BOOL CTestDefaultDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    // Create the groupbox for the image wnd.
    CRect rect;
    rect.SetRect(10, 10, 344, 288);
    m_imageDisplayBox->Create(NULL, NULL, WS_CHILD | WS_VISIBLE, rect, this, IDC_IMAGE_DISPLAY_BOX, NULL);
    m_imageDisplayBox->SetText("Image Display");

    // Create the groupbox for layout options.
    rect.SetRect(350, 10, 490, 100);
    m_layoutOptionsBox->Create(NULL, NULL, WS_CHILD | WS_VISIBLE, rect, this, IDC_LAYOUT_OPTIONS_BOX, NULL);
    m_layoutOptionsBox->SetText("Layout Options");

    // Create the groupbox for log messages.
    rect.SetRect(350, 115, 490, 303);
    m_logMessagesBox->Create(NULL, NULL, WS_CHILD | WS_VISIBLE, rect, this, IDC_LOG_MESSAGES_BOX, NULL);
    m_logMessagesBox->SetText("Log Messages");

    // Create the image wnd.
    rect.SetRect(20, 20, 325, 262);
    m_imageWnd->Create(NULL, NULL, WS_CHILD | WS_VISIBLE, rect, this, IDC_IMAGE_WND, NULL);

    // Set the initial step size.
    m_layoutHelper->SetStepSize(m_stepSize);

    // Add all child controls in dialog to the layout helper.
    m_layoutHelper->AddChildControls();

    // Update constraints for image display groupbox.
    CLayoutInfo info;
    info.AddOption(CLayoutInfo::OT_MIN_LEFT,      10);
    info.AddOption(CLayoutInfo::OT_MAX_LEFT,      10);
    info.AddOption(CLayoutInfo::OT_MIN_TOP,       10);
    info.AddOption(CLayoutInfo::OT_MAX_TOP,       10);
    info.AddOption(CLayoutInfo::OT_RIGHT_OFFSET,  10);
    info.AddOption(CLayoutInfo::OT_RIGHT_ANCHOR,  360);
    info.AddOption(CLayoutInfo::OT_BOTTOM_OFFSET, 10);
    m_layoutHelper->AddControl(m_imageDisplayBox, info);

    // Update constraints for image wnd.
    info.Reset();
    info.AddOption(CLayoutInfo::OT_LEFT_OFFSET,   24);
    info.AddOption(CLayoutInfo::OT_TOP_OFFSET,    31);
    info.AddOption(CLayoutInfo::OT_RIGHT_OFFSET,  24);
    info.AddOption(CLayoutInfo::OT_RIGHT_ANCHOR,  360);
    info.AddOption(CLayoutInfo::OT_BOTTOM_OFFSET, 24);
    m_layoutHelper->AddControl(m_imageWnd, info);

    // Update constraints for layout options groupbox.
    info.Reset();
    info.AddOption(CLayoutInfo::OT_LEFT_OFFSET,   0);
    info.AddOption(CLayoutInfo::OT_LEFT_ANCHOR,   360);
    info.AddOption(CLayoutInfo::OT_TOP_OFFSET,    10);
    info.AddOption(CLayoutInfo::OT_RIGHT_OFFSET,  10);
    info.AddOption(CLayoutInfo::OT_BOTTOM_OFFSET, 10);
    info.AddOption(CLayoutInfo::OT_BOTTOM_ANCHOR, 125);
    m_layoutHelper->AddControl(m_layoutOptionsBox, info);

    // Update constraints for log messages groupbox.
    info.Reset();
    info.AddOption(CLayoutInfo::OT_LEFT_OFFSET,   0);
    info.AddOption(CLayoutInfo::OT_LEFT_ANCHOR,   360);
    info.AddOption(CLayoutInfo::OT_TOP_OFFSET,    -3);
    info.AddOption(CLayoutInfo::OT_TOP_ANCHOR,    125);
    info.AddOption(CLayoutInfo::OT_RIGHT_OFFSET,  10);
    info.AddOption(CLayoutInfo::OT_BOTTOM_OFFSET, 10);
    m_layoutHelper->AddControl(m_logMessagesBox, info);

    // Update constraints for log messages listbox.
    info.Reset();
    info.AddOption(CLayoutInfo::OT_LEFT_OFFSET,   13);
    info.AddOption(CLayoutInfo::OT_LEFT_ANCHOR,   360);
    info.AddOption(CLayoutInfo::OT_TOP_OFFSET,    19);
    info.AddOption(CLayoutInfo::OT_TOP_ANCHOR,    125);
    info.AddOption(CLayoutInfo::OT_RIGHT_OFFSET,  24);
    info.AddOption(CLayoutInfo::OT_BOTTOM_OFFSET, 24);
    m_layoutHelper->AddControl(GetDlgItem(IDC_LOG_MESSAGES_LIST), info);

    // Update constraints for step size edit box.
    info.Reset();
    info.AddOption(CLayoutInfo::OT_MIN_WIDTH,  40);
    info.AddOption(CLayoutInfo::OT_MAX_WIDTH,  40);
    info.AddOption(CLayoutInfo::OT_MIN_HEIGHT, 23);
    info.AddOption(CLayoutInfo::OT_MAX_HEIGHT, 23);
    m_layoutHelper->AddControl(GetDlgItem(IDC_STEP_SIZE_EDIT), info);

    return TRUE;  // return TRUE unless you set the focus to a control
                  // EXCEPTION: OCX Property Pages should return FALSE
}

void CTestDefaultDlg::OnOK()
{
    // Empty implementation.
}

void CTestDefaultDlg::OnCancel()
{
    // Empty implementation.
}

int CTestDefaultDlg::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message)
{
    int status = CDialog::OnMouseActivate(pDesktopWnd, nHitTest, message);

    // We handle this message so that when user clicks once in the
    // dialog, it will be given the focus, and this will allow
    // mousewheel messages to be directed to this window.
    SetFocus();

    return status;
}

void CTestDefaultDlg::OnPaint()
{
    CDialog::OnPaint();

    // Only purpose of handling OnPaint is to set the
    // caption text of the MDI child frame.
    CWnd* pFrame = GetParentFrame();
    if ( pFrame != NULL )
        pFrame->SetWindowText("Default Layout Style");
}

void CTestDefaultDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    m_scrollHelper->OnHScroll(nSBCode, nPos, pScrollBar);
}

void CTestDefaultDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    m_scrollHelper->OnVScroll(nSBCode, nPos, pScrollBar);
}

BOOL CTestDefaultDlg::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
    BOOL wasScrolled = m_scrollHelper->OnMouseWheel(nFlags, zDelta, pt);

    return wasScrolled;
}

void CTestDefaultDlg::OnSize(UINT nType, int cx, int cy)
{
    CDialog::OnSize(nType, cx, cy);

    // Initialization: Set the reference/minimum size for layout,
    // and the display size for scrolling.
    if ( m_layoutHelper->GetReferenceSize() == CSize(0,0) )
    {
        CRect rect;
        GetClientRect(&rect);

        if ( rect.Width() > 0 && rect.Height() > 0 )
        {
            m_layoutHelper->SetReferenceSize(rect.Width(), rect.Height());
            m_layoutHelper->SetMinimumSize(rect.Width(), rect.Height());

            m_scrollHelper->SetDisplaySize(rect.Width(), rect.Height());
        }
    }

    m_scrollHelper->OnSize(nType, cx, cy);
    m_layoutHelper->OnSize(nType, cx, cy);

    UpdateLogMessages();
}

void CTestDefaultDlg::OnMaintainAspectRatioClick()
{
    UpdateData(TRUE);

    CLayoutInfo info;
    if ( m_layoutHelper->GetLayoutInfo(m_imageWnd, info) )
    {
        if ( m_maintainAspectRatio )
            info.AddOption(CLayoutInfo::OT_ASPECT_RATIO, IMAGE_ASPECT_RATIO);
        else
            info.RemoveOption(CLayoutInfo::OT_ASPECT_RATIO);

        // Update the layout info for the image wnd.
        m_layoutHelper->AddControl(m_imageWnd, info);

        // Perform the layout so the change is reflected immediately.
        m_layoutHelper->LayoutControls();
        UpdateLogMessages();
    }
}

void CTestDefaultDlg::OnCenterImageClick()
{
    UpdateData(TRUE);

    CLayoutInfo info;
    if ( m_layoutHelper->GetLayoutInfo(m_imageWnd, info) )
    {
        if ( m_centerImage )
        {
            info.AddOption(CLayoutInfo::OT_CENTER_XPOS, 180);
            info.AddOption(CLayoutInfo::OT_CENTER_YPOS, 154);
        }
        else
        {
            info.RemoveOption(CLayoutInfo::OT_CENTER_XPOS);
            info.RemoveOption(CLayoutInfo::OT_CENTER_YPOS);
        }

        // Update the layout info for the image wnd.
        m_layoutHelper->AddControl(m_imageWnd, info);

        // Perform the layout so the change is reflected immediately.
        m_layoutHelper->LayoutControls();
        UpdateLogMessages();
    }
}

void CTestDefaultDlg::OnStepSizeEditChange()
{
    UpdateData(TRUE);

    m_layoutHelper->SetStepSize(m_stepSize);

    // Perform the layout so the change is reflected immediately.
    m_layoutHelper->LayoutControls();
    UpdateLogMessages();
}

void CTestDefaultDlg::UpdateLogMessages()
{
    CListBox* pListBox = (CListBox*)GetDlgItem(IDC_LOG_MESSAGES_LIST);
    if ( pListBox == NULL || !::IsWindow(pListBox->m_hWnd) )
        return;

    pListBox->ResetContent();

    CRect rect;
    m_imageWnd->GetWindowRect(&rect);
    ScreenToClient(&rect);

    CString msg;
    msg.Format("Image position: %d, %d", rect.left, rect.top);
    pListBox->InsertString(-1, msg);
    msg.Format("Image size: %d x %d", rect.Width(), rect.Height());
    pListBox->InsertString(-1, msg);
    msg.Format("Image aspect ratio: %0.3f", 1.0 * rect.Width() / rect.Height());
    pListBox->InsertString(-1, msg);
}

// END

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


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

Comments and Discussions