Open xml 操作Excel 透视表(Pivot table)-- 实现Excel多语言报表

我的一个ERP项目中,客户希望使用Excel Pivot table 做分析报表。 ERP 从数据库中读出数据,导出到Excel中的数据源表(统一命名为Data),刷新Pivot table!

客户还希望对Excel报表提供多语言支持, 根据用户的语言生成不同版本的Excel文件。

经过不断尝试,终于成功完成该任务, 本篇简要描述这个任务涉及到的知识点。

把一个包含透视表及透视图的Excel .xlsx文件重命名为.zip 文件,然后解压缩到某个文件夹下,就可以看到Excel是如何定义透视表及透视图了, 如下图所示,pivotTables 定义了透视表中行、列及数据字段等, PivotCache 中则定义了Pivot table 的数据源、字段匹配,以及缓存了上一次打开的数据

                       

                    (Excel文件结构)

 

                       (pivotTables\pivotTable1.xml截图)

    (pivotCache\pivotCacheDefinition1.xml 截图)

 

根据以上描述, 在导入数据后. 还需要完成以下步骤:

  1. 重新设置透视表 的数据源到数据区域.

重新设置Pivot table 数据源的代码如下:    

 1         //其中sheetName为作为数据源的工作表名,lastReference为数据源中最后一个单元格的引用名,比如最后一列为AG,共10行则为AG10
 2     public static void SetPivotSource(WorkbookPart wbPart, string sheetName, string lastReference)
 3         {
 4             var pivottableCashes = wbPart.PivotTableCacheDefinitionParts;
 5             foreach (PivotTableCacheDefinitionPart pivottablecachePart in pivottableCashes)
 6             {
 7                 pivottablecachePart.PivotCacheDefinition.CacheSource.RemoveAllChildren();
 8 //设置Pivot tabla的数据源为A1:lastReference
 9                 pivottablecachePart.PivotCacheDefinition.CacheSource.Append(new WorksheetSource() {
10                     Sheet = sheetName, Reference = new StringValue("A1:" + lastReference) });
11             }
12         }

 

//假设Data表格中的最后一列的Reference为AG,总共有100行(加上列头行共101行),则导入数据后调用 
using (SpreadsheetDocument document = SpreadsheetDocument.Open(rawFileName, true))

            {

                WorkbookPart wbPart = document.WorkbookPart;

                                SetPivotSource(wbPart,"data","AG101");

                    }

 

2.  翻译Excel 数据源表字段名

也就是翻译及修改Data表格中第一行的单元格内容

 

  public static void UpdateCellValue(WorkbookPart wbPart, Cell theCell,string newValue)
        {
            string value = theCell.InnerText;
            if (theCell.DataType != null)
            {
                switch (theCell.DataType.Value)
                {
                    case CellValues.SharedString:
                        var stringTable = wbPart.GetPartsOfType<SharedStringTablePart>().FirstOrDefault();
                        if (stringTable != null)
                        {
                            var ele = stringTable.SharedStringTable.ElementAt(int.Parse(value));
                            ele.RemoveAllChildren();
                            ele.Append(new DocumentFormat.OpenXml.Spreadsheet.Text(newValue));
                        }
                        break;

                    case CellValues.Boolean:
                        if (string.Compare(value,"FALSE",true) ==0)
                        {
                            theCell.InnerXml = "1";
                        }
                        else
                        {
                            theCell.InnerXml = "0";
                        }
                        break;
                    default:
                        theCell.InnerXml = newValue;
                        break;
                }
            }
        }

   using (SpreadsheetDocument document = SpreadsheetDocument.Open(rawFileName, true))
            {
                WorkbookPart wbPart = document.WorkbookPart;
                
                var dataSheet = wbPart.Workbook.Descendants<Sheet>().FirstOrDefault(c => string.Compare(c.Name, "Data",true)==0);
                WorksheetPart worksheetPart = (WorksheetPart)wbPart.GetPartById(dataSheet.Id);
                var headerRow = worksheetPart.Worksheet.GetFirstChild<SheetData>().Elements<Row>().
                           FirstOrDefault(c => c.RowIndex == 1);
                var cells = headerRow.Elements<Cell>().ToList();
                foreach (var cell in cells)
                {
                    var rawText = ExcelHelper.GetCellValue(wbPart, cell);
                    ExcelHelper.UpdateCellValue(wbPart, cell, _translator.Translate(rawText));
                    //cell.CellValue = new CellValue(_translator.Translate(rawText));
                    //cell.DataType = new EnumValue<CellValues>(CellValues.String);
                }
                worksheetPart.Worksheet.Save();
    }

 

3.  翻译pivotCacheDefinition缓存区的字段定义。

 using (SpreadsheetDocument document = SpreadsheetDocument.Open(rawFileName, true))
            {
                WorkbookPart wbPart = document.WorkbookPart;
            var pivottableCashes = wbPart.PivotTableCacheDefinitionParts;
                foreach (PivotTableCacheDefinitionPart pivottablecachePart in pivottableCashes)
                {
                    pivottablecachePart.PivotCacheDefinition.RefreshOnLoad = true;
                    var pivotCacheFields = pivottablecachePart.PivotCacheDefinition.CacheFields;
                    foreach (OpenXmlElement pivotCacheField in pivotCacheFields)
                    {
                        OpenXmlAttribute nameEle = pivotCacheField.GetAttribute("name", "");
                        nameEle.Value = _translator.Translate(nameEle.Value);
                        pivotCacheField.SetAttribute(nameEle);
                    }   
                }
    }

 

4.  翻译Pivot Table 透视表定义区域的数据字段名,以及图表区的数据源表名

 

 //sheet为Sheet类型对象
            oxPart = wbPart.GetPartById(sheet.Id);
                    //Translate Pivot table data(numeric) field defination, such as "Sum of [Vat...]"
                    if (oxPart.ContentType.Contains("worksheet"))
                    {
                        wsP = (WorksheetPart)oxPart;
                        tbDefParts = wsP.PivotTableParts;
                        foreach (PivotTablePart ptPart in tbDefParts)
                        {
                            dataFileds = ptPart.PivotTableDefinition.DataFields;
                            foreach (DataField df in dataFileds)
                            {
                                if (df.Name.Value.StartsWith(SUM_OF))
                                {
                                    df.Name = new StringValue(_translator.Translate(SUM_OF) + " " +
                                        _translator.Translate(df.Name.Value.Replace(SUM_OF, "").Trim()));
                                }
                            }
                        }
                    }

 var index =  rawFileName.LastIndexOf(@"\");
                var filename = rawFileName.Substring(index+1);
                foreach(ChartsheetPart cspart in wbPart.ChartsheetParts)
                {
                    var chartparts = cspart.DrawingsPart.ChartParts ;
                    foreach(ChartPart cp in chartparts)
                    {
                        PivotSource pivotSource =  cp.RootElement.OfType<PivotSource>().First();
                        string originalName = pivotSource.PivotTableName.InnerText;
                        Regex reg = new Regex(@"^[[]([^]]+)[]]([^!]+)!(.*)$");
                        var matches = reg.Matches(originalName);
                        if (matches.Count > 0 && matches[0].Groups.Count >3)
                        {
                            string newName = string.Format("[{0}]{1}!{2}", filename,
                                _translator.Translate(matches[0].Groups[2].Value), matches[0].Groups[3].Value);
                            pivotSource.PivotTableName = new PivotTableName(newName);
                        }
                    }

 

5. 翻译表格名, 需要翻译所有除了Data表外的工作表名。

  foreach (Sheet sheet in sheets)
                {
                    if (string.Compare(sheet.Name, "data", true) != 0)
                    {
                        var translatedName = _translator.Translate(sheet.Name);
                        if (!string.IsNullOrEmpty(translatedName) && translatedName.Length > 30)
                        {
                            translatedName = translatedName.Substring(0, 30);
                        }
                        sheet.Name = translatedName;
                    }
        }

 

 

posted @ 2013-07-29 17:54  Blue Bird  阅读(4447)  评论(4编辑  收藏  举报