WPF 系列教程:控件之布局

布局(Layout)控件用于管理子元素的大小、尺寸、位置和排列方式。

边框(Border)

Border 可以在另一个元素周围绘制边框与背景。

需要注意的是,Border 内最多只能有一个子级。

若要显示多个子元素,需要在父 Border 中放置其他 Panel 元素,可以将子元素放在该 Panel 元素中。

1
<Border Background="LightBlue" BorderBrush="Black" BorderThickness="2" CornerRadius="45" Padding="25" />

子弹装饰器(BulletDecorator)

BulletDecorator 是一个布局控件,该控件可以将项目符号与另一个可视对象对齐。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<StackPanel>
<BulletDecorator Margin="5" VerticalAlignment="Center" Background="Yellow">
<BulletDecorator.Bullet>
<Image Width="50" Source="pack://application:,,,/WpfExample;component/images/bullet.png"/>
</BulletDecorator.Bullet>
<TextBlock TextWrapping="Wrap" VerticalAlignment="Center" HorizontalAlignment="Left" Foreground ="Purple">A Simple BulletDecorator</TextBlock>
</BulletDecorator>
<BulletDecorator Margin="5" VerticalAlignment="Center" Background="Yellow">
<BulletDecorator.Bullet>
<Image Width="20" Source="pack://application:,,,/WpfExample;component/images/apple.jpg"/>
</BulletDecorator.Bullet>
<TextBlock TextWrapping="Wrap" VerticalAlignment="Center" HorizontalAlignment="Left" Foreground ="Purple">A Simple BulletDecorator</TextBlock>
</BulletDecorator>
<BulletDecorator Margin="5" VerticalAlignment="Center" Background="Yellow">
<BulletDecorator.Bullet>
<Ellipse Margin="5,0,0,0" Height="10" Width="10" Fill="Purple" HorizontalAlignment="Left" ></Ellipse>
</BulletDecorator.Bullet>
<TextBlock Margin="10,0,0,0" TextWrapping="Wrap" VerticalAlignment="Center" HorizontalAlignment="Left" Foreground ="Purple">A Simple BulletDecorator</TextBlock>
</BulletDecorator>
<BulletDecorator Margin="5" VerticalAlignment="Center" Background="Yellow">
<BulletDecorator.Bullet>
<Ellipse Margin="5,0,0,0" Height="10" Width="10" Fill="Purple" HorizontalAlignment="Left" ></Ellipse>
</BulletDecorator.Bullet>
<TextBlock Margin="10,0,0,0" TextWrapping="Wrap" VerticalAlignment="Center" HorizontalAlignment="Left" Foreground ="Purple">A Simple BulletDecorator</TextBlock>
</BulletDecorator>
<BulletDecorator Margin="5" VerticalAlignment="Center" Background="Yellow">
<BulletDecorator.Bullet>
<CheckBox />
</BulletDecorator.Bullet>
<TextBlock Margin="10,0,0,0" TextWrapping="Wrap" Width="200" VerticalAlignment="Center" HorizontalAlignment="Left" Foreground ="Purple">A very very veryvery very very very long text BulletDecorator</TextBlock>
</BulletDecorator>
</StackPanel>

如上代码所展示的是一个使用 BulletDecorator 控件实现一个无序列表。可以任意定义 Bullet 以及 Child 内的内容。

以上例子分别使用了图形与圆形放置在 Bullet 中作为列表的符号,Child 内容固定为一段文本内容,当文本换行时,文本的第一行将与列表符号对齐。

该控件除可以作为列表提供展示外,还可以在 Bullet 中放置单选框、多选框、输入框等。

Canvas(画布)

定义一个区域,可在其中使用相对于 Canvas 区域的坐标以显式方式来定位子元素。

1
2
3
4
5
<Canvas Height="400" Width="400">
<Canvas Height="100" Width="100" Top="0" Left="0" Background="Red"/>
<Canvas Height="100" Width="100" Top="100" Left="100" Background="Green"/>
<Canvas Height="100" Width="100" Top="50" Left="50" Background="Blue"/>
</Canvas>

需要注意的是 Canvas 作为一个存储控件的容器,其不会自动调整内部元素的大小与排列。

Canvas 默认也不会裁剪超出自身范围的内容,溢出的内容将会显示在 Canvas 外,可以通过通过调整 ClipToBounds 属性,来控制是否裁剪多出的内容。

停靠面板(DockPanel)

定义一个区域,从中可以按相对位置水平或垂直排列各个子元素。

1
2
3
4
5
6
7
8
9
10
11
<DockPanel>
<Button DockPanel.Dock="Top" Background="Aqua">1(Top)</Button>
<Button DockPanel.Dock="Left" Background="Green">2(Left)</Button>
<Button DockPanel.Dock="Right" Background="Yellow">3(Right)</Button>
<Button DockPanel.Dock="Bottom" Background="Blue">4(Bottom)</Button>
<DockPanel Background="Orange" LastChildFill="False">
<Button Height="20">Button 1</Button>
<Button Height="20">Button 2</Button>
<Button DockPanel.Dock="Right" Height="20">Button 3</Button>
</DockPanel>
</DockPanel>

SetDock 方法更改某个元素相对于同一容器中的其他元素的位置。对齐属性(如 HorizontalAlignment)更改元素相对于其父元素的位置。

默认情况下 DockPanel 中的最后一个元素将填充剩余空间,而不考虑最后一个元素设置的任何其他 Dock 值,可以通过指定 LastChildFill 属性并设置停靠方向来调整。

默认情况下,Panel 元素不接收焦点。 若要强制 Panel 元素接收焦点,请将 Focusable 属性设置为 true

扩展器(Expander)

Expander 是一个可以展开和折叠的控件,它包含标头 Header 和内容 Content 两个部分。

1
2
3
4
5
6
7
8
9
10
<Expander Background="Tan" HorizontalAlignment="Left" ExpandDirection="Down" IsExpanded="True" Width="100">
<Expander.Header>
<TextBlock>hd2y</TextBlock>
</Expander.Header>
<StackPanel HorizontalAlignment="Center">
<TextBlock><Hyperlink>用户中心</Hyperlink></TextBlock>
<TextBlock><Hyperlink>博客</Hyperlink></TextBlock>
<TextBlock><Hyperlink>注销</Hyperlink></TextBlock>
</StackPanel>
</Expander>

ContentHeader 属性可以是任何类型(如字符串、图像或面板)。

如果展开窗口的内容对窗口而言太大,则可以将 Expander 的内容包装在 ScrollViewer 控件中以提供可滚动的内容。 Expander 控件不自动提供滚动功能。

网格(Grid)

Grid 用来定义由列和行组成的灵活的网格区域。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<Grid VerticalAlignment="Top" HorizontalAlignment="Left" ShowGridLines="True" Width="250" Height="100">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>

<TextBlock FontSize="20" FontWeight="Bold" Grid.ColumnSpan="3" Grid.Row="0">2005 Products Shipped</TextBlock>
<TextBlock FontSize="12" FontWeight="Bold" Grid.Row="1" Grid.Column="0">Quarter 1</TextBlock>
<TextBlock FontSize="12" FontWeight="Bold" Grid.Row="1" Grid.Column="1">Quarter 2</TextBlock>
<TextBlock FontSize="12" FontWeight="Bold" Grid.Row="1" Grid.Column="2">Quarter 3</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="0">50000</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="1">100000</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="2">150000</TextBlock>
<TextBlock FontSize="16" FontWeight="Bold" Grid.ColumnSpan="3" Grid.Row="3">Total Units: 300000</TextBlock>
</Grid>

默认情况下列宽与行高是等比例分配,可以设置 WidthHeight 属性为 Auto 让宽高自适应。

宽高除可以设置为 Auto 外,其默认值为 1*,这里 * 会使用设定了固定值外的剩余空间,按前面设置的数值进行等比例分配。

固定值我们可以设置 100100px100pt100cm100in,默认单位为 px,所以 100100px 是同样的。

在子元素中,我们可以通过设置 ColumnSpanRowSpan,让这个元素跨列或跨行显示。

网格拆分器(GridSplitter)

利用 Grid 我们可以很方便的划分单元格,并通过设定宽高来控制每行每列所占用的空间。

但是如果想要让用户来改变每行每列的宽高,就需要使用 GridSplitter 来实现这样的效果。

例如以下代码,定义了一个 GridSplitter,通过覆盖列的边缘来调整 Grid 中列的大小:

1
2
3
4
5
6
7
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<GridSplitter Grid.Column="0" Background="Blue" Width="5" HorizontalAlignment="Right" VerticalAlignment="Stretch"/>
</Grid>

而以下代码中,GridSplitter 将在 Grid 中占用一列:

1
2
3
4
5
6
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<GridSplitter Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Stretch" Background="Black" ShowsPreview="True" Width="5"/>

组框(GroupBox)

GroupBox 控件可以在用户界面的内容外创建一个带有边框与标题的容器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<GroupBox>
<GroupBox.Header>
<Label>health information</Label>
</GroupBox.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label VerticalAlignment="Center" HorizontalAlignment="Right">height:</Label>
<Label Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Right">weight:</Label>
<TextBox Grid.Row="0" Grid.Column="1" Margin="3"></TextBox>
<TextBox Grid.Row="1" Grid.Column="1" Margin="3"></TextBox>
</Grid>
</GroupBox>

GroupBox 的效果类似 html 中的 fieldset 标签。

另外需要注意的是,GroupBoxHeaderedContentControl,这意味着其 ContentHeader 属性可以是任何类型(如字符串、图像或面板)。

分离器(Separator)

Separator 控件是用来分隔控件中的多个项。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<ToolBarTray Background="White">
<ToolBar Band="1" BandIndex="1">
<Button>New</Button>
<Button>Open</Button>
<Button>Save</Button>
<Separator/>
<Button>Cut</Button>
<Button>Copy</Button>
<Button>Paste</Button>
<Separator/>
<Button>Print</Button>
<Button>Preview</Button>
</ToolBar>
</ToolBarTray>

滚动条(ScrollBar)

ScrollBar 提供一个滚动条的控件,其位置对应于值。

1
<ScrollBar Orientation="Horizontal" Width ="4in" Value="50" Minimum="1" Maximum="100" />

滚动查看器(ScrollViewer)

ScrollViewer 定义可滚动的区域,可以包含其他可见元素。

1
2
3
4
5
6
<ScrollViewer HorizontalScrollBarVisibility="Auto">
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Left">
<TextBlock TextWrapping="Wrap" Margin="0,0,0,20">Scrolling is enabled when it is necessary. Resize the window, making it larger and smaller.</TextBlock>
<Rectangle Fill="Red" Width="500" Height="500"></Rectangle>
</StackPanel>
</ScrollViewer>

堆栈面板(StackPanel)

StackPanel 可以将子元素排列成水平或垂直的一行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel>
<Button>Test1</Button>
<Button>Test2</Button>
<Button>Test3</Button>
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Horizontal">
<Button>Test4</Button>
<Button>Test5</Button>
<Button>Test6</Button>
</StackPanel>
</Grid>

默认子元素会垂直排列,可以修改 Orientation 属性为 Horizontal,子元素将水平排列。

查看框(ViewBox)

ViewBox 组件的作用是拉伸或延展位于其中的组件,使之有更好的布局及视觉效果。

1
2
3
<Viewbox Stretch="Fill" StretchDirection="Both" Width="400" Height="60">
<Button>测试</Button>
</Viewbox>

ViewBox 内只能有一个子元素,另外可以通过设置 StretchStretchDirection 属性,来设置内容的缩放效果。

Stretch 可选项有:

  • None:原始大小。
  • Fill:填充,子元素占满整个 Viewbox,不保证宽高比例。
  • Uniform:等比例缩放。在 Viewbox 能完整显示子元素的最大尺寸;
  • UniformToFill:等比例缩放填充,子元素在保证缩放的情况下填充 Viewbox,多余的部分被裁剪。

StretchDirection 可选项有:

  • Both:内容根据 Stretch 模式进行缩放以适合父项的大小;
  • DownOnly:内容仅在大于父级时缩放。如果内容较小,则不会执行任何缩放。
  • UpOnly:内容仅在小于父级时缩放。如果内容较大,则不执行缩放。

窗口(Window)

Window 提供了创建、配置、显示、管理窗口与对话框的生命周期的能力。

1
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Title="Main Window in Markup Only" Height="300" Width="300" />

用户与独立应用程序之间的交互点是一个窗口。WPF 窗口包含两个不同的区域:

  • 承载 Windows 修饰的非工作区,包括图标、标题、系统菜单、最小化按钮、最大化按钮、还原按钮、关闭按钮和边框。
  • 承载特定于应用程序的内容的工作区。

Window 封装了创建、配置、显示和管理窗口和对话框的声明周期的功能,并提供了以下关键服务:

  • 生命周期管理:ActivateActivatedCloseClosedClosingDeactivatedHideIsActiveShowSourceInitialized
  • 窗口管理:GetWindowOwnedWindowsOwner
  • 外观和行为:AllowsTransparencyContentRenderedDragMoveIconLeftLocationChangedResizeModeRestoreBoundsShowActivatedShowInTaskbarSizeToContentStateChangedTitleTopTopmostWindowStartupLocationWindowStateWindowStyle
  • 对话框:DialogResultShowDialog

此外,Application 公开了对管理应用程序中的所有窗口的特殊支持:应用程序维护当前在应用程序中实例化的所有窗口的列表。 此列表由 Windows 属性公开。

默认情况下,将使用对应用程序中实例化的第一个 Window 的引用自动设置 MainWindow。 从而使窗口成为主应用程序窗口。

Window 主要用于显示独立应用程序的窗口和对话框。但是,对于需要在窗口级别导航的应用程序(如向导),可以改用 NavigationWindowNavigationWindow 派生自 Window 并通过浏览器样式的导航支持对其进行扩展。

注: 本文的目的是为了熟悉常见的布局控件,所以存在大段的复制粘贴。
示例代码来自 MSDN,内容已经上传至 githubhd2y/WpfExample

参考: