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

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

2010-04-26 04:00 543 查看
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);

/// <summary>
/// 构造器, 通过设置All属性, 设置上下左右四个属性值
/// </summary>
public Padding(int all) {
// 设置all标志为true
this.all = true;
// 设置上下左右四个字段, 值相同
this.top = this.left = this.right = this.bottom = all;
}

/// <summary>
/// 设置上下左右四个属性值
/// </summary>
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);
}

/// <summary>
/// All属性, 如果上下左右属性值相同, 则返回其中一个值, 否则返回-1
/// </summary>
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;
}
}

/// <summary>
/// 设置或获取底部空白
/// </summary>
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);
}
}

/// <summary>
/// 设置或获取左边空白
/// </summary>
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);
}
}

/// <summary>
/// 设置或获取右边空白
/// </summary>
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);
}
}

/// <summary>

/// 设置或获取顶部空白
/// </summary>
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);
}
}

/// <summary>
/// 获取水平空白总和
/// </summary>
public int Horizontal {
get {
return this.right + this.left;
}
}

/// <summary>
/// 获取垂直空白总和
/// </summary>
public int Vertical {
get {
return this.top + this.bottom;
}
}

/// <summary>
/// 获取以空白值为属性的Size类型对象
/// </summary>
public Size Size {
get {
return new Size(this.Horizontal, this.Vertical);
}
}

/// <summary>
/// 静态方法, 获取两个Padding对象的和
/// (即将两个对象的上下左右属性各自相加后得到的Padding对象)
/// </summary>
public static Padding Add(Padding p1, Padding p2) {
// 调用重载的+运算符
return (p1 + p2);
}

/// <summary>
/// 静态方法, 获取两个Padding对象的差
/// (即将两个对象的上下左右属性各自想减后得到的Padding对象)
/// </summary>
public static Padding Subtract(Padding p1, Padding p2) {

// 调用重载的-运算符
return (p1 - p2);
}

/// <summary>
/// 覆盖对象的比较方法
/// </summary>
public override bool Equals(object other) {
// 判断对象的类型后调用对象重载的==运算符
return ((other is Padding) && (((Padding)other) == this));
}

/// <summary>
/// 重载+运算符, 表示两个Padding类型对象相加
/// </summary>
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);
}

/// <summary>
/// 重载-运算符, 表示两个Padding类型对象相减
/// </summary>
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);
}

/// <summary>
/// 重载==运算符
/// </summary>
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);
}
}

/// <summary>
/// 重载!=运算符
/// </summary>
public static bool operator !=(Padding p1, Padding p2) {
return !(p1 == p2);
}

/// <summary>
/// 覆盖GetHashCode方法(因为覆盖了Equals方法)
/// </summary>
public override int GetHashCode() {
return this.left ^ this.Right ^ this.Top ^ this.Bottom;
}

/// <summary>
/// 覆盖ToString方法, 返回字符串表示
/// </summary>
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 {
/// <summary>
/// 窗体类
/// </summary>
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;

/// <summary>
/// 构造器, 初始化所有控件
/// </summary>
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);
}

/// <summary>
/// 覆盖窗体OnLoad方法, 处理窗体第一次加载通知消息
/// </summary>
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;
}

/// <summary>
/// 处理flowDirectionComboBox下拉列表控件选项改变事件
/// </summary>
private void FlowDirectionComboBoxSelectedValueChanged(object sender, EventArgs e) {
// 设置bottomPane容器布局方向与flowDirectionComboBox下拉列表选项一致
this.bottomPane.FlowDirection = (FlowDirection)this.flowDirectionComboBox.SelectedItem;
}

/// <summary>
/// 处理wrapCheckBox复选框选择状态改变事件
/// </summary>
private void WarpCheckBoxCheckedChanged(object sender, EventArgs e) {
// 设置bottomPane容器是否自动换行属性与wrapCheckBox复选框选中状态一致
this.bottomPane.WrapContents = this.wrapCheckBox.Checked;
}

/// <summary>
/// 处理autoScrollCheckBox复选框选择状态改变事件
/// </summary>
private void AutoScrollCheckBoxChanged(object sender, EventArgs e) {
// 设置bottomPane容器是否具备滚动条属性与autoScrollCheckBox复选框选中状态一致
this.bottomPane.AutoScroll = this.autoScrollCheckBox.Checked;
}

/// <summary>
/// 处理panddingTrackBar控件数值改变事件
/// </summary>
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();
}

/// <summary>
/// 处理marginTrackBarValueTextBox控件数值改变事件
/// </summary>
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();
}

/// <summary>
/// 覆盖父类OnResize方法, 处理窗体尺寸变化消息通知
/// </summary>
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;
}
}

/// <summary>
/// 包含住方法的类
/// </summary>
static class Program {
/// <summary>
/// 应用程序的主入口点。
/// </summary>
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MyForm());
}
}
}

本节代码下载

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

文本标签控件(Label类);

文本框控件(TextBox类);

组合下拉列表框控件(ComboBox类);

数值调节滑块控件(TrackBar类)

代码中除了展示FlowLayoutPanel容器和上述控件的用法外(特别注意这些控件的事件处理),还展示了容器的Padding属性和控件(以Button为例)的Margin属性,仔细阅读代码,灵活的掌握上述内容。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: