實作視窗大小變更時,所有子控制項按比例縮放位置與大小,支援巢狀容器、最小尺寸保護與捲軸自動切換。核心思路:載入時記錄每個控制項的初始 `Location`、`Size` 及表單 `ClientSize`,`SizeChanged` 事件中依比例重新計算。
class ControlLayoutInfo
{
public Control Control;
public Point InitialLocation;
public Size InitialSize;
public ControlLayoutInfo(Control c) { Control = c; InitialLocation = c.Location; InitialSize = c.Size; }
}
public partial class AutoScaleForm : Form
{
private readonly List _infos = new List();
private Size _initialFormSize;
private bool _initialized;
public AutoScaleForm()
{
InitializeComponent();
Load += (s, e) => InitializeLayout();
SizeChanged += (s, e) => ApplyScale();
}
private void InitializeLayout()
{
_initialFormSize = ClientSize;
CollectControls(this);
_initialized = true;
}
private void CollectControls(Control container)
{
foreach (Control c in container.Controls)
{
_infos.Add(new ControlLayoutInfo(c));
if (c.HasChildren) CollectControls(c); // 遞迴處理巢狀容器
}
}
private void ApplyScale()
{
if (!_initialized || ClientSize.Width == 0 || ClientSize.Height == 0) return;
double scaleX = (double)ClientSize.Width / _initialFormSize.Width;
double scaleY = (double)ClientSize.Height / _initialFormSize.Height;
// 縮小至低於初始尺寸 → 顯示捲軸、不再縮放控制項
if (scaleX < 1 || scaleY < 1)
{
AutoScroll = true;
AutoScrollMinSize = _initialFormSize;
ResetToInitial();
return;
}
AutoScroll = false;
foreach (var info in _infos)
{
var c = info.Control;
if (c.IsDisposed) continue;
c.Location = new Point(
(int)(info.InitialLocation.X * scaleX),
(int)(info.InitialLocation.Y * scaleY)
);
c.Size = new Size(
(int)(info.InitialSize.Width * scaleX),
(int)(info.InitialSize.Height * scaleY)
);
}
}
private void ResetToInitial()
{
foreach (var info in _infos)
{
var c = info.Control;
if (c.IsDisposed) continue;
c.Location = info.InitialLocation;
c.Size = info.InitialSize;
}
}
}
| 方案 | 優點 | 缺點 |
|---|---|---|
| 本文比例縮放 | 像素級精確、支援任意佈局、無第三方依賴 | 需手動管理、字體/圖片可能模糊 |
| TableLayoutPanel / FlowLayoutPanel | 聲明式、自動重排、支援響應式 | 像素級控制難、複雜佈局嵌套深 |
| Anchor / Dock | 簡單、標準屬性 | 僅邊緣固定、無法整體縮放 |
| 第三方 (DevExpress LayoutControl 等) | 功能豐富、設計器支援 | 授權費、學習曲線 |
選擇方案取決於專案複雜度與維護團隊熟悉度。本文程式碼可直接複製到專案中繼承使用。