Skip to content

控件布局

控件布局分为三种

  • 拖动布局
  • 代码布局
  • 布局器布局

1.拖动布局

1.1 实现方法

我们的控件都可以直接鼠标拖动到form里,可以自行调整位置

但是有时候鼠标拖动也不是很准确

我们可以按住ctrl,鼠标依次点击需要布局的控件,然后编辑器上方会有一些图标亮起来

或者鼠标框选一片区域,被框选中的控件也可以进行此操作

image-20250107110822303

上方form里可以看到有一个文本框,两个按钮,我们已经将这三个控件选中,若想让这三个空间水平对齐,可以点击如下按钮

image-20250107111003869

之后可以看到这三个控件水平在一条线上了

image-20250107111012839

其次,当我们点击某个空间聚焦时,也可以点击编辑器菜单的格式栏,来设置间距、大小、对齐

image-20250107143906073

1.2 实现原理

观察代码我们可以发现,上面方法是修改了空间的Locationsize

c#
this.button1.Location = new System.Drawing.Point(399, 14);
this.button1.Size = new System.Drawing.Size(75, 23);

1.3 缺点

我们使用编辑器手动拖动控件,创建以下布局

image-20250107112935092

当我们调整窗口的大小时,发现控件没有随着窗口改变而改变,也就是没有自适应

image-20250107112957875

2.手动布局

用代码计算每个空间的位置

2.1 窗口的尺寸

在手动布局之前,我们需要了解窗口的尺寸,窗口的尺寸分为窗口大小和客户区大小

  • 窗口大小:整个窗口,包含标题栏和边框,通过FormSize属性获取
  • 客户区大小:仅客户区,不包含标题栏和边框,通过FormClientSize属性获取
c#
int w=this.Size.Width;      		// 窗口区宽度
int h=this.Size.Height;				// 窗口区高度

int cw=this.ClientSize.Width;		// 客户区宽度
int ch=this.ClientSize.Height;		// 客户区高度

image-20250107113353838

2.2 实现方法

重写OnLayout方法

c#
override void OnLayout(LayoutEventArgs levent){
	// ...
}

当窗口大小改变时,会自动调用这个方法重新布局

c#
protected override void OnLayout(LayoutEventArgs e) { 
    base.OnLayout(e);			// 先调用父类OnLayout方法

    int cw=this.ClientSize.Width;
    int ch=this.ClientSize.Height;

    this.textBox1.AutoSize = false;			// 使文本框大小可调节
    this.textBox1.Location=new System.Drawing.Point(4, 4);
    this.textBox1.Size=new System.Drawing.Size(cw-100, 30);

    this.button1.Location = new System.Drawing.Point(cw - 80-4, 4);
    this.button1.Size = new System.Drawing.Size(80, 30);

    this.pictureBox1.Location = new System.Drawing.Point(4, 40);
    this.pictureBox1.Size = new System.Drawing.Size(cw-8, ch - 40 - 4);
}

TIP

需要给TextBoxAutoSize属性设置为falseTextBox控件的宽高才可调节

c#
this.textBox1.AutoSize = false;

PixPin_2025-01-07_14-13-16

2.3 Anchor

Anchor可以设置控件与父窗口的边距是否保持不变

Anchor可以设置四个方向,分别是上下左右,这几个方向也可以结合

  • 上:System.Windows.Forms.AnchorStyles.Top
  • 下:System.Windows.Forms.AnchorStyles.Bottom
  • 左:System.Windows.Forms.AnchorStyles.Left
  • 右:System.Windows.Forms.AnchorStyles.Right

给控件设置单个方向的锚点时,只需如下写法

c#
this.button1.Anchor = System.Windows.Forms.AnchorStyles.Top;

但是给控件设置多个方向锚点时,需要如下写法

c#
  this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
  | System.Windows.Forms.AnchorStyles.Left) 
  | System.Windows.Forms.AnchorStyles.Right)));

手写太麻烦,还是从工具栏点击比较好

选中空间,在工具箱查找Anchor属性,下拉框打开之后,发现四边都有一个矩形,点击几个添加/取消此方向的锚点

image-20250107145337037

锚点有以下常用的方法

  • 锚定与左下角:左下锚点

    PixPin_2025-01-07_15-01-37

  • 锚定与右下角:右下锚点

    PixPin_2025-01-07_15-02-38

  • 锚定与上边缘、水平拉伸:左右上锚点

    PixPin_2025-01-07_15-03-19

  • 锚定与上边缘、水平居中:水平居中、上锚点

    PixPin_2025-01-07_15-04-16

  • 拉伸:上下左右锚点

    PixPin_2025-01-09_17-05-38

  • 居中与窗口中央:水平居中、垂直居中、锚点无

    PixPin_2025-01-07_15-05-38

2.4 Dock

Dock可以将控件停靠在一侧或中央

选中空间后在属性栏找到Dock属性,可以看到有几块区域可以点击,点哪个方向,该控件就会停靠在哪个位置

image-20250107153720432

停靠后,控件会随着窗口的变化而拉伸

我们创建了三个Panel空间,分别停靠在上、左、中央,这类似一个后台管理的布局,如下所示

PixPin_2025-01-07_15-39-11

代码实现如图所示

c#
this.panel1.Dock = System.Windows.Forms.DockStyle.Top;			// 停靠在 上 端
this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom;		// 停靠在 下 端
this.panel1.Dock = System.Windows.Forms.DockStyle.Left;			// 停靠在 左 端
this.panel1.Dock = System.Windows.Forms.DockStyle.Right;		// 停靠在 右 端
this.panel1.Dock = System.Windows.Forms.DockStyle.None;			// 无停靠

WARNING

AnchorDock是冲突的,不可以同时使用

若使用Dock布局,最终的布局效果依赖于空间添加的顺序

比如:在一个Panel里像如下两种情况添加两个按钮

  • 第一个Dock设置Left,第二个设置Fill,会是如下情况

    image-20250108145329789

  • 第一个Dock设置Fill,第二个设置Left,会是如下情况

    image-20250108145417752

可以看到,第二种情况,第二个按钮会把第一个按钮的左边挡住

3.布局器

一些具有布局功能的控件,会有一个LayoutEngine属性,该属性用于负责子控件的布局,叫做布局器

一个Form或Panel都自带一个布局器

当窗口改变大小时,由窗口的布局器来负责调整布局

3.1 自定义布局器

自定义布局器使用步骤:

1.编辑器菜单栏=》工具=》选项=》Windows窗口设计器=》常规=》自动填充工具箱=True

image-20250107164656516

2.添加自定义Panel或Control类

我们创建一个自动水平拉伸的布局空间

c#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.Layout;

namespace 布局器
{
    public partial class CustomPanel:Panel
    {
        private readonly LayoutEngine layoutEngine=new CustomLayoutEngine();
        public override LayoutEngine LayoutEngine
        {
            get
            {
                return layoutEngine;
            }
        }

    }

    class CustomLayoutEngine : LayoutEngine
    {
        public override bool Layout(object container, LayoutEventArgs layoutEventArgs)
        {
            CustomPanel parent = (CustomPanel)container;
            int w=parent.Width;
            int h=parent.Height;

            int x=parent.Padding.Left;
            int y=parent.Padding.Top;

            w-=(x+parent.Padding.Right);
            h-=(y+parent.Padding.Bottom);

            int gap = 2;
            foreach (Control control in parent.Controls) { 
                control.Location=new System.Drawing.Point(x, y);
                control.Size=new System.Drawing.Size(w, control.PreferredSize.Height);

                y += control.Size.Height;
                y += gap;
            }


            return false;
        }
    }
}

3.编辑器菜单栏=》生成=》重新生成解决方案

image-20250107165009991

4.关闭Form1.cs重新打开,观察工具箱,发现多了一个我们自定义的布局空间

image-20250107165118425

把此布局空间拖到窗口中,再往这个布局空间中拖进几个其他空间,我们发现里面的空间会被自动拉伸

image-20250107165237139

3.2 FlowLayoutPanel

FlowLayoutPanelwinform自带的一个面板,里面实现了流式布局器,在工具栏的容器中可以找到

image-20250107165753348

然后我们把一些控件拖进这个布局器,发现这些空间都是从左到右依次排列,若剩余空间不足,则另起一行,而且当我们拖动改变这个布局器的大小时,布局也会随着变化

PixPin_2025-01-07_17-08-03

布局器也可以设置AnchorDock属性

3.3 TableLayoutPanel

TableLayoutPanel是winform自带的一个面板,里面有一个表格布局器,在工具栏的容器中可以找到

image-20250108112737768

拖进来之后可以看到,这是一个默认两行两列的表格

image-20250108112810917

3.3.1 表格操作

右键任意单元格,或者属性栏中找到Rows和Columns属性,可以设置当前行/列的属性

  • 右键任意单元格

    image-20250108112900705

  • 属性栏中找到Rows和Columns属性

    image-20250108114129341

    点击属性值的三个点...,在弹出的窗口中设置行/列属性

    image-20250108114213549

分别有如下操作

  • 添加行/列
    • 添加行:在当前表格末尾(最底部)添加一行
    • 添加行:在当前表格末尾(最右边)添加一列
  • 在当前单元格插入
    • 插入行:在被选中行上方插入一行
    • 插入列:在被选中列左边插入一行
  • 删除行/列:删除当前行/列

3.3.2 尺寸模式

右键任意单元格,可以设置当前单元格横向/竖向的SizeMode,也就是尺寸模式,分别有以下三种模式

  • 绝对:固定的宽度/高度
  • 百分比:去除其他非百分比的单元格后,剩余空间的占比
  • 自动调整大小:根据单元格内容自动调整

MIT Licensed