首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

Windows 視窗大小限制機制:模擬命令提示字元可調整但離散的尺寸

     編輯:程式設計疑難解答

Windows 命令提示字元視窗拖曳調整大小時,寬高不是連續變化,而是以字元格為單位「跳躍」。這是透過處理 `WM_GETMINMAXINFO` 與 `WM_SIZING` 訊息,結合 `SetWindowLong` 修改視窗樣式實現的。本文解析原理並給出 MFC/Win32 實作範例。

核心訊息處理

訊息時機用途
`WM_GETMINMAXINFO`視窗建立、最大化、最小化前設定最小/最大追蹤尺寸(`MINMAXINFO` 結構)
`WM_SIZING`使用者正在拖曳調整大小即時修正 `RECT*` 使寬高對齊字元格
`WM_SIZE`調整完成重新佈局子控制項

Win32 純 API 實作

#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;
}

MFC 實作(CWnd 衍生類)

// 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);
}

關鍵點總結

  • 字元格尺寸(`CHAR_W`/`CHAR_H`)需根據實際字型(`GetTextMetrics` 取得 `tmAveCharWidth`、`tmHeight`)動態計算
  • `WM_SIZING` 中修改 `RECT*` 並返回 `TRUE`,系統會強制視窗框對齊修正後大小
  • `WM_GETMINMAXINFO` 限制最小/最大追蹤尺寸,防止使用者拖到極小或極大
  • 若需保留標準視窗樣式(標題列、邊框),**不要**用 `SetWindowLong(GWL_STYLE)` 移除 `WS_SIZEBOX`/`WS_THICKFRAME`,否則無法拖曳調整

延伸:模擬 Console 視窗完整行為

  1. 建立視窗時用 `WS_OVERLAPPEDWINDOW | WS_VSCROLL`
  2. 字型固定為等寬(Consolas / 終端機)
  3. 客戶區繪製時按字元格對齊 `TextOut`
  4. 滾動條範圍按行數設定 `SetScrollInfo`

掌握 `WM_GETMINMAXINFO` + `WM_SIZING` 組合,即可實現類似命令提示字元「以字元格為單位離散調整大小」的視窗行為。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved