您的位置:首页 > 编程语言

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

2016-10-25 15:27 309 查看
点击打开链接

从ContainerControl类继承的子类作为容器窗体,可以容纳除Form类对象外的其余窗体对象。

在所有容器窗体内,最基本的就是顶级容器Form类以及面板容器Panel类。这两者的主要区别为:前者具有Windows标准框架(标题栏,最大化、最小化和关闭按钮,窗体边框,可调整尺寸),并且可以独立存在;后者只是一块区域,并且必须依附在某个容器窗体上,无法独立存在。除了它们的区别外,他们都具有:Controls属性,可以在上面放置控件;控件放置的位置需要通过控件的Top和Left属性或者控制。

1 绝对布局

对于容器类型的控件(包括Form类型),出了Size,Bounds属性外,还有ClientSizeClientRectangle属性,前者表示客户区尺寸,Size类型;后者表示客户区的矩形,是一个Rectangle类型。所谓客户区,就是容器实际可以使用的空间,对于Form类型来说,客户区就是除过标题栏,四周的边框外剩余的部分。



图1
客户区示意图
一般来说,直接在Form上或Panel上放置控件,控件的位置不会自动调整,完全依赖控件的Left, Top和Location等属性控制,控件的大小也不会自动改变,完全依靠控件的Width, Height和Size等属性控制。这种方式称为绝对位置布局

我们再来熟悉一下这些用于定位一个控件的属性们,它们可以用于获取或设置控件的位置和尺寸:

Left属性:控件距离其容器左边界的距离,int类型;
Top属性:控件距离其容器上边界的距离,int类型;
Location属性:控件左上角坐标距离其容器的距离,Point类型。一般而言,可以把容器的左上角认为是坐标轴原点,则Location属性表示控件相对于其容器的坐标。所以其X属性等于控件的Left属性,Y属性等于控件的Top属性;
 

Width属性:控件的宽度,int类型;
Height属性:控件的高度,int类型;
Size属性:控件的尺寸,包括宽度和高度,Size类型;
除了以上六个属性外,还可以使用Bounds属性,这是一个Rectangle类型的属性,表示一个相对于容器左上角为坐标原点的矩形,即控件的位置和尺寸。利用SetBounds方法还可以使用X, Y, Width, Height四个分量设置Bounds属性。

2 锚定相对布局

如果进一步设置控件的Dock属性,则可以设定控件再容器内的相对位置,Dock属性可以设置控件按照其所在容器的“左右上下中”这五个方位来放置控件,此时控件只能设置Width、Height和Size属性,而无法设置Left、Top和Location属性,即控件只能调整大小,无法自由设置位置。这种控件依照容器的相对位置放置控件的方式称为“相对位置布局”。

Dock的属性值是一个DockType枚举,其称谓为“锚定”,就是将控件和其容器的左右上下中这五个位置绑定,无论容器移动到什么地方,也无论容器的尺寸如何变化,控件始终确定在这五个位置之一的地方。这个枚举其定义如下:

    /// <summary>
    /// 定义锚定方位的枚举
    /// </summary>
    public enum DockStyle {
        /// <summary>
        /// 取消锚定
        /// </summary>
        None = 0,
 
        /// <summary>
        /// 锚定在顶部
        /// </summary>
        Top = 1,
 
        /// <summary>
        /// 锚定在底部
        /// </summary>
        Bottom = 2,
 
        /// <summary>
        /// 锚定在左边
        /// </summary>
        Left = 3,
 
        /// <summary>
        /// 锚定在右边
        /// </summary>
        Right = 4,
 
        /// <summary>
        /// 锚定在中央并充满容器
        /// </summary>
        Fill = 5,
    }


图2
锚定布局方位图
下面我们用一段代码来演示一下:

using System;
using System.Drawing;
using System.Windows.Forms;
 
namespace Edu.Study.Graphics.DockLayout {
 
    /// <summary>
    /// 继承Form类, 定义窗体类
    /// </summary>
10     class MyForm : Form {
11  
12         // 以不变模式设定WIN_TITLE, 表示窗体标题
13         private const string WIN_TITLE = "面板演示";
14  
15         /// <summary>
16         /// 面板类
17         /// </summary>
18         private Panel panel;
19  
20         /// <summary>
21         /// 按钮
22         /// </summary>
23         private Button button;
24  
25         /// <summary>
26         /// 单选按钮数组
27         /// </summary>
28         private RadioButton[] radioButtons;
29  
30         /// <summary>
31         /// 构造器
32         /// </summary>
33         public MyForm() {
34             // 设定窗体标题
35             this.Text = WIN_TITLE;
36             // 设定窗体最大化
37             this.WindowState = FormWindowState.Maximized;
38  
39             // 实例化面板类对象
40             this.panel = new Panel();
41             // 设置面板的边框为三维效果
42             this.panel.BorderStyle = BorderStyle.Fixed3D;
43             // 设定面板的锚定方式为无
44             this.panel.Dock = DockStyle.None;
45             // 设定面板的背景色为白色
46             this.panel.BackColor = Color.White;
47             // 为面板更改尺寸事件绑定委托方法
48             this.panel.Resize += new EventHandler(OnPanelResized);
49  
50             // 实例化按钮对象
51             this.button = new Button();
52             // 因为button是panel的子窗体, 所以背景色默认也会变为白色
53             // 这里为按钮重新指定颜色, SystemColors.Control是操作系统默认的控件背景色
54             this.button.BackColor = SystemColors.Control;
55             // 设置按钮文本
56             button.Text = "点下我!";
57             // 为按钮点击事件绑定委托方法
58             button.Click += new EventHandler(OnButtonClick);
59  
60             // 将按钮加入面板
61             this.panel.Controls.Add(this.button);
62  
63             // 实例化radioButtons数组, 数组长度和锚定样式数量相同
64             this.radioButtons = new RadioButton[(int)DockStyle.Fill + 1];
65             for (int i = 0; i < this.radioButtons.Length; i++) {
66                 RadioButton rb = new RadioButton();
67                 // 设定RadioButton的显示文本
68                 rb.Text = ((DockStyle)i).ToString();
69  
70                 // 将RadioButton对象加入面板
71                 this.panel.Controls.Add(rb);
72                 // 为数组元素赋值RadioButton对象引用
73                 this.radioButtons[i] = rb;
74             }
75  
76             // 将面板加入窗体
77             this.Controls.Add(this.panel);
78         }
79  
80         /// <summary>
81         /// 处理按钮点击事件的委托方法
82         /// </summary>
83         private void OnButtonClick(object sender, EventArgs e) {
84  
85             // 设置面板的宽度和高度为父窗体的1/3
86             this.panel.Width = this.ClientSize.Width / 3;
87             this.panel.Height = this.ClientSize.Height / 3;
88  
89             // 每点击一次按钮, 更改面板的锚定方式
90             if (this.panel.Dock == DockStyle.Fill) {
91                 this.panel.Dock = DockStyle.None;
92             } else {
93                 this.panel.Dock = (DockStyle)this.panel.Dock + 1;
94             }
95  
96             // 更改主窗体标题
97             this.Text = String.Format("{0} 面板的锚定方式目前为:{1}", WIN_TITLE, this.panel.Dock);
98             this.radioButtons[(int)this.panel.Dock].Checked = true;
99         }
100  
101         /// <summary>
102         /// 面板尺寸改变事件委托方法
103         /// </summary>
104         private void OnPanelResized(object sender, EventArgs e) {
105             // 在面板改变尺寸后, 设置按钮位于面板中央
106             this.button.Left = (this.panel.ClientSize.Width - this.button.Width) / 2;
107             this.button.Top = (this.panel.ClientSize.Height - this.button.Height) / 2;
108  
109             // 间隔距离
110             int marginPart = 0;
111             // 起始位置
112             int start = 0;
113             // 遍历RadioButton数组, 设置每一个RadioButton的位置
114             foreach (RadioButton rb in this.radioButtons) {
115                 // 根据面板的锚定方式选择计算位置的分支
116                 switch (this.panel.Dock) {
117                 case DockStyle.Fill:
118                 case DockStyle.Top:
119                 case DockStyle.Bottom:
120                     // 对于填充, 置顶, 置底三种锚定方式, 采用横向摆放RadioButton控件
121  
122                     // 设定每个RadioButton的上边距为面板高度的四分之一
123                     rb.Top = this.panel.ClientSize.Height / 4;
124  
125                     // 计算每个RadioButton控件的间距
126                     if (marginPart == 0) {
127                         marginPart = this.ClientSize.Width / this.radioButtons.Length;
128                         start = marginPart;
129                     }
130  
131                     // 按照RadioButton的间距等距设定其左边距
132                     rb.Left = start - rb.Width;
133                     break;
134                 case DockStyle.Left:
135                 case DockStyle.Right:
136                     // 对于靠左、靠右两种种锚定方式, 采用纵向摆放RadioButton控件
137  
138                     // 设定RadioButton的左边距为面板宽度的四分之一
139                     rb.Left = this.panel.ClientSize.Width / 4;
140                     if (this.panel.Dock == DockStyle.Right) {
141                         // 如果面板靠右锚定, 设置RadioButton的左边距为面板的四分之三
142                         rb.Left = this.panel.ClientSize.Width - rb.Width;
143                     }
144  
145                     // 计算每个RadioButton控件的间距
146                     if (marginPart == 0) {
147                         marginPart = this.ClientSize.Height / this.radioButtons.Length;
148                     }
149                     // 按照RadioButton的高度垂直等间距设置上边距
150                     rb.Top = start + rb.Height;
151                     break;
152                 }
153                 start += marginPart;
154             }
155         }
156     }
157  
158  
159     static class Program {
160         static void Main() {
161             Application.EnableVisualStyles();
162             Application.SetCompatibleTextRenderingDefault(false);
163             Application.Run(new MyForm());
164         }
165     }
166 }
本章代码下载

可以看到,在主窗体上,我们放置了一个面板(Panel),面板上有一个按钮(Button)和五个单项按钮(RadioButton),第40-77行代码是Form类构造器,实例化并创建了上述控件,并将它们都加入各自的容器(这里将Panel的容器设置为From)。对于面板,我们绑定了它的Resize事件,当面板尺寸发生改变时引发,执行OnPanelResized方法(第104-156行);对于按钮,我们绑定了它的Click事件,当按钮点击时引发,执行OnButtonClick方法(第83-99行)。

在OnButtonClick方法中,重新定义了面板的尺寸,然后将面板的Dock属性切换为另一个值。所以我们可以看到,一旦点击按钮,面板的位置就会发生变化,分别会位于Form容器的上(值为1)、下(值为2)、左(值为3)、右(值为4)、中(值为5)几个位置,而面板的Dock属性为None(值为0)时,面板绝对定位位置,不在按照锚定位置布局。如下图:













图3 锚定方位示意图
可以看到,当Dock属性为DockStyle.Top或DocStyle.Bottom时,只能设置控件的Height属性,其它位置和尺寸属性均无效;当设置为DockType.Left或DocType.Right时,只能设置控件的Width属性,其它属性无效。当控件的Dock属性为DockType.None时,则控件的位置按照其Top,Left和Location属性定位,控件的尺寸按照其Width,Height和Size属性来设定。

在OnPanelResized方法中,由于按钮和单选按钮都没有设定其Dock属性(即Dock属性为DockType.None),所以我们采用绝对定位,通过一个简单的算法,根据面板当前的的锚定方式,计算按钮和五个单选按钮的位置。如果是面板按照DockType.Top,
DockType.Buttom, DockType.Fill布局,则单选按钮按照横向平均间距布局,否则按照纵向平均间距布局,对于DockType.None不作处理。

通过上面的比较可以看出,绝对布局存在的问题是:除非不允许容器改变大小,否则容器一旦改变尺寸,将会破坏布局结构,而相对布局则不存在这个问题。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: