【问题标题】:How to identify merged column and row in word table?如何识别单词表中合并的列和行?
【发布时间】:2017-02-01 06:09:23
【问题描述】:

我需要读取表格中的所有文本。通过使用下面的内容,我可以从表格中读取内容。 https://1drv.ms/w/s!Ah-Jh2Ok5SuHel9ZpB-V5DZ66DM 在这里我上传了一个包含表格的虚拟 Docx 文件。

foreach (Word.Row row in tb.Rows)
{
    foreach (Word.Cell cell in row.Cells)
    {
        string cellText = "";
        if (cell.Range != null)
        {
            cellText = cell.Range.Text.ToString().Trim().Replace("\r\a", "");
        }
    }
} 

但对于以下 3 种情况,我会遇到问题。

  • 案例 1:如表 1 所示,合并了两个列单元格。通过使用上面的代码,我能够读取所有值,但与其他行相比,它的迭代次数更少。在这里如何确定该行是否已合并单元格?
  • 案例2:如表2所示,这里合并了2行。迭代时,我从 tb.Rows 得到以下错误 无法访问此集合中的各个行,因为该表具有垂直合并的单元格。 这里如何判断一个表是否有合并行?
  • 案例 3:如表 3 所示。这里的表在表中。通过使用上面的代码,它只识别一个表和cellText = cell.Range.Text.ToString().Trim().Replace("\r\a", "");在此行的末尾执行cellText 获取当前单元格的所有值 + 内部表格单元格值。即 2abcd。在这里我如何迭代内部表?

【问题讨论】:

    标签: c# ms-word office-interop


    【解决方案1】:

    您可以通过查看表格的 XML 来检测单元格是否合并。我放在一起的这个例子分析了 XML 表,然后找到了相应的互操作单元。一切都变成了一个二维数组,然后打印出来。

    希望这是一个好的开始。

    using System;
    using System.Linq;
    using System.Xml.Linq;
    using Word = NetOffice.WordApi;
    
    namespace TableTest
    {
        class Program
        {
            static void Main(string[] args)
            {
                try
                {
                    using (var app = Word.Application.GetActiveInstance())
                    {
                        var document = app.ActiveDocument;
                        var documentTitle = $"* Tables in {document.Name} *";
                        Console.WriteLine(new string('*', documentTitle.Length));
                        Console.WriteLine(documentTitle);
                        Console.WriteLine(new string('*', documentTitle.Length));
                        Console.WriteLine();
    
    
                        for (int i = 1; i <= document.Tables.Count; i++)
                        {
                            var table = document.Tables[i];
    
                            var tableTitle = $"Table #{i}";
    
                            Console.WriteLine(tableTitle);
                            Console.WriteLine(new string('-', tableTitle.Length));
    
                            foreach (var cellInfo in CellInfo.GetInfosFromTable(table))
                            {
                                Console.WriteLine(" - " + cellInfo);
                            }
    
                            Console.WriteLine();
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
    
                Console.WriteLine();
                Console.WriteLine("Done executing!");
                Console.ReadLine();
            }
    
            class CellInfo
            {
                public Word.Cell Cell { get; set; }
                public XElement XmlCell { get; set; }
                public int XmlRow { get; set; }
                public int XmlColumn { get; set; }
                public int Row { get; set; }
                public int Column { get; set; }
                public XElement MergedHorizontallyWith { get; set; }
                public XElement MergedVerticallyWith { get; set; }
    
                public override string ToString()
                {
                    if (MergedHorizontallyWith == null && MergedVerticallyWith == null)
                    {
                        var range = Cell?.Range;
    
                        var interopText = string.Empty;
    
                        if (!Equals(range, null))
                        {
                            interopText = range.Text;
    
                            //\r\a marks the end of a cell, \r and \n are normal line breaks
                            interopText = interopText.Replace("\r\a", "<>").Replace("\r", "\\r").Replace("\n", "\\n");
    
                            //Remove the last cell ending marker (it's always there)
                            if (interopText.EndsWith("<>"))
                                interopText = interopText.Substring(0, interopText.Length - 2);
                        }
    
                        return $"xml: {Row}, {Column} (interop: {XmlRow}, {XmlColumn}): {XmlCell?.Value ?? string.Empty} = {interopText}";
                    }
                    else if (MergedHorizontallyWith != null)
                    {
                        return $"xml: {Row}, {Column} (interop: {XmlRow}, {XmlColumn}): MERGED HORIZONTALLY";
                    }
                    else if (MergedVerticallyWith != null)
                    {
                        return $"xml: {Row}, {Column} (interop: {XmlRow}, {XmlColumn}): MERGED VERTICALLY";
                    }
                    else
                    {
                        return $"xml: {Row}, {Column} (interop: {XmlRow}, {XmlColumn}): this shouldn't happen";
    
                    }
                }
    
                public static CellInfo[,] GetInfosFromTable(Word.Table table)
                {
                    var doc = XDocument.Parse(table.Range.XML);
    
                    CellInfo[,] cellInfos = GetInitializedArray(table);
    
                    var xmlTable = doc.Descendants().First(n => n.Name.LocalName == "tbl");
    
                    var rows = xmlTable.Elements().Where(e => e.Name.LocalName == "tr").ToArray();
    
                    for (int r = 0; r < rows.Length; r++)
                    {
                        var row = rows[r];
                        var rowCells = row.Elements().Where(e => e.Name.LocalName == "tc").ToArray();
                        var c = 0;
                        foreach (var rowCell in rowCells)
                        {
                            cellInfos[r, c].XmlCell = rowCell;
                            cellInfos[r, c].XmlRow = r;
                            cellInfos[r, c].XmlColumn = c;
    
                            var gridSpan = int.Parse(rowCell.Descendants().FirstOrDefault(d => d.Name.LocalName == "gridSpan")?.Attributes().FirstOrDefault(a => a.Name.LocalName == "val")?.Value ?? "1");
    
                            if (gridSpan > 1)
                            {
                                for (int i = 1; i < gridSpan; i++)
                                {
                                    cellInfos[r, c + i].MergedHorizontallyWith = rowCell;
                                    cellInfos[r, c + i].XmlRow = r;
                                    cellInfos[r, c + i].XmlColumn = c + i;
                                }
    
                            }
    
                            c += gridSpan;
                        }
                    }
    
                    ApplyVerticalMerges(cellInfos);
    
                    FindCorrespondingInteropCells(table, cellInfos);
    
                    return cellInfos;
                }
    
    
                private static CellInfo[,] GetInitializedArray(Word.Table table)
                {
                    var cellInfos = new CellInfo[table.Rows.Count, table.Columns.Count];
                    for (int r = 0; r < cellInfos.GetLength(0); r++)
                    {
                        for (int c = 0; c < cellInfos.GetLength(1); c++)
                        {
                            cellInfos[r, c] = new CellInfo();
                        }
                    }
    
                    return cellInfos;
                }
    
                private static void ApplyVerticalMerges(CellInfo[,] cellInfos)
                {
                    for (int r = 0; r < cellInfos.GetLength(0); r++)
                    {
                        for (int c = 0; c < cellInfos.GetLength(1); c++)
                        {
                            var cellInfo = cellInfos[r, c];
    
    
                            var vmerge = cellInfo.XmlCell?.Descendants().FirstOrDefault(d => d.Name.LocalName == "vmerge");
                            if (vmerge != null)
                            {
                                var isParent = (vmerge.Attributes().FirstOrDefault(a => a.Name.LocalName == "val")?.Value ?? string.Empty) == "restart";
    
                                if (isParent)
                                {
                                    MarkCellsBelow(cellInfos, r, c);
                                }
                            }
    
                        }
                    }
                }
    
                private static void MarkCellsBelow(CellInfo[,] cells, int parentR, int parentC)
                {
                    var parentCell = cells[parentR, parentC];
                    for (int r = parentR + 1; r < cells.GetLength(1); r++)
                    {
                        var cell = cells[r, parentC];
    
                        var vmerge = cell.XmlCell?.Descendants().FirstOrDefault(d => d.Name.LocalName == "vmerge");
                        if (vmerge == null) break;
    
                        var isParent = (vmerge?.Attributes().FirstOrDefault(a => a.Name.LocalName == "val")?.Value ?? string.Empty) == "restart";
                        if (isParent) break;
    
                        cell.MergedVerticallyWith = parentCell.XmlCell;
                    }
    
                }
    
                private static void FindCorrespondingInteropCells(Word.Table table, CellInfo[,] cellInfos)
                {
                    var interopRow = 1;
                    for (int r = 0; r < cellInfos.GetLength(0); r++)
                    {
                        var interopCol = 0;
                        for (int c = 0; c < cellInfos.GetLength(1); c++)
                        {
                            var cellInfo = cellInfos[r, c];
    
                            if (cellInfo.MergedVerticallyWith != null)
                            {
                                interopCol++;
                            }
    
                            else
                            {
                                interopCol++;
                                cellInfo.Row = interopRow;
                                cellInfo.Column = interopCol;
                                cellInfo.Cell = GetCell(cellInfo, table);
                            }
                        }
    
                        interopRow++;
                    }
                }
    
                private static Word.Cell GetCell(CellInfo cellInfo, Word.Table table)
                {
                    foreach (var cell in table.Range.Cells)
                    {
                        if (cell.NestingLevel == table.NestingLevel)
                        {
                            if (cellInfo.Column == cell.ColumnIndex && cellInfo.Row == cell.RowIndex)
                            {
                                return cell;
                            }
                        }
                    }
    
                    return null;
                }
    
            }
    
        }
    }
    

    如果您只想知道一个表格是否包含合并单元格,您可以使用 Table.Uniform 属性。

    【讨论】:

      猜你喜欢
      • 2021-04-18
      • 2021-08-05
      • 2012-02-28
      • 1970-01-01
      • 1970-01-01
      • 2015-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多