界面布局

布局管理对于创建响应式和用户友好的界面至关重要。Winform中,实现布局管理有这么几种方式:

  • 无视布局管理,直接用Visual Studio设计器拖拽绝对布局,当然这种方式很不友好,用户改变窗体的大小时界面上的控件并不会自适应的改变位置
  • 使用Anchor锚属性实现简单的动态布局适配
  • (推荐)使用Dock属性实现动态布局适配,并常搭配容器控件一起使用

本篇笔记我们将介绍Anchor属性、Dock属性以及容器控件的使用,最后讨论高分辨率屏幕下的DPI缩放适配问题。

设置Anchor和Dock属性

我们向窗体上拖入一个控件,在右侧属性中可以找到Anchor和Dock属性的设置。

Anchor表示锚定到父容器(通常是窗体,也可能是Panel等容器控件),图中的例子锚定的相对位置是窗体左上角,Location属性指定了锚定的位置,图中的位置是左上角(12, 12)位置;Dock表示停靠到窗体的某一边缘,它类似Swing中的BorderLayout,能够实现东西南北中的布局形式。要注意,Anchor和Dock只能设置一个,它们不会同时起效,因此如果Dock被设置,Anchor属性就不再生效了。

Anchor 锚定

Anchor具有TopLeftBottomRight四个方向,默认会锚定TopLeft,指定方向后控件与该方向边缘的距离会保持不变,下面是一个锚定到左上角的按钮。

无论我们怎么拖拽窗口,该按钮都不会相对窗体左上角改变位置。

Anchor还有一个很常用的技巧,同时锚定左右(Top, Left, Right)时,控件不会移动位置,而是宽度跟随窗体一起拉伸;同理同时锚定上下会拉伸高度,四个方向都锚定则整体随窗体缩放。例如一个多行文本框设置锚定四边,就会成为随窗口伸缩的编辑区。

Dock 停靠

选择Dock时,会弹出一个停靠边缘的选择,我们可以在东西南北中几个区块内选择停靠的区域(TopBottomLeftRightFill,以及None表示不停靠)。

下面例子是一个停靠到窗体底部边缘的按钮。

当有多个控件都设置了Dock时,它们的停靠顺序由控件的Z顺序决定,Z顺序靠后的控件先占据边缘,Z顺序最靠前的Fill控件填充剩余空间,我们可以在设计器中右键点击控件选择「置于顶层」、「置于底层」来调整Z顺序,或者直接在「文档大纲」视图中拖拽调整,如果停靠效果不符合预期,调整Z顺序往往能解决问题。

容器控件

Winform具备一组容器控件,容器控件可以近似理解为Web开发中的<div>,它一般都需要和Dock属性配合使用,并通过容器控件的层层嵌套来实现复杂的布局。

Panel:Panel是最简单的容器,没有什么额外的功能。

FlowLayoutPanel:FlowLayoutPanel即流式布局容器,它允许内部的控件从左向右流式排列,宽度不足时会自动换行,当控件数量增加或减少时,它也会自动填充可用的空间。当然,排列方向也不是只能从左向右,通过FlowDirection属性可以改变排列方向。

TableLayoutPanel:表格容器,可以将控件组织在行和列中,每个单元格内可以放置一个控件,控件的大小和位置可以根据需要调整。行高列宽支持三种尺寸模式:绝对像素、百分比、自动适应内容,行列设置为百分比模式后整个表格可以随窗口等比缩放,它是Winform中实现网格自适应布局的主力容器,适用于需要精确对齐的表单场景。

GroupBox:相比Panel增加了边框和标题。

TabControl:TabControl控件允许在同一位置显示多个面板,通过选项卡切换不同的内容区域,适用于需要在同一空间内展示多个视图的场景。

SplitContainer:SplitContainer控件将父容器分割为两个可调整大小的面板,用户可以通过拖动分割条来调整面板的大小,Orientation属性控制水平或垂直分割,FixedPanel属性可以固定某一个面板的大小不随窗口缩放,适用于需要动态调整区域大小的场景(例如左侧目录树右侧内容的经典布局)。

布局的实用套路

实际开发中,复杂界面几乎都是按下面的套路组合出来的:

  1. 最外层用Dock划分大区域:菜单、工具条、底部状态条、中间剩余区域放一个Dock = Fill的Panel或SplitContainer
  2. 大区域内部用TableLayoutPanelSplitContainer做网格或左右分割
  3. 表单类界面用TableLayoutPanel对齐Label和输入框,按钮区用FlowLayoutPanel自动排列
  4. 控件自身再用AnchorDock适配所在单元格

DPI缩放与高分屏适配

现代高分辨率显示器经常使用DPI缩放,如果程序不做适配,在高DPI屏幕上可能会出现界面模糊、控件错位等问题。Winform对此提供了以下机制:

AutoScaleMode:窗体的自动缩放模式,默认是Font,即按系统字体大小缩放,通常建议改为Dpi,即按DPI缩放,这样当程序在不同DPI环境下运行时,窗体和控件会按比例自动放大。

DPI感知级别:程序对系统缩放的感知程度。如果你使用的是新版.NET,可以在csproj项目文件中设置DPI感知模式:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0-windows</TargetFramework>
    <!-- DPI感知级别,默认 SystemAware -->
    <ApplicationHighDpiMode>PerMonitorV2</ApplicationHighDpiMode>
  </PropertyGroup>
</Project>
  • SystemAware:系统DPI感知,程序在系统主显示器DPI下清晰渲染
  • PerMonitorV2:每显示器DPI感知,程序拖到不同DPI的显示器上时也能保持清晰,推荐使用该选项

此外,我们也可以在Program.cs中调用Application.SetHighDpiMode(HighDpiMode.PerMonitorV2)来通过代码动态设置该选项。

注意:开启DPI感知后,设计器中和运行时不同缩放环境下控件的实际像素尺寸可能会发生变化,在不同DPI的显示器下也可能发生变化,因此高分屏适配更应该依赖Dock/Anchor/TableLayoutPanel等弹性布局,而非绝对坐标写死位置,这也是布局管理在高DPI时代愈发重要的原因。

作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。