合併分細胞
有時某些列在一個表格中需要標頭或寬度佔滿整張桌子的文本大塊。 若要正確設計表格,使用者可以將多個表格单元格合併成一個。Aspose.Words 在處理所有輸入格式時支援合併的單元格,包括從 HTML內容進口。
如何合併表格單元格
在 Aspose.Words 中,合併的細胞是由 CellFormat 類的以下屬性表示的:
- HorizontalMerge 描述細胞是否為水平合併細胞的一部分
- VerticalMerge 描述細胞是否是細胞垂直合併的一部分
這些屬性的值決定了細胞的合併動作:
- 串列中合併的單元的第一個單元將具有 CellMerge.First “- 任何之後合併的單元格將有 CellMerge.Previous”
- 沒有合併的單元格會具有 CellMerge.None
檢查細胞是否合併
若要檢查一個單元格是否為合併的單元格序列的一部分,我們只需檢查 HorizontalMerge 和 VerticalMerge 的屬性。
接下來的程式碼範例說明如何列印水平與垂直的单元合併類型。
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-.NET.git. | |
Document doc = new Document(MyDir + "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)); | |
} | |
} |
在使用 DocumentBuilder 時,合併細胞。
要在使用 DocumentBuilder 創建的表格中合併細胞,您需要為合併預期每個細胞設置合適的合併類型 – 第一是 CellMerge.First,然後是 CellMerge.Previous 。
此外,您必須記得將這些無需合并的單元格清除合併設定–可透過將第一個非合并單元格設置為 CellMerge.None 來完成。 若不進行此操作, bảng表中的所有單元格將會被合併。
以下範例示範如何建立一個由兩行組成的表格,其中第一行的格子會水平合併:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-.NET.git. | |
Document doc = new Document(); | |
DocumentBuilder builder = new DocumentBuilder(doc); | |
builder.InsertCell(); | |
builder.CellFormat.HorizontalMerge = CellMerge.First; | |
builder.Write("Text in merged cells."); | |
builder.InsertCell(); | |
// This cell is merged to the previous and should be empty. | |
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(ArtifactsDir + "WorkingWithTables.HorizontalMerge.docx"); |
以下範例顯示如何建立具二列的表格,第一列的細胞會垂直合併:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-.NET.git. | |
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(); | |
// This cell is vertically merged to the cell above and should be empty. | |
builder.CellFormat.VerticalMerge = CellMerge.Previous; | |
builder.InsertCell(); | |
builder.CellFormat.VerticalMerge = CellMerge.None; | |
builder.Write("Text in another cell"); | |
builder.EndRow(); | |
builder.EndTable(); | |
doc.Save(ArtifactsDir + "WorkingWithTables.VerticalMerge.docx"); |
其他案例中的合併細胞
在 DocumentBuilder 不使用的其它情況,例如在既存的表格中,以之前的那種方式合併列位可能就不那麼容易了。 相反,我們可以將合併屬性的應用涉及的基本操作包覆在一個方法中,讓這個任務變得更簡單。 此方法與名為合併自動化方法的合併自動化方法相似,用於合併表格中的多個細胞。
以下程式碼會將指定範圍內的表格單元格合併,從指定的单元格開始,到最後的单元格結束。 在这种情况下,这个范围可以横跨多个列或行:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-.NET.git. | |
internal void MergeCells(Cell startCell, Cell endCell) | |
{ | |
Table parentTable = startCell.ParentRow.ParentTable; | |
// Find the row and cell indices for the start and end cell. | |
Point startCellPos = new Point(startCell.ParentRow.IndexOf(startCell), | |
parentTable.IndexOf(startCell.ParentRow)); | |
Point endCellPos = new Point(endCell.ParentRow.IndexOf(endCell), parentTable.IndexOf(endCell.ParentRow)); | |
// Create a range of cells to be merged based on these indices. | |
// Inverse each index if the end cell is before the start cell. | |
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)); | |
// Check if the current cell is inside our merge range, then merge it. | |
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; | |
} | |
} | |
} | |
} |
接下來的程式碼範例示範了如何將指定兩個細胞之間的範圍欄位合併:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-.NET.git. | |
Document doc = new Document(MyDir + "Table with merged cells.docx"); | |
Table table = doc.FirstSection.Body.Tables[0]; | |
// We want to merge the range of cells found inbetween these two cells. | |
Cell cellStartRange = table.Rows[0].Cells[0]; | |
Cell cellEndRange = table.Rows[1].Cells[1]; | |
// Merge all the cells between the two specified cells into one. | |
MergeCells(cellStartRange, cellEndRange); | |
doc.Save(ArtifactsDir + "WorkingWithTables.MergeCellRange.docx"); |
根據你使用的 .NET Framework 版本,你可能希望透過將此方法轉換為擴充方法來改良它。 在這個情況下,您可以直接在一個細胞上調用此方法來合併一系列的細胞,例如 cell1.Merge(cell2)
。
HTML表格中垂直與水平合併的單元格
正如我們在之前的文章中說的,一張 Microsoft Word 表格是由一些獨立行構成的一組。 每行都有一套獨立於其他行的細胞。 因此,在Microsoft Word表格中,沒有像“列”这样的物件,而“第1列”就像是“表格中每行第1行的套件”。 這讓使用者可以有一個表格,例如,第1行包含2cm和1cm的2個格,第2行包含2個不同的格,1cm和2cm寬. 且 Aspose.Words 支持這種表格的概念。
在 HTML 中,表格具有完全不同的結構:每一行有相同的數量,以及(對於這個任務很重要)每一個細胞都有對應的欄的寬度,所有同一個欄中的細胞都一樣。 如果 HorizontalMerge 和 VerticalMerge 返回錯誤值,則使用以下範例程式碼:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-.NET.git. | |
Document doc = new Document(MyDir + "Table with merged cells.docx"); | |
SpanVisitor visitor = new SpanVisitor(doc); | |
doc.Accept(visitor); |
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-.NET.git. | |
/// <summary> | |
/// Helper class that contains collection of rowinfo for each row. | |
/// </summary> | |
public class TableInfo | |
{ | |
public List<RowInfo> Rows { get; } = new List<RowInfo>(); | |
} | |
/// <summary> | |
/// Helper class that contains collection of cellinfo for each cell. | |
/// </summary> | |
public class RowInfo | |
{ | |
public List<CellInfo> Cells { get; } = new List<CellInfo>(); | |
} | |
/// <summary> | |
/// Helper class that contains info about cell. currently here is only colspan and rowspan. | |
/// </summary> | |
public class CellInfo | |
{ | |
public CellInfo(int colSpan, int rowSpan) | |
{ | |
ColSpan = colSpan; | |
RowSpan = rowSpan; | |
} | |
public int ColSpan { get; } | |
public int RowSpan { get; } | |
} | |
public class SpanVisitor : DocumentVisitor | |
{ | |
/// <summary> | |
/// Creates new SpanVisitor instance. | |
/// </summary> | |
/// <param name="doc"> | |
/// Is document which we should parse. | |
/// </param> | |
public SpanVisitor(Document doc) | |
{ | |
mWordTables = doc.GetChildNodes(NodeType.Table, true); | |
// We will parse HTML to determine the rowspan and colspan of each cell. | |
MemoryStream htmlStream = new MemoryStream(); | |
Aspose.Words.Saving.HtmlSaveOptions options = new Aspose.Words.Saving.HtmlSaveOptions | |
{ | |
ImagesFolder = Path.GetTempPath() | |
}; | |
doc.Save(htmlStream, options); | |
// Load HTML into the XML document. | |
XmlDocument xmlDoc = new XmlDocument(); | |
htmlStream.Position = 0; | |
xmlDoc.Load(htmlStream); | |
// Get collection of tables in the HTML document. | |
XmlNodeList tables = xmlDoc.DocumentElement.GetElementsByTagName("table"); | |
foreach (XmlNode table in tables) | |
{ | |
TableInfo tableInf = new TableInfo(); | |
// Get collection of rows in the table. | |
XmlNodeList rows = table.SelectNodes("tr"); | |
foreach (XmlNode row in rows) | |
{ | |
RowInfo rowInf = new RowInfo(); | |
// Get collection of cells. | |
XmlNodeList cells = row.SelectNodes("td"); | |
foreach (XmlNode cell in cells) | |
{ | |
// Determine row span and colspan of the current cell. | |
XmlAttribute colSpanAttr = cell.Attributes["colspan"]; | |
XmlAttribute rowSpanAttr = cell.Attributes["rowspan"]; | |
int colSpan = colSpanAttr == null ? 0 : int.Parse(colSpanAttr.Value); | |
int rowSpan = rowSpanAttr == null ? 0 : int.Parse(rowSpanAttr.Value); | |
CellInfo cellInf = new CellInfo(colSpan, rowSpan); | |
rowInf.Cells.Add(cellInf); | |
} | |
tableInf.Rows.Add(rowInf); | |
} | |
mTables.Add(tableInf); | |
} | |
} | |
public override VisitorAction VisitCellStart(Cell cell) | |
{ | |
int tabIdx = mWordTables.IndexOf(cell.ParentRow.ParentTable); | |
int rowIdx = cell.ParentRow.ParentTable.IndexOf(cell.ParentRow); | |
int cellIdx = cell.ParentRow.IndexOf(cell); | |
int colSpan = 0; | |
int rowSpan = 0; | |
if (tabIdx < mTables.Count && | |
rowIdx < mTables[tabIdx].Rows.Count && | |
cellIdx < mTables[tabIdx].Rows[rowIdx].Cells.Count) | |
{ | |
colSpan = mTables[tabIdx].Rows[rowIdx].Cells[cellIdx].ColSpan; | |
rowSpan = mTables[tabIdx].Rows[rowIdx].Cells[cellIdx].RowSpan; | |
} | |
Console.WriteLine("{0}.{1}.{2} colspan={3}\t rowspan={4}", tabIdx, rowIdx, cellIdx, colSpan, rowSpan); | |
return VisitorAction.Continue; | |
} | |
private readonly List<TableInfo> mTables = new List<TableInfo>(); | |
private readonly NodeCollection mWordTables; | |
} |
將之轉換為水平方向合併的細胞
有時不可能辨別哪些細胞是合併的,因為有些較新版本的 Microsoft Word 在水平合併細胞時不再使用合併標誌。 不過,在細胞根據其寬度從水平方向合併的狀況中,Aspose.Words提供ConvertToHorizontallyMergedCells
方法來轉換細胞。 此方法簡單地將表格轉換並依需要新增新細胞。
以下範例顯示上述方法在實際操作上的應用:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-.NET.git. | |
Document doc = new Document(MyDir + "Table with merged cells.docx"); | |
Table table = doc.FirstSection.Body.Tables[0]; | |
// Now merged cells have appropriate merge flags. | |
table.ConvertToHorizontallyMergedCells(); |