一文读懂WPF布局

WPF布局

布局

WPF 布局的核心机制

WPF 布局基于布局面板(Panel) 和 测量/排列(Measure/Arrange) 机制:

  1. 测量(Measure):计算子元素所需的空间。
  2. 排列(Arrange):确定子元素最终的位置和大小。
  3. 自动适应:大多数布局控件支持动态调整子元素的位置和大小(基于内容或容器尺寸)

常用布局控件详解

Grid(网格布局)
  1. 核心功能:通过行(RowDefinition)和列(ColumnDefinition)定义网格,支持复杂的多区域布局。
  2. 关键属性:
    RowDefinitions/ColumnDefinitions:定义行和列(支持固定值、比例(*)、自动尺寸(Auto))。
    Grid.Row/Grid.Column:附加属性,设置子元素的位置。
    Grid.RowSpan/Grid.ColumnSpan:合并单元格
javascript 复制代码
    <Grid>
        <Grid.RowDefinitions>
            <!--定义三行-->
            <RowDefinition Height="100"/>
            <!-- 自动高度 -->
            <RowDefinition Height="*"/>
            <!-- 剩余空间占比 -->
            <RowDefinition Height="2*"/>
            <!-- 2倍剩余空间 -->
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <!--定义两列-->
            <ColumnDefinition Width="200"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>

        <Button Content="第一行合并俩列" Grid.Row="0" Grid.ColumnSpan="2"/>
        <TextBox Grid.Row="1" Grid.Column="0" Text="第2行第1列合并2行" Grid.RowSpan="2"/>
        <TextBox Grid.Row="1" Grid.Column="1" Text="第2行第2列" />
        <TextBox Grid.Row="2" Grid.Column="1" Text="第3行第2列" />
    </Grid>
StackPanel(堆叠布局)
  1. 核心功能:子元素按水平(Orientation="Horizontal")或垂直(Orientation="Vertical")方向依次排列。
  2. 适用场景:简单线性布局(如工具栏、菜单栏)
javascript 复制代码
 <Grid>
     
     <Grid.RowDefinitions>
         <!--定义三行-->
         <RowDefinition Height="100"/>
         <!-- 自动高度 -->
         <RowDefinition Height="*"/>
         <!-- 剩余空间占比 -->
         <RowDefinition Height="2*"/>
         <!-- 2倍剩余空间 -->
     </Grid.RowDefinitions>
     <Grid.ColumnDefinitions>
         <!--定义两列-->
         <ColumnDefinition Width="300"/>
         <ColumnDefinition Width="Auto"/>
     </Grid.ColumnDefinitions>

     <StackPanel Grid.Column="1" Orientation="Horizontal">
         <Button Content="New" Width="100"/>
         <Button Content="Open" Width="100"/>
         <Button Content="Save" Width="100"/>
     </StackPanel>
     <StackPanel Grid.Row="1" Orientation="Vertical">
         <TextBox  Text="1111"  Height="50"/>
         <TextBox  Text="2222" Height="50" />
     </StackPanel>
    <TextBox Grid.Row="1" Grid.Column="1" Text="第2行第2列" />
    <TextBox Grid.Row="2" Grid.Column="1" Text="第3行第2列" />
 </Grid>
DockPanel(停靠布局)
  1. 核心功能:子元素通过 DockPanel.Dock 附加属性停靠在容器的边缘。
  2. 关键属性:
    LastChildFill:最后一个子元素是否填充剩余空间(默认 True)
javascript 复制代码
<Grid>
    
    <Grid.RowDefinitions>
        <!--定义三行-->
        <RowDefinition Height="100"/>
        <!-- 自动高度 -->
        <RowDefinition Height="*"/>
        <!-- 剩余空间占比 -->
        <RowDefinition Height="2*"/>
        <!-- 2倍剩余空间 -->
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <!--定义两列-->
        <ColumnDefinition Width="300"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>

    <DockPanel LastChildFill="True">
        <Button Content="Top" DockPanel.Dock="Top" Height="30"/>
        <Button Content="Left" DockPanel.Dock="Left" Width="100"/>
        <Button Content="Right" DockPanel.Dock="Right" Width="100"/>
        <Button Content="Fill"/>
        <!-- 填充剩余区域 -->
    </DockPanel>
    <StackPanel Grid.Column="1" Orientation="Horizontal">
        <Button Content="New" Width="100"/>
        <Button Content="Open" Width="100"/>
        <Button Content="Save" Width="100"/>
    </StackPanel>
    <StackPanel Grid.Row="1" Orientation="Vertical">
        <TextBox  Text="1111"  Height="50"/>
        <TextBox  Text="2222" Height="50" />
    </StackPanel>
    <TextBox Grid.Row="1" Grid.Column="1" Text="第2行第2列" />
    <TextBox Grid.Row="2" Grid.Column="1" Text="第3行第2列" />
</Grid>
WrapPanel(自动换行布局)
  1. 核心功能:子元素按顺序排列,空间不足时自动换行。
  2. 适用场景:标签页、动态生成的内容列表
javascript 复制代码
<Grid>
    
    <Grid.RowDefinitions>
        <!--定义三行-->
        <RowDefinition Height="100"/>
        <!-- 自动高度 -->
        <RowDefinition Height="*"/>
        <!-- 剩余空间占比 -->
        <RowDefinition Height="2*"/>
        <!-- 2倍剩余空间 -->
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <!--定义两列-->
        <ColumnDefinition Width="300"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
    <DockPanel LastChildFill="True">
        <Button Content="Top" DockPanel.Dock="Top" Height="30"/>
        <Button Content="Left" DockPanel.Dock="Left" Width="100"/>
        <Button Content="Right" DockPanel.Dock="Right" Width="100"/>
        <Button Content="Fill"/>
        <!-- 填充剩余区域 -->
    </DockPanel>

    <StackPanel Grid.Column="1" Orientation="Horizontal">
        <Button Content="New" Width="100"/>
        <Button Content="Open" Width="100"/>
        <Button Content="Save" Width="100"/>
    </StackPanel>
    <StackPanel Grid.Row="1" Orientation="Vertical">
        <TextBox  Text="1111"  Height="50"/>
        <TextBox  Text="2222" Height="50" />
    </StackPanel>
    <TextBox Grid.Row="1" Grid.Column="1" Text="第2行第2列" />
    <TextBox Grid.Row="2" Grid.Column="1" Text="第3行第2列" />
    <WrapPanel Grid.Row="2" Width="150">
        <Button Content="Item1" Margin="2"/>
        <Button Content="Item2" Margin="2"/>
        <Button Content="Item3" Margin="2"/>
        <Button Content="Item4" Margin="2"/>
    </WrapPanel>
</Grid>
Canvas(绝对定位布局)
  1. 核心功能:通过 Canvas.Left、Canvas.Top 等附加属性直接指定子元素的坐标。
  2. 适用场景:需要精确控制位置的场景(如绘图、游戏界面)
javascript 复制代码
<Grid>
    
    <Grid.RowDefinitions>
        <!--定义三行-->
        <RowDefinition Height="100"/>
        <!-- 自动高度 -->
        <RowDefinition Height="*"/>
        <!-- 剩余空间占比 -->
        <RowDefinition Height="2*"/>
        <!-- 2倍剩余空间 -->
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <!--定义两列-->
        <ColumnDefinition Width="300"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
    <DockPanel LastChildFill="True">
        <Button Content="Top" DockPanel.Dock="Top" Height="30"/>
        <Button Content="Left" DockPanel.Dock="Left" Width="100"/>
        <Button Content="Right" DockPanel.Dock="Right" Width="100"/>
        <Button Content="Fill"/>
        <!-- 填充剩余区域 -->
    </DockPanel>

    <StackPanel Grid.Column="1" Orientation="Horizontal">
        <Button Content="New" Width="100"/>
        <Button Content="Open" Width="100"/>
        <Button Content="Save" Width="100"/>
    </StackPanel>
    <StackPanel Grid.Row="1" Orientation="Vertical">
        <TextBox  Text="1111"  Height="50"/>
        <TextBox  Text="2222" Height="50" />
    </StackPanel>
    <TextBox Grid.Row="1" Grid.Column="1" Text="第2行第2列" />
    <WrapPanel Grid.Row="2" Width="150">
        <Button Content="Item1" Margin="2"/>
        <Button Content="Item2" Margin="2"/>
        <Button Content="Item3" Margin="2"/>
        <Button Content="Item4" Margin="2"/>
    </WrapPanel>
    <Canvas Grid.Row="2" Grid.Column="1">
        <Rectangle Canvas.Left="50" Canvas.Top="20" Width="100" Height="60" Fill="Blue"/>
        <Ellipse Canvas.Left="120" Canvas.Top="80" Width="80" Height="80" Fill="Red"/>
    </Canvas>
</Grid>
UniformGrid(均布网格)
  1. UniformGrid 的核心概念
    UniformGrid 是 WPF 中的一种特殊布局控件,所有子元素会被均匀分配到相同大小的单元格中,且每个单元格的尺寸一致。它适用于需要等分空间的场景(如棋盘、按钮矩阵、图片缩略图等)
  2. 关键属性详解
    Rows 和 Columns
    作用:手动指定行数和列数。若未设置,根据子元素数量和容器尺寸自动计算。
    自动计算规则:
    优先填满行(类似 Horizontal 方向的 WrapPanel)。
    行数 = ⌈子元素总数 / 列数⌉,列数 = ⌈子元素总数 / 行数⌉(取决于宽高比例)。
    FirstColumn
    作用:设置第一行的起始列(默认为0)。例如,若 FirstColumn=2,则第一行的第一个元素从第3列开始放置。
    UniformGrid.RowSpan 和 UniformGrid.ColumnSpan
    作用:允许子元素跨越多行或多列(类似 Grid 的 RowSpan/ColumnSpan)
javascript 复制代码
<Grid>
    
    <Grid.RowDefinitions>
        <!--定义三行-->
        <RowDefinition Height="100"/>
        <!-- 自动高度 -->
        <RowDefinition Height="*"/>
        <!-- 剩余空间占比 -->
        <RowDefinition Height="2*"/>
        <!-- 2倍剩余空间 -->
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <!--定义两列-->
        <ColumnDefinition Width="300"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
    <DockPanel LastChildFill="True">
        <Button Content="Top" DockPanel.Dock="Top" Height="30"/>
        <Button Content="Left" DockPanel.Dock="Left" Width="100"/>
        <Button Content="Right" DockPanel.Dock="Right" Width="100"/>
        <Button Content="Fill"/>
        <!-- 填充剩余区域 -->
    </DockPanel>

    <StackPanel Grid.Column="1" Orientation="Horizontal">
        <Button Content="New" Width="100"/>
        <Button Content="Open" Width="100"/>
        <Button Content="Save" Width="100"/>
    </StackPanel>
    <StackPanel Grid.Row="1" Orientation="Vertical">
        <TextBox  Text="1111"  Height="50"/>
        <TextBox  Text="2222" Height="50" />
    </StackPanel>
    <!-- 默认自动计算行列 -->
    <UniformGrid Grid.Row="1" Grid.Column="1">
        <Button Content="1"/>
        <Button Content="2"/>
        <Button Content="3"/>
        <Button Content="4"/>
        <Button Content="5"/>
        <Button Content="6"/>
        <Button Content="7"/>
        <Button Content="8"/>
    </UniformGrid>
    <WrapPanel Grid.Row="2" Width="150">
        <Button Content="Item1" Margin="2"/>
        <Button Content="Item2" Margin="2"/>
        <Button Content="Item3" Margin="2"/>
        <Button Content="Item4" Margin="2"/>
    </WrapPanel>
    <Canvas Grid.Row="2" Grid.Column="1">
        <Rectangle Canvas.Left="50" Canvas.Top="20" Width="100" Height="60" Fill="Blue"/>
        <Ellipse Canvas.Left="120" Canvas.Top="80" Width="80" Height="80" Fill="Red"/>
    </Canvas>
</Grid>
  1. 与其他布局的对比

  2. 应用场景

  3. 进阶使用

    1 结合数据绑定 显示图片库或商品列表,结合ItemsControl动态生成条目,实现等宽等高布局

    2 自定义单元格间距 注意:UniformGrid 没有内置的 Spacing 属性,需通过子元素的 Margin 或容器的 Padding 实现间距

    3 响应式布局 通过绑定动态调整列数,适配不同窗口尺寸

javascript 复制代码
<ItemsControl ItemsSource="{Binding ImageItems}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Columns="5"/>  <!-- 每行5列 -->
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>
javascript 复制代码
<!-- 通过 Margin 模拟间距 -->
<UniformGrid>
    <Button Content="1" Margin="2"/>
    <Button Content="2" Margin="2"/>
    <Button Content="3" Margin="2"/>
</UniformGrid>

<!-- 或通过 Padding 控制整体边距 -->
<UniformGrid Padding="5">
    <!-- 子元素 -->
</UniformGrid>
javascript 复制代码
<UniformGrid Columns="{Binding ColumnCount}">
    <!-- 动态列数 -->
</UniformGrid>
  1. 注意事项
    性能:子元素数量过多时,优先使用虚拟化面板(如 VirtualizingStackPanel),但 UniformGrid 本身不支持虚拟化。
    尺寸约束:若容器尺寸未明确指定(如未设置 Width/Height),可能因自动布局计算导致渲染异常。
    跨平台兼容性:UniformGrid 是 WPF 原生控件,在 UWP 或 MAUI 中需使用替代方案(如 Grid 均分行列)
VirtualizingStackPanel 虚拟化技术
  1. 什么是虚拟化技术
    WPF 中一种特殊的布局容器,专为大数据量列表控件(如 ListBox、ListView、DataGrid)设计,通过 虚拟化技术(Virtualization) 优化性能。其核心思想是:仅渲染当前可视区域的子元素,而非一次性加载所有数据项,从而大幅减少内存占用和渲染开销
    说白了就是相当于分页一样只展示分页内容 而不是全部内容
  2. 虚拟化的工作机制
    可视区域计算
    根据容器大小和滚动条位置,计算当前需要显示的数据范围(如索引 0~19)。
    动态生成 UI
    仅为可视范围内的数据项生成 UI 元素,其余项保持为 逻辑占位符。
    滚动时复用
    当用户滚动列表时:
    移除移出可视区域的 UI 元素。
    将新进入可视区域的数据绑定到复用的 UI 元素上(避免重复创建)
布局对比

布局设计原则

  1. 先整体后局部

    整体框架:用 Grid 划分界面的大区域(如顶部导航栏、侧边栏、主内容区)。

    局部细节:在 Grid 的单元格内嵌套其他布局控件(如 StackPanel、UniformGrid)处理子元素排列。

  2. 避免过度嵌套

    性能优化:嵌套层级越深,布局计算越复杂,尽量保持层级扁平。

    可维护性:嵌套过多会降低代码可读性,优先用 Grid 的行列定义替代多层 StackPanel。

  3. 善用布局特性

    自动尺寸:利用 Auto(根据内容自适应)和 *(按比例分配剩余空间)优化布局。

    内容驱动:优先让子元素决定自身大小(如 WrapPanel 自动换行、StackPanel 线性排列)

javascript 复制代码
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <!-- 标题 -->
        <RowDefinition Height="*"/>
        <!-- 表单内容 -->
        <RowDefinition Height="Auto"/>
        <!-- 操作栏 -->
    </Grid.RowDefinitions>

    <!-- 标题 -->
    <TextBlock Text="用户注册" FontSize="16" Grid.Row="0" Margin="10"/>

    <!-- 表单内容:嵌套 Grid -->
    <Grid Grid.Row="1" Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <!-- 表单项 -->
        <TextBlock Text="用户名:" Grid.Row="0" Grid.Column="0"/>
        <TextBox Grid.Row="0" Grid.Column="1" Margin="5"/>

        <TextBlock Text="密码:" Grid.Row="1" Grid.Column="0"/>
        <PasswordBox Grid.Row="1" Grid.Column="1" Margin="5"/>

        <TextBlock Text="性别:" Grid.Row="2" Grid.Column="0"/>
        <StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal" Margin="5">
            <RadioButton Content="男" GroupName="Gender"/>
            <RadioButton Content="女" GroupName="Gender" Margin="10,0,0,0"/>
        </StackPanel>

        <TextBlock Text="内容:" Grid.Row="3" Grid.Column="0"/>
        <UniformGrid Grid.Row="3" Grid.Column="1" Columns="3" Margin="5">
            <TextBlock  Text="123"/>
            <TextBlock Text="456"/>
        </UniformGrid>
    </Grid>

    <!-- 操作栏:水平排列按钮 -->
    <StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="10">
        <Button Content="提交" Width="80" Margin="0,0,10,0"/>
        <Button Content="取消" Width="80"/>
    </StackPanel>
</Grid>
相关推荐
WineMonk19 小时前
.NET WPF 控件类分层结构
.net·wpf
Marzlam19 小时前
一文读懂WPF系列之控件模版数据模板
wpf
界面开发小八哥3 天前
界面控件DevExpress WPF v25.1新功能预览 - 数据网格、报表性能增强
wpf·界面控件·devexpress·ui开发·.net 9
WineMonk3 天前
.NET WPF 可视化树(Visual Tree)
.net·wpf
ALex_zry4 天前
构建高可靠C++服务框架:从日志系统到任务调度器的完整实现
开发语言·c++·wpf
Bruce_Cheung5 天前
WPF旋转板栈设计一例
wpf·rack·tube·料盒·料管
leslie_xin5 天前
(原创)[开源][.Net Framework 4.5] SimpleMVVM(极简MVVM框架)更新 v1.1,增加NuGet包
c#·wpf
不知名君6 天前
WPF轮播图动画交互 动画缩放展示图片
wpf
Sitarrrr7 天前
【WPF】IOC控制反转的应用:弹窗但不互相调用ViewModel
设计模式·c#·wpf