ReoGrid 支持多行列标题,允许您创建具有多行、合并单元格和独立样式的复杂标题结构。此功能适用于显示层级式或分组式的列标题。

创建多行标题
通过 ExtensionColumnHeader 属性访问扩展列标题:
var extensionHeader = worksheet.ExtensionColumnHeader;
设置标题行数
使用 RowCount 属性或 SetRowCount 方法来定义标题行数(1 到 10):
// 设置 3 行标题
extensionHeader.SetRowCount(3);
// 或使用属性
extensionHeader.RowCount = 3;
效果如下:

注意: 扩展列标题支持 1 到 10 行。将
RowCount设置为 1 等同于恢复默认的单行标题。
获取标题行数和总高度
int rowCount = extensionHeader.RowCount;
int totalHeight = extensionHeader.TotalHeight; // 所有标题行的总高度(像素)
访问标题单元格
多行标题中的单元格使用 (行, 列) 坐标,原点 (0, 0) 位于左上角:

// 通过行和列索引访问单元格
HeaderCell cell = extensionHeader[0, 0];
// 或通过 Cells 集合
HeaderCell cell2 = extensionHeader.Cells[1, 3];
HeaderCell 属性
| 属性 | 类型 | 说明 |
|---|---|---|
Text | string | 标题单元格的显示文本 |
Style | HeaderCellStyle | 用于自定义外观的样式对象 |
Body | IHeaderBody | 用于渲染的自定义标题主体 |
RowIndex | int | 单元格的行索引(只读) |
ColumnIndex | int | 单元格的列索引(只读) |
IsValid | bool | 单元格是否有效(未被合并消耗) |
IsMerged | bool | 单元格是否属于合并范围 |
IsMergedStartCell | bool | 是否为合并范围的左上角单元格 |
MergedStartCell | HeaderCell | 合并区域的左上角单元格(未合并时返回自身) |
CellPosition | CellPosition | 作为 CellPosition 结构体的位置 |
RangePosition | RangePosition | 如果该单元格属于合并区域,则为其范围 |
PositionAddress | string | 地址字符串(例如 “A1” 或合并时为 “A1:B2”) |
TextColor | SolidColor? | 文本颜色(已弃用 — 请使用 Style.TextColor) |
BackColor | SolidColor? | 背景颜色(已弃用 — 请使用 Style.BackColor) |
合并和取消合并单元格
合并单元格
使用 MergeCells 来合并单元格。参数为:起始行、起始列、行跨度、列跨度。
extensionHeader.MergeCells(int startRow, int startColumn, int rowSpan, int columnSpan);
示例:
// 合并单元格 (0,0),跨 3 行 1 列
extensionHeader.MergeCells(0, 0, 3, 1);
// 从单元格 (0,1) 开始合并,跨 1 行 4 列
extensionHeader.MergeCells(0, 1, 1, 4);

设置单元格文本
extensionHeader[0, 0].Text = "No.";
extensionHeader[0, 1].Text = "Category A";

完整的多行标题示例
以下是创建复杂层级标题的完整示例:
// 设置 3 行
extensionHeader.SetRowCount(3);
// 合并单元格以创建层级结构
extensionHeader.MergeCells(0, 0, 3, 1); // "No." 跨所有 3 行
extensionHeader.MergeCells(0, 1, 1, 4); // 顶层分组 1
extensionHeader.MergeCells(1, 1, 1, 2); // 中层分组 1a
extensionHeader.MergeCells(1, 3, 1, 2); // 中层分组 1b
extensionHeader.MergeCells(0, 5, 3, 1); // "Target" 跨所有 3 行
extensionHeader.MergeCells(0, 6, 1, 4); // 顶层分组 2
extensionHeader.MergeCells(1, 6, 2, 1); // 中层分组 2a(跨 2 行)
extensionHeader.MergeCells(1, 8, 2, 2); // 中层分组 2b(跨 2 行)
// 设置所有单元格的文本
extensionHeader[0, 0].Text = "No.";
extensionHeader[0, 1].Text = "Category A";
extensionHeader[1, 1].Text = "Sub-A1";
extensionHeader[1, 3].Text = "Sub-A2";
extensionHeader[2, 1].Text = "Detail 1";
extensionHeader[2, 2].Text = "Detail 2";
extensionHeader[2, 3].Text = "Detail 3";
extensionHeader[2, 4].Text = "Detail 4";
extensionHeader[0, 5].Text = "Target";
extensionHeader[0, 6].Text = "Category B";
效果如下:

取消合并单元格
extensionHeader.UnmergeCells(0, 7);
检查合并状态
var cell = extensionHeader[0, 0];
if (cell.IsMerged)
{
Console.WriteLine($"Cell is merged. Start cell: {cell.MergedStartCell.PositionAddress}");
}
if (cell.IsMergedStartCell)
{
Console.WriteLine($"This is the start of a merge spanning {cell.RangePosition}");
}
// 检查一个单元格是否包含另一个
bool contains = cell.Contains(extensionHeader[1, 0]);
标题单元格样式
每个标题单元格都有一个 Style 属性,类型为 HeaderCellStyle,用于自定义外观。
HeaderCellStyle 属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
TextColor | SolidColor? | null(系统默认) | 文本颜色。设置为 null 可移除。 |
BackColor | SolidColor? | null(系统默认) | 背景颜色。设置为 null 可移除。 |
FontSize | float | 10.5f (WinForms) | 字体大小 |
FontFamily | string | ”Arial” (WinForms) | 字体族名称(WinForms) |
Font | FontFamily | 系统默认 (WPF) | 字体族(WPF) |
FontStyle | FontStyles | Regular | 字体样式(粗体、斜体) |
FontWeight | FontWeight | 系统默认 (WPF) | 字体粗细(仅 WPF) |
TextUnderLine | bool | false | 文本是否有下划线 |
HorizontalAlignment | ReoGridHorAlign | Center | 水平文本对齐方式 |
VerticalAlignment | ReoGridVerAlign | Middle | 垂直文本对齐方式 |
TextWrapMode | TextWrapMode | NoWrap | 文本换行模式 |
Padding | PaddingValue | (空) | 单元格内边距 |
示例:设置标题单元格样式
// 为分类标题设置背景颜色
extensionHeader[0, 1].Style.BackColor = new SolidColor(Color.LightBlue);
// 为标题单元格设置字体样式
extensionHeader[0, 0].Style.FontSize = 12;
extensionHeader[0, 0].Style.FontStyle = FontStyles.Bold;
extensionHeader[0, 0].Style.TextColor = new SolidColor(Color.DarkBlue);
// 设置文本对齐方式
extensionHeader[0, 0].Style.HorizontalAlignment = ReoGridHorAlign.Center;
extensionHeader[0, 0].Style.VerticalAlignment = ReoGridVerAlign.Middle;
// 为长标题文本启用文本换行
extensionHeader[0, 1].Style.TextWrapMode = TextWrapMode.WordBreak;
// 添加内边距
extensionHeader[0, 0].Style.Padding = new PaddingValue(4, 2, 4, 2);
重置单元格样式
extensionHeader[0, 0].Style.Reset();
在单元格之间复制样式
extensionHeader[1, 1].Style.CopyFrom(extensionHeader[0, 1].Style);
转换为 WorksheetRangeStyle
将 HeaderCellStyle 转换为 WorksheetRangeStyle 以用于工作表单元格:
WorksheetRangeStyle rangeStyle = extensionHeader[0, 0].Style.ToRangeStyle();
调整行高
每个标题行的高度可以单独调整:
// 设置第一行标题的高度
extensionHeader.Rows[0].Height = 40;
// 设置第二行标题的高度
extensionHeader.Rows[1].Height = 25;
// 获取行数
int count = extensionHeader.Rows.Count;
鼠标事件
扩展列标题提供鼠标事件以实现交互行为:
// 整个扩展标题的鼠标事件
extensionHeader.MouseDown += (s, e) =>
{
Console.WriteLine($"Mouse down at row:{e.RowIndex}, col:{e.ColumnIndex}");
};
extensionHeader.MouseMove += (s, e) =>
{
Console.WriteLine($"Mouse move at row:{e.RowIndex}, col:{e.ColumnIndex}");
};
extensionHeader.MouseUp += (s, e) =>
{
Console.WriteLine($"Mouse up at row:{e.RowIndex}, col:{e.ColumnIndex}");
};
标题鼠标事件参数
| 属性 | 类型 | 说明 |
|---|---|---|
RowIndex | int | 扩展标题中的行索引(忽略合并单元格) |
ColumnIndex | int | 扩展标题中的列索引(忽略合并单元格) |
PositionToColumn | int | 鼠标位置相对于物理列的位置 |
单独单元格鼠标事件
每个 HeaderCell 也会触发自己的鼠标事件:
var cell = extensionHeader[0, 0];
cell.MouseDown += (s, e) =>
{
Console.WriteLine($"Clicked header cell: {e.HeaderCell.Text}");
};
cell.MouseUp += (s, e) =>
{
// 处理鼠标释放
};
cell.MouseMove += (s, e) =>
{
// 处理鼠标移动
};
HeaderCellMouseEventArgs
| 属性 | 类型 | 说明 |
|---|---|---|
HeaderCell | HeaderCell | 触发事件的标题单元格 |
IsCancelled | bool | 设置为 true 可取消事件 |
列选择
多行列标题始终允许选择单独的列,无论单元格是否水平合并。选中的列会以高亮颜色显示:

如果合并单元格区域中只有部分列被选中,这些单元格会部分高亮:

隐藏列
当列被隐藏时,多行标题中会在隐藏列的位置显示一条粗线:

配合冻结窗格使用
多行列标题与工作表冻结功能的配合方式与普通列标题相同。当工作表滚动时,冻结的列保持可见:

配合大纲使用
多行列标题可以与大纲配合使用。如果列被折叠,对应的标题单元格会被隐藏:

重置扩展标题
将扩展标题重置为默认状态(移除所有合并和文本):
extensionHeader.Reset();
转换为 PartialGrid
将扩展标题导出为 PartialGrid 对象:
// 导出所有列
var partialGrid = extensionHeader.ConvertToPartialGrid();
// 仅导出特定列
var partialGrid = extensionHeader.ConvertToPartialGrid(new int[] { 0, 1, 2, 3 });