首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

Windows SDK 筆記(七):MDI 視窗架構完整實作指南

     編輯:關於VC++

MDI 視窗架構概述

MDI(Multiple Document Interface)視窗包含一個框架視窗與多個子視窗。框架視窗本質是普通主視窗,其客戶區被系統預定義類別 "MDICLIENT" 覆蓋,該類別負責管理所有 MDI 子視窗。

視窗建立完整流程

1. 註冊 MDI 框架視窗類別

// MDI 框架視窗訊息處理函式
LRESULT CALLBACK MDIFrameWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    // 處理自訂訊息...
    // 未處理訊息交由 DefFrameProc 處理
    // 第二參數為客戶區視窗控制代碼
    return ::DefFrameProc(hwnd, hwndClient, message, wParam, lParam);
}

2. 註冊 MDI 子視窗類別

// MDI 子視窗訊息處理函式
LRESULT CALLBACK MDIChildWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    // 處理自訂訊息...
    // 未處理訊息交由 DefMDIChildProc 處理
    return ::DefMDIChildProc(hwnd, message, wParam, lParam);
}

3. 在框架視窗客戶區建立 MDI 管理視窗

於主視窗 WM_CREATE 訊息中建立:

case WM_CREATE:
{
    hinst = ((LPCREATESTRUCT)lParam)->hInstance;

    CLIENTCREATESTRUCT clientcreate;
    clientcreate.hWindowMenu = hMenuInitWindow;  // 用於新增視窗清單的功能表控制代碼
    clientcreate.idFirstChild = 50000;           // 子視窗起始 ID

    hwndClient = CreateWindowEx(0,
        "MDICLIENT",        // 系統預定義類別名稱
        NULL,
        WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
        0, 0, 0, 0,
        hwnd,
        (HMENU)1,           // 控制 ID
        hinst,
        &clientcreate);     // 建立參數
}
return 0;

視窗大小無關緊要,預設框架處理函式會使其覆蓋整個客戶區。建立後透過發送訊息管理子視窗建立、銷毀、排列等操作。

4. MDI 子視窗建立

在功能表加入建立命令,框架視窗收到命令後發送建立訊息:

case ID_NEW:
{
    MDICREATESTRUCT mdicreate;
    mdicreate.szClass = szMDIChildName;      // 子視窗類別名稱
    mdicreate.szTitle = TEXT("Hello");
    mdicreate.hOwner = hinst;
    mdicreate.x = mdicreate.y = CW_USEDEFAULT;
    mdicreate.cx = mdicreate.cy = CW_USEDEFAULT;
    mdicreate.style = 0;
    mdicreate.lParam = 0;

    SendMessage(hwndClient, WM_MDICREATE, 0,
        (LPARAM)(LPMDICREATESTRUCT)&mdicreate);
}
break;

訊息迴路中的 MDI 熱鍵處理

while (GetMessage(&msg, NULL, 0, 0))
{
    if (!TranslateMDISysAccel(hwndClient, &msg) &&
        !TranslateAccelerator(hwndFrame, hAccel, &msg))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

命令訊息流向機制

框架視窗收到 WM_COMMAND 等通知訊息後,應給予當前啟用的 MDI 子視窗處理機會:

case WM_COMMAND:
switch (LOWORD(wParam))
{
    case ID_ONE:  // 框架專屬命令
        // ...
        return 0;

    case IDM_WINDOW_TILE:  // 子視窗管理命令
        SendMessage(hwndClient, WM_MDITILE, 0, 0);
        return 0;

    default:  // 子視窗專屬命令
        hwndChild = (HWND)SendMessage(hwndClient, WM_MDIGETACTIVE, 0, 0);
        if (IsWindow(hwndChild))
            SendMessage(hwndChild, WM_COMMAND, wParam, lParam);
        break;  // 交由 DefFrameProc 處理剩餘命令
}
break;

子視窗管理操作

1. 標準排列命令

case WM_COMMAND:
switch (LOWORD(wParam))
{
    case IDM_WINDOW_TILE:
        SendMessage(hwndClient, WM_MDITILE, 0, 0);
        return 0;

    case IDM_WINDOW_CASCADE:
        SendMessage(hwndClient, WM_MDICASCADE, 0, 0);
        return 0;

    case IDM_WINDOW_ARRANGE:
        SendMessage(hwndClient, WM_MDIICONARRANGE, 0, 0);
        return 0;
}

2. 關閉當前啟用子視窗

先發送 WM_QUERYENDSESSION 查詢,子視窗回應後決定是否銷毀:

// 框架視窗命令處理
case IDM_FILE_CLOSE:
    hwndChild = (HWND)SendMessage(hwndClient, WM_MDIGETACTIVE, 0, 0);
    if (SendMessage(hwndChild, WM_QUERYENDSESSION, 0, 0))
        SendMessage(hwndClient, WM_MDIDESTROY, (WPARAM)hwndChild, 0);
    return 0;

// 子視窗訊息處理
case WM_QUERYENDSESSION:
case WM_CLOSE:
    if (IDOK != MessageBox(hwnd, TEXT("確定關閉視窗?"), TEXT("Hello"),
        MB_ICONQUESTION | MB_OKCANCEL))
        return 0;
    break;  // 呼叫 DefMDIChildProc

3. 關閉所有子視窗

case IDM_WINDOW_CLOSEALL:
    EnumChildWindows(hwndClient, CloseEnumProc, 0);
    return 0;

// 列舉回呼函式
BOOL CALLBACK CloseEnumProc(HWND hwnd, LPARAM lParam)
{
    if (GetWindow(hwnd, GW_OWNER)) return TRUE;  // 跳過圖示標題

    SendMessage(GetParent(hwnd), WM_MDIRESTORE, (WPARAM)hwnd, 0);  // 還原最小化視窗

    if (!SendMessage(hwnd, WM_QUERYENDSESSION, 0, 0))
        return TRUE;  // 使用者取消

    SendMessage(GetParent(hwnd), WM_MDIDESTROY, (WPARAM)hwnd, 0);
    return TRUE;
}

MDI 功能表控制機制

根據啟用的子視窗切換框架視窗功能表,並將視窗清單加入功能表:

1. 準備每種子視窗類別的功能表資源

2. 載入功能表取得控制代碼

3. 框架建立時使用框架功能表

4. 子視窗啟用時載入自身功能表,失去焦點時還原框架功能表

使用 WM_MDISETMENU 訊息,wParam 為功能表控制代碼,lParam 為欲加入視窗清單的子功能表控制代碼:

case WM_MDIACTIVATE:
    // 啟用時設定框架功能表
    if (lParam == (LPARAM)hwnd)
        SendMessage(hwndClient, WM_MDISETMENU,
            (WPARAM)hMenuHello, (LPARAM)hMenuHelloWindow);

    // 失去焦點時還原框架功能表
    if (lParam != (LPARAM)hwnd)
        SendMessage(hwndClient, WM_MDISETMENU,
            (WPARAM)hMenuInit, (LPARAM)hMenuInitWindow);

    DrawMenuBar(hwndFrame);

    // 取得 hwndFrame 方法:
    // hwndClient = GetParent(hwnd);
    // hwndFrame = GetParent(hwndClient);

    return 0;

關鍵 API 總覽表

操作API / 訊息說明
建立 MDI 客戶區CreateWindowEx("MDICLIENT")框架視窗 WM_CREATE 中呼叫
建立子視窗WM_MDICREATE發送至 hwndClient
銷毀子視窗WM_MDIDESTROY參數為子視窗控制代碼
取得啟用子視窗WM_MDIGETACTIVE回傳子視窗控制代碼
平鋪排列WM_MDITILE水平/垂直由參數決定
階層疊放WM_MDICASCADE標準疊放效果
圖示排列WM_MDIICONARRANGE最小化視窗整理
設定功能表WM_MDISETMENU動態切換功能表
熱鍵轉譯TranslateMDISysAccel訊息迴路中呼叫
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved