你是否遇到過這樣的問題:用編譯器拖出一些控件放到對話框上,併合理安排好了位置;但編譯運行,改變對話框的大小後,所有控件的位置都亂了,讓人感覺非常糟糕。如果控件不太多,你可以嘗試手寫代碼定位每個控件的位置,但若是控件數量以十或百爲單位計數甚至更多,逐一爲每個控件指定位置就非常麻煩了。
本文提供一個宏,可以按照通常對話框的佈局要求快速爲每個控件佈局,對話框大小改變時,控件的大小和位置都會隨之而改變。改變控件位置和大小的代碼都在宏內部,你只需瞭解宏的作用即可達到所需佈局。
注意,編寫宏和測試環境爲XP + VC++6.0SP6 + MFC,其他編譯環境可能需對宏稍加修改,我相信你有這個實力。
// CtrlId : 控件ID
// LeftChangeMode : 0:與窗口客戶區左邊的距離不變; 1:按比例變化; 2:保持控件寬度不變;
// RightChangeMode : 0:與窗口客戶區右邊的距離不變; 1:按比例變化; 2:保持控件寬度不變;
// TopChangeMode : 0:與窗口客戶區上邊的距離不變; 1:按比例變化; 2:保持控件高度不變;
// BottomChangeMode : 0:與窗口客戶區下邊的距離不變; 1:按比例變化; 2:保持控件高度不變;
#ifndef AUTO_SET_CONTROL_POS
// LeftChangeMode取值宏定義
#define LEFT_CHANGE_MODE_FIXED_LEFTPADDING 0
#define LEFT_CHANGE_MODE_RATIO 1
#define LEFT_CHANGE_MODE_FIXED_WIDTH 2
// RightChangeMode取值宏定義
#define RIGHT_CHANGE_MODE_FIXED_RIGHTPADDING 0
#define RIGHT_CHANGE_MODE_RATIO 1
#define RIGHT_CHANGE_MODE_FIXED_WIDTH 2
// TopChangeMode取值宏定義
#define TOP_CHANGE_MODE_FIXED_TOPPADDING 0
#define TOP_CHANGE_MODE_RATIO 1
#define TOP_CHANGE_MODE_FIXED_HEIGHT 2
// BottomChangeMode取值宏定義
#define BOTTOM_CHANGE_MODE_FIXED_LEFEPADDING 0
#define BOTTOM_CHANGE_MODE_RATIO 1
#define BOTTOM_CHANGE_MODE_FIXED_HEIGHT 2
#define AUTO_SET_CONTROL_POS(CtrlId, LeftChangeMode, RightChangeMode, TopChangeMode, BottomChangeMode)\
{\
static int nFirstCtrlId = -1;\
static CRect rectLastClientWindow;\
static CRect rectLastClientWindowTmp;\
if (-1 == nFirstCtrlId)\
{\
nFirstCtrlId = CtrlId;\
}\
if (nFirstCtrlId == CtrlId)\
{\
rectLastClientWindowTmp = rectLastClientWindow;\
}\
rectLastClientWindow.right = cx;\
rectLastClientWindow.bottom = cy;\
CWnd *pWndDlgItem = GetDlgItem(CtrlId);\
if (pWndDlgItem)\
{\
CRect rectDlgItem;\
pWndDlgItem->GetWindowRect(rectDlgItem);\
ScreenToClient(rectDlgItem);\
\
static int nDefaultLeftPadding_##CtrlId = rectDlgItem.left;\
static float fDefaultLeftRatio_##CtrlId = (float)rectDlgItem.left\
/ rectLastClientWindowTmp.right;\
static int nDefaultRightPadding_##CtrlId = rectLastClientWindowTmp.right\
- rectDlgItem.right;\
static float fDefaultRightRatio_##CtrlId = (float)rectDlgItem.right\
/ rectLastClientWindowTmp.right;\
static int nDefaultCtrlWidth_##CtrlId = rectDlgItem.right - rectDlgItem.left;\
int nXChangeMode = (LeftChangeMode << 8) + RightChangeMode;\
\
static int nDefaultTopPadding_##CtrlId = rectDlgItem.top;\
static float fDefaultTopRatio_##CtrlId = (float)rectDlgItem.top\
/ rectLastClientWindowTmp.bottom;\
static int nDefaultBottomPadding_##CtrlId = rectLastClientWindowTmp.bottom\
- rectDlgItem.bottom;\
static float fDefaultBottomRatio_##CtrlId = (float)rectDlgItem.bottom\
/ rectLastClientWindowTmp.bottom;\
static int nDefaultCtrlHeight_##CtrlId = rectDlgItem.bottom - rectDlgItem.top;\
int nYChangeMode = (TopChangeMode << 8) + BottomChangeMode;\
\
if (0x0000 == nXChangeMode)\
{\
rectDlgItem.right = cx - nDefaultRightPadding_##CtrlId;\
}\
else if (0x0001 == nXChangeMode)\
{\
rectDlgItem.right = fDefaultRightRatio_##CtrlId * cx;\
}\
else if (0x0002 == nXChangeMode)\
{\
/*這裏不用更改*/\
}\
else if (0x0100 == nXChangeMode)\
{\
rectDlgItem.left = fDefaultLeftRatio_##CtrlId * cx;\
rectDlgItem.right = cx - nDefaultRightPadding_##CtrlId;\
}\
else if (0x0101 == nXChangeMode)\
{\
rectDlgItem.left = fDefaultLeftRatio_##CtrlId * cx;\
rectDlgItem.right = fDefaultRightRatio_##CtrlId * cx;\
}\
else if (0x0102 == nXChangeMode)\
{\
rectDlgItem.left = fDefaultLeftRatio_##CtrlId * cx;\
rectDlgItem.right = rectDlgItem.left + nDefaultCtrlWidth_##CtrlId;\
}\
else if (0x0200 == nXChangeMode)\
{\
rectDlgItem.right = cx - nDefaultRightPadding_##CtrlId;\
rectDlgItem.left = rectDlgItem.right - nDefaultCtrlWidth_##CtrlId;\
}\
else if (0x0201 == nXChangeMode)\
{\
rectDlgItem.right = fDefaultRightRatio_##CtrlId * cx;\
rectDlgItem.left = rectDlgItem.right - nDefaultCtrlWidth_##CtrlId;\
}\
else if (0x0202 == nXChangeMode)\
{\
/*這裏不用更改*/\
}\
\
\
if (0x0000 == nYChangeMode)\
{\
rectDlgItem.bottom = cy - nDefaultBottomPadding_##CtrlId;\
}\
else if (0x0001 == nYChangeMode)\
{\
rectDlgItem.bottom = fDefaultBottomRatio_##CtrlId * cy;\
}\
else if (0x0002 == nYChangeMode)\
{\
/*這裏不用更改*/\
}\
else if (0x0100 == nYChangeMode)\
{\
rectDlgItem.top = fDefaultTopRatio_##CtrlId * cy;\
rectDlgItem.bottom = cy - nDefaultBottomPadding_##CtrlId;\
}\
else if (0x0101 == nYChangeMode)\
{\
rectDlgItem.top = fDefaultTopRatio_##CtrlId * cy;\
rectDlgItem.bottom = fDefaultBottomRatio_##CtrlId * cy;\
}\
else if (0x0102 == nYChangeMode)\
{\
rectDlgItem.top = fDefaultTopRatio_##CtrlId * cy;\
rectDlgItem.bottom = rectDlgItem.top + nDefaultCtrlHeight_##CtrlId;\
}\
else if (0x0200 == nYChangeMode)\
{\
rectDlgItem.bottom = cy - nDefaultBottomPadding_##CtrlId;\
rectDlgItem.top = rectDlgItem.bottom - nDefaultCtrlHeight_##CtrlId;\
}\
else if (0x0201 == nYChangeMode)\
{\
rectDlgItem.bottom = fDefaultBottomRatio_##CtrlId * cy;\
rectDlgItem.top = rectDlgItem.bottom - nDefaultCtrlHeight_##CtrlId;\
}\
else if (0x0202 == nYChangeMode)\
{\
/*這裏不用更改*/\
}\
pWndDlgItem->MoveWindow(rectDlgItem);\
}\
}
#endif
使用時,只需在對話框的WM_SIZE消息處理函數中加上“AUTO_SET_CONTROL_POS(CtrlId, LeftChangeMode, RightChangeMode, TopChangeMode, BottomChangeMode)”即可,各個參數含義見宏定義的頭部註釋。對話框大小改變,就會自動修改指定控件的位置和大小。
上圖是測試中MFC控件初化的位置,下圖爲修改控件大小後的佈局:
代碼中所作的處理如下:
void CDialogApplicationDlg::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
AUTO_SET_CONTROL_POS( IDC_EDIT1, LEFT_CHANGE_MODE_FIXED_LEFTPADDING,
RIGHT_CHANGE_MODE_FIXED_RIGHTPADDING,
TOP_CHANGE_MODE_FIXED_TOPPADDING,
BOTTOM_CHANGE_MODE_FIXED_HEIGHT);
AUTO_SET_CONTROL_POS(IDC_RICHEDIT1, LEFT_CHANGE_MODE_FIXED_LEFTPADDING,
RIGHT_CHANGE_MODE_FIXED_RIGHTPADDING,
TOP_CHANGE_MODE_FIXED_TOPPADDING,
BOTTOM_CHANGE_MODE_FIXED_LEFEPADDING);
AUTO_SET_CONTROL_POS( IDOK, LEFT_CHANGE_MODE_FIXED_WIDTH,
RIGHT_CHANGE_MODE_FIXED_RIGHTPADDING,
TOP_CHANGE_MODE_FIXED_TOPPADDING,
BOTTOM_CHANGE_MODE_FIXED_HEIGHT);
AUTO_SET_CONTROL_POS( IDCANCEL, LEFT_CHANGE_MODE_FIXED_WIDTH,
RIGHT_CHANGE_MODE_FIXED_RIGHTPADDING,
TOP_CHANGE_MODE_FIXED_TOPPADDING,
BOTTOM_CHANGE_MODE_FIXED_HEIGHT);
AUTO_SET_CONTROL_POS( IDC_STATIC, LEFT_CHANGE_MODE_FIXED_LEFTPADDING,
RIGHT_CHANGE_MODE_FIXED_WIDTH,
TOP_CHANGE_MODE_FIXED_HEIGHT,
BOTTOM_CHANGE_MODE_FIXED_LEFEPADDING);
AUTO_SET_CONTROL_POS( IDC_BUTTON1, LEFT_CHANGE_MODE_FIXED_WIDTH,
RIGHT_CHANGE_MODE_FIXED_RIGHTPADDING,
TOP_CHANGE_MODE_FIXED_HEIGHT,
BOTTOM_CHANGE_MODE_FIXED_LEFEPADDING);
}
每個控件調用一次AUTO_SET_CONTROL_POS,各個參數以宏的形式提供,非常容易瞭解,比起手寫代碼非常清晰。當然,如果想重新設計各個控件的佈局,只需重新指定佈局參數即可。