WPF布局
在WPF中非常抵制基于坐标的布局,而是注重创造更灵活的布局,使布局能够适应内容的变化、不同的语言以及各种窗口尺寸。
布局原则
- 不应显式设定元素(如控件)的尺寸。元素应当可以改变尺寸以适合它们的内容。可通过设置最大和最小尺寸来限制可以接受的控件尺寸范围
- 不应使用屏幕坐标指定元素的位置。元素应当由它们的容器根据它们的尺寸/顺序以及(可选的)其他特定于具体布局容器的信息进行排列。如果需要在元素之间添加空白空间。可使用Margin
- 布局容器的子元素“共享”可用的空间。如果空间允许,布局容器会根据每个元素的内容尽可能为元素设置更合理的尺寸。它们还会向一个或多个元素分配多余的空间
- 可嵌套的布局容器。布局容器之间可以进行嵌套组合
布局过程
WPF 布局包括两个阶段:测量(measure
)阶段和排列(arrange
)阶段。在测量阶段,容器遍历所有子元素,并询问子元素它们所期望的尺寸。在排列阶段,容器在合适的位置放置子元素。
布局容器
所有WPF布局容器都是派生自System.Windows.Controls.Panel
抽象类的面板。
Panel类还包含几个内部属性,如果希望创建自己的容器,就可以使用它们。最特别的是,可重写继承自FrameworkElement类的 MeasureOverride()和ArangeOverride()方法,以修改当组织子元素时面板处理测量阶段和排列阶段的方式。
名称 | 说明 |
---|---|
StackPanel | 在水平或垂直的堆栈中放置元素。这个布局容器通常用于更大/更复杂窗口中的一些小区域 |
WrapPanl | 在一些列可换行的行中放置元素 |
DockPanel | 根据容器的整个边界调整元素 |
Grid | 根据不可见的表格在行和列中排列元素,这是最灵活、最常见的容器之一 |
UniformGrid | 在不可见但是强制所有单元格具有相同尺寸的表中放置元素,整个布局容器不常用 |
Canvas | 使用固定坐标绝对定位元素 |
StackPanel
名称 | 说明 |
---|---|
HorizontalAlignment | 当水平方向上有额外的空间时,该属性决定了子元素在布局容器中如何定位。可选用 Center、Left、Right或 Stretch 等属性值 |
VerticalAlignment | 当垂直方向上有额外的空间时,该属性决定了子元素在布局容器控件中如何定位。可选用 Center、Top、Bottom或Stretch 等属性值 |
Margin | 该属性用于在元索的周围添加一定的空间 |
MinWidth和MinHeight | 最小尺寸 |
MaxWidth和MaxHeight | 最大尺寸 |
Width和Height | 显示地设置元素的尺寸 |
Border
Border控件不是布局面板,而是非常便于使用的元素,经常与布局面板一起使用。
名称 | 说明 |
---|---|
Background | 使用Brush对象设置边框中所有内容后面的背景 |
BorderBrush和BorderThickness | 使用Brush对象设置位于Border对象边缘的边框的颜色,并设置边框的宽度 |
CornerRadius | 该属性可使边框具有精致的圆角。CornerRadius的值越大,圆角效果就越明显 |
Padding | 该属性在边框和内部的内容之间添加空间(与此相对,Margin属性在边框之外添加空间) |
WrapPanel
WrapPanel 面板在可能的空间中,以一次一行或一列的方式布置控件。默认情况下WrapPanel.Orientation 属性设置为 Horizontal;控件从左向右进行排列,再在下一行中排列。但可将 WrapPanel.Orientation 属性设置为 Vertical,从而在多个列中放置元素。
与StackPanel面板类似,WrapPanel面板实际上主要用来控制用户界面中一小部分的布局细节,并非用于控制整个窗口布局
DockPanel
DockPanel面板沿着一条外边缘来拉伸所包含的控件,通过锚点停靠的方式设置子元素的位置
名称 | 说明 |
---|---|
DockPanel.Dock | 子元素的附加属性,设置子元素在DockPanel的停靠位置 |
LastChildFill | 设置DockPanel中最后一个元素是否填充DockPanel剩余控件(默认为true) |
Grid
Grid面板将元素分隔到不可见的行列网格中。尽管可在一个单元格中放置多个元素(这时这些元素会相互重叠),但在每个单元格中只放置一个元素通常更合理
名称 | 说明 |
---|---|
RowDefinitions和ColumnDefinitions | 定义Grid中的行列集合,行列需要在此集合内声明 |
RowDefinition和ColumnDefinition | 用于定义一行或一列 |
Grid.Row和Grid.Column | 子元素的附加属性,设置子元素在Grid中的行列位置(索引从0开始) |
Grid.RowSpan和Grid.ColumnSpan | 子元素的附加属性,可设置子元素跨行或跨列 |
ShowGridLines | 用于显示Grid网格中的行列线(默认false) |
GridSplitter | Grid分割线,可用于分割窗口 |
Grid.IsSharedSizeScope | 用于共享尺寸组 |
调整行和列
Grid面板支持以下三种设置尺寸的方式:
-
绝对设置尺寸方式。使用设备无关单位准确地设置尺寸
1
2<!-- 设置行高为100 -->
<RowDefinition Height="100"/> -
自动设置尺寸方式。每行或每列的尺寸刚好满足需要
1
2<!-- 设置行高自适应高度,由内容撑开 -->
<RowDefinition Height="auto"/> -
按比例设置尺寸方式。按比例将控件分割到一组行和列中
1
2
3<!-- 按比例划分Grid剩下空间,第二行是第一行的2倍高度 -->
<RowDefinition Height="*"/>
<RowDefinition Height="2*"/>
分割窗口
每个 Windows用户都见过分割条–能将窗口的一部分与另一部分分离的可拖动分割器例如,当使用 Windows 资源管理器时,会看到一系列文件夹(在左边)和一系列文件(在右边)。可拖动它们之间的分割条来确定每部分占据窗口的比例。
1 | <Grid> |
共享尺寸组
共享尺寸组的目标是保持用户界面独立部分的一致性。例如,可能希望改变一列的尺寸以适应其内容,并改变另一列的尺寸使其与前面一列改变后的尺寸相匹配。然而,共享尺寸组的真正优点是使独立的 Grid 控件具有相同的比例。
-
默认情况,尽管每一列都设置了Auto,但是在同一列中都是以最长的一列作为当前列的宽度,不会影响其他列
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<StackPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0" Content="One"/>
<Button Grid.Row="0" Grid.Column="1" Content="Two"/>
<Button Grid.Row="1" Grid.Column="0" Content="Three (Which is Longer)"/>
<Button Grid.Row="1" Grid.Column="1" Content="Four"/>
</Grid>
</StackPanel> -
共享尺寸组,开启
Grid.IsSharedSizeScope="True"
共享尺寸组,为需要共享尺寸的列设置相同的组SharedSizeGroup="Group1"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<StackPanel>
<Grid Grid.IsSharedSizeScope="True">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" SharedSizeGroup="Group1"/>
<ColumnDefinition Width="auto" SharedSizeGroup="Group1"/>
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0" Content="One"/>
<Button Grid.Row="0" Grid.Column="1" Content="Two"/>
<Button Grid.Row="1" Grid.Column="0" Content="Three (Which is Longer)"/>
<Button Grid.Row="1" Grid.Column="1" Content="Four"/>
</Grid>
</StackPanel>
UniformGrid
有一种网格不遵循前面讨论的所有原则–UniformGrid 面板。与 Grid 面板不同,UniformGrid 面板不需要(甚至不支持)预先定义的列和行。相反,通过简单地设置 Rows 和Columns属性来设置其尺寸。每个单元格始终具有相同的大小,因为可用的空间被均分。最后元素根据定义的顺序被放置到适当的单元格中。UniformGrid面板中没有 Row 和 Column 附加属性,也没有空白单元格。
名称 | 说明 |
---|---|
Rows | 设置面板的行数 |
Columns | 设置面板的列数 |
Canvas
Canvas面板允许使用精确的坐标放置元素,如果设计数据驱动的富窗体和标准对话框,这并非好的选择;但如果需要构建其他一些不同的内容(例如,为图形工具创建绘图表面),Canvas面板可能是个有用的工具。Canvas面板还是最轻量级的布局容器。这是因为Canvas面板没有包含任何复杂的布局逻辑,用以改变其子元素的首选尺寸。Canvas面板只是在值定的位置放置其子元素,并且子元素具有所希望的精确尺寸。
名称 | 说明 |
---|---|
Canvas.Left | 子元素附加属性,用于设置子元素相对于Canvas左侧的坐标 |
Canvas.Right | 子元素附加属性,用于设置子元素相对于Canvas右侧的坐标 |
Canvas.Top | 子元素附加属性,用于设置子元素相对于Canvas上边的坐标 |
Canvas.Bottom | 子元素附加属性,用于设置子元素相对于Canvas下边的坐标 |
Canvas.ZIndex | 子元素附加属性,用于设置子元素层叠关系,值越大越靠上 |
InkCanvas
InkCanvas 元素的主要目的是用于接收手写笔输入。手写笔是一种在平板 PC中使用的类似于钢笔的输入设备,然而,InkCanvas 元素同时也可使用鼠标进行工作,就像使用手写笔一样。因此,用户可使用鼠标在 InkCanvas 元素上绘制线条,或者选择以及操作 InkCanvas 中的元素。
InkCanyas 元素实际上包含两个子内容集合。一个是为人熟知的 Children 集合,它保存任意元素,就像 Canvas 面板一样。每个子元素可根据 Top、Left、Bottom 和 Right 属性进行定位。另一个是 Strokes 集合,它保存 System.Windows.Ink.Stroke 对象,该对象表示用户在 InkCanvas元素上绘制的图形输入。用户绘制的每条直线或曲线都变成独立的 Stroke对象。得益于这两个集合,可使用 InkCanvas 让用户使用存储在 Strokes集合中的笔画(stroke)为保存在 Children 集合中的内容添加注释。
根据为InkCanvas.EditingMode属性设置的值,可以采用截然不同的方式使用InkCanvas元素
名称 | 说明 |
---|---|
Ink | InkCanvas元素允许用户绘制批注,这是默认模式。但用户用鼠标或手写笔绘图时,会绘制笔画 |
GestureOnly | InkCanvas元素不允许用户绘制笔画标注,党费关注预先定义的特定姿势(例如在某个方向拖动手写笔或涂画内容) |
InkAndGesture | InkCanvas元素允许用户绘制笔画批注,也可以识别预先定义的姿势 |
EraseByStroke | 当单击笔画时,InkCanvas元素会擦除笔画 |
EraseByPoint | 但单击笔画时,InkCanvas元素会擦除笔画中被单击的部分(笔画上的一个点) |
Select | InkCanvas面板允许用户选择保存在Children集合中的元素。要选择一个元素,用户必须单击该元素或拖动“索套”选择该元素。一旦选择一个元素,就可以移动该元素、改变其尺寸或将其删除 |
None | InkCanvas元素忽略鼠标和手写笔输入 |