
























在使用 Aspose.Words 生成 Word 表格时,经常需要合并单元格,例如表头占满整行、某一列跨多行等。如果不了解 Aspose.Words 的合并机制,很容易出现合并逻辑错误,例如 2×2 单元格合并配置错误的情况。
本文系统讲解 Aspose.Words 合并单元格的原理、配置方式、代码示例以及常见误区,并解释水平合并与垂直合并之间的区别。
在 Aspose.Words 中,单元格合并不是一个“执行合并操作”,而是通过 为每个单元格标记合并状态 来实现。
核心控制属性位于:
CellFormat.HorizontalMerge
CellFormat.VerticalMerge
分别控制:
| 属性 | 作用 |
|---|---|
| HorizontalMerge | 控制单元格是否参与 横向合并 |
| VerticalMerge | 控制单元格是否参与 纵向合并 |
两者的值来自枚举:
Aspose.Words.Tables.CellMerge
定义如下:
public enum CellMerge
{
None = 0,
First = 1,
Previous = 2
}
三个值的含义:
| 枚举值 | 含义 |
|---|---|
| None | 单元格不参与合并 |
| First | 合并区域中的第一个单元格 |
| Previous | 与前一个单元格合并 |
可以把它理解为:
每个合并区域都是一条“合并链”
例如:
First -> Previous -> Previous -> Previous
表示这些单元格组成一个合并区域。
假设有一行 4 个单元格,其中前 3 个需要合并。
字符画结构:
+-----+-----+-----+-----+
| A | | | B |
+-----+-----+-----+-----+
逻辑标记如下:
+-----------+-----------+-----------+-----------+
| h:first | h:previous| h:previous| h:none |
| v:none | v:none | v:none | v:none |
+-----------+-----------+-----------+-----------+
解释:
| 单元格 | HorizontalMerge |
|---|---|
| A | First |
| 第二格 | Previous |
| 第三格 | Previous |
| B | None |
假设第一列三行需要合并:
+-----+-----+
| A | B |
+-----+-----+
| | C |
+-----+-----+
| | D |
+-----+-----+
逻辑标记:
+-----------+-----------+
| v:first | v:none |
| h:none | h:none |
+-----------+-----------+
| v:previous| v:none |
| h:none | h:none |
+-----------+-----------+
| v:previous| v:none |
| h:none | h:none |
+-----------+-----------+
在官方文档示例中,只给出了两种情况:
但是没有提供 同时跨行跨列(例如 2×2)合并 的完整示例。这会导致很多人在第一次实现多行多列合并时,按照直觉组合这两种示例,写出类似下面的配置:
+-----------+-----------+
| v:first | v:none |
| h:first | h:previous|
+-----------+-----------+
| v:previous| v:previous|
| h:none | h:previous|
+-----------+-----------+
乍一看似乎是合理的:
但实际上这是 错误的配置。
原因在于:
在 Aspose.Words 中,每一行的横向合并链、每一列的纵向合并链都必须独立重新开始。
也就是说:
VerticalMergeHorizontalMerge因此正确写法应该是:
+-----------+-----------+
| v:first | v:first |
| h:first | h:previous|
+-----------+-----------+
| v:previous| v:previous|
| h:first | h:previous|
+-----------+-----------+
字符结构可以理解为:
+---------+---------+
| | |
| A | |
| | |
+---------+---------+
| | |
| | |
| | |
+---------+---------+
关键理解:
First 起点如果理解为“合并区域是由横向链和纵向链交叉组成的结构”,就不会再写错这种配置。
假设有一个 4×4 表格:
+---+---+---+---+
| A | B | C | D |
+---+---+---+---+
| E | F | G | H |
+---+---+---+---+
| I | J | K | L |
+---+---+---+---+
| M | N | O | P |
+---+---+---+---+
需要合并中间 2×2 区域:
F G
J K
合并后结构:
+---+---+---+---+
| A | B | C | D |
+---+---+---+---+
| E | | H |
+---| merged|---+
| I | | L |
+---+-------+---+
| M | N | O | P |
+---+---+---+---+
逻辑标记如下:
+-----------+-----------+-----------+-----------+
| none none | none none | none none | none none |
+-----------+-----------+-----------+-----------+
| none none | v:f h:f | v:f h:p | none none |
+-----------+-----------+-----------+-----------+
| none none | v:p h:f | v:p h:p | none none |
+-----------+-----------+-----------+-----------+
| none none | none none | none none | none none |
+-----------+-----------+-----------+-----------+
重点四个单元格:
| 位置 | VerticalMerge | HorizontalMerge |
|---|---|---|
| F | First | First |
| G | First | Previous |
| J | Previous | First |
| K | Previous | Previous |
如果使用 DocumentBuilder 创建表格,需要在插入单元格时设置合并属性。
示例:第一行横向合并。
Document doc = new Document();
DocumentBuilder builder = new DocumentBuilder(doc);
builder.InsertCell();
builder.CellFormat.HorizontalMerge = CellMerge.First;
builder.Write("Text in merged cells.");
builder.InsertCell();
builder.CellFormat.HorizontalMerge = CellMerge.Previous;
builder.EndRow();
builder.InsertCell();
builder.CellFormat.HorizontalMerge = CellMerge.None;
builder.Write("Text in one cell.");
builder.InsertCell();
builder.Write("Text in another cell.");
builder.EndRow();
builder.EndTable();
doc.Save("HorizontalMerge.docx");
关键步骤:
| 步骤 | 说明 |
|---|---|
| InsertCell | 创建单元格 |
| HorizontalMerge = First | 开始合并 |
| HorizontalMerge = Previous | 继续合并 |
| HorizontalMerge = None | 结束合并 |
如果忘记设置 None,后续单元格可能继续被合并。
创建一个 两行一列合并 的表格:
Document doc = new Document();
DocumentBuilder builder = new DocumentBuilder(doc);
builder.InsertCell();
builder.CellFormat.VerticalMerge = CellMerge.First;
builder.Write("Text in merged cells.");
builder.InsertCell();
builder.CellFormat.VerticalMerge = CellMerge.None;
builder.Write("Text in one cell");
builder.EndRow();
builder.InsertCell();
builder.CellFormat.VerticalMerge = CellMerge.Previous;
builder.InsertCell();
builder.CellFormat.VerticalMerge = CellMerge.None;
builder.Write("Text in another cell");
builder.EndRow();
builder.EndTable();
doc.Save("VerticalMerge.docx");
结构:
+---------+
| A |
| (First) |
+---------+
| |
|Previous |
+---------+
如果加载一个已有 Word 文件,可以读取属性判断。
示例代码:
Document doc = new Document("Table with merged cells.docx");
Table table = (Table)doc.GetChild(NodeType.Table, 0, true);
foreach (Row row in table.Rows)
{
foreach (Cell cell in row.Cells)
{
Console.WriteLine(PrintCellMergeType(cell));
}
}
检查两个属性:
cell.CellFormat.HorizontalMerge
cell.CellFormat.VerticalMerge
返回值含义:
| 返回值 | 含义 |
|---|---|
| First | 合并区域起点 |
| Previous | 合并区域延续 |
| None | 未合并 |
如果不是使用 DocumentBuilder 创建表格,而是修改已有表格,可以写一个工具函数。
internal void MergeCells(Cell startCell, Cell endCell)
{
Table parentTable = startCell.ParentRow.ParentTable;
Point startCellPos = new Point(
startCell.ParentRow.IndexOf(startCell),
parentTable.IndexOf(startCell.ParentRow));
Point endCellPos = new Point(
endCell.ParentRow.IndexOf(endCell),
parentTable.IndexOf(endCell.ParentRow));
Rectangle mergeRange = new Rectangle(
Math.Min(startCellPos.X, endCellPos.X),
Math.Min(startCellPos.Y, endCellPos.Y),
Math.Abs(endCellPos.X - startCellPos.X) + 1,
Math.Abs(endCellPos.Y - startCellPos.Y) + 1);
foreach (Row row in parentTable.Rows)
{
foreach (Cell cell in row.Cells)
{
Point currentPos = new Point(row.IndexOf(cell), parentTable.IndexOf(row));
if (mergeRange.Contains(currentPos))
{
cell.CellFormat.HorizontalMerge =
currentPos.X == mergeRange.X ? CellMerge.First : CellMerge.Previous;
cell.CellFormat.VerticalMerge =
currentPos.Y == mergeRange.Y ? CellMerge.First : CellMerge.Previous;
}
}
}
}
调用:
Document doc = new Document("Table with merged cells.docx");
Table table = doc.FirstSection.Body.Tables[0];
Cell start = table.Rows[0].Cells[0];
Cell end = table.Rows[1].Cells[1];
MergeCells(start, end);
doc.Save("MergeCellRange.docx");
这个方法可以合并:
理解这个差异很重要。
Word 表格是:
Table
├─ Row
│ ├─ Cell
│ ├─ Cell
│ └─ Cell
└─ Row
├─ Cell
└─ Cell
特点:
例如:
第一行
| 2cm | 1cm |
第二行
| 1cm | 2cm |
HTML 表格是严格网格:
+----+----+----+
| | | |
+----+----+----+
| | | |
+----+----+----+
HTML 使用:
rowspan
colspan
例如:
<td colspan="2">
<td rowspan="3">
某些 Word 版本 不会保存 HorizontalMerge 标记。
表现为:
解决方法:
table.ConvertToHorizontallyMergedCells();
示例:
Document doc = new Document("Table with merged cells.docx");
Table table = doc.FirstSection.Body.Tables[0];
table.ConvertToHorizontallyMergedCells();
该方法会:
rowSpan == 1
colSpan > 1
结构:
First -> Previous -> Previous
代码:
HorizontalMerge = CellMerge.First
HorizontalMerge = CellMerge.Previous
rowSpan > 1
colSpan == 1
结构:
First
Previous
Previous
规则:
左上角
First First
第一行
First Previous
第一列
Previous First
其他
Previous Previous
代码:
if (i == 0 && j == 0)
{
VerticalMerge = CellMerge.First;
HorizontalMerge = CellMerge.First;
}
else if (i == 0 && j > 0)
{
VerticalMerge = CellMerge.First;
HorizontalMerge = CellMerge.Previous;
}
else if (i > 0 && j == 0)
{
VerticalMerge = CellMerge.Previous;
HorizontalMerge = CellMerge.First;
}
else
{
VerticalMerge = CellMerge.Previous;
HorizontalMerge = CellMerge.Previous;
}
原因:
后续单元格仍然保持
CellMerge.Previous
解决方法:
在合并区域之后设置
CellMerge.None
Aspose.Words 合并单元格的本质是:
通过标记每个单元格的合并状态来构建合并结构。
核心规则:
FirstPreviousNone理解这些规则后,就可以正确构造:
rowspan / colspan 转换。此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。