C# WinForms界面设计

界面布局 (Layout)

界面开发就是先了解各个控件的作用,然后拖拽摆放你需要的控件。

但我们发现如果窗体大小变化,控件没有相应的缩放,就会显得很不美观。

这里就需要我们学习界面设计的重点:界面布局 (Layout)

在 WinForms 中,实现控件随窗口缩放而自动调整的响应式布局,主要依赖 DockAnchor 属性,以及 FlowLayoutPanelTableLayoutPanel 这两个布局容器。

Anchor 属性

Anchor 属性定义了控件的边缘父容器边缘相对距离

设定了 Anchor 属性后,控件会在父容器缩放时相对于设计时的情况保持该控件和父容器指定边缘的距离不变

Anchor 属性 可以设置为多个边界的组合,比如 Top, LeftTop, Left, RightTop, Left, Right, Bottom 等等

如下图所示:

中间的这个All 是 Anchor 属性设置为 Top, Left, Right, Bottom,表示控件在父容器缩放时,始终保持与四个边的距离不变。

  • 默认情况下,控件锚定在左上角 (Top, Left)。

  • 如果你希望控件在窗口水平缩放时也跟着变宽,就把它同时锚定到 LeftRight

  • 如果你希望控件在窗口垂直缩放时也跟着变高,就把它同时锚定到 TopBottom

  • 如果你希望一个按钮始终停留在某个角落,就设定该角落对应的2个边作为锚点

    比如,右下角,就把它锚定到 Bottom, Right

Anchor属性经常和 MinimumSizeMaximumSize 属性一起使用,以确保控件在缩放时保持合适的大小,MinimumSizeMaximumSize可以限制窗体的最大最小尺寸

比如这样的界面

输入框在上面,按钮在下面, 窗体缩小太多,可能会导致输入框消失,可以指定 输入框 MinimumSize 400,200

指定窗体 MinimumSize 500, 470

Dock 属性

Dock(停靠) : Dock 属性让控件完全填充 父容器的某一个边缘(上、下、左、右)或剩余的全部空间 (Fill)。

一旦新设定 Dock 属性,Anchor 属性将被忽略

Dock 属性可以设置为以下值:

  • Top: 停靠在父容器的顶部。
  • Bottom: 停靠在父容器的底部。
  • Left: 停靠在父容器的左侧。
  • Right: 停靠在父容器的右侧。
  • Fill: 填充整个父容器的剩余空间。
  • None: 不停靠,允许手动调整位置和大小。

比如,Dock 属性值设置为 Bottom,就意味着:

  • 控件停靠在父容器的底部,

  • 扩展个控件宽度到父容器的宽度。

这时候我们没法手工(拖拽)设置它的宽度,margin等都没有效果,因为它的宽度已经由dock策略决定了。

但是我们却可以设置控件的高度,因为 `Dock` 值为 `Bottom` 或者 `top` 时,高度并不受策略限制。

当多个控件停靠在同一父容器中时,它们会按照 Z-order (Z顺序) 依次排列。

Z-order 可以通过上下文菜单的 Bring to FrontSend to Back 来调整。也可以通过 Document Outline 窗口来调整控件的次序。

Dock 对于创建主次分明的布局(如导航栏、状态栏和主内容区)非常有用。通常会使用 Panel 控件作为容器来组合使用 Dock

容器控件

容器(container) 控件, 是 WinForms 中用于组织和管理控件布局的特殊控件。

Visual StudioToolbox 中,容器控件放在 Container 类别中。

常用的布局容器有:

Panel🏷️

Panel 是最基本的容器控件,可以用来组织其他控件。它可以通过 Dock 属性停靠在窗体的任意边缘,或者使用 Anchor 属性来控制其大小和位置。

  • 可以设置背景色、边框样式等。
  • 通常用于分组相关控件,或者作为其他布局容器的父容器。
TableLayoutPanel

TableLayoutPanel 非常常用,它将区域划分为 行和列 ,形成一个 网格,控件可以放置在任意单元格中。

一般做复杂界面的顶级区域设置,都会使用 TableLayoutPanel, 比如典型的网站布局,如下

就是 3行2列的网格布局,左侧是导航栏,右侧是内容区,顶部是标题栏,底部是页脚。

TableLayoutPanel 特点如下:

  • 可以拖动控件放入目标单元格中;

  • 一个网格只能 放一个控件。 如果需要放多个,可以先放一个容器父控件,里面放多个子控件。

  • 一旦放入某个单元格,并且Dock设置为Fill,就不好再拖放到别的单元格,这时可以先修改为None, 再拖拽。或者通过该控件的属性设置 Layout 分类里面的 RowColumn 进行调整。

  • 可以通过该控件的属性设置 Layout 分类里面的 RowSpanColumnSpan 来设置控件跨越的行数和列数。

  • 可以设置行和列的缩放行为(绝对像素、百分比或根据内容自动调整)。

    设置可以在属性窗口的 RowsColumns 中进行。

  • 非常适合创建复杂的表单布局,或者需要精确对齐的界面。

    比如 注册/登录界面, 设置项界面 等等

SplitContainer

SplitContainer 允许用户通过拖动分隔条来调整两个子控件的大小。

可以设置水平或垂直分割,适用于需要动态调整布局的场景。

比如上面网页布局的左侧导航栏和右侧内容区,可以使用 SplitContainer 来实现。

可以设置 Orientation 属性来选择水平或垂直分割。

Layout -> Splitter Width 可以设置 分割线宽度

要设置分割线的颜色 比较麻烦一些,不能直接设置。可以设置整个 SplitContainer的背景色为你需要的分割线颜色, 然后设置每个子Panel 的 背景色为不同的颜色(比如白色)。

如果你分隔的超过2个分区,可以嵌套使用 SplitContainer

Dock 属性设置为 Fill的控件不能在设计器中拖动到 SplitContainer 中,可以在Document Outline 窗口中拖动。

TabControl

TabControl 用于创建选项卡式界面。它允许在同一窗口中组织多个页面,每个页面可以包含不同的控件和布局。

  • 可以通过添加 TabPage 来创建新的选项卡。

  • 每个选项卡可以有自己的布局和控件,适合需要分组显示内容的场景。

  • 通过 Behavior -> TabPages 属性可以 添加/删除/修改 标签页。

  • 通过 Behavior -> Alignment 属性可以设置选项卡标签在 上下左右 的位置。

  • 通过 Behavior -> Appearance 属性可以设置选项卡标签的外观(如 Normal, Buttons, FlatButtons 等)。

  • 通过 Behavior -> Padding 属性可以设置选项卡标签的距离

FlowLayoutPanel

FlowLayoutPanel 会将其中的控件按照指定的方向(从左到右、从右到左、从上到下或从下到上)依次排列。当一行或一列空间不足时,它会自动换行/换列。

适合用来展示一组内容, 比如 商品列表、图片库 等内容。

FlowLayoutPanel 的主要作用是作为布局容器 ,它会自动排列你添加到其中的子控件(比如 Button, Label, PictureBox 等):

  • FlowDirection 属性可以设置为 LeftToRight (水平布局) 或 TopDown (垂直布局)。

  • 可以设置 WrapContents 属性来控制是否自动换行/换列。

  • 如果要设置控件的间距,可以设置控件的 Margin 属性。

  • 通过代码添加控件, 使用 Controls.Add() 方法,比如

    复制代码
    flowLayoutPanel1.Controls.Add(new Button { Text = "按钮1" });
    flowLayoutPanel1.Controls.Add(new Button { Text = "按钮2" });
  • 如果要清空里面的控件,可以使用 Controls.Clear() 方法。

通常要添加的控件都是动态的,比如从数据库中获取的商品列表,或者从文件中读取的图片列表。

这些数据源往往是一个 List<T>DataTable 或其他集合类型

要实现根据数据源动态创建控件并添加到 FlowLayoutPanel 中,你需要手动编写代码来完成这个过程。基本步骤如下:

  1. 遍历你的数据源 (例如 List<T>DataTable 或其他集合)。
  2. 在循环中,为每一个数据项创建一个或多个新的控件实例
  3. 将当前数据项的信息赋值给新创建控件的属性 (如 Text, Image, Tag 等)。
  4. 将新创建的控件添加到 FlowLayoutPanelControls 集合中。

假设你有一个字符串列表,想为每个字符串创建一个按钮并显示在 FlowLayoutPanel 中。

cs 复制代码
// 你的数据源
List<string> categories = new List<string> { "Electronics", "Books", "Clothing", "Home & Kitchen", "Sports" };

// 获取 FlowLayoutPanel 控件的引用 (假设在设计器中已命名为 flowLayoutPanel1)
// this.flowLayoutPanel1

// 清空面板中现有的所有控件,以防重复添加
this.flowLayoutPanel1.Controls.Clear();

// 遍历数据源
foreach (string categoryName in categories)
{
    // 1. 创建一个新的 Button 控件
    Button btn = new Button();

    // 2. 设置按钮的属性
    btn.Text = categoryName;
    btn.Tag = categoryName; // 可以用 Tag 属性存储相关数据
    btn.AutoSize = true;   // 让按钮大小自适应内容
    btn.Margin = new Padding(5); // 设置一些外边距,让布局更好看

    // (可选) 为按钮添加点击事件
    btn.Click += (sender, e) => {
        Button clickedButton = sender as Button;
        MessageBox.Show($"You clicked on: {clickedButton.Tag}");
    };

    // 3. 将创建的按钮添加到 FlowLayoutPanel 中
    this.flowLayoutPanel1.Controls.Add(btn);
}

如果每个数据项需要显示的布局比较复杂(例如,一张图片 + 一个标题 + 一段描述),最佳实践是:

  1. 创建一个自定义 UserControl,在其中设计好单个数据项的布局(比如包含一个 PictureBox 和两个 Label)。
  2. 为这个 UserControl 创建一个公共方法或属性,用于接收数据并更新其内部的控件。
  3. 在主窗体的循环中,创建这个 UserControl 的实例,调用方法或属性填充数据,然后将其添加到 FlowLayoutPanel 中。

这种方式能让你的代码结构更清晰、更易于维护。

相关推荐
-Rane2 小时前
【C++】内存管理
开发语言·c++
DARLING Zero two♡2 小时前
【计算机网络】简学深悟启示录:序列化&&反序列化
开发语言·计算机网络·php
ID_180079054732 小时前
乐天(Letian)商品详情API接口的调用示例与代码实现
开发语言·python
一位搞嵌入式的 genius2 小时前
深入理解 JavaScript 原型与继承:从基础到进阶
开发语言·前端·javascript
晨非辰2 小时前
C++波澜壮阔40年|类和对象篇:拷贝构造与赋值重载的演进与实现
运维·开发语言·c++·人工智能·后端·python·深度学习
m0_719084112 小时前
滴滴滴滴滴
java·开发语言
董世昌412 小时前
深度解析var、let、const的区别与最佳使用场景
开发语言·前端·javascript
FJW0208142 小时前
Python中的闭包
开发语言·python
C_心欲无痕2 小时前
Next.js 平行路由:构建模块化动态布局
开发语言·前端·javascript