图形界面编程(五) 布局容器类(4)

点击打开链接


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行)。定时器必须可以在适当的时候被关闭,本例中,如果分隔线达到指定距离后,定时器被关闭。


阅读更多
换一批

没有更多推荐了,返回首页