excel OpenXML SDK - C# -计算链/将单元格标记为已编辑以强制在Open for移动的/Web上进行完全重新计算

fnvucqvd  于 2022-12-20  发布在  C#
关注(0)|答案(1)|浏览(156)

用例

我有一个Windows服务,它利用OpenXML sdk每天将表格定价数据写入Excel工作表。此报告可通过文件共享获得,也可作为附件发送。工作簿中还有其他几个工作表,其中包含引用通过OpenXML写入的数据的公式,以便进行可视化等。
如果未设置ForceFullCalculation属性,则公式在打开时不会更新,直到对公式相关性链中最靠下的单元格进行了编辑。
第一个月
此行解决了所有计算问题,因为所有公式在打开.. (如果在Excel桌面客户端中打开)时都会重新计算。

问题

在移动的Office应用程序、移动Excel应用程序、Excel Online和电子邮件预览应用程序上,只有在对触发重新计算的单元格进行编辑后,才会计算公式。所有公式均显示为N/A。

努力解决

对于移动的/Web/附件查看器,尚未解决此问题的事情:

  • 强制满负荷计算=真
  • 保存时计算=真
  • 计算模式=计算模式值.自动(Excel自动计算模式)
  • 正在删除CalculaitonChainPart元素。
  • 解压缩xlsx并从CalcChain文件中删除/编辑/移除单元格数据。
  • 通过非桌面Excel版本手动点击“计算工作簿”/“计算单元格”/“计算表”按钮
  • 在打开之前将文件发布到sharepoint online / office 365

什么“解决”了问题

  • 在分发工作表之前,在桌面版本的Excel上打开文件。这可以工作,但需要手动干预,这对于基于服务的应用程序不实用。
  • 在非桌面版本的Excel上打开文件,并在“公式树”中编辑单元格值。

目标

我希望有人能对此问题提供更多的见解,并说明使用OpenXML sdk是否可以解决这个问题。如果非桌面版本的excel不考虑ForceFullCalculation属性,是否可以通过XML使excel将单元格视为脏单元格并运行重新计算?

理想情景

我的服务创建了一个新的xlsx文件,并通过OpenXML SDK写入数据。然后,该文件被写出到一个文件共享,并作为电子邮件附件发送。收件人可以在桌面、移动的、网络以及不太重要的附件查看器上打开工作簿,并看到公式的结果,而不是N/A,直到找到要编辑的特定单元格,以欺骗Excel更新所有值。

附加注解

我痛苦地意识到,这是对Excel/OpenXML的误用-Tableau/Power BI这样的报表解决方案更适合我的用例,但这是最初由业务用户创建的工作表,并且多年来一直是一个人每天更新的手动工作量。自动化工作表的日常数据输入和分发允许我们的用户继续使用引用此工作表的其他工作表,而不It“”我们不需要花很大力气来重新定义整个过程,因为它本质上可以归结为人们每天早上都要查看的报告。
最终,我们会有一个更好的解决方案,但这是一个缓慢和持续的努力。我希望有一个前进的道路,满足现在的需要。
感谢阅读和任何帮助:)

样品代码

using System;
using System.IO;
using System.Threading.Tasks;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;

namespace SampleProject.Services {

    public interface IExportPriceDataService
    {
        public Task CreateDailyWorkbookAsync();
    }

    public class ExportPriceDataService : IExportPriceDataService
    {
        private readonly IDistributionService _distributionService;
        
        public ExportPriceDataService(IDistributionService distributionService)
        {
            _ditributionService = distributionService; 
        }

        public async Task CreateDailyWorkbookAsync()
        {
            // Template file - contains constant data (headers, formulas, etc.) but no data.
            var templateFile = "C:\\DailyExportTemplate.xlsx"; 
            var filename = "C:\\DailyExport.xlsx";
            
            // Load price data from the database - assume already shaped into the PriceData data structure (classes below).
            List<PriceData> priceData = await fetchTabularPriceDataAsync(DateTime.Now.Year, DateTime.Now.Month);

            // Delete previous day exports.
            if (File.Exists(filename))
            {
                File.Delete(filename);
            }

            // Create today's copy.
            File.Copy(templateFile, filename);

            // Populate today's copy with data. 
            using (SpreadsheetDocument document = SpreadsheetDocument.Open(filename))
            {
                if (document == null) return; 
                var workbookPart = document.WorkbookPart; 

                // Extension method to get the worksheet part by the sheet name. 
                var worksheetPart = workbookPart.GetWorksheetPartByName("PriceDataSheet");

                writePriceData(worksheetPart, priceData); 

                document.WorkbookPart.Workbook.CalculationProperties.CalculationMode = CalculateModeValues.Auto;
                document.WorkbookPart.Workbook.CalculationProperties.CalculationOnSave = true;
                document.WorkbookPart.Workbook.CalculationProperties.ForceFullCalculation = true;

                document.Save(); 
            }

            _distributionService.PublishToSharepointOnline(filename);
            _distributionService.SendAsEmailAttachment(filename); 
        }

        private void writePriceData(WorksheetPart worksheetPart, List<PriceData> priceData)
        {
            foreach (var item in priceData)
            {
                // Match ItemName with constant column headers in the sheet.
                var col = GetColumnForItem(); 
                
                // Header row is always row 1. 
                var row = 1; 
                
                // Custom extension methods for creating and writing to cells. 
                // Internal code from: https://learn.microsoft.com/en-us/office/open-xml/how-to-insert-text-into-a-cell-in-a-spreadsheet
                worksheetPart.InsertCellInWorksheet(col, row)
                             .SetCellType(CellValues.String)
                             .SetCellValue(new CellValue(item.ItemName));
                
                foreach (var price in Item.Prices)
                {
                    // Row # matches up with the day plus offset of 1 for the header row. 
                    row = Date.day + 1; 

                    worksheetPart.InsertCellInWorksheet(col, row)
                                 .SetCellType(CellValues.Number)
                                 .SetCellValue(new CellValue(price.Price));
                }
            }
        }
    }

    public class PriceData
    {
        public string ItemName {get; set;}
        public List<PriceDate> Prices { get; set; }
    }

    public class PriceDate
    {
        public DateTime Date {get; set;}
        public decimal Price {get; set;}
    }
}
idfiyjo8

idfiyjo81#

尝试使用属性.工作簿.计算属性.FullCalculationOnLoad=true

相关问题