2012-07-10 49 views
6

Estoy usando Epplus para representar una hoja de cálculo de Excel en HTML. Hasta ahora todo va muy, muy bien, excepto por una cosa ... que abarca celdas combinadas. Parece que no puedo entender bien la lógica. Pensé que lo tiraría allí para ver cómo la comunidad lidiaría con eso. Aquí está mi código hasta ahora.Manejar celdas combinadas en la conversión de Epplus Excel a HTML

public String ParseExcelStamps(String FileName) 
{ 
    FileInfo theFile = new FileInfo(FileName); 
    String html = ""; 
    using (ExcelPackage xlPackage = new ExcelPackage(theFile)) 
    { 
     var workbook = xlPackage.Workbook; 
     if (workbook != null) 
     { 
      for (int j = 1; j <= workbook.Worksheets.Count; j++) 
      { 
       Tab tab = new Tab(); 
       html+= "<table style='border-collapse: collapse;font-family:arial;'><tbody>"; 
       var worksheet = workbook.Worksheets[j]; 
       tab.Title = worksheet.Name; 
       if (worksheet.Dimension == null) { continue; } 
       int rowCount = 0; 
       int maxColumnNumber = worksheet.Dimension.End.Column; 
       var convertedRecords = new List<List<string>>(worksheet.Dimension.End.Row); 
       var excelRows = worksheet.Cells.GroupBy(c => c.Start.Row).ToList(); 
       excelRows.ForEach(r => 
       { 
        rowCount++; 
        html += String.Format("<tr>"); 
        var currentRecord = new List<string>(maxColumnNumber); 
        var cells = r.OrderBy(cell => cell.Start.Column).ToList(); 
        Double rowHeight = worksheet.Row(rowCount).Height; 
        for (int i = 1; i <= maxColumnNumber; i++) 
        { 
         var currentCell = cells.Where(c => c.Start.Column == i).FirstOrDefault(); 

         //look aheads for colspan and rowspan 
         ExcelRangeBase previousCellAbove = null; 
         ExcelRangeBase previousCell = null; 
         ExcelRangeBase nextCell = null; 
         ExcelRangeBase nextCellBelow = null; 
         try { previousCellAbove = worksheet.Cells[rowCount-1, i]; }catch (Exception) { } 
         try { previousCell = worksheet.Cells[rowCount, (i - 1)]; }catch (Exception) { } 
         try { nextCell = worksheet.Cells[rowCount, (i + 1)]; }catch (Exception) { } 
         try { nextCellBelow = worksheet.Cells[rowCount+1, i]; }catch (Exception) { } 

         if ((previousCell != null) && (previousCell.Merge) && (currentCell != null) && (currentCell.Merge)){continue;} 
         if ((previousCellAbove != null) && (previousCellAbove.Merge) && (currentCell != null)) {continue; } 

         if (currentCell == null) 
         { 
          html += String.Format("<td>{0}</td>", String.Empty); 
         } 
         else 
         { 
          int colSpan = 1; 
          int rowSpan = 1; 
          if ((nextCell != null) && (nextCell.Merge) && (currentCell.Merge)) { 
           colSpan = 2; 
           // Console.WriteLine(String.Format("{0} - {1}", currentCell.Address, nextCell.Address)); 
          } 

          if ((nextCellBelow != null) && (nextCellBelow.Merge) && (currentCell.Merge)) { 
           Console.WriteLine(String.Format("{0} - {1}", currentCell.Address, nextCellBelow.Address)); 
          } 

          html += String.Format("<td colspan={0} rowspan={1}>{2}</td>", colSpan, rowSpan, currentCell.Value); 
         } 
        } 
        html += String.Format("</tr>"); 
       }); 
       html += "</tbody></table>"; 
      }//worksheet loop 
     } 
    } 
    return html; 
} 
+0

La primera pregunta que haría es si es posible editar la hoja de cálculo de desinstalar las células. ¿Vale la pena intentarlo? –

+0

Realmente me gustaría resolver esto sin tener que editar la fuente, además de que no tengo el control de los documentos originales. – BigBadOwl

+0

El truco de esto es que cell.merge de Epplus solo devuelve un booleano. Cuando revisa una celda para ver si está fusionada, no puede decir si se fusionó con la celda antes, después, abajo o arriba y, por lo tanto, no puede determinar si se trata de un colspan o un rowspan. – BigBadOwl

Respuesta

13

Por lo que puedo decir, esto es exactamente lo que necesita. Lo que se estaba perdiendo era la propiedad MergedCells en la hoja de trabajo que enumera todas las celdas combinadas en la hoja.

Mi código maneja intervalos de filas, espacios entre columnas y ambos al mismo tiempo. Hice algunas pruebas con una hoja de cálculo que incluía la extensión de filas, columnas y filas/columnas. En todos los casos, funcionaron perfectamente.

Código

int colSpan = 1; 
int rowSpan = 1; 

//check if this is the start of a merged cell 
ExcelAddress cellAddress = new ExcelAddress(currentCell.Address); 

var mCellsResult = (from c in worksheet.MergedCells 
       let addr = new ExcelAddress(c) 
        where cellAddress.Start.Row >= addr.Start.Row && 
        cellAddress.End.Row <= addr.End.Row && 
        cellAddress.Start.Column >= addr.Start.Column && 
        cellAddress.End.Column <= addr.End.Column 
       select addr); 

if (mCellsResult.Count() >0) 
{ 
    var mCells = mCellsResult.First(); 

    //if the cell and the merged cell do not share a common start address then skip this cell as it's already been covered by a previous item 
    if (mCells.Start.Address != cellAddress.Start.Address) 
     continue; 

    if(mCells.Start.Column != mCells.End.Column) { 
     colSpan += mCells.End.Column - mCells.Start.Column; 
    } 

    if (mCells.Start.Row != mCells.End.Row) 
    { 
     rowSpan += mCells.End.Row - mCells.Start.Row; 
    } 
} 

//load up data 
html += String.Format("<td colspan={0} rowspan={1}>{2}</td>", colSpan, rowSpan, currentCell.Value); 
+0

Esto es brillante, funciona como un encanto. Es genial poder finalmente mostrar un documento de Excel como HTML. Pondré el código completo en GitHub pronto y publicaré el enlace aquí. Gracias Peter, tu solución vale 50 puntos y más. – BigBadOwl

+1

Asegúrese de poner un HtmlEncode alrededor de su currentCell.Value. Estoy bastante seguro de que, tal como está, un signo < or > causaría algunos problemas con el renderizado. – Peter

+2

+1, una mejora menor: siempre use 'Enumerable.Any()' en lugar de 'Enumerable.Count()'. El primero solo verifica si existe algún elemento, el último itera todos los elementos. Mejor: 'if (mCellsResult.Any())' –

Cuestiones relacionadas