C#语言 第四部分 图形界面编程(五) 布局容器类(2)

3 布局中的空白区域

在.net Framework中,每个控件和其所处的容器以及容器内控件之间,都存在一个可以调整的空白区域,这个空白区域纯粹是为了布局的美观性而存在的。

容器和容器内控件之间的空白称为容器的Padding属性,容器内控件之间的空白称为控件的Margin属性。看一下示意图:


图1 Padding和Margin示意图

很容易理解,蓝色箭头表示的四个空白位置为窗体容器的Padding属性;黄色箭头表示的四个空白位置为按钮控件的Margin属性。

注意:一个控件离其所在容器四周的距离,是容器的Padding属性和该控件的Margin属性之和。例如图中的按钮0,它和Form容器的左边的距离就是Form容器的Padding属性和按钮0的Margin属性之和。

Padding属性和Margin属性的值都是一个Padding类的对象,Padding类为值类型对象,具有四个整型类型属性:Left,Top,Right,Bottom,分别表左上右下四个方向的空白大小。Padding类还有一个特殊的整型类型属性:All,当Left,Top,Right,Bottom属性值相同时,All属性是Left,Top,Right,Bottom其中一个的值,否则All属性值为-1。即当Padding对象的All属性为-1时,表示其Left,Top,Right,Bottom属性值不同,当All属性值为正整数时,表示Left,Top,Right,Bottom值相同,等于All的值。

可以猜想,Padding类大致如下:

Padding.cs

public struct Padding { // 字段 private bool all; private int top; private int left; private int right; private int bottom; // 静态只读字段, 表示一个四周为0的Padding对象 public static readonly Padding Empty = new Padding(0); /// /// 构造器, 通过设置All属性, 设置上下左右四个属性值 /// public Padding(int all) { // 设置all标志为true this.all = true; // 设置上下左右四个字段, 值相同 this.top = this.left = this.right = this.bottom = all; } /// /// 设置上下左右四个属性值 /// public Padding(int left, int top, int right, int bottom) { // 设置上下左右四个属性值 this.top = top; this.left = left; this.right = right; this.bottom = bottom; // 根据上下左右四个属性值是否相同设置all标志 this.all = (this.top == this.left) && (this.left == this.right) && (this.right == this.bottom) && (this.bottom == this.top); } /// /// All属性, 如果上下左右属性值相同, 则返回其中一个值, 否则返回-1 /// public int All { get { // 根据all标志是否为true, 决定返回上下左右任意值或-1 return this.all ? this.top : -1; } set { // 设置all标志为true this.all = true; // 设置上下左右四个字段, 值相同 this.top = this.left = this.right = this.bottom = value; } } /// /// 设置或获取底部空白 /// public int Bottom { get { return this.bottom; } set { this.bottom = value; // 根据上下左右四个属性值是否相同设置all标志 this.all = (this.top == this.left) && (this.left == this.right) && (this.right == this.bottom) && (this.bottom == this.top); } } /// /// 设置或获取左边空白 /// public int Left { get { return this.left; } set { this.left = value; this.all = (this.top == this.left) && (this.left == this.right) && (this.right == this.bottom) && (this.bottom == this.top); } } /// /// 设置或获取右边空白 /// public int Right { get { return this.right; } set { this.right = value; this.all = (this.top == this.left) && (this.left == this.right) && (this.right == this.bottom) && (this.bottom == this.top); } } /// /// 设置或获取顶部空白 /// public int Top { get { return this.top; } set { this.top = value; this.all = (this.top == this.left) && (this.left == this.right) && (this.right == this.bottom) && (this.bottom == this.top); } } /// /// 获取水平空白总和 /// public int Horizontal { get { return this.right + this.left; } } /// /// 获取垂直空白总和 /// public int Vertical { get { return this.top + this.bottom; } } /// /// 获取以空白值为属性的Size类型对象 /// public Size Size { get { return new Size(this.Horizontal, this.Vertical); } } /// /// 静态方法, 获取两个Padding对象的和 /// (即将两个对象的上下左右属性各自相加后得到的Padding对象) /// public static Padding Add(Padding p1, Padding p2) { // 调用重载的+运算符 return (p1 + p2); } /// /// 静态方法, 获取两个Padding对象的差 /// (即将两个对象的上下左右属性各自想减后得到的Padding对象) /// public static Padding Subtract(Padding p1, Padding p2) { // 调用重载的-运算符 return (p1 - p2); } /// /// 覆盖对象的比较方法 /// public override bool Equals(object other) { // 判断对象的类型后调用对象重载的==运算符 return ((other is Padding) && (((Padding)other) == this)); } /// /// 重载+运算符, 表示两个Padding类型对象相加 /// public static Padding operator +(Padding p1, Padding p2) { // 将两个对象的上下左右属性各自相加后得到的新的Padding对象 return new Padding(p1.Left + p2.Left, p1.Top + p2.Top, p1.Right + p2.Right, p1.Bottom + p2.Bottom); } /// /// 重载-运算符, 表示两个Padding类型对象相减 /// public static Padding operator -(Padding p1, Padding p2) { // 将两个对象的上下左右属性各自相减后得到的新的Padding对象 return new Padding(p1.Left - p2.Left, p1.Top - p2.Top, p1.Right - p2.Right, p1.Bottom - p2.Bottom); } /// /// 重载==运算符 /// public static bool operator ==(Padding p1, Padding p2) { if (p1.all && p2.all) { // 如果p1和p2的all字段都为true, 则比较它们的Top属性 return p1.Top == p2.Top; } else { // 否则将它们对应属性一一比较 return (p1.Left == p2.Left) && (p1.Top == p2.Top) && (p1.Right == p2.Right) && (p1.Bottom == p2.Bottom); } } /// /// 重载!=运算符 /// public static bool operator !=(Padding p1, Padding p2) { return !(p1 == p2); } /// /// 覆盖GetHashCode方法(因为覆盖了Equals方法) /// public override int GetHashCode() { return this.left ^ this.Right ^ this.Top ^ this.Bottom; } /// /// 覆盖ToString方法, 返回字符串表示 /// public override string ToString() { return string.Format("{ Left={0}, Top={1}, Right={2}, Bottom={3} }", this.Left, this.Top, this.Right, this.Bottom); } }

通过Padding类的源码(猜想,非官方源码),可以了解Padding五个属性的工作方式,顺便复习一下值类型构造、比较、Empty字段以及运算符重载的知识。

4 流式布局

我们已经了解,控件都具备Dock方位布局能力,可以按照“上下左右中”五个方位将自身锚定在容器上进行布局。但我们也看到,如果要让容器内的控件进行其它形式的布局(例如上一节中控件成行列布局),则一般需要在容器的Resize事件处理方法内,为容器内的所有控件使用算法进行布局。其复杂度和代码量都不好控制。

除非有特殊需要,一般情况下我们尽量不要自己书写大段的控件布局代码,这些代码很不经济。.net Framework提供了若干个专门用来负责布局控件的容器,使用起来非常方便。

FlowLayoutPanel称为流式布局面板容器,它的作用是将容器内控件按照从左到右(或从右到左)以及从上到下(或从下到上)的顺序排列布局。几点重要属性:

  • FlowDirection属性:FlowDirection枚举类型。表示容器内控件布局方向。分别为:LeftToRight(从左到右),TopDown(从上到下),RightToLeft(从右到左),BottomUp(从下到上)四个枚举项。
  • WrapContents属性:bool类型。表示是否为容器内控件布局自动换行。对于属性值为true,当控件超出容器范围,则将控件自动排列到下一行;对于false,则忽略超出部分的显示。
  • AutoScroll属性:bool类型。表示当WrapContents属性为false时,当控件超出容器范围时,容器是否显示一个滚动条。

好了,该容器使用起来很简单,我们通过实例来说明:

界面显示效果图如下:

图2 程序界面显示效果图

代码如下:

Program.cs

using System; using System.Drawing; using System.Windows.Forms; namespace Edu.Study.Graphics.FlowLayout { /// /// 窗体类 /// class MyForm : Form { /************ 流式布局面板 ************/ // 顶部流式布局面板 private FlowLayoutPanel topPane; // 底部流式布局面板 private FlowLayoutPanel bottomPane; /************ 文字标签 ************/ // 布局方向标签 private Label flowDirectionComboBoxLabel; // 容器Padding尺寸标签 private Label panddingTrackBarLabel; // 控件Margin尺寸标签 private Label marginTrackBarLabel; /************ 组合下拉列表框 ************/ // 选择流式布局方向的下拉列表框 private ComboBox flowDirectionComboBox; /************ 复选框 ************/ // 布局容器是否自动换行复选框 private CheckBox wrapCheckBox; // 容器是否自动换行复选框 private CheckBox autoScrollCheckBox; /************ 数字调节块 ************/ // 设置容器Padding属性的调节块 private TrackBar panddingTrackBar; // 设置容器内控件Margin属性的调节块 private TrackBar marginTrackBar; /************ 文本框 ************/ // 显示容器Padding属性值的调节块 private TextBox pandingTrackBarValueTextBox; // 显示容器内控件Margin属性值的调节块 private TextBox marginTrackBarValueTextBox; /************ 按钮 ************/ // bottomPane内放置的按钮控件, 是一个Button类数组 private Button[] buttons; /// /// 构造器, 初始化所有控件 /// public MyForm() { /************ topPane控件初始化 ************/ this.topPane = new FlowLayoutPanel(); // 设置topPane容器停靠在窗体顶部 this.topPane.Dock = DockStyle.Top; // 设置topPane容器布局方向: 从左到右 this.topPane.FlowDirection = FlowDirection.LeftToRight; // 设置topPane容器的Padding属性 this.topPane.Padding = new Padding(30); // 设置topPane容器的边框属性, 呈现3D效果 this.topPane.BorderStyle = BorderStyle.Fixed3D; // 设置topPane容器不自动换行 this.topPane.WrapContents = false; // 设置topPane容器具有自动滚动条 this.topPane.AutoScroll = true; /************ flowDirectionComboBoxLabel控件初始化 ************/ this.flowDirectionComboBoxLabel = new Label(); // 设置flowDirectionComboBoxLabel控件Margin属性 this.flowDirectionComboBoxLabel.Margin = new Padding(2, 6, 0, 3); // 设置flowDirectionComboBoxLabel控件呈现文本 this.flowDirectionComboBoxLabel.Text = "布局方向:"; // 设置flowDirectionComboBoxLabel控件自动调整尺寸 this.flowDirectionComboBoxLabel.AutoSize = true; // 将flowDirectionComboBoxLabel控件增加在topPane容器内 this.topPane.Controls.Add(this.flowDirectionComboBoxLabel); /************ flowDirectionComboBox控件初始化 ************/ this.flowDirectionComboBox = new ComboBox(); // 设置flowDirectionComboBox控件Margin属性 this.flowDirectionComboBox.Margin = new Padding(0, 3, 0, 3); // 设置flowDirectionComboBox控件下拉列表内容 this.flowDirectionComboBox.Items.AddRange(new object[] { FlowDirection.LeftToRight, FlowDirection.RightToLeft, FlowDirection.TopDown, FlowDirection.BottomUp }); // 设置flowDirectionComboBox控件下拉默认选中项 this.flowDirectionComboBox.SelectedIndex = 0; // 设置flowDirectionComboBox控件选择项改变后通知事件 this.flowDirectionComboBox.SelectedIndexChanged += new EventHandler(FlowDirectionComboBoxSelectedValueChanged); // 将flowDirectionComboBox控件增加在topPane容器内 this.topPane.Controls.Add(this.flowDirectionComboBox); /************ wrapCheckBox控件初始化 ************/ this.wrapCheckBox = new CheckBox(); // 设置wrapCheckBox控件Margin属性 this.wrapCheckBox.Margin = new Padding(20, 5, 0, 5); // 设置wrapCheckBox控件自动调整尺寸 this.wrapCheckBox.AutoSize = true; // 设置wrapCheckBox控件呈现文本 this.wrapCheckBox.Text = "是否自动换行"; // 设置wrapCheckBox控件选中状态改变通知事件 this.wrapCheckBox.CheckedChanged += new EventHandler(WarpCheckBoxCheckedChanged); // 将wrapCheckBox控件增加在topPane容器内 this.topPane.Controls.Add(this.wrapCheckBox); /************ autoScrollCheckBox控件初始化 ************/ this.autoScrollCheckBox = new CheckBox(); // 设置autoScrollCheckBox控件Margin属性 this.autoScrollCheckBox.Margin = new Padding(20, 5, 0, 5); // 设置autoScrollCheckBox控件自动调整尺寸 this.autoScrollCheckBox.AutoSize = true; // 设置autoScrollCheckBox控件呈现文本 this.autoScrollCheckBox.Text = "是否使用滚动条"; // 设置autoScrollCheckBox控件选中状态改变通知事件 this.autoScrollCheckBox.CheckedChanged += new EventHandler(AutoScrollCheckBoxChanged); // 将autoScrollCheckBox控件增加在topPane容器内 this.topPane.Controls.Add(this.autoScrollCheckBox); /************ panddingTrackBarLabel控件初始化 ************/ this.panddingTrackBarLabel = new Label(); // 设置panddingTrackBarLabel控件自动调整尺寸 this.panddingTrackBarLabel.AutoSize = true; // 设置panddingTrackBarLabel控件Margin属性 this.panddingTrackBarLabel.Margin = new Padding(20, 5, 0, 5); // 设置panddingTrackBarLabel控件呈现文本 this.panddingTrackBarLabel.Text = "更改容器的 Padding:"; // 将panddingTrackBarLabel控件增加在topPane容器内 this.topPane.Controls.Add(this.panddingTrackBarLabel); /************ panddingTrackBar控件初始化 ************/ this.panddingTrackBar = new TrackBar(); // 设置panddingTrackBar控件Margin属性 this.panddingTrackBar.Margin = new Padding(0, 3, 3, 3); // 设置panddingTrackBar控件宽度属性 this.panddingTrackBar.Width = 120; // 设置panddingTrackBar控件数值最小值 this.panddingTrackBar.Minimum = 0; // 设置panddingTrackBar控件数值最大值 this.panddingTrackBar.Maximum = 100; // 设置panddingTrackBar控件单位数值 this.panddingTrackBar.TickFrequency = 1; // 设置panddingTrackBar控件数值改变时通知事件 this.panddingTrackBar.ValueChanged += new EventHandler(PandingTrackBarValueChanged); // 将panddingTrackBar控件增加在topPane容器内 this.topPane.Controls.Add(this.panddingTrackBar); /************ pandingTrackBarValueTextBox控件初始化 ************/ this.pandingTrackBarValueTextBox = new TextBox(); // 设置pandingTrackBarValueTextBox控件Margin属性 this.pandingTrackBarValueTextBox.Margin = new Padding(0, 3, 3, 3); // 设置pandingTrackBarValueTextBox控件只读 this.pandingTrackBarValueTextBox.ReadOnly = true; // 设置pandingTrackBarValueTextBox控件宽度属性 this.pandingTrackBarValueTextBox.Width = 80; // 设置pandingTrackBarValueTextBox控件背景色属性 this.pandingTrackBarValueTextBox.BackColor = SystemColors.Window; // 将pandingTrackBarValueTextBox控件增加在topPane容器内 this.topPane.Controls.Add(this.pandingTrackBarValueTextBox); /************ marginTrackBarLabel控件初始化 ************/ this.marginTrackBarLabel = new Label(); // 设置marginTrackBarLabel控件自动调整尺寸 this.marginTrackBarLabel.AutoSize = true; // 设置marginTrackBarLabel控件Margin属性 this.marginTrackBarLabel.Margin = new Padding(20, 5, 0, 5); // 设置marginTrackBarLabel控件呈现文本 this.marginTrackBarLabel.Text = "更改容器内控件的 Margin:"; // 将marginTrackBarLabel控件增加在topPane容器内 this.topPane.Controls.Add(this.marginTrackBarLabel); /************ marginTrackBar控件初始化 ************/ this.marginTrackBar = new TrackBar(); // 设置marginTrackBar控件Margin属性 this.marginTrackBar.Margin = new Padding(0, 3, 3, 3); // 设置marginTrackBar控件宽度属性 this.marginTrackBar.Width = 120; // 设置marginTrackBar控件数值最小值 this.marginTrackBar.Minimum = 0; // 设置marginTrackBar控件数值最大值 this.marginTrackBar.Maximum = 100; // 设置marginTrackBar控件单位数值 this.marginTrackBar.TickFrequency = 1; // 设置marginTrackBar控件数值改变时通知事件 this.marginTrackBar.ValueChanged += new EventHandler(MarginTrackBarValueChanged); // 将marginTrackBar控件增加在topPane容器内 this.topPane.Controls.Add(this.marginTrackBar); /************ marginTrackBarValueTextBox控件初始化 ************/ this.marginTrackBarValueTextBox = new TextBox(); // 设置marginTrackBarValueTextBox控件Margin属性 this.marginTrackBarValueTextBox.Margin = new Padding(0, 3, 3, 3); // 设置marginTrackBarValueTextBox控件只读 this.marginTrackBarValueTextBox.ReadOnly = true; // 设置marginTrackBarValueTextBox控件宽度属性 this.marginTrackBarValueTextBox.Width = 80; // 设置marginTrackBarValueTextBox控件背景色属性 this.marginTrackBarValueTextBox.BackColor = SystemColors.Window; // 将marginTrackBarValueTextBox控件增加在topPane容器内 this.topPane.Controls.Add(this.marginTrackBarValueTextBox); // 将topPane容器增加到窗体上 this.Controls.Add(this.topPane); /************ bottomPane容器初始化 ************/ this.bottomPane = new FlowLayoutPanel(); // 设置bottomPane容器停靠在窗体底部 this.bottomPane.Dock = DockStyle.Bottom; // 设置bottomPane容器布局方向: 从左到右 this.bottomPane.FlowDirection = FlowDirection.LeftToRight; // 设置bottomPane容器的Padding属性 this.bottomPane.Padding = new Padding(30); // 设置bottomPane容器的边框属性, 呈现3D效果 this.bottomPane.BorderStyle = BorderStyle.Fixed3D; /************ wrapCheckBox控件初始化 ************/ // 初始化200个Button对象的数组 this.buttons = new Button[200]; // 初始化数组中的每一项 for (int i = 0; i < this.buttons.Length; i++) { Button btn = new Button(); // 设置按钮呈现文字 btn.Text = i.ToString(); // 设置按钮的尺寸 btn.Size = new Size(100, 80); buttons[i] = btn; } // 将所有的按钮增加到bottomPane容器上 this.bottomPane.Controls.AddRange(buttons); // 将bottomPane容器增加到窗体上 this.Controls.Add(this.bottomPane); // 设置窗体最大化 this.WindowState = FormWindowState.Maximized; // 设置窗体最小尺寸 this.MinimumSize = new Size(800, 600); } /// /// 覆盖窗体OnLoad方法, 处理窗体第一次加载通知消息 /// protected override void OnLoad(EventArgs e) { base.OnLoad(e); // 设置wrapCheckBox复选框Checked属性和bottomPane容器WrapContents属性值一致 this.wrapCheckBox.Checked = this.bottomPane.WrapContents; // 设置autoScrollCheckBox复选框Checked属性和bottomPane容器AutoScroll属性值一致 this.autoScrollCheckBox.Checked = this.bottomPane.AutoScroll; // 设置pandingTrackBarValueTextBox文本框文本为bottomPane容器Padding属性值 this.pandingTrackBarValueTextBox.Text = this.bottomPane.Padding.All.ToString(); // 设置marginTrackBarValueTextBox文本框文本为按钮控件Margin属性值 this.marginTrackBarValueTextBox.Text = this.buttons[0].Margin.All.ToString(); // 设置panddingTrackBar控件当前数值为bottomPane容器Padding属性值 this.panddingTrackBar.Value = this.bottomPane.Padding.All; // 设置marginTrackBar控件当前数值为按钮控件Margin属性值 this.marginTrackBar.Value = this.buttons[0].Margin.All; } /// /// 处理flowDirectionComboBox下拉列表控件选项改变事件 /// private void FlowDirectionComboBoxSelectedValueChanged(object sender, EventArgs e) { // 设置bottomPane容器布局方向与flowDirectionComboBox下拉列表选项一致 this.bottomPane.FlowDirection = (FlowDirection)this.flowDirectionComboBox.SelectedItem; } /// /// 处理wrapCheckBox复选框选择状态改变事件 /// private void WarpCheckBoxCheckedChanged(object sender, EventArgs e) { // 设置bottomPane容器是否自动换行属性与wrapCheckBox复选框选中状态一致 this.bottomPane.WrapContents = this.wrapCheckBox.Checked; } /// /// 处理autoScrollCheckBox复选框选择状态改变事件 /// private void AutoScrollCheckBoxChanged(object sender, EventArgs e) { // 设置bottomPane容器是否具备滚动条属性与autoScrollCheckBox复选框选中状态一致 this.bottomPane.AutoScroll = this.autoScrollCheckBox.Checked; } /// /// 处理panddingTrackBar控件数值改变事件 /// private void PandingTrackBarValueChanged(object sender, EventArgs e) { // 设置bottomPane容器Padding属性与panddingTrackBar当前数值一致 this.bottomPane.Padding = new Padding(this.panddingTrackBar.Value); // 设置pandingTrackBarValueTextBox文本框文本内容为bottomPane容器Padding属性值 this.pandingTrackBarValueTextBox.Text = this.bottomPane.Padding.All.ToString(); } /// /// 处理marginTrackBarValueTextBox控件数值改变事件 /// private void MarginTrackBarValueChanged(object sender, EventArgs e) { // 设置所有按钮控件的Margin属性与marginTrackBar当前数值一致 foreach (Button btn in this.buttons) { btn.Margin = new Padding(this.marginTrackBar.Value); } // 设置marginTrackBarValueTextBox文本框文本内容为按钮Margin属性值 this.marginTrackBarValueTextBox.Text = this.buttons[0].Margin.All.ToString(); } /// /// 覆盖父类OnResize方法, 处理窗体尺寸变化消息通知 /// protected override void OnResize(EventArgs e) { base.OnResize(e); // 设置topPane容器高度为窗体客户区高度1/6 this.topPane.Height = this.ClientSize.Height / 6; // 设置bottomPane容器高度为窗体客户区高度5/6 this.bottomPane.Height = this.ClientSize.Height - this.ClientSize.Height / 6; } } /// /// 包含住方法的类 /// static class Program { /// /// 应用程序的主入口点。 /// static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MyForm()); } } }

本节代码下载

本次代码中,我们用到了一些其它控件,包括:

  • 文本标签控件(Label类);
  • 文本框控件(TextBox类);
  • 组合下拉列表框控件(ComboBox类);
  • 数值调节滑块控件(TrackBar类)

代码中除了展示FlowLayoutPanel容器和上述控件的用法外(特别注意这些控件的事件处理),还展示了容器的Padding属性和控件(以Button为例)的Margin属性,仔细阅读代码,灵活的掌握上述内容。

你可能感兴趣的:(C#语言 第四部分 图形界面编程(五) 布局容器类(2))