2.WPF布局基础
WPF 布局基础核心就一句话:用容器(Panel)管理子元素的排列,而不是用绝对坐标硬定位。所有布局容器都继承自 Panel。
布局流程#
WPF 布局分两轮测量:
- Measure(测量):父容器问每个子元素”你想要多大?”
- Arrange(排列):父容器根据可用空间决定子元素最终位置和大小
理解这个有助于明白为什么 Width/Height 与 HorizontalAlignment 会”失效”——因为容器拥有最终裁量权。
五大布局容器#
1. Grid —— 最常用(表格化布局)#
用 RowDefinitions / ColumnDefinitions 定义行列,子元素用 Grid.Row / Grid.Column 指定位置:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <!-- 高度随内容 -->
<RowDefinition Height="*"/> <!-- 占满剩余 -->
<RowDefinition Height="2*"/> <!-- 占剩余的 2/3 -->
<RowDefinition Height="50"/> <!-- 固定 50 -->
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="姓名:"/>
<TextBox Grid.Row="0" Grid.Column="1"/>
<Button Grid.Row="2" Grid.ColumnSpan="2" Content="确定"/>
</Grid>xml*(星号)= 按比例分配剩余空间,是做自适应界面的关键Auto= 内容多大它就多大- 支持
Grid.RowSpan/Grid.ColumnSpan跨行跨列
2. StackPanel —— 简单堆叠#
<StackPanel Orientation="Vertical"> <!-- 或 Horizontal -->
<TextBlock Text="第一行"/>
<TextBlock Text="第二行"/>
<Button Content="按钮"/>
</StackPanel>xml- 垂直方向:高度由内容决定,宽度默认拉伸
- 坑:放 ScrollViewer 里没问题;直接放进窗口可能导致内容被裁掉(不自动换行也不出现滚动条)
3. WrapPanel —— 自动换行#
<WrapPanel Orientation="Horizontal">
<Button Content="A" Width="60"/>
<Button Content="B" Width="60"/>
<!-- 一行放不下时自动换到下一行 -->
</WrapPanel>xml适合做标签流、图片墙。
4. DockPanel —— 边缘停靠#
<DockPanel>
<Menu DockPanel.Dock="Top"/>
<StatusBar DockPanel.Dock="Bottom"/>
<TreeView DockPanel.Dock="Left" Width="150"/>
<ContentControl/> <!-- 剩余空间默认占满 -->
</DockPanel>xml最后一个子元素默认填满剩余区域。经典主窗口布局全靠它。
5. Canvas —— 绝对定位#
<Canvas>
<Button Canvas.Left="50" Canvas.Top="30" Content="确定"/>
</Canvas>xml唯一用坐标(Left/Top)的容器。除非做游戏、画图、拖动控件等场景,一般不要用。
对齐与边距(所有容器通用)#
| 属性 | 说明 |
|---|---|
HorizontalAlignment | Stretch(拉伸填满)/ Left / Center / Right |
VerticalAlignment | Stretch / Top / Center / Bottom |
Margin | 外边距,如 Margin="10,20,10,20"(左、上、右、下) |
Padding | 内边距(部分控件如 Button、Border 有) |
Visibility | Visible / Collapsed(Collapsed 不占空间,区别于 Hidden) |
⚠️ 常见误区:在 StackPanel 里设 VerticalAlignment="Stretch" 无效——因为 StackPanel 给子元素的高度就是内容高度,没有”剩余空间”可拉伸。Grid 里才可以。
经典实战:登录窗口布局#
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="用户名" VerticalAlignment="Center" Margin="0,0,8,8"/>
<TextBox Grid.Row="0" Grid.Column="1" Margin="0,0,0,8"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="密码" VerticalAlignment="Center" Margin="0,0,8,8"/>
<PasswordBox Grid.Row="1" Grid.Column="1" Margin="0,0,0,12"/>
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="登录" Width="80" Margin="0,0,8,0"/>
<Button Content="取消" Width="80"/>
</StackPanel>
</Grid>xml效果:标签自动宽度、输入框拉伸填满、按钮靠右——窗口拉大缩小都能自适应。
布局原则小结#
- 优先 Grid,配合
*做自适应;DockPanel 做主窗口骨架 - 局部小布局(按钮组、表单)用 StackPanel
- 避免用 Margin 做绝对定位凑布局
- 需要滚动条就把内容包进
<ScrollViewer> - 复杂界面可以容器嵌套(Grid 里嵌 DockPanel 里再嵌 StackPanel),但嵌套别太深,影响性能