控件佈局通用解決方案

  你是否遇到過這樣的問題:用編譯器拖出一些控件放到對話框上,併合理安排好了位置;但編譯運行,改變對話框的大小後,所有控件的位置都亂了,讓人感覺非常糟糕。如果控件不太多,你可以嘗試手寫代碼定位每個控件的位置,但若是控件數量以十或百爲單位計數甚至更多,逐一爲每個控件指定位置就非常麻煩了。

  本文提供一個宏,可以按照通常對話框的佈局要求快速爲每個控件佈局,對話框大小改變時,控件的大小和位置都會隨之而改變。改變控件位置和大小的代碼都在宏內部,你只需瞭解宏的作用即可達到所需佈局。

  注意,編寫宏和測試環境爲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,各個參數以宏的形式提供,非常容易瞭解,比起手寫代碼非常清晰。當然,如果想重新設計各個控件的佈局,只需重新指定佈局參數即可。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章