【写在前面】
平常在编写界面程序时,使窗口中的控件随窗口大小改变而改变,只需调用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();
};