解決 MDI 父視窗背景自訂、控件佈局與子視窗最大化遮擋難題
WinForms MDI 應用中,直接在父視窗 (`IsMdiContainer=true`) 放置控件(如 Logo、工具列、狀態列)會導致子視窗最大化時被遮擋,或隨父視窗縮放產生佈局錯亂。本文實作一個 **MDI 控制台 Form**——專門承載背景圖片、固定 UI 元件,永遠停留在 MDI 客戶區最底層,不參與子視窗 Z-Order 競爭。
效果展示
核心實作步驟
- 建立專用 Form:`MdiConsoleForm`,`FormBorderStyle=None`、`ShowInTaskbar=false`、`TopLevel=false`。
- 設為 MDI 子視窗但置底:
public MdiConsoleForm(Form mdiParent) {
InitializeComponent();
this.MdiParent = mdiParent;
this.Dock = DockStyle.Fill;
this.SendToBack(); // 關鍵:永遠在最底層
}
- 攔截 MdiClient 繪製:MDI 父視窗內部實際客戶區是一個 `MdiClient` 控件。在父視窗 `Load` 事件:
var mdiClient = this.Controls.OfType().FirstOrDefault();
if (mdiClient != null) {
mdiClient.BackColor = Color.White; // 或透明
// 將 Console Form 加入 MdiClient 控件集合,而非 Form.Controls
mdiClient.Controls.Add(consoleForm);
consoleForm.BringToFront(); // 確保在 MdiClient 內部最底層
}
- 處理尺寸變更:`MdiConsoleForm` `Dock=Fill` 自動跟隨 `MdiClient` 大小,無需額外代碼。
- 背景圖片繪製:在 `MdiConsoleForm.OnPaint` 使用 `DrawImage` 或 `FillRectangle` + `TextureBrush`,支援平鋪/拉伸/置中。
關鍵技術點
- `MdiClient` 是內部控件,需透過反射或枚舉 `Controls` 取得。
- 將 Console Form 加入 `MdiClient.Controls` 而非 `MdiParent.Controls`,可避開 MDI 視窗管理器的 Z-Order 排序。
- 子視窗最大化時,標題欄合併到父視窗選單,`MdiClient` 區域不變,Console Form 仍正常顯示。
- 可在 Console Form 放置任何控件(`PictureBox`、`Label`、`Panel` 等)作為全域 UI 區。
進階擴充
- 雙緩衝繪製背景避免閃爍:`SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);`
- 支援多層背景(如固定 Logo + 動態資訊列)。
- 整合主題切換:Console Form 訂閱 `ThemeChanged` 事件重繪。