布局原则
1、不显式设置元素大小。
2、不使用绝对定位。
元素应该根据容器的内容来进行排列。绝对定位在开发前期会带来一些便捷,但扩展性比较差。一旦显示器尺寸或分辨率发生改变,界面的显示效果可能会达不到预期的效果。
3、布局容器可以嵌套使用
常用布局容器
WPF中的布局控件继承自System.Windows.Controls.Panel抽象类。
常用的布局容器如下:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Grid
Grid容器是WPF中最常用的布局容器。创建一个WPF工程后,系统会自动添加Grid标签作为顶级容器。
Grid容器通过定义行和列来设置控件的位置
<!--ShowGridLines属性可以开启行列显示--><Grid ShowGridLines="True"><!--添加行列--><Grid.RowDefinitions><RowDefinition/><RowDefinition/></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition/></Grid.ColumnDefinitions><Label Content="Row=0,Column=0"/><Label Content="Row=1,Column=1"/></Grid>
说明:
如果没有指定行和列,元素会默认放置在Grid的0行0列。
在Grid容器中,行和列的尺寸支持三种模式:
1、指定尺寸
<!--指定尺寸--><Grid Grid.Column="1" ShowGridLines="True"><Grid.ColumnDefinitions><ColumnDefinition Width="120"/><ColumnDefinition/></Grid.ColumnDefinitions><Label Content="宽度120"/><Label Content="宽度为Grid容器大小 - 120" Grid.Column="1"/></Grid>
这种模式可以指定行和列的高度或宽度。
说明:
1、WPF中默认宽高单位是像素(Pixel),完整的写法应该是Width="120px",但是这个px可以省略。
2、WPF还支持英寸(in)、厘米(cm)、点(pt)等单位,这里不做详细介绍。可参阅推荐阅读。
3、这种模式不建议使用,因为这种方式是使用设备无关单位准确的设置尺寸。
2、指定比例
<Grid Grid.Column="2" ShowGridLines="True"><Grid.RowDefinitions><RowDefinition Height="*"/><RowDefinition Height="3*"/></Grid.RowDefinitions><!--不指定比例,会等比拆分行列--><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition/></Grid.ColumnDefinitions></Grid>
可通过(*)来指定行列所占比例。未使用(*)指定比例的,按等比划分。
上面的示例代码中,定义了两行,两列。
第一行占Grid容器高度的1/4,第二行占Grid容器高度的3/4。
在定义列的时候,未指定比例,所以两列各占Grid容器宽度的1/2
3、自动设置
自动设置就是在指定宽高时使用Auto,使用了Auto的行列宽高是根据内部所放置元素来决定。
<Grid ShowGridLines="True"><Grid.RowDefinitions><RowDefinition/><RowDefinition/><RowDefinition Height="Auto"/></Grid.RowDefinitions><Label Content="高度 50" Grid.Row="2"/></Grid>
上面的示例代码中,在Grid中定义了三行。
第三行的高度使用了Auto。
如果不在第三行放置任何元素,第一行和第二行会各占Grid元素的1/2,即 Grid.Height /2
当在第三行放置一个高度为50的元素后,第三行有了50的高度。
这个时候,前面两行的高度就会发生变化,变成 (Grid.Height - 50 ) / 2
说明:
1、指定为Auto时,可以通过MinHeight来指定最小高度,MinHeight来指定最小宽度。
2、在布局时,通常会混合使用以上各种模式。
3、可以使用 Grid.UseLayoutRounding="True"来启用抗锯齿功能。启用后,会将容器内所有内容对齐到最近的像素边界。
跨越行和列
在定义行和列以后,有时候会需要跨越行来列来进行布局。这个时候可以使用Grid.RowSpan和Grid.ColumnSpan这两个附加属性来进行设置。
<Grid><Grid.RowDefinitions><RowDefinition/><RowDefinition/></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition/></Grid.ColumnDefinitions><Label Name="label1" Content="不跨越列" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Center"/><Label Name="label2" Content="跨越两列" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Center"/><Label Name="label3" Content="不跨越行" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"/><Label Name="label4" Content="跨越两行" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" VerticalAlignment="Center"/></Grid>
说明:
Grid布局技巧:
1、根据需要动态变化的内容,设置行列宽高来进行布局。
2、针对长度或宽度不固定的区域,可以设置宽度为Auto
3、WPF提供了一个GridSplitter类,可以动态的调整Grid的行高和列宽
<Grid><Grid.RowDefinitions><RowDefinition/><RowDefinition Height="auto"/><RowDefinition/></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition Width="auto"/><ColumnDefinition/></Grid.ColumnDefinitions><Grid Grid.Row="0" Grid.Column="0" Background="Green"></Grid><Grid Grid.Row="0" Grid.Column="2" Background="Silver"></Grid><Grid Grid.Row="2" Grid.Column="0" Background="Pink"></Grid><Grid Grid.Row="2" Grid.Column="2" Background="LightSkyBlue"></Grid><GridSplitter HorizontalAlignment="Stretch" VerticalAlignment="Center" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Height="2"></GridSplitter><GridSplitter VerticalAlignment="Stretch" HorizontalAlignment="Center" Grid.Column="1" Grid.Row="0" Grid.RowSpan="3" Width="2"></GridSplitter></Grid>
StackPanel
StackPanel容器的使用比较简单,它可以在单行或单列中以堆栈形式放置元素。
使用方法如下:
<!--垂直堆放元素(默认)--><StackPanel Grid.Column="0"><Button Content="Button1" Margin="10"/><Button Content="Button2" HorizontalAlignment="Left"/><Button Content="Button3" HorizontalAlignment="Right"/></StackPanel>
<!--水平堆放元素 --><!--需要指定属性 Orientation="Horizontal"--><StackPanel Grid.Column="1" Orientation="Horizontal"><Button Content="Button1" Margin="10"/><Button Content="Button2" VerticalAlignment="Top"/><Button Content="Button3" VerticalAlignment="Bottom"/></StackPanel>
通过FlowDirection属性可以指定元素的浮动方向
<!--FlowDirection可以指定元素的浮动方向--><!--LeftToRight是默认值,表示元素从左向右浮动--><StackPanel FlowDirection="LeftToRight"><Button Content="Button1" Width="120" Height="28" HorizontalAlignment="Left"/></StackPanel><!--RightToLeft表示元素从右向左浮动--><StackPanel FlowDirection="RightToLeft" Grid.Row="1"><Button Content="Button1" Width="120" Height="28" HorizontalAlignment="Left"/></StackPanel>
WrapPanel
WrapPanel和StackPanel差不多,它采用了流式 布局,控件也是一次一行或一列的方式布置。但它与StackPanel又有区别,WrapPanel的 控件从左向右或从上往下进行排列,排列满了以后再在下一行或下一列排列。
WrapPanel可用于工具栏类似的场景,因为控件可以依次排列下去。
<!--默认水平排列--><WrapPanel Grid.Row="0" Grid.Column="0"><Button Content="abc"/><Button Content="abc"/><Button Content="abc"/><Button Content="abc"/><Button Content="abc"/><Button Content="abc"/><!--当水平方向排列不下时,自动排列到下一行--><Button Content="abc"/></WrapPanel>
<!--设置为垂直排列--><WrapPanel Grid.Row="0" Grid.Column="1" Orientation="Vertical"><Button Content="abc"/><Button Content="abc"/><Button Content="abc"/><!--当垂直方向排列不下时,自动排列到下一列--><Button Content="abc"/></WrapPanel>
DockPanel
DockPanel是一种停靠式布局容器。通过附加属性DockPanel.Dock设置元素的停靠方向。
当元素被指定为停靠在顶部时,元素会占据布局容器的整个宽度,高度会根据内容和MinHeight属性而定。当元素被停靠在左边时,元素会占据整个布局容器的高度,而宽度会根据内容和MinWidth属性而定。
<DockPanel Grid.Row="0" Margin="10"><Button Content="左停靠" DockPanel.Dock="Left" MinWidth="80"/><Button Content="右停靠" DockPanel.Dock="Right"/><Button Content="上停靠" DockPanel.Dock="Top"/><Button Content="下停靠" DockPanel.Dock="Bottom"/></DockPanel>
默认情况下DockPanel.LastChildFill属性值为true,该属性会设置最后一个元素否占满整个布局容器。如果设置LastChildFill为false,可以看到
使用DockPanel容器进行布局时,元素的先后顺序影响会很大。在上面的示例中,最先放置向左停靠的元素,所以【上停靠】这个按钮会占据整个面板的高度,其它的元素会放置在它的右边。
如果最先放置向上停靠的元素,那这个向上停靠的元素会占据整个面板的宽度,其它的元素会放置在它的下面
<!--可以看到元素的先后顺序对布局会产生很大的影响--><DockPanel Grid.Row="2" Margin="10"><Button Content="左停靠" DockPanel.Dock="Top" MinWidth="80"/><Button Content="右停靠" DockPanel.Dock="Right"/><Button Content="上停靠" DockPanel.Dock="Left"/><Button Content="下停靠" DockPanel.Dock="Bottom"/></DockPanel>
重复上面的过程,可以更清楚的看到元素的先后对布局的影响以及DockPanel元素排列的规则
<DockPanel Grid.Row="3" Margin="10"><Button Content="左停靠" DockPanel.Dock="Left" MinWidth="80"/><Button Content="右停靠" DockPanel.Dock="Right"/><Button Content="上停靠" DockPanel.Dock="Top"/><Button Content="下停靠" DockPanel.Dock="Bottom"/><Button Content="上停靠" DockPanel.Dock="Top" MinWidth="80"/><Button Content="右停靠" DockPanel.Dock="Right"/><Button Content="左停靠" DockPanel.Dock="Left"/><Button Content="下停靠" DockPanel.Dock="Bottom"/></DockPanel>
Canvas
Canvas面板,画布面板,它可以通过使用精确的坐标来放置元素。大多数情况下,我们不需要绝对定位来放置元素,因为显示器的大小和分辨率有可能会改变。但是如果需要设计绘图相关的界面,或需要对元素进行动画时,就要用到Canvas面板。
Canvas面板是最轻量级的布局容器,因为它不包含任何复杂的布局逻辑。
通过Canvas.Left附加属性,设置元素左边和Canvas面板左边的单位数。不指定单位时,默认是px。通过Canvas.Top附加属性,设置元素上边和Canvas面板上边的单位数。
也可以设置Canvas.Bottom和Canvas.Right附加属性来指定距离面板底部和右边的单位数(设置无关单位,当将系统DPI指定为96dpi时,设置无关单位恰好等于通常使用的像素)。
说明:
1、不能同时指定Canvas.Left和Canvas.Right,只能选择一种指定。
2、不能同时指定Canvas.Top和Canvas.Bottom,只能选择一种指定。
在Canvas面板中,需要指定元素的大小,即指定Width和Height属性的值 。如果未指定元素宽高,元素的大小就是刚好能展示其全部内容的大小
<Canvas><!--基本使用--><!--未指定宽高--><Button Content="Canvas" Canvas.Left="100" Canvas.Top="200"/><Button Content="Canvas" Canvas.Left="200px" Canvas.Top="200px"/><Button Content="Canvas" Canvas.Left="2in" Canvas.Top="2.5in"/><Button Content="Canvas" Canvas.Right="100" Canvas.Bottom="200"/><!--指定宽高 --><Button Content="Canvas" Canvas.Left="50" Canvas.Top="100" Width="150" Height="28"/></Canvas>
说明:
当Canvas面板的大小改变时,面板内放置的元素大小不会改变。
元素叠放的顺序Z-Order
如果Canvas面板中有重叠的元素,可以通过设置Canvas.ZIndex附加属性来控制层叠方式。
默认每个元素会有默认的ZIndex值0,具有更高ZIndex值的元素会始终显示在较低ZIndex值元素的上面。ZIndex值可以为负数
<!--指定叠放顺序--><Button Content="ZIndex=0" Canvas.Left="300" Canvas.Top="190"/><Button Content="ZIndex=1" Canvas.Left="310" Canvas.Top="200"/><Button Content="ZIndex=2" Canvas.Left="290" Canvas.Top="200"/><!--ZIndex值都为0的情况下,后放置的元素会显示在上面--><Button Content="ZIndex=0-1" Canvas.Left="380" Canvas.Top="190"/><Button Content="ZIndex=0-2" Canvas.Left="380" Canvas.Top="190"/>
UniformGrid
-
UniformGrid是一种简化版的Grid,所有单元格宽高完全一致,它适用于网格状按钮、图片墙、图标矩阵等。UnfirmGrid特点如下: -
自动均分空间
所有单元格的宽高完全一致,子元素会填充整个单元格(默认拉伸)。
简化布局逻辑
无需像 Grid 那样定义 RowDefinitions/ColumnDefinitions,只需指定行数 / 列数,或让控件自动计算。
子元素自动排列
按 “从左到右、从上到下” 的顺序填充单元格,超出行数 / 列数时自动调整布局。
支持空白填充
当子元素数量不足时,剩余单元格为空(不显示内容)。
自动计算行列
<UniformGrid Margin="10" Gap="5"><!-- 6个子元素,自动分配为 2行3列(或3行2列,取决于控件宽高) --><Button Content="按钮1"/><Button Content="按钮2"/><Button Content="按钮3"/><Button Content="按钮4"/><Button Content="按钮5"/><Button Content="按钮6"/></UniformGrid>
指定固定行数(列数自动计算)
<UniformGrid Margin="10" Rows="2" Gap="5"><!-- 固定2行,6个子元素 → 3列 --><Button Content="1"/><Button Content="2"/><Button Content="3"/><Button Content="4"/><Button Content="5"/><Button Content="6"/></UniformGrid>
指定固定列数(行数自动计算)
<UniformGrid Margin="10" Columns="3" Gap="5"><!-- 固定3列,7个子元素 → 3行(第3行第3列为空) --><Button Content="1"/><Button Content="2"/><Button Content="3"/><Button Content="4"/><Button Content="5"/><Button Content="6"/><Button Content="7"/></UniformGrid>
常用属性
|
|
|
|
|
|---|---|---|---|
Rows |
|
int |
|
Columns |
|
int |
|
Gap |
|
Thickness |
|
FirstColumn |
Columns 固定时有效)
|
int |
|
HorizontalAlignment |
|
HorizontalAlignment |
|
VerticalAlignment |
|
VerticalAlignment |
|
推荐阅读
宽高支持的单位
https://docs.microsoft.com/en-us/dotnet/api/system.windows.frameworkelement.width?view=netcore-3.1#xaml-values
WPF控件库示例
https://github.com/Microsoft/WPF-Samples/tree/master/Getting%20Started/ControlsAndLayout
本文示例代码:
https://github.com/zhaotianff/DotNetCoreWPF/tree/master/五、WPF中的布局容器

