WPF布局
常用的布局控件
| 控件名称 | 布局方式 |
|---|---|
| Grid | 网格,根据自定义行和列来设置控件的布局 |
| StackPanel | 栈式面板,包含的元素在竖直或水平方向排成一条直线 |
| WrapPanel | 自动折行面板,包含的元素在排满一行后,自动换行 |
| DockPanel | 泊靠式面板,内部的元素可以选择泊靠方向 |
| UniformGrid | 网格,UniformGrid就是Grid的简化版,每个单元格的大小相同。 |
| Canvas | 画布,内部元素根据像素为单位绝对坐标进行定位 |
| Border | 装饰的控件,此控件用于绘制边框及背景,在Border中只能有一个子控件 |
注意: 这里面除了Border控件,其它控件都继承于Panel基类。即Border控件只能添加一个子元素。
一、WPF 布局黄金 4 原则
-
不写死宽高(用 Auto / *,让控件自适应)
-
不用绝对坐标(少用 Canvas,多用自动布局)
-
空间自动分配(多余空间由容器智能分配)
-
布局可以嵌套(Grid 套 StackPanel 套 WrapPanel 随便用)
二、布局容器
所有WPF布局容器都是派生自System.Windows.Controls.Panel抽象类的面板。

Panel类添加了少量成员,包括三个公有属性,如下表所示:
| 名称 | 说明 |
|---|---|
| Background | 该属性用于为面板背景着色画刷。如果想接收鼠标事件,就必须将该属性设置为非空值(如果想接收鼠标事件,又不希望显示固定颜色的背景,那么只需要将背景色设置为透明即可)。 |
| Children | 该属性是在面板中存储的条目集合。这是第一级条目——换句话说,这些条目自身也可以包含更多的条目。 |
| IsItemsHost | 该属性是一个布尔值,如果面板用于显示与 ItemsControl 控件关联的项(例如,TreeView 控件中的节点或列表框中的列表项),该属性值为 true。在大多数情况下,设置不需要知道列表控件使用后台面板来管理它所包含的条目的布局。但如果希望创建自定义的列表,以不同方式防止子元素(例如,以平布方式显示图像的 ListBox 控件),该细节就变得很重要。 |

要添加子控件,可以通过 Children.Add() 方法进行添加

WPF 六大常用布局容器
| 名称 | 说明 |
|---|---|
| StackPanel | 在水平或垂直的堆栈中放置元素。这个布局容器通常用于更大、更复杂窗口中的一些小区域。 |
| WrapPanel | 在一系列可换行的行中放置元素。在水平方向上,从左向右放置条目,然后在后续的行中放置剩余元素。在垂直方向上,自上而下在列中放置元素,并使用附加的列放置剩余的条目。 |
| DockPanel | 根据容器的整体边界调整元素。 |
| Grid | 根据不可见的表符在行和列中排列元素,这是最灵活、最常用的容器之一。 |
| UniformGrid | 在不可见但是强制所有单元格具有相同尺寸的表中放置元素,这个布局容器不常用。 |
| Canvas | 使用固定坐标定位元素。这个布局容器与传统 Windows 窗体应用程序最相似,但没有提供锚定或停靠功能。因此,对于尺寸可变的窗体,该布局容器不是合适的选择。如果是选择的话,需要另外做一些工作。 |
容器特性对比
| 容器 | 布局方式 | 适用场景 | 与四原则的对应 |
|---|---|---|---|
| StackPanel | 水平/垂直堆叠 | 工具栏、菜单栏、简单表单 | ✅ 自适应、嵌套 |
| WrapPanel | 自动换行排列 | 标签云、缩略图列表 | ✅ 不写死宽高 |
| DockPanel | 贴靠边界停靠 | 窗口整体框架(顶栏、侧栏、底栏) | ✅ 空间自动分配 |
| Grid | 行列网格 | 复杂表单、整体页面布局 | ✅ 最灵活 |
| UniformGrid | 等尺寸网格 | 棋盘、计算器按键 | ✅ 自动均分 |
| Canvas | 固定坐标定位 | 绘图、动画、可视化编辑器 | ❌ 违反四原则(用绝对坐标) |
三、StackPanel面板
1、布局属性
| 名称 | 说明 |
|---|---|
| HorizontalAlignment | 当水平方向上有额外的空间时,该属性决定了子元素在布局容器中如何定位。可选用 Center、Left、Right 或 Stretch 等属性值。 |
| VerticalAlignment | 当垂直方向上有额外的空间时,该属性决定了子元素在布局容器控件中如何定位。可选用 Center、Top、Bottom 或 Stretch 等属性值。 |
| Margin | 该属性用于在元素的周围添加一定的空间。Margin 属性是 System.Windows.Thickness 结构的一个实例,该结构具有分别用于为顶部、底部、左边和右边添加空间的独立组件。 |
| MinWidth 和 MinHeight | 这两个属性用于设置元素的最小尺寸。如果一个元素对于其他布局容器来说太大,该元素将被剪裁以适合容器。(注:图片原文中写的是"最大尺寸",应为笔误) |
| MaxWidth 和 MaxHeight | 这两个属性用来设置元素的最大尺寸。如果有更多可以使用的空间,那么在扩展子元素时就不会超出这一限制,即使将 HorizontalAlignment 和 VerticalAlignment 属性设置为 Stretch 也同样如此。 |
| Width 和 Height | 这两个属性用来显式地设置元素的尺寸。这一设置会重写为 HorizontalAlignment 和 VerticalAlignment 属性设置的 Stretch 值。但不能超过 MinWidth、MinHeight、MaxWidth 和 MaxHeight 属性设置的范围。 |
2、尺寸计算优先级
显式 Width/Height↓ (覆盖 Stretch,但受 Min/Max 约束)
MaxWidth / MaxHeight (上限)↓
内容所需尺寸 (DesiredSize)↓
MinWidth / MinHeight (下限)
最终实际尺寸 = Clamp(内容需求尺寸, Min, Max),若显式设置了 Width/Height 则直接使用该值(但仍受 Min/Max 限制)。
3、StackPanel 对齐规则
| 方向 | 子元素排列 | 默认宽度 | 默认高度 | 有效对齐 | 无效对齐 |
|---|---|---|---|---|---|
| Vertical(垂直) | 自上而下堆叠 | = StackPanel 宽度 | = 内容高度 | HorizontalAlignment |
VerticalAlignment |
| Horizontal(水平) | 自左而右排列 | = 内容宽度 | = StackPanel 高度 | VerticalAlignment |
HorizontalAlignment |
4、StackPanel 示例
默认情况 垂直排列 Orientation="Vertical"

水平排列 Orientation="Horizontal"

无效对齐(垂直排列,水平排列一样暂不演示)

Margin使用

内容对齐方式
-
VerticalContentAlignment:垂直方向 -
HorizontalContentAlignment:水平方向

5、StackPanel 注意事项
-
最小尺寸:每个按钮的尺寸始终不能小于最小尺寸
-
最大尺寸: 每个按钮的尺寸始终不能超过最大尺寸(除非执行错误操作,使最大尺寸比最小尺寸还小)
-
内容: 如果按钮中的内容需要更大的宽度,StackPanel容器会尝试扩展按钮(可以通过检查DesiredSized属性确定所需的按钮大小,该属性返回最小宽度或内容的宽度,返回两者中较大的那个)。
-
容器大小:如果最小宽度大于StackPanel面板的宽度,按钮的一部分将被剪裁掉。否则,不允许按钮比StackPanel面板更宽,即使不能适合按钮表面的所有文本。
-
水平对齐方式:因为默认情况下按钮的HorizontalAlignment属性设置为Stretch,所以StackPanel面板尝试反映按钮所期望的尺寸(以适合其内容)以及对齐方式的设置。
<StackPanel Orientation="Horizontal"><!-- 情况1:内容宽度 > MinWidth, 以内容宽度为准 --><Button Content="Very Long Text Here" MinWidth="50" Background="LightBlue"/><!-- 情况2:MinWidth > 内容宽度 以MinWidth为准 --><Button Content="OK" MinWidth="100" Background="LightGreen"/><!-- 情况3:精确匹配 --><Button Content="Cancel" Width="80" Background="LightCoral" 以Width为准 />
</StackPanel>
四、Border控件
1、Border 控件属性
| 名称 | 说明 |
|---|---|
| Background | 使用 Brush 对象设置边框中所有内容后面的背景。可使用固定颜色背景,也可使用其他更特殊的背景(如渐变、图片等)。 |
| BorderBrush 和 BorderThickness | 使用 Brush 对象设置位于 Border 对象边缘的边框的颜色,并设置边框的宽度。为显示边框,必须同时设置这两个属性。 |
| CornerRadius | 该属性可使边框具有雅致的圆角。CornerRadius 的值越大,圆角效果就越明显。 |
| Padding | 该属性在边框和内部的内容之间添加空间(与此相对,Margin 属性在边框之外添加空间)。 |
2、Border 核心要点
| 要点 | 说明 |
|---|---|
| 本质 | Border 不是布局面板,而是装饰控件 |
| 子元素数量 | 只能包含一个子控件(通常是一个布局面板) |
| 典型用法 | 包裹一个 StackPanel/Grid 等面板,为整组控件添加背景或边框 |
作用:作为视觉增强层,不影响内部的自适应布局
3、使用示例
<StackPanel Orientation="Horizontal"><Border Height="50" Margin="10" BorderThickness="1" Padding="10" BorderBrush="Red"><TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="直角按钮"/></Border><Border Height="50" Margin="10" BorderThickness="1" Padding="10" CornerRadius="10" BorderBrush="Red"><TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="圆角按钮"/></Border><Border Background="#666" Margin="10" BorderThickness="1" Width="100" Height="100" CornerRadius="50" BorderBrush="Red"><TextBlock Text="圆角按钮" HorizontalAlignment="Center" VerticalAlignment="Center"/></Border>
</StackPanel>
效果如下:

总结: Border = 单子元素的装饰容器,用于添加背景、边框、圆角,常与布局面板嵌套使用以增强视觉效果。
五、WrapPanel 面板
1、基本特性
| 属性 | 说明 |
|---|---|
| 默认方向 | Orientation="Horizontal"(从左到右排列,自动换行) |
| 可选方向 | Orientation="Vertical"(从上到下排列,自动换列) |
| 行高规则 | 每行高度 = 该行中最高元素的高度 |
| 列宽规则 | 垂直方向时,每列宽度 = 该列中最宽元素的宽度 |
可以设置ItemWidth,ItemHeight属性,统一给所有子控件设置宽高
2、关键点
WrapPanel 是唯一一个不能通过灵活使用 Grid 面板代替的面板
| 特性 | WrapPanel | Grid |
|---|---|---|
| 动态换行/换列 | ✅ 自动根据可用空间调整 | ❌ 行列数固定,需代码动态调整 |
| 未知数量的子元素 | ✅ 自然支持 | ❌ 需预先定义行列或动态添加 RowDefinition |
| 流式布局 | ✅ 类似 WinForm 的 FlowLayOutPanel | ❌ 本质是固定网格 |
Grid 虽然最灵活,但无法原生实现"根据容器宽度自动换行"的行为,这正是 WrapPanel 的不可替代之处。
3、使用示例
<WrapPanel Orientation="Horizontal" ItemWidth="60" ItemHeight="60"><Button Content="WPF按钮1" Margin="5" HorizontalAlignment="Left"/><Button Content="WPF按钮2" Margin="5" HorizontalAlignment="Right"/><Button Content="WPF按钮3" Margin="5" HorizontalAlignment="Center"/><Button Content="WPF按钮4" Margin="5" HorizontalAlignment="Stretch"/><Button Content="WPF按钮5" Margin="5" /><Button Content="WPF按钮6" Margin="5" /><Button Content="WPF按钮7" Margin="5" /><Button Content="WPF按钮8" Margin="5" /><Button Content="WPF按钮9" Margin="5" /><Button Content="WPF按钮10" Margin="5" />
</WrapPanel>
效果如下:

六、DockPanel 面板
1、DockPanel 核心机制
基本特性
| 属性/特性 | 说明 |
|---|---|
| 停靠方式 | 通过附加属性 DockPanel.Dock 设置(Left、Right、Top、Bottom) |
| 拉伸规则 | 停靠方向的垂直方向会被拉伸至容器边缘 |
| LastChildFill | 最后一个子元素是否填满剩余空间(默认 True) |
停靠方向的拉伸行为
| 停靠位置 | 拉伸方向 | 可自由调整的方向 |
|---|---|---|
| Top / Bottom | 宽度拉伸至面板宽度 | 高度(由内容/MinHeight 决定) |
| Left / Right | 高度拉伸至面板高度 | 宽度(由内容/MinWidth 决定) |
2、停靠顺序的重要性
示例代码停靠顺序分析
<DockPanel LastChildFill="True"><Button DockPanel.Dock="Top">Top Button</Button> <!-- 先停靠顶部 --><Button DockPanel.Dock="Bottom">Bottom Button</Button> <!-- 再停靠底部 --><Button DockPanel.Dock="Left">Left Button</Button> <!-- 然后左边 --><Button DockPanel.Dock="Right">Right Button</Button> <!-- 最后右边 --><Button>Content Button</Button> <!-- 剩余空间 -->
</DockPanel>
顺序影响对比
| 声明顺序 | 效果 |
|---|---|
| Top → Bottom → Left → Right | Top/Bottom 占满整宽,Left/Right 夹在中间 |
| Left → Right → Top → Bottom | Left/Right 占满整高,Top/Bottom 夹在中间 |
原则:先声明的元素优先占据边缘空间。

3、同一边缘停靠多个元素
示例分析
<DockPanel LastChildFill="True"><Button DockPanel.Dock="Top">A Stretched Top Button</Button><Button DockPanel.Dock="Top" HorizontalAlignment="Center">Centered</Button><Button DockPanel.Dock="Top" HorizontalAlignment="Left">Left-Aligned</Button>
</DockPanel>
同一边缘停靠规则
| 规则 | 说明 |
|---|---|
| 排列顺序 | 按 XAML 中声明的顺序依次停靠 |
| 堆叠方式 | 先声明的靠近边缘,后声明的向内堆叠 |
| 对齐控制 | 可通过 HorizontalAlignment(对 Top/Bottom)或 VerticalAlignment(对 Left/Right)调整 |
如图:

4、嵌套布局
很少单独使用StackPanel、WrapPanel和DockPanel面板。相反,它们通常用来设置一部分用户界面的布局。
代码结构
<DockPanel LastChildFill="True"><!-- 底部按钮区域 --><StackPanel DockPanel.Dock="Bottom" HorizontalAlignment="Right" Orientation="Horizontal"><Button Margin="10,10,2,10" Padding="3">OK</Button><Button Margin="2,10,10,10">Cancel</Button></StackPanel><!-- 顶部文本框 --><TextBox DockPanel.Dock="Top" Margin="10">This is test.</TextBox><!-- 剩余空间由 LastChildFill 自动填充 -->
</DockPanel>
效果图:

七、Grid面板
Gird面板是WPF中功能最强大的布局容器。很多实用其他布局控件能完成的功能,用Grid面板也能实现。Grid面板也是将窗口分割成更小区域的理想工具。实际上,由于Grid面板十分由于强大,因此在Visual Studio中为窗口添加新的XAML文档时,会自动添加Grid标签作为顶级容器,并嵌套在Window根元素中。
尽管可在一个单元格中放置多个元素,但在每个单元格中放置一个元素通常更合理。
1、Grid 面板核心概念
基本结构
<Grid ShowGridLines="True"> <!-- ShowGridLines 仅用于调试 --><!-- 定义行 --><Grid.RowDefinitions><RowDefinition Height="*"/><RowDefinition Height="Auto"/></Grid.RowDefinitions><!-- 定义列 --><Grid.ColumnDefinitions><ColumnDefinition Width="*"/><ColumnDefinition Width="2*"/></Grid.ColumnDefinitions><!-- 子元素通过附加属性定位 --><Button Grid.Row="0" Grid.Column="0">按钮</Button>
</Grid>
默认行为
| 规则 | 说明 |
|---|---|
未指定 Grid.Row |
默认为 0 |
未指定 Grid.Column |
默认为 0 |
空 RowDefinition/ColumnDefinition |
默认均分空间(*) |
2、三种尺寸设置方式
| 方式 | 语法 | 行为 | 适用场景 |
|---|---|---|---|
| 绝对尺寸 | Width="100" |
固定像素值 | 不推荐(违反自适应原则) |
| 自动尺寸 | Width="Auto" |
适应内容大小 | 标签、按钮等固定内容 |
| 比例尺寸 | Width="*" / Width="2*" |
按权重分配剩余空间 | 主体内容区域 |
混合使用示例
<Grid><Grid.RowDefinitions><RowDefinition Height="Auto"/> <!-- 标题行,适应内容 --><RowDefinition Height="*"/> <!-- 内容行,占满剩余空间 --><RowDefinition Height="Auto"/> <!-- 按钮行,适应内容 --></Grid.RowDefinitions>
</Grid>
星号权重规则
<Grid.ColumnDefinitions><ColumnDefinition Width="*"/> <!-- 占 1/3 --><ColumnDefinition Width="2*"/> <!-- 占 2/3 -->
</Grid.ColumnDefinitions>
3、跨越行和列
| 附加属性 | 作用 | 默认值 |
|---|---|---|
Grid.RowSpan |
跨越多行 | 1 |
Grid.ColumnSpan |
跨越多列 | 1 |
示例
<!-- 文本框跨越三列 -->
<TextBox Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" Margin="10"/><!-- 按钮占据 2x2 区域 -->
<Button Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2"/>
4、布局舍入(Layout Rounding)
| 问题 | 解决方案 |
|---|---|
| 比例分配产生小数像素,导致边缘模糊 | 设置 UseLayoutRounding="True" |
<Grid UseLayoutRounding="True"><!-- 内容将对齐到物理像素边界 -->
</Grid>
WPF会确保布局容器中的所有内容对齐到最近的像素边界,从而消除了所有模糊问题。
5、GridSplitter 分割条
- 放置位置
-
GridSplitter必须放在 Grid 的单元格中 -
可以与其他内容共享单元格(需调整边距)
-
对应的行或列的 Height/Width 应设为
Auto
- 跨行/列拉伸
-
GridSplitter总是调整整行或整列的尺寸 -
使用
RowSpan或ColumnSpan使其跨越整个区域 -
避免将其限制在单个单元格内
-
可见性与尺寸设置
GridSplitter对象很小不易看见。为了使其更可用,需要为其设置最小尺寸。对于竖直分隔条,需要将VerticalAlignment属性设置为Stretch(使分隔条填满区域的整个高度),并将Width设置为固定值。对于水平分隔条,需要设置HorizontalAlignment属性来拉伸,并将Height属性设置为固定值。 -
对齐方式决定方向
GridSplitter对齐方式还决定了分隔条是水平的(用于改变行的尺寸)还是竖直的(用于改变列的尺寸)。对于水平分隔条,需要将VerticalAlignment属性设置为Center(这也是默认值),以指明拖动分隔条改变上面行和下面行的尺寸。对于竖直分隔条,需要将HorizontalAlignment属性设置为Center,以改变分隔条两侧列的尺寸。水平分隔条(调整行高):
VerticalAlignment="Center" <!-- 默认值 --> HorizontalAlignment="Stretch" Height="5"垂直分隔条(调整列宽):
HorizontalAlignment="Center" <!-- 默认值 --> VerticalAlignment="Stretch" Width="5"
代码示例
<Grid><Grid.ColumnDefinitions><ColumnDefinition Width="*" /><ColumnDefinition Width="Auto" /><!-- Auto 用于分隔条列 --><ColumnDefinition Width="2*" /></Grid.ColumnDefinitions><!-- 左侧内容 --><TextBlock Grid.Column="0" Text="左侧面板" /><!-- 垂直分隔条 --><GridSplitter Grid.Column="1"Width="5"VerticalAlignment="Stretch"HorizontalAlignment="Center"ShowsPreview="False" Background="Gray" /><!-- 右侧内容 --><TextBlock Grid.Column="2" Text="右侧面板" /></Grid>
效果:

6、共享尺寸组
共享尺寸组(Shared Size Group) 是Grid布局中的一个重要功能,它允许多个Grid中的列或行共享相同的尺寸,即使这些Grid不在同一个视觉树中。
核心属性
| 属性 | 位置 | 作用 |
|---|---|---|
Grid.IsSharedSizeScope="True" |
父容器 | 启用尺寸共享 |
SharedSizeGroup="组名" |
ColumnDefinition/RowDefinition | 相同组名共享尺寸 |
完整示例
<Grid Grid.IsSharedSizeScope="True" Margin="3"><!--启用共享尺寸组--><Grid.RowDefinitions><RowDefinition/><RowDefinition/></Grid.RowDefinitions><!-- 第一个 Grid --><Grid Grid.Row="0"><Grid.ColumnDefinitions><ColumnDefinition Width="Auto" SharedSizeGroup="LabelColumn"/><!--设置组名--><ColumnDefinition Width="*"/></Grid.ColumnDefinitions><Label Content="很长的标签文本"/><TextBox Grid.Column="1"/></Grid><!-- 第二个 Grid --><Grid Grid.Row="1"><Grid.ColumnDefinitions><ColumnDefinition Width="Auto" SharedSizeGroup="LabelColumn"/><!--设置组名--><ColumnDefinition Width="*"/></Grid.ColumnDefinitions><Label Content="短标签"/><TextBox Grid.Column="1"/></Grid>
</Grid>
效果:

7、使用ZIndex设置层级
没有设置ZIndex,后面元素在前面元素上一层,ZIndex越大,越在上层。
Panel提供了GetZIndex和SetZIndex方法成员,分别表示获取某个元素的ZIndex顺序和设置某个元素的ZIndex顺序。
什么是ZIndex?这是Panel提供的一个附加属性。假如一个单行单列的Grid布局控件中有两个Button,正常情况下,这两个Button都会以撑满Grid的方式呈现在Grid中,那么,到底哪一个Button在上面,哪一个Button在下面呢?就看这两个Button的Panel.ZIndex附加属性的值,值越大越在上面,而值较小的那个Button将被上面的Button遮盖,从而在视觉上,用户只能看到一个Button。
规则对比
| 情况 | 说明 |
|---|---|
| 未设置 ZIndex | 按 XAML 声明顺序:先声明在下,后声明在上 |
| 设置 ZIndex | 按数值大小:值越小越在下,值越大越在上 |
| 仅在同级容器内有效 | ZIndex 只在同一个 Panel 的子元素之间比较 |
| 嵌套容器 | 不同 Panel 中的元素各自独立计算 ZIndex |
| 值类型 | int 类型,可为负数 |
代码示例:
<Grid><Button Panel.ZIndex="2" Content="按钮1" Width="200" Background="Red" Height="50"/><!-- 下层 --><Button Panel.ZIndex="1" Content="按钮2" Width="100" Height="80" Background="Green"/><!-- 上层(遮盖按钮1) -->
</Grid>
如图:

八、UniformGrid面板
UniformGrid面板不需要(甚至不支持)预先定义的列和行。相反,通过简单地设置Rows和Columns属性来设置其尺寸。每个单元格始终具有相同的大小,因为可用的空间被均分。最后,元素根据定义的顺序被放置到适当的单元格中。UniformGrid面板中没有Row和Column附加属性,也没有空白单元格。
UniformGrid是自动等分的简单网格,Grid是支持复杂布局的自定义网格。
1、UniformGrid 核心特性
基本定义
| 属性 | 说明 |
|---|---|
| Rows | 指定行数 |
| Columns | 指定列数 |
| 子元素排列 | 按声明顺序从左到右、从上到下自动填充 |
| 单元格特性 | 所有单元格等宽等高,空间完全均分 |
与其他面板的区别
| 特性 | UniformGrid | Grid |
|---|---|---|
| 行列定义 | 仅需 Rows/Columns |
需逐一定义 RowDefinition/ColumnDefinition |
| 单元格尺寸 | 强制等宽等高 | 每行每列可独立设置 |
| 子元素定位 | 自动按顺序填充 | 需显式指定 Grid.Row/Grid.Column |
| 附加属性 | 无 | Grid.Row、Grid.Column、RowSpan、ColumnSpan |
| 空白单元格 | 不支持 | 支持 |
2、示例解析
代码
<UniformGrid Rows="2" Columns="2"><Button>Top Left</Button> <!-- 自动放入 (0,0) --><Button>Top Right</Button> <!-- 自动放入 (0,1) --><Button>Bottom Left</Button> <!-- 自动放入 (1,0) --><!--<Button>Bottom Right</Button> <!-- 自动放入 (1,1) -->
</UniformGrid>
如图:

3、省略行列数的行为
| 情况 | 行为 |
|---|---|
只指定 Rows |
自动计算列数 = ⌈子元素总数 / Rows⌉ |
只指定 Columns |
自动计算行数 = ⌈子元素总数 / Columns⌉ |
| 都不指定 | 自动计算行列数,尽量形成接近正方形的布局 |
示例
<!-- 6个按钮,只指定列数=3 → 自动计算行数=2 -->
<UniformGrid Columns="3"><Button>1</Button> <Button>2</Button> <Button>3</Button><Button>4</Button> <Button>5</Button> <Button>6</Button>
</UniformGrid>
如图:

九、使用Canvas面板进行基于坐标的布局
1、Canvas 面板核心特性
基本定位方式
| 附加属性 | 说明 |
|---|---|
Canvas.Left |
元素左边缘与 Canvas 左边缘的距离 |
Canvas.Top |
元素上边缘与 Canvas 上边缘的距离 |
Canvas.Right |
元素右边缘与 Canvas 右边缘的距离 |
Canvas.Bottom |
元素下边缘与 Canvas 下边缘的距离 |
Canvas 特点
| 特点 | 说明 |
|---|---|
| 最轻量级 | 无复杂布局逻辑,不改变子元素尺寸 |
| 无自适应 | 窗口大小变化时,子元素位置和尺寸不变 |
| 无锚定/停靠 | 刻意设计,防止被滥用于标准界面 |
| 适用场景 | 绘图表面、图形工具、自定义可视化 |
2、Canvas 示例解析
代码:
<Canvas><Button Canvas.Left="10" Canvas.Top="10">(10,10)</Button><Button Canvas.Left="120" Canvas.Top="30">(120,30)</Button><Button Canvas.Left="180" Canvas.Top="110" Canvas.ZIndex="1" Width="80" Height="60">(180,110)</Button><Button Canvas.Left="180" Canvas.Top="150" Width="100" Height="60">(180,150)</Button></Canvas>
如图:

3、Canvas 中的 ZIndex
默认层叠规则
| 情况 | 层叠顺序 |
|---|---|
| 未设置 ZIndex | 按 XAML 声明顺序:后声明的在上层 |
| 设置 ZIndex | 值越大越在上层 |
示例对比
<!-- 默认情况:Button2 在 Button1 上面 -->
<Canvas><Button Canvas.Left="10" Canvas.Top="10" Width="100">按钮1</Button><Button Canvas.Left="30" Canvas.Top="30" Width="100">按钮2</Button> <!-- 上层 -->
</Canvas><!-- 使用 ZIndex 反转 -->
<Canvas><Button Canvas.Left="10" Canvas.Top="10" Canvas.ZIndex="2" Width="100">按钮1</Button> <!-- 上层 --><Button Canvas.Left="30" Canvas.Top="30" Canvas.ZIndex="1" Width="100">按钮2</Button>
</Canvas>
跟Grid中的一样效果,不再展示
4、InkCanvas 元素
1、与 Canvas 的区别
| 对比项 | Canvas | InkCanvas |
|---|---|---|
| 基类 | Panel | FrameworkElement |
| 主要用途 | 坐标定位布局 | 手写笔/鼠标输入绘制 |
| 子内容集合 | Children(单一) | Children + Strokes(双集合) |
2、InkCanvas.EditingMode属性
| 模式 | 说明 |
|---|---|
| Ink | 默认模式,允许绘制笔画 |
| GestureOnly | 仅检测手势,不绘制笔画 |
| InkAndGesture | 同时支持绘制和手势识别 |
| Select | 可选择、移动、调整子元素 |
| None | 禁止所有输入 |
| EraseByPoint | 按点擦除笔画 |
| EraseByStroke | 按笔画整体擦除 |
3、关键事件
| 事件 | 触发时机 |
|---|---|
ActiveEditingModeChanged |
编辑模式改变时 |
Gesture |
识别到手势时(GestureOnly/InkAndGesture 模式) |
SelectionChanging |
选择即将改变时(可取消) |
SelectionChanged |
选择已改变后 |
SelectionMoving |
选中元素即将移动时(可取消) |
SelectionMoved |
选中元素已移动后 |
SelectionResizing |
选中元素即将调整大小时(可取消) |
SelectionResized |
选中元素已调整大小后 |
命名规律:以 -ing 结尾的事件发生在动作执行前,可以通过设置EventArgs对象的Cancel属性取消事件。
4、示例代码
<InkCanvas Name="inkCanvas" Background="LightYellow" EditingMode="Ink"><Image Width="300" Height="240" Stretch="Fill" InkCanvas.Top="10" InkCanvas.Left="10"></Image></InkCanvas>
如图:

总结
-
WPF 布局核心:自适应 + 容器嵌套 + 自动测量排列
-
最常用容器:Grid > StackPanel > WrapPanel > DockPanel
-
开发原则:不写死尺寸、不写死坐标
