从 WPF/Avalonia 到 CSS Grid:跨平台网格布局迁移手册
对于习惯 WPF 或 Avalonia 的开发者而言,CSS Grid 并非陌生的概念,只是语法层面需要重新建立映射。本文将以实际场景为线索,梳理两套技术栈在网格布局上的对应关系,助你快速在前端环境中复现桌面端的布局习惯。
基础网格容器
以下是一个 Avalonia 的常规 Grid 定义:
<Grid Width="300" Height="300" Background="Aqua">
<TextBlock Text="hello avalonia" />
</Grid>
对应到前端,核心在于 display: grid 的声明:
<div class="layout-box">
<span class="content-text">hello html+css</span>
</div>
.layout-box {
display: grid;
background-color: aqua;
width: 300px;
height: 300px;
}
| WPF / Avalonia | CSS | 说明 |
|---|---|---|
<Grid> | display: grid | 激活网格布局上下文 |
Background | background-color | CSS 中背景图需使用 background-image 另行设置 |
Width / Height | width / height | CSS 中须带单位,如 px |
子元素的对齐控制
Avalonia 通过 HorizontalAlignment 与 VerticalAlignment 控制子元素位置:
<Grid Width="300" Height="300" Background="Aqua">
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="hello avalonia" />
</Grid>
CSS Grid 则使用 justify-self(水平)与 align-self(垂直)实现等价效果:
.content-text {
justify-self: center;
align-self: center;
}
| WPF / Avalonia | CSS | 含义 |
|---|---|---|
HorizontalAlignment="Left" | justify-self: start | 水平左对齐 |
HorizontalAlignment="Right" | justify-self: end | 水平右对齐 |
HorizontalAlignment="Center" | justify-self: center | 水平居中 |
VerticalAlignment="Top" | align-self: start | 垂直顶部对齐 |
VerticalAlignment="Bottom" | align-self: end | 垂直底部对齐 |
VerticalAlignment="Center" | align-self: center | 垂直居中 |
...="Stretch" | ...-self: stretch | 拉伸填充(需避免同时指定固定尺寸) |
配合 margin 实现拉伸带间距的效果:
.fill-area {
align-self: stretch;
justify-self: stretch;
margin: 5px;
background-color: yellow;
}
行列的定义与分配
Avalonia / WPF 方式
<Grid Width="300" Height="300" Background="Aqua">
<Grid.RowDefinitions>
<RowDefinition Height="80" />
<RowDefinition Height="1*" />
<RowDefinition Height="2*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="0,0" />
<TextBlock Grid.Row="0" Grid.Column="1" Text="1,0" />
<TextBlock Grid.Row="0" Grid.Column="2" Text="2,0" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="0,1" />
<TextBlock Grid.Row="1" Grid.Column="1" Text="1,1" />
<TextBlock Grid.Row="1" Grid.Column="2" Text="2,1" />
<TextBlock Grid.Row="2" Grid.Column="0" Text="0,2" />
<TextBlock Grid.Row="2" Grid.Column="1" Text="1,2" />
<TextBlock Grid.Row="2" Grid.Column="2" Text="2,2" />
<TextBlock Grid.Row="3" Grid.Column="0" Text="0,3" />
<TextBlock Grid.Row="3" Grid.Column="1" Text="1,3" />
<TextBlock Grid.Row="3" Grid.Column="2" Text="2,3" />
</Grid>
CSS Grid 等价实现
<div class="layout-box">
<span class="cell c-0-0">0,0</span>
<span class="cell c-1-0">1,0</span>
<span class="cell c-2-0">2,0</span>
<span class="cell c-0-1">0,1</span>
<span class="cell c-1-1">1,1</span>
<span class="cell c-2-1">2,1</span>
<span class="cell c-0-2">0,2</span>
<span class="cell c-1-2">1,2</span>
<span class="cell c-2-2">2,2</span>
<span class="cell c-0-3">0,3</span>
<span class="cell c-1-3">1,3</span>
<span class="cell c-2-3">2,3</span>
</div>
.layout-box {
display: grid;
background-color: aqua;
width: 300px;
height: 300px;
grid-template-rows: 80px 1fr 2fr auto;
grid-template-columns: 1fr 3fr 2fr;
}
.cell {
align-self: center;
justify-self: center;
}
.c-0-0 { grid-row: 1; grid-column: 1; }
.c-1-0 { grid-row: 1; grid-column: 2; }
.c-2-0 { grid-row: 1; grid-column: 3; }
.c-0-1 { grid-row: 2; grid-column: 1; }
.c-1-1 { grid-row: 2; grid-column: 2; }
.c-2-1 { grid-row: 2; grid-column: 3; }
.c-0-2 { grid-row: 3; grid-column: 1; }
.c-1-2 { grid-row: 3; grid-column: 2; }
.c-2-2 { grid-row: 3; grid-column: 3; }
.c-0-3 { grid-row: 4; grid-column: 1; }
.c-1-3 { grid-row: 4; grid-column: 2; }
.c-2-3 { grid-row: 4; grid-column: 3; }
| WPF / Avalonia | CSS | 关键差异 |
|---|---|---|
RowDefinitions | grid-template-rows | 1* → 1fr,Auto → auto |
ColumnDefinitions | grid-template-columns | 同上 |
Grid.Row | grid-row | 索引起始:WPF 从 0 开始,CSS 从 1 开始 |
Grid.Column | grid-column | 同上 |
RowSpan / ColumnSpan | grid-row-start/end / grid-column-start/end | 显式指定跨越范围 |
层叠效果的实现
在 Avalonia 中,同一单元格内的元素会自动层叠:
<Grid Width="300" Height="300" Background="Aqua">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="Hello, Avalonia!" />
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Top" Text="New!!!!!!!!" />
</Grid>
CSS Grid 默认会将子元素自动放置到不同单元格,因此必须显式声明单格容器并指定元素位置:
<div class="layout-box">
<span class="main-info">Hello, html+css!</span>
<span class="badge">new!!!!!!!</span>
</div>
.layout-box {
display: grid;
background-color: aqua;
width: 300px;
height: 300px;
grid-template-columns: 1fr;
grid-template-rows: 1fr;
}
.main-info {
grid-row: 1;
grid-column: 1;
align-self: center;
justify-self: center;
}
.badge {
grid-row: 1;
grid-column: 1;
align-self: start;
justify-self: end;
}
若不限制为单格(即省略 grid-template-* 的定义),两个 <span> 会被自动分配到第一行的两个独立单元格,导致布局与预期不符。