以下是 **自定义 `SpanningUniformGrid` 的完整实现**(无需 NuGet 依赖),支持跨行(`RowSpan`)和跨列(`ColumnSpan`),同时保持 `UniformGrid` 的均等分布特性:
---
### **1. 定义 `SpanningUniformGrid` 类**
```csharp
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
public class SpanningUniformGrid : UniformGrid
{
// 注册附加属性:RowSpan
public static readonly DependencyProperty RowSpanProperty =
DependencyProperty.RegisterAttached(
"RowSpan",
typeof(int),
typeof(SpanningUniformGrid),
new PropertyMetadata(1));
public static int GetRowSpan(DependencyObject obj) =>
(int)obj.GetValue(RowSpanProperty);
public static void SetRowSpan(DependencyObject obj, int value) =>
obj.SetValue(RowSpanProperty, value);
// 注册附加属性:ColumnSpan
public static readonly DependencyProperty ColumnSpanProperty =
DependencyProperty.RegisterAttached(
"ColumnSpan",
typeof(int),
typeof(SpanningUniformGrid),
new PropertyMetadata(1));
public static int GetColumnSpan(DependencyObject obj) =>
(int)obj.GetValue(ColumnSpanProperty);
public static void SetColumnSpan(DependencyObject obj, int value) =>
obj.SetValue(ColumnSpanProperty, value);
// 重写布局逻辑
protected override Size ArrangeOverride(Size finalSize)
{
if (Children.Count == 0)
return finalSize;
// 计算每个单元格的宽度和高度
double cellWidth = finalSize.Width / Columns;
double cellHeight = finalSize.Height / Rows;
foreach (UIElement child in Children)
{
if (child == null)
continue;
// 获取子元素的位置和跨度
int row = GetRow(child);
int column = GetColumn(child);
int rowSpan = Math.Max(1, GetRowSpan(child));
int columnSpan = Math.Max(1, GetColumnSpan(child));
// 计算子元素的布局矩形
Rect rect = new Rect(
column * cellWidth,
row * cellHeight,
columnSpan * cellWidth,
rowSpan * cellHeight);
// 应用布局
child.Arrange(rect);
}
return finalSize;
}
}
```
---
### **2. 在 XAML 中使用**
```xml
<Window xmlns:local="clr-namespace:YourNamespace">
<local:SpanningUniformGrid Rows="2" Columns="3">
<!-- 跨2列 -->
<Button
Content="跨2列"
local:SpanningUniformGrid.ColumnSpan="2"
Background="LightBlue"/>
<!-- 跨2行 -->
<Button
Content="跨2行"
local:SpanningUniformGrid.RowSpan="2"
Grid.Row="0" Grid.Column="2"
Background="LightGreen"/>
<!-- 普通单元格 -->
<Button Content="A" Grid.Row="1" Grid.Column="0"/>
<Button Content="B" Grid.Row="1" Grid.Column="1"/>
</local:SpanningUniformGrid>
</Window>
```
---
### **3. 效果展示**
布局结果如下(类似 HTML 表格的 `colspan`/`rowspan`):
```
[ 跨2列 (占2列) ][ 跨2行 (占2行) ]
[ A ][ B ] [ 跨2行延续 ]
```
---
### **4. 关键点说明**
1. **附加属性**:
- 通过 `RowSpan` 和 `ColumnSpan` 控制子元素的跨度。
- 默认值为 `1`(不跨越)。
2. **布局逻辑**:
- 计算每个单元格的固定大小(`cellWidth`/`cellHeight`)。
- 根据跨度和位置分配子元素的实际区域。
3. **兼容性**:
- 保留 `UniformGrid` 的所有原生功能(如 `Rows`/`Columns` 属性)。
- 支持与 `Grid.Row`/`Grid.Column` 共存。
---
### **5. 性能优化建议**
- **静态布局**:如果子元素位置固定,可在加载后调用 `UpdateLayout()` 冻结布局。
- **虚拟化**:对大量动态项,建议结合 `VirtualizingPanel`。
---
### **6. 对比现成方案**
| 特性 | 自定义 `SpanningUniformGrid` | NuGet 包(如 `EnhancedUniformGrid`) |
|--------------------|-----------------------------|--------------------------------------|
| **跨行/跨列** | ✅ | ✅ |
| **代码量** | 需手动实现(约 50 行) | 直接安装,零代码 |
| **维护性** | 需自行维护 | 依赖第三方更新 |
| **适用场景** | 拒绝 NuGet 的严格环境 | 快速开发 |
---
### **总结**
- **推荐场景**:
- 企业内部分享控件库。
- 对 NuGet 引入限制严格的项目。
- **备用方案**:
- 直接使用 `Grid`(原生支持跨单元格,但需手动定义行列)。
- 安装 `EnhancedUniformGrid` 包(省时省力)。
如果需要进一步扩展(如动态跨度计算),可以重写 `MeasureOverride` 方法优化测量逻辑。