6 分隔容器类
分隔容器(SplitContainer)由分隔线和两个面板容器组成,可以通过分隔线将容器所在区域分为两个部分(左右或上下),每个部分里面有一个面板容器(Panel容器类),可以放置其它控件。
通过鼠标拖动分隔线,可以改变容器两个区域的尺寸。
分隔线可以通过SplitterWidth 属性控制,例如:
-
- this.splitPane.SplitterWidth = 2;
这里的splitPane是一个SplitContainer类型字段,表示一个分隔容器。上述代码将其分隔线宽度设置为2个单位。
SplitContainer容器的两个面板分别为Panel1属性和Panel2属性,也成为1号面板和2号面板。正常情况下,1号面板在左边(分隔线垂直)或者1号面板在上方(分隔线水平),参看示意图:
图1 分隔容器分隔方向示意图
通过SplitContainer类对象的Orientation属性可以设置分隔容器分隔线的方向,例如:
-
- this.splitPane.Orientation = Orientation.Vertical;
通过SplitContainer类对象Panel1(或者Panel2)属性得到一个Panel类型对象引用,通过其Controls属性的Add方法可以将其它控件增加在这两个面板之一上。从而将控件放置在SplitContainer容器的左边(上边)或右边(下边),例如:
-
- this.splitPane.Panel1.Controls.Add(this.textLabel);
这里的textLabel是一个Label类型字段,表示任意控件。
通过SplitContainer对象的SplitterDistance属性可以控制分隔线和其起始位置的距离(对于分隔线垂直,起始位置在容器最左边;对于分隔线水平,起始位置在容器最顶端),例如:
- this.splitPane.SplitterDistance = 120;
上述代码设置分隔线距离起始位置120个单位。
通过SplitContainer对象的Panel1MinSize属性可以控制1号面板的最小宽度(对于分隔线垂直)或高度(对于分隔线水平),即拖动分隔线可以使1号面板成为德最小尺寸;同理Panel2MinSize用于设置2号面板,例如:
- this.splitPane.Panel1MinSize = 50;
设置1号面板最小尺寸(宽度或高度,根据分隔线方向决定)为50个单位。
通过SplitContainer对象的Panel1Collapsed属性,可以将1号面板收起(属性值为true)或展开(属性值为false);同理,Panel2Collapsed属性用于2号面板,例如:
- this.splitPane.Panel1Collapsed = !this.splitPane.Panel1Collapsed;
将1号面板收起或展开(根据面板的当前状态决定)。
一般来说,分隔容器用于显示侧边栏或底边栏,就像我们使用的Visual Studio一样。在不需要的时候收起,并可以随时调整尺寸。
下面通过一个例子来展示SplitContainer的具体使用:
界面布局如下:
图3 界面布局示意图
为了让两个按钮放置的更为合理,使用了一个流式布局面板(flowPane字段),将按钮放在流式面板上,再把流式面板放在分隔容器的2号面板中。
代码如下:
Program.cs
- using System;
- using System.Windows.Forms;
-
- namespace Edu.Study.Graphics.SplitLayout {
-
-
-
-
- class MyForm : Form {
-
-
- private SplitContainer splitPane;
-
-
- private FlowLayoutPanel flowPane;
-
-
- private Label textLabel;
-
-
-
- private Button switchButton;
-
- private Button collapsedButton;
-
-
- private Timer colapsedTimer;
-
-
- private bool isColapsed = false;
-
-
-
-
- public MyForm() {
-
- this.Text = "分隔容器测试";
-
-
- this.splitPane = new SplitContainer();
-
- this.splitPane.Orientation = Orientation.Vertical;
-
- this.splitPane.Dock = DockStyle.Fill;
-
- this.splitPane.SplitterWidth = 2;
-
- this.splitPane.BorderStyle = BorderStyle.FixedSingle;
-
-
- this.textLabel = new Label();
-
- this.textLabel.Text = "分隔面板测试";
-
- this.textLabel.AutoSize = true;
-
- this.splitPane.Panel1.Controls.Add(this.textLabel);
-
-
- this.flowPane = new FlowLayoutPanel();
-
- this.flowPane.FlowDirection = FlowDirection.TopDown;
-
- this.flowPane.WrapContents = false;
-
- this.flowPane.AutoScroll = false;
-
- this.flowPane.Dock = DockStyle.Left;
-
- this.splitPane.Panel2.Controls.Add(this.flowPane);
-
-
- this.switchButton = new Button();
-
- this.switchButton.Text = "切换分隔方向";
-
- this.switchButton.AutoSize = true;
-
- this.switchButton.Click += new EventHandler(SwitchButtonClick);
-
- this.switchButton.Margin = new Padding(0, 3, 3, 3);
-
- this.switchButton.FlatStyle = FlatStyle.Popup;
-
- this.flowPane.Controls.Add(this.switchButton);
-
-
- this.collapsedButton = new Button();
- this.collapsedButton.Text = "收起左边栏";
- this.collapsedButton.AutoSize = true;
- this.collapsedButton.Click += new EventHandler(CollapsedButtonClicked);
- this.collapsedButton.Margin = new Padding(0, 3, 3, 3);
- this.collapsedButton.FlatStyle = FlatStyle.Popup;
- this.flowPane.Controls.Add(this.collapsedButton);
-
-
- this.colapsedTimer = new Timer();
-
- this.colapsedTimer.Interval = 20;
-
- this.colapsedTimer.Tick += new EventHandler(ColapsedTimerTick);
-
-
- this.Controls.Add(this.splitPane);
- }
-
-
-
-
-
- private int GetPanel1MiniSize() {
- int miniDistance = 0;
-
- if (this.splitPane.Orientation == Orientation.Vertical) {
- miniDistance = this.Width / 4;
- } else {
- miniDistance = this.Height / 4;
- }
- return miniDistance;
- }
-
-
-
-
- private void ChangeSpliterDistanceAndPanel1MiniSize() {
-
-
- this.splitPane.SplitterDistance =
- this.splitPane.Panel1MinSize = this.GetPanel1MiniSize();
- }
-
-
-
-
- protected override void OnLoad(EventArgs e) {
- base.OnLoad(e);
-
-
- this.ChangeSpliterDistanceAndPanel1MiniSize();
-
-
-
-
- this.flowPane.Width = Math.Max(
- this.switchButton.Width + this.switchButton.Margin.Horizontal,
- this.collapsedButton.Width + this.collapsedButton.Margin.Horizontal
- );
- }
-
-
-
-
- protected override void OnResize(EventArgs e) {
- base.OnResize(e);
-
-
- this.ChangeSpliterDistanceAndPanel1MiniSize();
- }
-
-
-
-
-
- private void SwitchButtonClick(object sender, EventArgs e) {
-
- if (this.splitPane.Orientation == Orientation.Vertical) {
-
- this.splitPane.Orientation = Orientation.Horizontal;
-
-
-
- this.flowPane.Dock = DockStyle.Top;
-
-
-
- this.flowPane.FlowDirection = FlowDirection.LeftToRight;
-
-
- this.collapsedButton.Margin =
- this.switchButton.Margin = new Padding(3, 0, 3, 3);
- } else {
-
- this.splitPane.Orientation = Orientation.Vertical;
-
-
-
- this.flowPane.Dock = DockStyle.Left;
-
-
-
- this.flowPane.FlowDirection = FlowDirection.TopDown;
-
-
- this.collapsedButton.Margin =
- this.switchButton.Margin = new Padding(0, 3, 3, 3);
- }
-
-
- this.ChangeSpliterDistanceAndPanel1MiniSize();
- }
-
-
-
-
- private void CollapsedButtonClicked(object sender, EventArgs e) {
-
-
-
- DialogResult dr = MessageBox.Show(
- "是否显示动画方式?",
- "提问",
- MessageBoxButtons.YesNo,
- MessageBoxIcon.Question,
- MessageBoxDefaultButton.Button1
- );
-
- if (dr == DialogResult.Yes) {
- if (this.isColapsed) {
-
-
- this.splitPane.SplitterDistance = 0;
-
-
-
-
- this.splitPane.Panel1Collapsed = false;
- } else {
-
-
- this.splitPane.Panel1MinSize = 0;
- }
-
-
- this.colapsedTimer.Start();
- } else {
- if (this.splitPane.Panel1MinSize == 0) {
-
-
- this.splitPane.Panel1MinSize = this.GetPanel1MiniSize();
- }
-
-
- this.splitPane.Panel1Collapsed = !this.splitPane.Panel1Collapsed;
-
- this.isColapsed = this.splitPane.Panel1Collapsed;
- }
- }
-
-
-
-
-
- private void ColapsedTimerTick(object sender, EventArgs e) {
- if (this.isColapsed) {
-
- int miniSize = this.GetPanel1MiniSize();
-
- if (this.splitPane.SplitterDistance < miniSize) {
-
-
- miniSize = Math.Min(miniSize - this.splitPane.SplitterDistance, 30);
-
- this.splitPane.SplitterDistance += miniSize;
- } else {
-
-
- this.colapsedTimer.Stop();
-
- this.splitPane.Panel1MinSize = miniSize;
-
- this.isColapsed = false;
- }
- } else {
-
- if (this.splitPane.SplitterDistance > 0) {
-
- this.splitPane.SplitterDistance -= Math.Min(this.splitPane.SplitterDistance, 30);
- } else {
-
-
- this.colapsedTimer.Stop();
-
- this.splitPane.Panel1Collapsed = true;
-
- this.isColapsed = true;
- }
- }
- }
- }
-
-
-
-
- static class Program {
-
-
-
- static void Main() {
- Application.EnableVisualStyles();
- Application.SetCompatibleTextRenderingDefault(false);
- Application.Run(new MyForm());
- }
- }
- }
本节代码下载
在代码中,通过“切换分隔方向”按钮来切换分隔线的方向(164-200行),切换分隔线方向很简单,设置其Orientation属性即可,但要注意,切换分隔线方向后,要对两个面板内控件的布局方向做必要的调整,因为两个面板的放置方向改变了;
代码中使用了两种方式来收起面板,普通方式和动画方式,普通方式很简单,直接设置Panel1Collapsed(或Panel2Collapsed)属性即可(第243行);动画方式是通过一个定时器控件(即定时呼叫事件委托方法的控件)不断定时改变面板的SplitterDistance属性(收起为减小属性值,展开为增加属性值),直到SplitterDistance属性值达到预定大小(253-288行);注意,SplitterDistance属性不能为负数,代码261行和277行保证了这一点。
定时器在固定时间间隔后呼叫某个委托函数一次,并一直重复直到定时器被关闭。其Interval属性用于设定时间间隔(即多久呼叫委托函数一次,第99行),Tick事件用于指定呼叫的委托函数(第100行),使用Start方法可以启动定时器(第234行),Stop方法可以停止(也可以说是关闭)定时器(第267行)。定时器必须可以在适当的时候被关闭,本例中,如果分隔线达到指定距离后,定时器被关闭。