NO IMAGE

點選開啟連結

6 分隔容器類

分隔容器(SplitContainer)由分隔線和兩個面板容器組成,可以通過分隔線將容器所在區域分為兩個部分(左右或上下),每個部分裡面有一個面板容器(Panel容器類),可以放置其它控制元件。

通過滑鼠拖動分隔線,可以改變容器兩個區域的尺寸。

分隔線可以通過SplitterWidth 屬性控制,例如:

[c-sharp] view
plain
 copy

  1. // 設定分隔線寬度為2單位  
  2. this.splitPane.SplitterWidth = 2;  

這裡的splitPane是一個SplitContainer型別欄位,表示一個分隔容器。上述程式碼將其分隔線寬度設定為2個單位。

SplitContainer容器的兩個面板分別為Panel1屬性Panel2屬性,也成為1號面板和2號面板。正常情況下,1號面板在左邊(分隔線垂直)或者1號面板在上方(分隔線水平),參看示意圖:

垂直分隔示意圖水平分割示意圖

圖1 分隔容器分隔方向示意圖

通過SplitContainer類物件的Orientation屬性可以設定分隔容器分隔線的方向,例如:

[c-sharp] view
plain
 copy

  1. // 設定分隔面板容器分隔線方向為垂直方向  
  2. this.splitPane.Orientation = Orientation.Vertical;  

通過SplitContainer類物件Panel1(或者Panel2)屬性得到一個Panel型別物件引用,通過其Controls屬性的Add方法可以將其它控制元件增加在這兩個面板之一上。從而將控制元件放置在SplitContainer容器的左邊(上邊)或右邊(下邊),例如:

[c-sharp] view
plain
 copy

  1. // 將標籤控制元件加入splitPane容器1號面板容器內  
  2. this.splitPane.Panel1.Controls.Add(this.textLabel);  

這裡的textLabel是一個Label型別欄位,表示任意控制元件。

通過SplitContainer物件的SplitterDistance屬性可以控制分隔線和其起始位置的距離(對於分隔線垂直,起始位置在容器最左邊;對於分隔線水平,起始位置在容器最頂端),例如:

[c-sharp] view
plain
 copy

  1. this.splitPane.SplitterDistance = 120;  

上述程式碼設定分隔線距離起始位置120個單位。

通過SplitContainer物件的Panel1MinSize屬性可以控制1號面板的最小寬度(對於分隔線垂直)或高度(對於分隔線水平),即拖動分隔線可以使1號面板成為德最小尺寸;同理Panel2MinSize用於設定2號面板,例如:

[c-sharp] view
plain
 copy

  1. this.splitPane.Panel1MinSize = 50;  

設定1號面板最小尺寸(寬度或高度,根據分隔線方向決定)為50個單位。

通過SplitContainer物件的Panel1Collapsed屬性,可以將1號面板收起(屬性值為true)或展開(屬性值為false);同理,Panel2Collapsed屬性用於2號面板,例如:

[c-sharp] view
plain
 copy

  1. this.splitPane.Panel1Collapsed = !this.splitPane.Panel1Collapsed;  

將1號面板收起或展開(根據面板的當前狀態決定)。

一般來說,分隔容器用於顯示側邊欄底邊欄,就像我們使用的Visual Studio一樣。在不需要的時候收起,並可以隨時調整尺寸。

下面通過一個例子來展示SplitContainer的具體使用:

介面佈局如下:

介面佈局示意圖 
圖3 介面佈局示意圖

為了讓兩個按鈕放置的更為合理,使用了一個流式佈局面板(flowPane欄位),將按鈕放在流式面板上,再把流式面板放在分隔容器的2號面板中。

程式碼如下:

Program.cs

[c-sharp] view
plain
 copy

  1. using System;  
  2. using System.Windows.Forms;  
  3.    
  4. namespace Edu.Study.Graphics.SplitLayout {  
  5.    
  6.     /// <summary>  
  7.     /// SplitContainer測試窗體  
  8.     /// </summary>  
  9.     class MyForm : Form {  
  10.    
  11.         // 分隔面板容器  
  12.         private SplitContainer splitPane;  
  13.    
  14.         // 放置在分隔面板2號面板內的流式面板容器  
  15.         private FlowLayoutPanel flowPane;  
  16.    
  17.         // 放置在分隔面板1號面板的標籤控制元件  
  18.         private Label textLabel;  
  19.    
  20.         /***** 放置在分隔面板2號面板的按鈕 *****/  
  21.         // 用於切換分隔面板分隔方向的按鈕  
  22.         private Button switchButton;  
  23.         // 用於合起分隔面板左邊部分的按鈕  
  24.         private Button collapsedButton;  
  25.    
  26.         // 定時器元件  
  27.         private Timer colapsedTimer;  
  28.    
  29.         // 分隔面板左邊部分是否被合起的標誌  
  30.         private bool isColapsed = false;  
  31.    
  32.         /// <summary>  
  33.         /// 構造器  
  34.         /// </summary>  
  35.         public MyForm() {  
  36.    
  37.             this.Text = "分隔容器測試";  
  38.    
  39.             /***** 初始splitPane化分隔面板容器 *****/  
  40.             this.splitPane = new SplitContainer();  
  41.             // 設定分隔面板容器分隔線方向為垂直方向  
  42.             this.splitPane.Orientation = Orientation.Vertical;  
  43.             // 設定分隔面板容器錨定在父容器中央  
  44.             this.splitPane.Dock = DockStyle.Fill;  
  45.             // 設定分隔線寬度為2單位  
  46.             this.splitPane.SplitterWidth = 2;  
  47.             // 設定分隔面板容器邊框樣式為實線邊框, 這樣分隔線可以看得更清楚  
  48.             this.splitPane.BorderStyle = BorderStyle.FixedSingle;  
  49.    
  50.             /***** 初始化textLabel標籤控制元件 *****/  
  51.             this.textLabel = new Label();  
  52.             // 設定標籤控制元件內顯示文字  
  53.             this.textLabel.Text = "分隔面板測試";  
  54.             // 設定標籤控制元件根據內容自動調整尺寸  
  55.             this.textLabel.AutoSize = true;  
  56.             // 將標籤控制元件加入splitPane容器1號面板容器內  
  57.             this.splitPane.Panel1.Controls.Add(this.textLabel);  
  58.    
  59.             /***** 初始化flowPane容器 *****/  
  60.             this.flowPane = new FlowLayoutPanel();  
  61.             // 設定流式面板容器佈局方向為上下方向  
  62.             this.flowPane.FlowDirection = FlowDirection.TopDown;  
  63.             // 禁止容器內控制元件自動換行  
  64.             this.flowPane.WrapContents = false;  
  65.             // 禁止容器自動出現滾動條  
  66.             this.flowPane.AutoScroll = false;  
  67.             // flowPane容器錨定在父容器左邊位置  
  68.             this.flowPane.Dock = DockStyle.Left;  
  69.             // flowPane容器加入splitPane容器2號面板容器內  
  70.             this.splitPane.Panel2.Controls.Add(this.flowPane);  
  71.    
  72.             /***** 初始化switchButton按鈕控制元件 *****/  
  73.             this.switchButton = new Button();  
  74.             // 設定按鈕文字  
  75.             this.switchButton.Text = "切換分隔方向";  
  76.             // 設定按鈕自動調整尺寸  
  77.             this.switchButton.AutoSize = true;  
  78.             // 向按鈕增加事件委託  
  79.             this.switchButton.Click += new EventHandler(SwitchButtonClick);  
  80.             // 設定按鈕四周的空白  
  81.             this.switchButton.Margin = new Padding(0, 3, 3, 3);  
  82.             // 設定按鈕的樣式, 彈出式按鈕  
  83.             this.switchButton.FlatStyle = FlatStyle.Popup;  
  84.             // 按鈕增加在flowPane容器內  
  85.             this.flowPane.Controls.Add(this.switchButton);  
  86.    
  87.             /***** 初始化collapsedButton按鈕控制元件, 同上 *****/  
  88.             this.collapsedButton = new Button();  
  89.             this.collapsedButton.Text = "收起左邊欄";  
  90.             this.collapsedButton.AutoSize = true;  
  91.             this.collapsedButton.Click += new EventHandler(CollapsedButtonClicked);  
  92.             this.collapsedButton.Margin = new Padding(0, 3, 3, 3);  
  93.             this.collapsedButton.FlatStyle = FlatStyle.Popup;  
  94.             this.flowPane.Controls.Add(this.collapsedButton);  
  95.    
  96.             /***** 初始化colapsedTimer定時器控制元件 *****/  
  97.             this.colapsedTimer = new Timer();  
  98.             // 定時器激發時間間隔20毫秒  
  99.             this.colapsedTimer.Interval = 20;  
  100.             // 設定事件委託, 定時器到達激發時間執行的方法  
  101.             this.colapsedTimer.Tick += new EventHandler(ColapsedTimerTick);  
  102.    
  103.             // splitPane容器增加在主窗體上  
  104.             this.Controls.Add(this.splitPane);  
  105.         }  
  106.    
  107.         /// <summary>  
  108.         /// 獲取splitPane容器1號面板最小尺寸  
  109.         /// </summary>  
  110.         /// <returns></returns>  
  111.         private int GetPanel1MiniSize() {  
  112.             int miniDistance = 0;  
  113.             // 根據splitPane容器的方向, 獲取窗體高度或寬度的1/4作為1號面板尺寸  
  114.             if (this.splitPane.Orientation == Orientation.Vertical) {  
  115.                 miniDistance = this.Width / 4;  
  116.             } else {  
  117.                 miniDistance = this.Height / 4;  
  118.             }  
  119.             return miniDistance;  
  120.         }  
  121.    
  122.         /// <summary>  
  123.         /// 將splitPane容器1號面板固定在某個最小尺寸上  
  124.         /// </summary>  
  125.         private void ChangeSpliterDistanceAndPanel1MiniSize() {  
  126.             // 設定SplitterDistance屬性和Panel1MinSize屬性,  
  127.             // 將分隔欄距離和1號面板最小尺寸設定為GetPanel1MiniSize方法返回值  
  128.             this.splitPane.SplitterDistance =  
  129.                 this.splitPane.Panel1MinSize = this.GetPanel1MiniSize();  
  130.         }  
  131.    
  132.         /// <summary>  
  133.         /// 窗體載入事件  
  134.         /// </summary>  
  135.         protected override void OnLoad(EventArgs e) {  
  136.             base.OnLoad(e);  
  137.    
  138.             // 設定splitPane容器1號面板最小寬度  
  139.             this.ChangeSpliterDistanceAndPanel1MiniSize();  
  140.    
  141.             // 重新計算flowPane容器的寬度  
  142.             //  由於flowPane中具有兩個按鈕, 所以設定flowPane寬度為兩個按鈕  
  143.             // 各自佔據橫向空間的最大值.  
  144.             this.flowPane.Width = Math.Max(  
  145.                 this.switchButton.Width + this.switchButton.Margin.Horizontal,  
  146.                 this.collapsedButton.Width + this.collapsedButton.Margin.Horizontal  
  147.             );  
  148.         }  
  149.    
  150.         /// <summary>  
  151.         /// 窗體尺寸改變事件  
  152.         /// </summary>  
  153.         protected override void OnResize(EventArgs e) {  
  154.             base.OnResize(e);  
  155.    
  156.             // 窗體改變尺寸時重新設定splitPane容器1號面板最小寬度  
  157.             this.ChangeSpliterDistanceAndPanel1MiniSize();  
  158.         }  
  159.    
  160.         /// <summary>  
  161.         /// switchButton按鈕控制元件點選事件  
  162.         /// 轉換splitPane容器的分隔方向  
  163.         /// </summary>  
  164.         private void SwitchButtonClick(object sender, EventArgs e) {  
  165.    
  166.             if (this.splitPane.Orientation == Orientation.Vertical) {  
  167.                 // 如果splitPane容器分隔線原本為垂直方向, 則改為水平方向  
  168.                 this.splitPane.Orientation = Orientation.Horizontal;  
  169.    
  170.                 // 指定flowPane容器錨定為錨地在父容器頂端  
  171.                 // splitPane容器垂直分隔後, 分隔線呈水平處於splitPane容器2號面板的頂部  
  172.                 this.flowPane.Dock = DockStyle.Top;  
  173.    
  174.                 // 更改flowPane的佈局方向為水平佈局  
  175.                 // 此時flowPane中的兩個按鈕變為橫向排列  
  176.                 this.flowPane.FlowDirection = FlowDirection.LeftToRight;  
  177.    
  178.                 // 重新設定兩個按鈕周圍的空白  
  179.                 this.collapsedButton.Margin =  
  180.                     this.switchButton.Margin = new Padding(3, 0, 3, 3);  
  181.             } else {  
  182.                 // 如果splitPane容器分隔線原本水平方向, 則改為垂直方向  
  183.                 this.splitPane.Orientation = Orientation.Vertical;  
  184.    
  185.                 // 指定flowPane容器錨定為錨地在父容器左邊  
  186.                 // splitPane容器水平分隔後, 分隔線呈垂直處於splitPane容器2號面板的左側  
  187.                 this.flowPane.Dock = DockStyle.Left;  
  188.    
  189.                 // 更改flowPane的佈局方向為垂直佈局  
  190.                 // 此時flowPane中的兩個按鈕變為縱向排列  
  191.                 this.flowPane.FlowDirection = FlowDirection.TopDown;  
  192.    
  193.                 // 重新設定兩個按鈕周圍的空白  
  194.                 this.collapsedButton.Margin =  
  195.                     this.switchButton.Margin = new Padding(0, 3, 3, 3);  
  196.             }  
  197.    
  198.             // 重新設定splitPane容器1號面板最小寬度  
  199.             this.ChangeSpliterDistanceAndPanel1MiniSize();  
  200.         }  
  201.    
  202.         /// <summary>  
  203.         /// "收起左邊欄"按鈕點選事件  
  204.         /// </summary>  
  205.         private void CollapsedButtonClicked(object sender, EventArgs e) {  
  206.    
  207.             // 顯示一個訊息框, 具有"是"和"否"兩個按鈕.  
  208.             // 返回DialogResult列舉值  
  209.             DialogResult dr = MessageBox.Show(  
  210.                 "是否顯示動畫方式?",        // 訊息框顯示內容  
  211.                 "提問",                   // 訊息框標題  
  212.                 MessageBoxButtons.YesNo,    // 訊息框顯示的按鈕型別  
  213.                 MessageBoxIcon.Question,    // 訊息框圖示型別  
  214.                 MessageBoxDefaultButton.Button1     // 訊息框預設按鈕(可以用回車鍵啟用)  
  215.             );  
  216.               
  217.             if (dr == DialogResult.Yes) {   // 選擇了"是"按鈕  
  218.                 if (this.isColapsed) {  
  219.                     // 如果splitPane容器的1號面板已經收起  
  220.                     // 則設定splitPane容器分隔線距離起始位置距離為0  
  221.                     this.splitPane.SplitterDistance = 0;  
  222.    
  223.                     // 設定splitPane容器1號面板不再收起  
  224.                     //  由於此時splitPane容器的SplitterDistance屬性值為0  
  225.                     // 所以1號面板也並沒有顯示出來  
  226.                     this.splitPane.Panel1Collapsed = false;  
  227.                 } else {  
  228.                     // 如果splitPane容器的1號面板已經展開  
  229.                     // 則設定1號面板的最小尺寸為0, 使其可以改變尺寸  
  230.                     this.splitPane.Panel1MinSize = 0;  
  231.                 }  
  232.    
  233.                 // 啟動定時器  
  234.                 this.colapsedTimer.Start();  
  235.             } else {    // 選擇了"否"按鈕, 表示無需動畫效果  
  236.                 if (this.splitPane.Panel1MinSize == 0) {  
  237.                     //   如果splitPane容器的1號面板最小尺寸為0,  
  238.                     // 表示它通過動畫方式收起過, 這裡將最小尺寸恢復正常值  
  239.                     this.splitPane.Panel1MinSize = this.GetPanel1MiniSize();  
  240.                 }  
  241.    
  242.                 // 改變1號面板的收起狀態  
  243.                 this.splitPane.Panel1Collapsed = !this.splitPane.Panel1Collapsed;  
  244.                 // 根據1號面板的收起狀態設定isColapsed標誌  
  245.                 this.isColapsed = this.splitPane.Panel1Collapsed;  
  246.             }  
  247.         }  
  248.    
  249.    
  250.         /// <summary>  
  251.         /// 定時器到到達事件處理方法  
  252.         /// </summary>  
  253.         private void ColapsedTimerTick(object sender, EventArgs e) {  
  254.             if (this.isColapsed) {  // 如果面板之前是合上的  
  255.                 // 計算1號面板最小尺寸  
  256.                 int miniSize = this.GetPanel1MiniSize();  
  257.    
  258.                 if (this.splitPane.SplitterDistance < miniSize) {  
  259.                     /***** 如果分隔線還未到達指定位置, 則將分隔線距離增加最多30個單位 *****/  
  260.                     // 求分隔線移動的最大距離  
  261.                     miniSize = Math.Min(miniSize – this.splitPane.SplitterDistance, 30);  
  262.                     // 設定分隔線與起始位置的距離  
  263.                     this.splitPane.SplitterDistance += miniSize;  
  264.                 } else {  
  265.                     // 如果分隔線距離起始位置已經到達要求距離  
  266.                     // 停止定時器  
  267.                     this.colapsedTimer.Stop();  
  268.                     // 設定1號面板最小尺寸  
  269.                     this.splitPane.Panel1MinSize = miniSize;  
  270.                     // 設定isColapsed欄位, 表示面板已經展開  
  271.                     this.isColapsed = false;  
  272.                 }  
  273.             } else {  
  274.                 /***** 如果分隔線還未到達指定位置, 則將分隔線距離減少最多30個單位 *****/  
  275.                 if (this.splitPane.SplitterDistance > 0) {  
  276.                     // 將分隔線距離起始位置距離減少最多30個單位  
  277.                     this.splitPane.SplitterDistance -= Math.Min(this.splitPane.SplitterDistance, 30);  
  278.                 } else {  
  279.                     // 如果分隔線距離起始位置已經到達要求距離  
  280.                     // 停止定時器   
  281.                     this.colapsedTimer.Stop();  
  282.                     // 設定面板1完全收起  
  283.                     this.splitPane.Panel1Collapsed = true;  
  284.                     // 設定isColapsed欄位, 表示面板已經收起  
  285.                     this.isColapsed = true;  
  286.                 }  
  287.             }  
  288.         }  
  289.     }  
  290.    
  291.     /// <summary>  
  292.     /// 主方法類  
  293.     /// </summary>  
  294.     static class Program {  
  295.         /// <summary>  
  296.         /// 應用程式的主入口點。  
  297.         /// </summary>  
  298.         static void Main() {  
  299.             Application.EnableVisualStyles();  
  300.             Application.SetCompatibleTextRenderingDefault(false);  
  301.             Application.Run(new MyForm());  
  302.         }  
  303.     }  
  304. }  

本節程式碼下載

在程式碼中,通過“切換分隔方向”按鈕來切換分隔線的方向(164-200行),切換分隔線方向很簡單,設定其Orientation屬性即可,但要注意,切換分隔線方向後,要對兩個面板內控制元件的佈局方向做必要的調整,因為兩個面板的放置方向改變了;

程式碼中使用了兩種方式來收起面板,普通方式和動畫方式,普通方式很簡單,直接設定Panel1Collapsed(或Panel2Collapsed)屬性即可(第243行);動畫方式是通過一個定時器控制元件(即定時呼叫事件委託方法的控制元件)不斷定時改變面板的SplitterDistance屬性(收起為減小屬性值,展開為增加屬性值),直到SplitterDistance屬性值達到預定大小(253-288行);注意,SplitterDistance屬性不能為負數,程式碼261行和277行保證了這一點。

定時器在固定時間間隔後呼叫某個委託函式一次,並一直重複直到定時器被關閉。其Interval屬性用於設定時間間隔(即多久呼叫委託函式一次,第99行),Tick事件用於指定呼叫的委託函式(第100行),使用Start方法可以啟動定時器(第234行),Stop方法可以停止(也可以說是關閉)定時器(第267行)。定時器必須可以在適當的時候被關閉,本例中,如果分隔線達到指定距離後,定時器被關閉。