上篇使用 `SplitContainer` 實現左右分欄,功能完整但代碼較多。本文提供更輕量的方案:
優點:代碼極簡、無 SplitContainer 開銷、子視窗最大化時標題列**自動併入父視窗選單列**(標準 MDI 行為),視覺更原生。
public ParentForm()
{
InitializeComponent();
// 分割器滑鼠事件
panel1.MouseDown += Panel1_MouseDown;
panel1.MouseMove += Panel1_MouseMove;
panel1.MouseUp += Panel1_MouseUp;
// 初始位置:緊貼 TreeView 右側
panel1.Top = menuStrip1.Height;
panel1.Left = treeView1.Left + treeView1.Width;
panel1.Height = panel1.Parent.Height;
// 視窗大小改變時同步分割器高度
Resize += ParentForm_Resize;
}
private bool _isDragging = false;
private void Panel1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left) _isDragging = true;
}
private void Panel1_MouseMove(object sender, MouseEventArgs e)
{
if (_isDragging)
{
panel1.Left += e.X; // 累加滑鼠水平位移
}
}
private void Panel1_MouseUp(object sender, MouseEventArgs e)
{
if (_isDragging)
{
panel1.Left += e.X;
_isDragging = false;
// 關鍵:同步 TreeView 寬度
treeView1.Width = panel1.Left;
}
}
private void ParentForm_Resize(object sender, EventArgs e)
{
panel1.Height = panel1.Parent.Height;
}
private void ParentForm_Load(object sender, EventArgs e)
{
LoadMenuNodes(); // 動態載入導航節點
}
private void LoadMenuNodes()
{
treeView1.Nodes.Clear();
var root = treeView1.Nodes.Add("Root");
var rand = new Random();
for (int i = 1; i <= 10; i++)
{
var section = root.Nodes.Add($"Section-{i}");
int maxNodes = rand.Next(1, 10);
for (int n = 1; n <= maxNodes; n++)
{
section.Nodes.Add($"Level-{i}-{n}");
}
}
root.ExpandAll();
}
private void TreeView1_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
{
// 僅葉子節點(無子節點)觸發開啟
if (e.Node.Nodes.Count == 0)
ShowChildForm<ChildForm>();
}
private void ShowChildForm<TForm>() where TForm : Form, new()
{
var child = new TForm
{
MdiParent = this,
Name = "ChildForm - " + DateTime.Now.Millisecond,
Text = "ChildForm - " + DateTime.Now.Millisecond
};
child.Show();
}
using System;
using System.Windows.Forms;
namespace WinFormTest
{
public partial class ParentForm : Form
{
private bool _isDragging = false;
public ParentForm()
{
InitializeComponent();
panel1.MouseDown += Panel1_MouseDown;
panel1.MouseMove += Panel1_MouseMove;
panel1.MouseUp += Panel1_MouseUp;
panel1.Top = menuStrip1.Height;
panel1.Left = treeView1.Left + treeView1.Width;
panel1.Height = panel1.Parent.Height;
Resize += ParentForm_Resize;
}
private void Panel1_MouseDown(object sender, MouseEventArgs e)
=> _isDragging = e.Button == MouseButtons.Left;
private void Panel1_MouseMove(object sender, MouseEventArgs e)
{
if (_isDragging) panel1.Left += e.X;
}
private void Panel1_MouseUp(object sender, MouseEventArgs e)
{
if (_isDragging)
{
panel1.Left += e.X;
_isDragging = false;
treeView1.Width = panel1.Left;
}
}
private void ParentForm_Resize(object sender, EventArgs e)
=> panel1.Height = panel1.Parent.Height;
private void ParentForm_Load(object sender, EventArgs e)
=> LoadMenuNodes();
private void LoadMenuNodes()
{
treeView1.Nodes.Clear();
var root = treeView1.Nodes.Add("Root");
var rand = new Random();
for (int i = 1; i <= 10; i++)
{
var section = root.Nodes.Add($"Section-{i}");
int max = rand.Next(1, 10);
for (int n = 1; n <= max; n++)
section.Nodes.Add($"Level-{i}-{n}");
}
root.ExpandAll();
}
private void TreeView1_NodeMouseDoubleClick(object sender, TreeNodeMouseClickEventArgs e)
{
if (e.Node.Nodes.Count == 0) ShowChildForm<ChildForm>();
}
private void ShowChildForm<TForm>() where TForm : Form, new()
{
var child = new TForm { MdiParent = this, Name = "ChildForm - " + DateTime.Now.Millisecond };
child.Text = child.Name;
child.Show();
}
}
}

關鍵差異:
| 特性 | SplitContainer 方案 (上篇) | TreeView + Panel 方案 (本文) |
|---|---|---|
| 子視窗最大化標題列 | 保留在 Panel2 內部 | **併入父視窗選單列** (標準 MDI) |
| 代碼量 | 較多 (事件處理、選單同步) | 極少 (利用內建 MdiWindowListItem) |
| 分割器功能 | 內建、專業 | 手動實現、輕量 |
| 擴展性 | 高 (Panel1 可放任意控制項) | 中 (TreeView 固定左側) |
下一步將研究 WinForms **外掛式編程架構**(動態載入 DLL、介面隔離、依賴注入),實現模組化業務擴展,敬請期待。