需在主視窗 Form1 的 Panel1 內嵌入 Form2、Form3 等子視窗,但不想使用 MDI 架構。關鍵在於攔截視窗建立參數,指定父視窗控制代碼與 WS_CHILD 風格。
type
TForm2 = class(TForm)
public
procedure CreateParams(var Params: TCreateParams); override;
procedure Loaded; override;
end;
procedure TForm2.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
// 指定父視窗為擁有者 (Form1) 的控制代碼
Params.WndParent := (Owner as TForm).Handle;
// 設為子視窗風格,支援同層剪裁
Params.Style := WS_CHILD or WS_CLIPSIBLINGS;
Params.X := 0;
Params.Y := 0;
end;
procedure TForm2.Loaded;
begin
inherited Loaded;
Visible := False; // 建立期間先隱藏
Position := poDefault; // 避免預設定位干擾
BorderIcons := []; // 移除標題列圖示
BorderStyle := bsNone; // 無邊框
HandleNeeded; // 強制建立視窗控制代碼
SetBounds(0, 0, Width, Height); // 維持原始尺寸
Visible := True; // 顯示於容器內
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Form2 := TForm2.Create(Self); // Self 為 Owner,對應 CreateParams 中的 Owner
Form2.Parent := Panel1; // VCL 親子關係(可選,僅影響座標系統)
Form2.Show;
Form3 := TForm3.Create(Self);
Form3.Parent := Panel1;
Form3.Show;
end;
| 方法 | 呼叫時機 | 用途 |
|---|---|---|
CreateParams | 視窗控制代碼建立前 | 設定 WndParent、Style、ExStyle 等底層參數 |
Loaded | DFM 載入完成、控制代碼已建立後 | 調整視窗屬性、呼叫 HandleNeeded、設定邊界 |
SendMessage 與子視窗通訊WS_TABSTOP 風格讓子視窗參與 Tab 順序BorderStyle := bsNone 導致雙重邊框Owner 與 Parent 混淆:Owner 負責釋放記憶體,Parent 負責視覺包含HandleNeeded 導致 Handle 為 0 而後續操作失敗透過覆寫 CreateParams 指定 WndParent 與 WS_CHILD 風格,配合 Loaded 微調視窗屬性,即可輕鬆實現「表單即控制項」的靈活佈局,完全替代傳統 MDI 架構。