【问题标题】:Having issue with writing text to specific Excel Cell using OpenXml使用 OpenXml 将文本写入特定 Excel 单元格时遇到问题
【发布时间】:2017-05-23 12:02:39
【问题描述】:

我想要达到的效果如下图所示:

我遇到的结果和问题如下图所示:

这是我的代码生成的结果文件,应该有预期的内容。

单击“是”按钮后的窗口提示。

我的运行代码如下:

主要方法:

public static void Main(string[] args)
{
    WriteExcelService writeExcelService = new WriteExcelService();
    Dictionary<string, List<string>> contentList = new Dictionary<string, List<string>>
    {
        { "en-US",new List<string> (new string[] { "Dummy text 01","Dummy text 02"}) },
        { "es-ES",new List<string> (new string[] { "Texto ficticio 01", "Texto ficticio 02"}) }
    };
    string inputFile = @"C:\{username}\Desktop\Valentines_Day.xlsx";
    string sheetName = "Copy";

    writeExcelService.WriteValueToCell(inputFile, sheetName, contentList);
}

WriteValueToCell 方法:

char columnName = 'I';
        uint rowNumber = 1;
        foreach (var keys in contentList.Keys)
        {
            foreach (var value in contentList.Where(v => v.Key == keys).SelectMany(v => v.Value))
            {
                string cellAddress = String.Concat(columnName, rowNumber);
                this.Write(filepath, sheetName, value, cellAddress, rowNumber);
                int tempColumn = (int)columnName;
                columnName = (char)++tempColumn;
            }
            columnName = 'I';
            ++rowNumber;
        }

写法:

private void Write(string filepath, string sheetName, string value, string cellAddress,uint rowNumber)
{
    // Create a spreadsheet document by supplying the filepath.
    // By default, AutoSave = true, Editable = true, and Type = xlsx.
    using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(filepath, SpreadsheetDocumentType.Workbook))
    {
        //writeExcelService.WriteValueToCell(outputFilePath, sheetName, cellAddress, value.Value);
        // Add a WorkbookPart to the document.
        WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
        workbookpart.Workbook = new Workbook();

        // Add a WorksheetPart to the WorkbookPart.
        WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
        worksheetPart.Worksheet = new Worksheet(new SheetData());

        // Add Sheets to the Workbook.
        Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets());

        // Append a new worksheet and associate it with the workbook.
        Sheet sheet = new Sheet() { Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart), SheetId = 1, Name = sheetName };
        sheets.Append(sheet);

        // Get the sheetData cell table.
        SheetData sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>();

        // Add a row to the cell table.
        Row row;
        row = new Row() { RowIndex = rowNumber };
        sheetData.Append(row);

        // In the new row, find the column location to insert a cell.
        Cell refCell = null;
        foreach (Cell cell in row.Elements<Cell>())
        {
            if (string.Compare(cell.CellReference.Value, cellAddress, true) > 0)
            {
                refCell = cell;
                break;
            }
        }

        // Add the cell to the cell table.
        Cell newCell = new Cell() { CellReference = cellAddress };
        row.InsertBefore(newCell, refCell);

        // Set the cell value to be a numeric value.
        newCell.CellValue = new CellValue(value);
        newCell.DataType = new EnumValue<CellValues>(CellValues.Number);
    }
}

我的问题是:

我的代码执行,但是一旦打开结果文件,它会提示我上面发布的窗口,并且文件是空的。如果我调试代码以逐个插入内容列表,它可以正确写入单元格@ 987654330@ 或J2。由于我的代码为每个列表内容创建SpreadsheetDocument,因此我在下面的代码中更改了SpreadsheetDocument的创建方法:

using (SpreadsheetDocument spreadsheetDocument = File.Exists(filePath) ?
SpreadsheetDocument.Open(filepath, true) : 
SpreadsheetDocument.Create(filepath, SpreadsheetDocumentType.Workbook))
   {

   }

但我遇到了异常

此父级只允许该类型的一个实例。

有人可以帮我解决这个问题吗?

提前欣赏。

【问题讨论】:

  • 您想插入文本对吗?那为什么你的代码中有这一行:newCell.DataType = new EnumValue&lt;CellValues&gt;(CellValues.Number);?
  • @FortyTwo 我的代码有误。我修改了算法并将行号作为参数传递,而不是硬编码。

标签: c# excel openxml vba


【解决方案1】:

我自己想出了解决方案。我已经传递了字符串内容列表并将它们全部写入相应的单元格,然后关闭了SpreadSheetDocument。这样SpreadSheetDocument就可以创建一次。工作代码如下:

public void WriteValueToCell(string filepath, string sheetName, Dictionary<string, List<string>> contentList)
{
    // Create a spreadsheet document by supplying the filepath.
    // By default, AutoSave = true, Editable = true, and Type = xlsx.
    using (SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(filepath, SpreadsheetDocumentType.Workbook, true))
    {
        // Add a WorkbookPart to the document.
        WorkbookPart workbookpart = spreadsheetDocument.AddWorkbookPart();
        workbookpart.Workbook = new Workbook();

        //Add a WorkbookStylesPart to the workbookpart
        WorkbookStylesPart stylesPart = workbookpart.AddNewPart<WorkbookStylesPart>();
        stylesPart.Stylesheet = new Stylesheet();

        // Add a WorksheetPart to the WorkbookPart.
        WorksheetPart worksheetPart = workbookpart.AddNewPart<WorksheetPart>();
        worksheetPart.Worksheet = new Worksheet(new SheetData());

        // Add Sheets to the Workbook.
        Sheets sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild<Sheets>(new Sheets());

        // Append a new worksheet and associate it with the workbook.
        Sheet sheet = new Sheet() { Id = spreadsheetDocument.WorkbookPart.GetIdOfPart(worksheetPart), SheetId = 1, Name = sheetName };
        sheets.Append(sheet);

        // Get the sheetData cell table.
        SheetData sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>();

        char columnName = 'I';
        uint rowNumber = 1;
        foreach (var keys in contentList.Keys)
        {
            foreach (var value in contentList.Where(v => v.Key == keys).SelectMany(v => v.Value))
            {
                string cellAddress = String.Concat(columnName, rowNumber);
                // Add a row to the cell table.
                Row row;
                row = new Row() { RowIndex = rowNumber };
                sheetData.Append(row);

                // In the new row, find the column location to insert a cell.
                Cell refCell = null;
                foreach (Cell cell in row.Elements<Cell>())
                {
                    if (string.Compare(cell.CellReference.Value, cellAddress, true) > 0)
                    {
                        refCell = cell;
                        break;
                    }
                }

                // Add the cell to the cell table.
                Cell newCell = new Cell() { CellReference = cellAddress };
                row.InsertBefore(newCell, refCell);
                // Set the cell value to be a numeric value.
                newCell.CellValue = new CellValue(value);
                newCell.DataType = new EnumValue<CellValues>(CellValues.String);

                int tempColumn = (int)columnName;
                columnName = (char)++tempColumn;
            }
            columnName = 'I';
            ++rowNumber;
        }
    }
}

主要方法:

public static void Main(string[] args)
{
    WriteExcelService writeExcelService = new WriteExcelService();
    Dictionary<string, List<string>> contentList = new Dictionary<string, List<string>>
    {
        { "en-US",new List<string> (new string[] { "Dummy text 01","Dummy text 02"}) },
        { "es-ES",new List<string> (new string[] { "Texto ficticio 01", "Texto ficticio 02"}) }
    };
    string inputFile = @"C:\{username}\Desktop\Valentines_Day.xlsx";
    string sheetName = "Copy";

    writeExcelService.WriteValueToCell(inputFile, sheetName, contentList);
}

【讨论】:

  • 这不能回答你自己的问题!您询问插入单元格并回答有关 SpreadSheetDocument 被创建一次的问题?然后代理你的答案?
  • 使用块确保spreadsheetDocument 只创建一次。您不能拥有多个同名文件。可以吗?
  • 确实如此。我曾经将 Cell 文本编写过程放入 foreach 循环中。因此,自从遇到 SpreadSheetDocument 创建问题以来,我已经更改了算法以处理 Using 语句中所有单元格的文本写入部分。现在代码创建了一次SpreadSheetDocument,用相应的字符串填充所有预期的单元格并关闭文档。我已经达到了我想要的。只是改变了我以前的想法。
  • 你是什么意思SpreadSheetDocument 被创建一次?只能创建一次!!我再重复一遍:多个文件不能同名!
  • 我在回答中添加了主要方法。代码结果告诉你一切。
【解决方案2】:

excel 中的字符串保存在 sharedStringTable 下。插入字符串时,添加字符串或引用 sharedStringTable 中的字符串很重要。此外,您需要为单元格提供正确的DataType。在您的代码中,您将所有值作为数字插入:

// Set the cell value to be a numeric value.
newCell.CellValue = new CellValue(value);
newCell.DataType = new EnumValue<CellValues>(CellValues.Number);

要插入字符串,我建议您在创建新单元格后使用以下方法:

private SpreadsheetDocument _spreadSheet;
private WorksheetPart _worksheetPart;
..
..
private void UpdateCell(Cell cell, DataTypes type, string text)
{
    if (type == DataTypes.String)
    {
        cell.DataType = CellValues.SharedString;

        if (!_spreadSheet.WorkbookPart.GetPartsOfType<SharedStringTablePart>().Any())
        {
            _spreadSheet.WorkbookPart.AddNewPart<SharedStringTablePart>();
        }

        var sharedStringTablePart = _spreadSheet.WorkbookPart.GetPartsOfType<SharedStringTablePart>().First();
        if (sharedStringTablePart.SharedStringTable == null)
        {
            sharedStringTablePart.SharedStringTable = new SharedStringTable();
        }
        //Iterate through shared string table to check if the value is already present.
        foreach (SharedStringItem ssItem in sharedStringTablePart.SharedStringTable.Elements<SharedStringItem>())
        {
            if (ssItem.InnerText == text)
            {
                cell.CellValue = new CellValue(ssItem.ElementsBefore().Count().ToString());
                return;
            }
        }
        // The text does not exist in the part. Create the SharedStringItem.
        var item = sharedStringTablePart.SharedStringTable.AppendChild(new SharedStringItem(new Text(text)));
        cell.CellValue = new CellValue(item.ElementsBefore().Count().ToString());
    }
    else if (type == DataTypes.Number)
    {
        cell.CellValue = new CellValue(text);
        cell.DataType = CellValues.Number;
    }
    else if (type == DataTypes.DateTime)
    {

        cell.DataType = CellValues.Number;
        cell.StyleIndex = Convert.ToUInt32(_dateStyleIndex);

        DateTime dateTime = DateTime.Parse(text);
        double oaValue = dateTime.ToOADate();
        cell.CellValue = new CellValue(oaValue.ToString(CultureInfo.InvariantCulture));
    }
    _worksheetPart.Worksheet.Save();
}

【讨论】:

  • 即使DataType 是数字,我的代码也会将内容写入单元格,无论如何我已将类型更改为SharedString。但现在问题是我的代码创建了SpreadsheetDocument 的新实例来将内容写入每个单元格。我添加了File.Exists(filePath) 检查,但它仍然每次都会创建 Excel 文件。不知道怎么解决。
  • 您可能忽略了一些小问题。也许将逻辑与 if 块分开,而不是使用三元运算符。找到错误后,您可以恢复使用三元运算符。
  • 我试过if..else语句代替三元运算符,结果和三元运算符一样。
  • 它在哪里创建新文件?新文件的名称是什么?
  • 它在using 语句中创建新文件。您可以检查我的 Main 方法以获取文件名。我有一个更好的想法如何解决这个问题。我会尽快发布我自己的答案。感谢您的付出。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多