Windows 命令提示字元視窗拖曳調整大小時,寬高不是連續變化,而是以字元格為單位「跳躍」。這是透過處理 `WM_GETMINMAXINFO` 與 `WM_SIZING` 訊息,結合 `SetWindowLong` 修改視窗樣式實現的。本文解析原理並給出 MFC/Win32 實作範例。
| 訊息 | 時機 | 用途 |
|---|---|---|
| `WM_GETMINMAXINFO` | 視窗建立、最大化、最小化前 | 設定最小/最大追蹤尺寸(`MINMAXINFO` 結構) |
| `WM_SIZING` | 使用者正在拖曳調整大小 | 即時修正 `RECT*` 使寬高對齊字元格 |
| `WM_SIZE` | 調整完成 | 重新佈局子控制項 |
#include
#define CHAR_W 8 // 字元寬度(依字型而定)
#define CHAR_H 16 // 字元高度
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg) {
case WM_GETMINMAXINFO: {
MINMAXINFO *mmi = (MINMAXINFO*)lParam;
mmi->ptMinTrackSize.x = 20 * CHAR_W; // 最小 20 字元寬
mmi->ptMinTrackSize.y = 5 * CHAR_H; // 最小 5 字元高
mmi->ptMaxTrackSize.x = 200 * CHAR_W; // 最大 200 字元寬
mmi->ptMaxTrackSize.y = 100 * CHAR_H; // 最大 100 字元高
return 0;
}
case WM_SIZING: {
RECT *rc = (RECT*)lParam;
int w = rc->right - rc->left;
int h = rc->bottom - rc->top;
// 對齊到字元格
rc->right = rc->left + ((w + CHAR_W/2) / CHAR_W) * CHAR_W;
rc->bottom = rc->top + ((h + CHAR_H/2) / CHAR_H) * CHAR_H;
return TRUE; // 表示已修改 RECT
}
case WM_DESTROY:
PostQuitMessage(0); return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int nShow)
{
WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInst;
wc.lpszClassName = TEXT("DiscreteSizeWnd");
wc.hCursor = LoadCursor(NULL, IDC_SIZEWE);
RegisterClass(&wc);
HWND hwnd = CreateWindow(wc.lpszClassName, TEXT("離散尺寸視窗"),
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
80*CHAR_W, 25*CHAR_H, NULL, NULL, hInst, NULL);
ShowWindow(hwnd, nShow); UpdateWindow(hwnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg); DispatchMessage(&msg);
}
return 0;
}
// Header
afx_msg void OnGetMinMaxInfo(MINMAXINFO* lpMMI);
afx_msg void OnSizing(UINT fwSide, LPRECT pRect);
// CPP
BEGIN_MESSAGE_MAP(CMyWnd, CFrameWnd)
ON_WM_GETMINMAXINFO()
ON_WM_SIZING()
END_MESSAGE_MAP()
void CMyWnd::OnGetMinMaxInfo(MINMAXINFO* lpMMI)
{
const int CW = 8, CH = 16;
lpMMI->ptMinTrackSize = CPoint(20*CW, 5*CH);
lpMMI->ptMaxTrackSize = CPoint(200*CW, 100*CH);
CFrameWnd::OnGetMinMaxInfo(lpMMI);
}
void CMyWnd::OnSizing(UINT fwSide, LPRECT pRect)
{
const int CW = 8, CH = 16;
int w = pRect->right - pRect->left;
int h = pRect->bottom - pRect->top;
pRect->right = pRect->left + ((w + CW/2) / CW) * CW;
pRect->bottom = pRect->top + ((h + CH/2) / CH) * CH;
CFrameWnd::OnSizing(fwSide, pRect);
}
掌握 `WM_GETMINMAXINFO` + `WM_SIZING` 組合,即可實現類似命令提示字元「以字元格為單位離散調整大小」的視窗行為。