WPF用不同的容器安排布局,每一个容器有它自己的布局逻辑—有些容器堆栈优速,另一些容器在网格中不可见的单元格中排列元素等。WPF非常抵制基于坐标的布局,反而更注重创建灵活的布局,以使布局能够适应内容的变化、不同语言以及各种窗口尺寸。
WPF窗口只能包含一个元素。为了在WPF窗口中放置多个元素并创建更实用的用户界面,需要在窗口上放置一个容器,然后再容器中添加元素。下面是几项在WPF中的布局原则:
- 不应该显示设定元素(如:控件)的尺寸
- 不应使用屏幕坐标指定元素的位置
- 布局容器和它们的子元素“共享”可以使用空间
- 可以嵌套布局容器
所有的WPF布局容器都派生自System.Windows.Controls.Panel抽象类的面板。DispatcherObject(抽象类)←DependencyObject←Visual(抽象类)←UIElement←FrameworkElement←Panel(抽象类)。Panel类提供了少量成员,包括三个公有属性如下:
Background | 该属性是用于为面板背景着色的画刷。如果想要接受鼠标事件,必须将该属性设置为非空值(可以将背景设置为透明,第6章可以学习基本画刷内容) |
Children | 该属性是在面板中存储的条目集合。这是第一级对象—换句话说,这些条目自身也可以包含更多条目。 |
IsItemsHost | 该属性是一个bool值,如果面板用于显示一个ItemsControl控件相关联的项(例如,TreeView控件中的节点或列表框的一些列项),那么该属性值为true。大多数情况下,甚至不需要知道列表控件使用一个后台面板来管理它所包含的条目的布局。但是如果希望创建一个自定义的列表,以不同的方式放置子元素,该细节就变得很重要(第20章将讲述)。 |
Panel基类是一个开始点,WPF提供了大量可用于布局的继承自Panel的类,如下几个就是几个最基本的类,它们都位于System.Windows.Controls命名空间中。
StackPanel | 在一个水平线或垂直的堆栈中放置元素。这一布局容器通常用于一个更大、更复杂窗口中的一些小区域 |
WrapPanel | 在一系列可换行的行中放置元素。水平方向上,WrapPanel面板以从左向右的方式放置条目,然后在在随后的行中放置元素。在垂直方向上,WrapPanel面板在自上而下的列种放置元素,并使用附加的列放置剩余的条目。 |
DockPanel | 根据容器的边界调整元素 |
Grid | 根据一个不可见的表格在行和列中安排元素。 |
UniformGrid | 在一个不可见但是强制所有单元格具有相同尺寸的表中放置元素。这个布局容器不常用 |
Canvas | 使用固定的坐标决定定位元素。这个布局容器和传统的Windows窗体很相似,但是它没有提供锚定或停靠功能,因此对于尺寸可变的窗口,该布局容器不是合适的选择,如果选择的话,需要另外做一些工作 |
除了这些核心容器之外还有几个更为专业的面板,如:TabPanel面板(在TabPanel面板中包含多个选项卡)、ToolbarPanel面板(工具栏中的多个按钮)以及ToolbarOverflowPanel面板(Toolbar控件的益处菜单中的多个命令)还有VirtualizingStackPanel面板,数据绑定列表控件使用该面板已大幅降低开销;还有InkCanvas控件,该控件和Canvas控件类似,但是该控件支持处理平板电脑上的手写笔输入。
●StackPanel面板
StackPanel面板是最简单的布局容器,该面板简单的在单行或单列中一堆栈的形式放置其子元素。默认情况下StackPanel面板自上而下的进行排列,使每一个元素的高度适合它的内容。通过设置Orientation属性,StackPanel面板可以用于水平的排列元素:<StackPanel Orientation="Horizontal">。下面是它的一些布局属性:
HorizontalAlignment | 当水平方向有额外的空间时,该属性决定了子元素在布局容器中如何定位 |
VerticalAlignment | 当垂直方向有额外的空间时,该属性决定了子元素在布局容器中如何定位 |
Margin | 该属性用于在元素的周围添加一定的控件 |
MinWidth、MinHeight | 这两个属性用于设置元素的最小尺寸。如果一个元素对于其他布局容器来说太大,该元素将被剪裁以合适容器 |
MaxWidth、MaxHeight | 这两个属性用于设置元素的最小尺寸。如果有空余的控件使用,那么在扩展子元素时就不会超出这一限制 |
Width、Height | 这两个属性用于显式的设置元素的尺寸。这一设置会覆盖HorizontalAlignment、VerticalAlignment,属性设置的值。但是不能超出MinWidth、MinHeight、MaxWidth、MaxHeight设置的范围。 |
Border控件不是一个布局面板,而是一个非常方便的元素,经常和布局面板一起使用。Border类非常简单,我们只需要了解一些属性就可以。
Background | 使用Brush对象设置边框中所有内容后面的背景。可以使用一个固定颜色背景,也可以使用其他更特殊的背景 |
BorderBrush和BroderThickness | 使用Brush对象设置位于Border对象边缘的边框的颜色,并相应地设置边框的宽度。为了显示边框,必须设置这两个属性 |
CornerRadius | 该属性可以使边框具有优美的圆角。 |
Padding | 该属性在边框和内部的内容之间添加空间。 |
●WrapPanel面板和DockPanel面板
WrapPanel面板一次以一行或一列来布置控件,默认Orientation属性为Horizontal,这时控件按行从左到右进行排列,如果超出则在下一行从新开始。如果想要按照列进行展示则可以把Orientation属性设置为Vertical。
DockPanel面板可以让元素停靠在它的边框上,停靠在DockPanel面板上将会被拉伸适应DockPanel的长度,子元素通过Dock附加属性设置元素的停靠方向,它还有一个LastChildFile属性,如果将它设置为true的话,那么最后一个元素将填充DockPanel面板剩余的所有空间。
●Grid面板
Grid面板是WPF中功能最强大的布局容器。Grid面板将元素分隔到不可见的行列网格中。尽管在一个单元格中可以放置多个元素,但是每一个单元格中放置一个元素更合理。当然Grid单元格中的元素本身也可能是另一个容器,该容器组织它包含的控件。Grid面板通过对象填充Grid.ColumnDefinitions集合和Grid.RowDefinitions集合来创建网格和行。如
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<Grid ShowGridLines="True"> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> </Grid>
这段代码创建了一个两行和三列的网格。如果要向单元格中放置元素需要用到Row和Column附加属性。这两个属性的值都是从0开始的索引数。如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<Button Grid.Row="0" Grid.Column="0">Top Left</Button> <Button Grid.Row="0" Grid.Column="1">Middle Left</Button> <Button Grid.Row="1" Grid.Column="2">Bottom Right</Button> <Button Grid.Row="1" Grid.Column="1">Bottom Middle</Button>
Row和Column属性的默认值为0。
Grid面板支持以下三种设置尺寸的方式:
- 绝对设置尺寸方式。使用设备无关单位设置确切的尺寸。这种方式不够灵活,难以应对内容大小的改变和容器大小的调整,并且难以处理本地化
- 自动设置尺寸方式。每行每列的尺寸刚好满足需要。
- 按照比例设置尺寸方式。空间被按比例地分割到一组行和列中。这是对所有行和列的标准设置。
为了得到最大的灵活性,可以混合使用着三种尺寸设置方式。
在Grid面板中也可以进行单元格的跨越,跨越行是Grid.RowSpan="",跨越列是Grid.ColumnSpan=""。
Windows用户用户都见过分隔条—可以拖动的能够将窗口的一部分与另一部分分离的分割器。在WPF中,分隔条用GridSplitter类表示,并且它是Grid面板的一个特性。GridSplitter有几条原则:1、GridSplitter对象必须放在Grid单元格中。2、GridSplitter对象总是改变整行或整列的尺寸。3、GridSplitter对象的尺寸很小不易见,为了使其可用,需要为其设置最小尺寸。4、GridSplitter对齐方式还决定了分隔条是水平的还是竖直的对于水平分割需要将VerticalAlignment属性设置为Center。如果要垂直分割需要将HorizontalAlignment属性设置为Center。具体示例如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<Grid> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition Width="Auto"></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <Grid Grid.Column="0" VerticalAlignment="Stretch"> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Button Margin="3" Grid.Row="0">Top Left</Button> <Button Margin="3" Grid.Row="1">Bottom Left</Button> </Grid> <GridSplitter Grid.Column="1" Width="3" VerticalAlignment="Stretch" HorizontalAlignment="Center" ShowsPreview="False"></GridSplitter> <Grid Grid.Column="2"> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Button Grid.Row="0" Margin="3">Top Right</Button> <Button Grid.Row="2" Margin="3">Bottom Right</Button> <GridSplitter Grid.Row="1" Height="3" VerticalAlignment="Center" HorizontalAlignment="Stretch" ShowsPreview="False"></GridSplitter> </Grid> </Grid>
在Grid面板包含一个航集合和一个列集合,可以明确地按比例确定行和列的尺寸,或根据其子元素的尺寸确定和列的尺寸。还有另一种确定一行或一列尺寸的方法—和其他行或列的尺寸相匹配的。这就是共享尺寸组。共享的列或行可以被用于不同的网格中,具体实例如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<Grid Grid.IsSharedSizeScope="True" Margin="3"> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition Height="Auto"></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Grid Grid.Row="0" Margin="3" Background="LightYellow" ShowGridLines="True"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" SharedSizeGroup="TextLabel"></ColumnDefinition> <ColumnDefinition Width="Auto"></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <Label Margin="5">A very long bit of text</Label> <!-- <GridSplitter Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Center" Width="10"></GridSplitter> --> <Label Grid.Column="1" Margin="5">More text</Label> <TextBox Grid.Column="2" Margin="5">A text box</TextBox> </Grid> <Label Grid.Row="1" >Some text in between the two grids...</Label> <Grid Grid.Row="2" Margin="3" Background="LightYellow" ShowGridLines="True"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" SharedSizeGroup="TextLabel"></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <Label Margin="5">Short</Label> <TextBox Grid.Column="1" Margin="5">A text box</TextBox> </Grid> </Grid>
●UniformGrid面板
UniformGrid面板是一个用于创建简单乃至复杂窗口布局的通用工具。主要用于在一个规则网格中快速的布局元素。示例如下:
<Window x:Class="LayoutPanels.TheUniformGrid" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="TheUniformGrid" Height="300" Width="300" > <UniformGrid Rows="2" Columns="2"> <Button>Top Left</Button> <Button>Top Right</Button> <Button>Bottom Left</Button> <Button>Bottom Right</Button> </UniformGrid> </Window>