MFC界面设计----改变界面大小使其中控件随其界面大小变化的方法

【写在前面】

       平常在编写界面程序时,使窗口中的控件随窗口大小改变而改变,只需调用WM_SIZE消息,在消息函数OnSize中进行处理,比如如下函数,就是使静态文本控件动态变化,但是这种方法计算繁琐不说,在改变控件位置时,此部分又需要从新计算,相当麻烦,下面我来介绍一下另一种方法。

     if (GetDlgItem(IDC_STATIC2))
     {
         CRect rt;
         GetClientRect(rt);
         rt.top = rt.bottom - 123;
         rt.bottom -=105;
        float x = (float)(rt.Width()*1.0/m_rect.Width());
         rt.left += 10;
         rt.right = (LONG)(rt.left + 80*x);
         GetDlgItem(IDC_STATIC2)->MoveWindow(rt);
     }

【方法】注:此方法考验理解能力,目前我也只能理解很浅显的部分,大家可以在评论区交流,谈谈自己的理解,我会逐步更新博文。

1.将Resizer.h以及Resizer.cpp添加至工程中。

2.在对话框窗口的头文件(.h文件)中添加#include "Resizer.h"以及CResizer m_resizer;

3. 将以下代码段插入对话框窗口源文件(.cpp文件)的配置函数(OnInitDialog())中,更改方法是,将数组中IDC_STATIC1等控件的ID号改成自己的;IDC_MAIN是参考坐标,可以更换成自己需要的参考坐标控件ID,但是最好慎重。eTop等根据需要更改。

我来说一下数组中IDC_STATIC1控件,

第一行以IDC_MAIN为参考坐标,将控件的顶部(eTop)固定(eFixed);

第二行以IDC_MAIN为参考坐标,将控件的左部(eLeft)固定(eFixed);

第三行以IDC_MAIN为参考坐标,将控件的右部(eRight)等宽(eWidth);

第四行以IDC_MAIN为参考坐标,将控件的顶部(eTop)等高(eHeight);

这四句的作用是将控件IDC_STATIC控件大小不变,固定在控件初始位置上。

static CResizer::CBorderInfo s_bi[] = {
	{ IDC_STATIC1,{ CResizer::eFixed, IDC_MAIN, CResizer::eTop },  //1
		      { CResizer::eFixed, IDC_MAIN, CResizer::eLeft },  //2
		      { CResizer::eWidth, IDC_MAIN, CResizer::eRight }, //3
		      { CResizer::eHeight, IDC_MAIN, CResizer::eTop } }, //4
	{ IDC_STATIC2,{ CResizer::eFixed, IDC_MAIN, CResizer::eTop },
		      { CResizer::eFixed, IDC_MAIN, CResizer::eLeft },
		      { CResizer::eWidth, IDC_MAIN, CResizer::eRight },
		      { CResizer::eHeight, IDC_MAIN, CResizer::eTop } },
	{ IDC_STATIC3,{ CResizer::eFixed, IDC_MAIN, CResizer::eTop },
	              { CResizer::eFixed, IDC_MAIN, CResizer::eLeft },
		      { CResizer::eWidth, IDC_MAIN, CResizer::eRight },
		      { CResizer::eHeight, IDC_MAIN, CResizer::eTop } },
		
	};

const int nSize = sizeof(s_bi) / sizeof(s_bi[0]);
//Second we initialize resizer with this array. 
//At the same time it will store the original controls positions.
m_resizer.Init(m_hWnd, NULL, s_bi, nSize);

4.将m_resizer.Move();填写在OnSize()函数中

        至此程序就编写完了,主要更改的就是配置函数中static CResizer::CBorderInfo s_bi[]数组。可以根据需要更改数组的参考坐标。

【写在最后】

        在使用这个方法时,需要较强的空间想象力,程序源代码例程有详细的使用方法我会传到我的资源中https://download.csdn.net/download/lifuran156/10710303,里边有示例软件https://download.csdn.net/download/lifuran156/10710316,可以看一下。Resizer.h以及Resizer.cpp文件放在结尾,

//Resizer.cpp
/******************************************************************************\
$Copyright: (C)2001 Dmitry Kochin <dco@mail.ru>
$Workfile: Resizer.cpp $
\******************************************************************************/
// Resizer.cpp: implementation of the CResizer class.
//
//
// Class to correctly move child windows after parent was resized
// Created: 06/07/01 by dukei@

#include "stdafx.h"
#include "Resizer.h"

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

//
// Construction/Destruction
//

CResizer::CResizer()
{
    memset(&m_rcInitial, 0, sizeof(m_rcInitial));
    m_vInfo = NULL;
    m_vRectInfo = NULL;
    m_nSize = 0;
    m_nCachedSize = 0;
}

CResizer::~CResizer()
{
    Clear();
}

void CResizer::Clear()
{
    delete [] m_vInfo;
    delete [] m_vRectInfo;
    m_vInfo = NULL;
    m_vRectInfo = NULL;
    m_nSize = 0;
    m_nCachedSize = 0;
}

bool CResizer::Init(HWND hWndParent, LPCRECT rcInitial, const CBorderInfo *pBorders, int nSize)
{
    Clear();
    m_vInfo = new CControlInfo[nSize];
    m_nSize = nSize;

    m_wndParent = hWndParent;

    if(rcInitial == NULL)
        ::GetClientRect(m_wndParent, &m_rcInitial);
    else
        ::CopyRect(&m_rcInitial, rcInitial);

    for(int i = 0; i < m_nSize; i++)
    {
        CControlInfo &ci = m_vInfo[i];
        ci.pInfo = pBorders + i;
        GetDlgItemRect(ci.pInfo->nID, ci.rcInitial);

#ifdef _DEBUG
        //Make some debug checking
        //Check that no controls have reserved IDs
        //IDC_MAIN == 0 isn't allowed for control identifiers!
        //Set another control ID with CWindow::SetDlgCtrlID() or ::SetWindowLong(m_hWnd, GWL_ID, nID)!
        _ASSERTE(ci.pInfo->nID != IDC_MAIN);
        //Check that this control ID is unique.
        //ALL control identifiers MUST BE UNIQUE!!!
        for(int j = 0; j < i; j++)
        {
            const CControlInfo &ciPrevious = m_vInfo[j];
            _ASSERTE(ciPrevious.pInfo->nID != ci.pInfo->nID); //Duplicated control ID!!!
            //Control j in initialization array has the same id as control i.
        }
#endif
    }
    return true;
}

void CResizer::Move() const
{
    if(m_vRectInfo == NULL && m_nSize > 0)
        m_vRectInfo = new CRectInfo[m_nSize];

    for(int i = 0; i < m_nSize; i++)
    {
        //Move(i) should be called always for i=0 to m_nSize!!
        //It is required by its implementation!
        Move(i);
    }

    MoveAndHideOverlapped();
}

void CResizer::Move(int nIndex) const
{
    const CControlInfo &ci = m_vInfo[nIndex];
    CRectInfo &ri = m_vRectInfo[nIndex];
    m_nCachedSize = nIndex + 1; //Now m_vRectInfo contains nIndex + 1 valid items

    ri.nID = ci.pInfo->nID;

    RECT &rc = ri.rc;

    rc.left = GetCoordinate(eLeft, ci.rcInitial, ci.pInfo->left, rc);
    rc.top = GetCoordinate(eTop, ci.rcInitial, ci.pInfo->top, rc);
    rc.right = GetCoordinate(eRight, ci.rcInitial, ci.pInfo->right, rc);
    rc.bottom = GetCoordinate(eBottom, ci.rcInitial, ci.pInfo->bottom, rc);

    HWND pCtl = GetDlgItem(ci.pInfo->nID);
    LONG dwStyle = ::GetWindowLong(pCtl, GWL_STYLE);
    ri.bVisible = (::IsWindowVisible(pCtl) != FALSE && (dwStyle & WS_CLIPSIBLINGS) == 0);
    ri.bHide = false;
}

int CResizer::GetCoordinate(ESize eType, const RECT &rcInitial, const CBorder &border, const RECT &rc) const
{
    int nOld = GetRectCoord(eType, rcInitial);
    switch(border.eType)
    {
    case eFixed:
		{
        //Get initial relative window position
        RECT rc;
        GetInitialDlgItemRect(border.nRelID, rc);
        int nRelOld = GetRectCoord(border.eRelType, rc);
        //Get current relative window position
        int nRelNew = GetRelativeCoord(border);
        //Compute and return new position
        return nOld - nRelOld + nRelNew;
		}
    case eProportional:
		{
        //Get initial relative window position
        RECT rcOld;
        GetInitialDlgItemRect(border.nRelID, rcOld);
        int nOldSize = GetRectSize(eType, rcOld);
        int nOldBase = GetRectCoord(border.eRelType, rcOld);
        //Get current relative window position
        RECT rcNew;
        GetCachedDlgItemRect(border.nRelID, rcNew);
        int nNewSize = GetRectSize(eType, rcNew);
        int nNewBase = GetRectCoord(border.eRelType, rcNew);
        //Compute and return new position
        return nNewBase + (nOld - nOldBase) * nNewSize / (nOldSize <= 0 ? 1 : nOldSize);
		}
    case eWidth:
        return rc.left + rcInitial.right - rcInitial.left;
    case eHeight:
        return rc.top + rcInitial.bottom - rcInitial.top;
    }
    _ASSERTE(FALSE); //Wrong relation type is specified. Use items from EBorder enum.
    return 0;
}

int CResizer::GetRectCoord(ESize eType, const RECT &rc)
{
    switch(eType)
    {
    case eLeft:
        return rc.left;
    case eTop:
        return rc.top;
    case eRight:
        return rc.right;
    case eBottom:
        return rc.bottom;
    case eXCenter:
        return (rc.right + rc.left) / 2;
    case eYCenter:
        return (rc.bottom + rc.top) / 2;
    }
    _ASSERTE(FALSE); //Wrong side is specified. Use items from ESize enum.
    return 0;
}

int CResizer::GetRectSize(ESize eType, const RECT &rc)
{
    switch(eType)
    {
    case eLeft:
    case eRight:
    case eXCenter:
        return rc.right - rc.left;
    case eTop:
    case eBottom:
    case eYCenter:
        return rc.bottom - rc.top;
    }
    _ASSERTE(FALSE); // Wrong side is specified. Use items from ESize enum.
    return 0;
}

int CResizer::GetRelativeCoord(const CBorder &border) const
{
    RECT rc;
    GetCachedDlgItemRect(border.nRelID, rc);
    return GetRectCoord(border.eRelType, rc);
}

void CResizer::GetDlgItemRect(int nID, RECT &rc) const
{
    switch(nID)
    {
    case IDC_MAIN:
        ::GetClientRect(m_wndParent, &rc);
        break;
    default:
        HWND pCtl = GetDlgItem(nID);
        ::GetWindowRect(pCtl, &rc);

        POINT pt1, pt2;
        pt1.x = rc.left, pt1.y = rc.top;
        pt2.x = rc.right, pt2.y = rc.bottom;
        ::ScreenToClient(m_wndParent, &pt1);
        ::ScreenToClient(m_wndParent, &pt2);
        rc.left = pt1.x, rc.top = pt1.y, rc.right = pt2.x, rc.bottom = pt2.y;
        break;
    }
}

void CResizer::GetCachedDlgItemRect(int nID, RECT &rc) const
{
    switch(nID)
    {
    case IDC_MAIN:
        GetDlgItemRect(nID, rc);
        break;
    default:
        int i = FindCached(nID);
        rc = m_vRectInfo[i].rc;
        break;
    }
}

void CResizer::GetInitialDlgItemRect(int nID, RECT &rc) const
{
    switch(nID)
    {
    case IDC_MAIN:
        rc = m_rcInitial;
        break;
    default:
        //Get initial relative window position
        int i = Find(nID);
        rc = m_vInfo[i].rcInitial;
        break;
    }
}

int CResizer::Find(int nID) const
{
    for(int i = 0; i < m_nSize; i++)
    {
        if(m_vInfo[i].pInfo->nID == nID)
            return i;
    }
    _ASSERTE(FALSE); //Possibly control id nID wasn't defined before it is used
    //as relative window. Read the TIP in the header file resizer.h
    return -1;
}

int CResizer::FindCached(int nID) const
{
    for(int i = 0; i < m_nCachedSize; i++)
    {
        if(m_vRectInfo[i].nID == nID)
            return i;
    }
    _ASSERTE(FALSE); //Possibly control id nID wasn't defined before it is used
    //as relative window. Read the TIP in the header file resizer.h
    return -1;
}

void CResizer::MoveAndHideOverlapped() const
{
    _ASSERTE(m_nSize == m_nCachedSize);
    for(int i = 0; i < m_nSize; i++)
    {
        CRectInfo &riSlave = m_vRectInfo[i];
        for(int j = 0; j < i; j++)
        {
            const CRectInfo &riMaster = m_vRectInfo[j];
            //if senior window is visible and intersects with juniur, junior is to be hidden
            if(riMaster.bVisible && riSlave.bVisible && !riSlave.bHide)
            {
                RECT rc;
                ::IntersectRect(&rc, &riMaster.rc, &riSlave.rc);
                riSlave.bHide = !::IsRectEmpty(&rc);
            }
        }
        //if the window doesn't intersect with seniors, it can be drawn
        HWND pCtl = GetDlgItem(riSlave.nID);
//        if(riSlave.bHide)
//            ::SetRectEmpty(&riSlave.rc);
        //SWP_NOCOPYBITS is obligatory, otherwise windows don't correctly redraw itselves
        ::SetWindowPos(pCtl, NULL, riSlave.rc.left, riSlave.rc.top,
                       riSlave.rc.right - riSlave.rc.left, riSlave.rc.bottom - riSlave.rc.top,
                       SWP_NOCOPYBITS | SWP_NOZORDER);
    }
}
//Resizer.h
/******************************************************************************\
$Copyright: (C)2001 Dmitry Kochin <dco@mail.ru>
$Workfile: Resizer.h $
\******************************************************************************/

// Resizer.h: interface for the CResizer class.
//
//
// Class to correctly move child windows after parent window was resized
// Created: 06/07/01 by dukei@
//

//An idea:
//
//Each child window side (left, top, right and bottom) is connected to a side of another window,
//so called relative window. It is tipically dialog window, that owns the child window.
//When dialog window is resized, child window sides are moved after the relative window,
//preserving the connections.
//
//Typical usage:
//
// 1. CResizer m_resizer is member variable;
//
// 2. Add the following code to OnInitDialog()
// (replacing control IDs to your specific ones).
// See array format description later in the comment
//
//  static CResizer::CBorderInfo s_bi[] = {
//    {IDC_CONTROL_ID,      {CResizer::eFixed, IDC_MAIN, CResizer::eLeft}, //Left side
//                          {CResizer::eFixed, IDC_MAIN, CResizer::eTop},  //Top side
//                          {CResizer::eFixed, IDC_MAIN, CResizer::eLeft}, //Right side
//                          {CResizer::eFixed, IDC_MAIN, CResizer::eTop}}, //Bottom side
//    {IDC_STATIC_ID,       {CResizer::eFixed, IDC_MAIN, CResizer::eLeft},
//                          {CResizer::eFixed, IDC_MAIN, CResizer::eTop},
//                          {CResizer::eFixed, IDC_MAIN, CResizer::eRight},
//                          {CResizer::eFixed, IDC_MAIN, CResizer::eTop}},
//  };
//  const nSize = sizeof(s_bi)/sizeof(s_bi[0]);
//  m_resizer.Init(m_hWnd, NULL, s_bi, nSize);
//
// 3. Add the following code to OnSize() handler
//
//  m_resizer.Move();
//
// 4. Everything should work now
//
// P.S. Data array format consists of one or more CBorderInfo structures, which contains
// moved control ID (first field) and four CBorder structures,
// for left, top, right and bottom sides of moved control accordingly.
//
// The main difficulty is to understand CBorder structure, which has the following format:
//
// {<how control side is connected to side of another window>, <another window id>,
//                  <side of another window, to which a control side is connected>}
//
//     CResizer::eFixed                                  CResizer::eLeft
//     CResizer::eProportional  IDC_MAIN                 CResizer::eTop
// or {CResizer::eWidth       , IDC_ANOTHER_CONTROL_ID , CResizer::eRight   }
//     CResizer::eHeight                                 CResizer::eBottom
//                                                       CResizer::eXCenter
//                                                       CResizer::eYCenter
//
// For example, {CResizer::eFixed, IDC_MAIN, CResizer::eLeft} means, that moved control side is
// on the fixed distance (CResizer::eFixed) from left side (CResizer::eLeft) of dialog window (IDC_MAIN)
//
// Another example: {CResizer::eProportional, IDC_CONTROL_ID, CResizer::eLeft} means, that
// moved control side preserves relation (proportionaly) (CResizer::eProportional)
// to the width (CResizer::eLeft or CResizer::eRight) of control IDC_CONTROL_ID.
//
// TIP: Resizer resizes controls in the order they are defined in the array, so
// <another window id> should always be defined (and, therefore, moved by the resizer) before
// it is used as relative window. Otherwise, resizer ASSERTs.

#pragma once

#define IDC_MAIN 0   //Parent dialog ID to be relative window

class CResizer
{
public:
    enum EBorder
    {
        eFixed = 1,  //Given distance to specified window side
        eProportional, //Coordinate is changed proportionally to width/height of specified window
        eWidth,   //The width is preserved (relative window and window side are ignored)
        eHeight,  //The height is preserved (relative window and window side are ignored)
    };

    enum ESize
    {
        eLeft = 1,  //Relative to left side
        eTop,       //Top
        eRight,     //Right
        eBottom,    //Bottom
        eXCenter,   //The center of width
        eYCenter    //The center of height
    };

    struct CBorder
    {
        EBorder eType;  //Type of relation to relative control side
        int nRelID;     //Relative control
        ESize eRelType; //Side of relative control
    };

    struct CBorderInfo
    {
        int nID;         //Control ID
        CBorder left;
        CBorder top;
        CBorder right;
        CBorder bottom;
    };

private:
    struct CControlInfo
    {
        RECT rcInitial; //initial control position;
        const CBorderInfo *pInfo;
    };

    struct CRectInfo
    {
        int nID;       //Control ID
        RECT rc;      //New control rect
        bool bVisible; //If control is visible
        bool bHide;    //If control should be hidden because it overlaps senior control
    };

    HWND m_wndParent;
    RECT m_rcInitial; //Initial window client area
    typedef CControlInfo *TInfo;
    typedef CRectInfo *TRectInfo;
    TInfo m_vInfo;
    int m_nSize; //Size of m_vInfo array
    mutable TRectInfo m_vRectInfo;
    mutable int m_nCachedSize; //Size of m_vRectInfo

protected:
    HWND GetDlgItem(int nID) const
    {
        return ::GetDlgItem(m_wndParent, nID);
    }

    void GetDlgItemRect(int nID, RECT &rc) const;
    void GetCachedDlgItemRect(int nID, RECT &rc) const;
    void GetInitialDlgItemRect(int nID, RECT &rc) const;
    void Move(int nIndex) const;
    int GetCoordinate(ESize eType, const RECT &rcInitial, const CBorder &border, const RECT &rc) const;
    static int GetRectCoord(ESize eType, const RECT &rc);
    static int GetRectSize(ESize eType, const RECT &rc);
    int GetRelativeCoord(const CBorder &border) const;
    int Find(int nID) const;
    int FindCached(int nID) const;
    void MoveAndHideOverlapped() const;
    void Clear();

public:
    //Initializes resizer
    bool Init(HWND hWndParent, LPCRECT rcInitial, const CBorderInfo *pBorders, int nSize);
    //Performs moving of controls
    void Move() const;
    //Just constructor
    CResizer();
    //Just destructor
    ~CResizer();
};

 

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值