我有带有列标题的宏Excel电子表格,有些列是计算列。
我需要做的是从数据库中获取数据并填充此文件。
DataTable我在查询后检索的所有列都不像Excel文件那样具有计算列。
所以我需要将数据表中的列映射到Excel文件并加载数据。 我需要确保在加载新数据之前从文件中删除现有数据,因为这个文件需要每周创建一次。
我从未在OpenXML Document和EPPlus中工作过。
尝试#1:使用EPPlus
private static void OtehrMethod(DataTable dataTable, string filePath)
{
// using EPPlus
var package = new ExcelPackage(new FileInfo(filePath));
ExcelWorksheet workSheet = package.Workbook.Worksheets["MySheet"];
foreach (DataRow row in dataTable.Rows)
{
int i = 1;
object cellValue = workSheet.Cells[2, i].Value;
workSheet.Cells[1, 1].Value = Conver.ToInt(row["Id"]);
// break;
//workSheet.Cells[2, i].Value =row["First_Name"].ToString();
//workSheet.Cells[3, i].Value = row["Last_Name"].ToString();
//workSheet.Cells[4, i].Value = row["Job_Title"].ToString();
//workSheet.Cells[5, i].Value = row["Skills"].ToString();
i++;
}
package.Save();
}
尝试#2:使用Open XML
private static void SomeMethod()
{
string filePath = ConfigurationManager.AppSettings["ExcelFilePath"];
string workingSheetName = ConfigurationManager.AppSettings["WorkingSheetName"];
using (SpreadsheetDocument document = SpreadsheetDocument.Open(filePath, true))
{
// WorkbookPart workbook = document.WorkbookPart;
WorkbookPart workbookPart = document.WorkbookPart;
Workbook workbook = document.WorkbookPart.Workbook;
int sheetIndex = 0;
foreach (WorksheetPart worksheetpart in workbook.WorkbookPart.WorksheetParts)
{
Worksheet worksheet = worksheetpart.Worksheet;
string sheetName = workbookPart.Workbook.Descendants<Sheet>().ElementAt(sheetIndex).Name;
if (sheetName.ToUpper() == workingSheetName.ToUpper())
{
IEnumerable<Row> rows = worksheet.GetFirstChild<SheetData>().Descendants<Row>();
foreach (Row row in rows)
{
// How do I map Excel sheet column with data table column and insert the values into Excel ?
// Column["FirstName"] = DTRow["FirstName"]
//Column["LastName"] = DTRow["LastName"]
}
}
sheetIndex++;
}
}
// throw new NotImplementedException();
}
答案 0 :(得分:1)
更清楚。您所在的区域我没有亲自编码,但每张工作表的SheetData都是清除数据的位置。下面是Vincent Tan的另一个程序,它显示了如何在现有工作表中添加和更改数据:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml.Drawing.Spreadsheet;
namespace ExcelOpenXmlFromTemplate
{
class Program
{
static void Main(string[] args)
{
// Switch between an Excel file with the styles and stuff
// and an actual Excel template file.
// I suggest you check out the accompanying template files first
// to compare how the original and final files will look like.
//string sFileTemplate = "FinancialYearReport.xlsx";
string sFileTemplate = "FinancialYearReportTemplate.xltx";
string sFile = "FinancialYearReportFinal.xlsx";
if (File.Exists(sFile))
{
File.Delete(sFile);
}
if (sFileTemplate.ToLower().EndsWith(".xlsx"))
{
File.Copy(sFileTemplate, sFile);
}
else
{
// ends with .xltx
// Excel template files work differently. You can't work on it
// directly and then save the result to a different file.
// More details on this in the accompanying PDF.
byte[] ba = File.ReadAllBytes(sFileTemplate);
using (MemoryStream ms = new MemoryStream())
{
ms.Write(ba, 0, ba.Length);
using (SpreadsheetDocument xl = SpreadsheetDocument.Open(ms, true))
{
xl.ChangeDocumentType(SpreadsheetDocumentType.Workbook);
xl.Close();
}
File.WriteAllBytes(sFile, ms.ToArray());
}
}
try
{
BuildWorkbookFromTemplate(sFile);
Console.WriteLine("Program end");
Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
Console.ReadLine();
}
}
private static void BuildWorkbookFromTemplate(string filename)
{
string sSheetname = "FYReport";
// take note that we're opening a file, and not creating a file as before
using (SpreadsheetDocument xl = SpreadsheetDocument.Open(filename, true))
{
WorkbookPart wbp = xl.WorkbookPart;
// Get the worksheet with the required name.
// To be used to match the ID for the required sheet data
// because the Sheet class and the SheetData class aren't
// linked to each other directly.
Sheet s = null;
if (wbp.Workbook.Sheets.Elements<Sheet>().Count(nm => nm.Name == sSheetname) == 0)
{
// no such sheet with that name
xl.Close();
return;
}
else
{
s = (Sheet)wbp.Workbook.Sheets.Elements<Sheet>().Where(nm => nm.Name == sSheetname).First();
}
WorksheetPart wsp = (WorksheetPart)xl.WorkbookPart.GetPartById(s.Id.Value);
SheetData sd = (SheetData)wsp.Worksheet.GetFirstChild<SheetData>();
// We will update in 2 ways.
// 1) Get the required cell, do the updates on it, then save the worksheet.
// 2) Create a Cell with our data, then throw it to the SheetData, then save the worksheet.
// If the required cell already exists, we overwrite it. If not, we create it.
// The difference between the 2 is that, if your worksheet doesn't have the required cell,
// it becomes more difficult, because you'll have to also append it to the row.
// More details explained in the accompanying PDF.
// I left the saving of the worksheet out of the UpdateCell() function
// because I want to be consistent with the GetCell() version of updating.
// So wsp.Worksheet.Save(); is done after every cell modification.
// Otherwise I'd put it at the end of the update function.
// The code is written for you to understand what's going on, not for optimisation.
int iPreviousYear = DateTime.Now.Year - 1;
int iTheYearBeforeThat = iPreviousYear - 1;
Cell c = null;
// main title
c = GetCell(sd, "A", 1);
if (c != null)
{
if (c.DataType == CellValues.SharedString)
{
c.DataType = CellValues.String;
}
c.CellValue = new CellValue(string.Format("Financial Year Report For {0}/{1}", iTheYearBeforeThat, iPreviousYear));
// I was going to show you how to work with an existing value like so:
// c.CellValue = new CellValue(string.Format("{0} {1}/{2}", c.CellValue.Text.Trim(), iTheYearBeforeThat, iPreviousYear));
// But the existing value is a string, and Excel had kindly saved it into the SharedString table
// when I created the template file.
// The shared string concept makes things a little more interesting if you really want to work with it.
// For now, it's what-you-see-is-what-you-get, which is much simpler.
wsp.Worksheet.Save();
}
// description of the year before the last
c = GetCell(sd, "A", 4);
if (c != null)
{
if (c.DataType == CellValues.SharedString)
{
c.DataType = CellValues.String;
}
c.CellValue = new CellValue(string.Format("Year {0}", iTheYearBeforeThat));
wsp.Worksheet.Save();
}
// description of last year
// I'll show you the difference in code for the 2nd method of updating
c = new Cell();
c.DataType = CellValues.String;
c.CellReference = "A13";
c.CellValue = new CellValue(string.Format("Year {0}", iPreviousYear));
UpdateCell(sd, c, 13);
wsp.Worksheet.Save();
// We will update the revenue and operational costs of "the year before the last" FY.
// You would probably retrieve the values from a database and fill it here.
// I don't have any meaningful data anyway, so I'm gonna cheat with a randomiser...
// I'll use the 2nd method of updating for convenience.
Random rd = new Random();
int i;
for (i = 5; i <= 8; ++i)
{
// revenue values
c = new Cell();
c.DataType = CellValues.Number;
c.CellReference = "B" + i.ToString();
// range from 8.0 to 12.0
c.CellValue = new CellValue((rd.NextDouble() * 4.0 + 8.0).ToString("f2"));
UpdateCell(sd, c, (uint)i);
wsp.Worksheet.Save();
// operational cost values
c = new Cell();
c.DataType = CellValues.Number;
c.CellReference = "C" + i.ToString();
// range from 0.5 to 1.5
c.CellValue = new CellValue((rd.NextDouble() + 0.5).ToString("f2"));
UpdateCell(sd, c, (uint)i);
wsp.Worksheet.Save();
}
// Previous financial year's revenue and operational cost values
for (i = 14; i <= 17; ++i)
{
// revenue values
c = new Cell();
c.DataType = CellValues.Number;
c.CellReference = "B" + i.ToString();
// range from 9.0 to 14.0
// We've got to have increased revenue, right? :)
c.CellValue = new CellValue((rd.NextDouble() * 5.0 + 9.0).ToString("f2"));
UpdateCell(sd, c, (uint)i);
wsp.Worksheet.Save();
// operational cost values
c = new Cell();
c.DataType = CellValues.Number;
c.CellReference = "C" + i.ToString();
// range from 0.4 to 1.4
// We've got to have decreased costs, right? :)
c.CellValue = new CellValue((rd.NextDouble() + 0.4).ToString("f2"));
UpdateCell(sd, c, (uint)i);
wsp.Worksheet.Save();
}
// this section is to show you that UpdateCell() works for
// out of order cells in the same row. Go ahead and mix up
// the order of the 5 cells (A25, B25, C25, D25 and E25) in
// the code and check that they are still ordered correctly in the file.
c = new Cell();
c.DataType = CellValues.String;
c.CellReference = "B25";
c.CellValue = new CellValue("2nd");
UpdateCell(sd, c, 25u);
wsp.Worksheet.Save();
c = new Cell();
c.DataType = CellValues.String;
c.CellReference = "E25";
c.CellValue = new CellValue("5th");
UpdateCell(sd, c, 25u);
wsp.Worksheet.Save();
c = new Cell();
c.DataType = CellValues.String;
c.CellReference = "C25";
c.CellValue = new CellValue("3rd");
UpdateCell(sd, c, 25u);
wsp.Worksheet.Save();
c = new Cell();
c.DataType = CellValues.String;
c.CellReference = "A25";
c.CellValue = new CellValue("1st");
UpdateCell(sd, c, 25u);
wsp.Worksheet.Save();
c = new Cell();
c.DataType = CellValues.String;
c.CellReference = "D25";
c.CellValue = new CellValue("4th");
UpdateCell(sd, c, 25u);
wsp.Worksheet.Save();
// Credits!
c = new Cell();
c.DataType = CellValues.String;
c.CellReference = "A27";
// well, we wouldn't want upper management to think this
// financial report was *easy* to generate, right? Claim credit!
c.CellValue = new CellValue("Generated by Vincent");
UpdateCell(sd, c, 27u);
wsp.Worksheet.Save();
c = new Cell();
c.DataType = CellValues.String;
c.CellReference = "E27";
// you could show upper management that you're working hard,
// and print a time that's in the wee hours of the morning...
// But shame on you...
c.CellValue = new CellValue(string.Format("Generated at {0}", DateTime.Now.ToString("dd MMM yyyy HH:mm:ss")));
UpdateCell(sd, c, 27u);
wsp.Worksheet.Save();
// we have formulae and charts to update in the template
xl.WorkbookPart.Workbook.CalculationProperties.ForceFullCalculation = true;
xl.WorkbookPart.Workbook.CalculationProperties.FullCalculationOnLoad = true;
xl.WorkbookPart.Workbook.Save();
xl.Close();
}
}
private static Cell GetCell(SheetData sd, string ColumnName, UInt32 RowIndex)
{
// There's a small chance that the row has no RowIndex assigned.
// You should know that the property RowIndex is optional.
// This means the following will fail.
// But we can't do much about a missing RowIndex in the template.
Row r = null;
Cell c = null;
if (sd.Elements<Row>().Count(ri => ri.RowIndex == RowIndex) > 0)
{
r = (Row)sd.Elements<Row>().Where(ri => ri.RowIndex == RowIndex).First();
}
if (r != null)
{
string sCellReference = ColumnName.ToUpper() + RowIndex.ToString();
if (r.Elements<Cell>().Count(cr => cr.CellReference.Value == sCellReference) > 0)
{
c = (Cell)r.Elements<Cell>().Where(cr => cr.CellReference.Value == sCellReference).First();
}
}
return c;
}
private static void UpdateCell(SheetData sd, Cell CellData, UInt32 RowIndex)
{
// There's a small chance that the row has no RowIndex assigned.
// You should know that the property RowIndex is optional.
// This means the following will fail.
// But we can't do much about a missing RowIndex in the template.
Row r = null;
if (sd.Elements<Row>().Count(ri => ri.RowIndex == RowIndex) == 0)
{
// There's no row at this index, so it also means there's no cell
// with the data required. So just create the row with the cell.
r = new Row();
r.RowIndex = RowIndex;
r.Append(CellData);
sd.Append(r);
}
else
{
r = (Row)sd.Elements<Row>().Where(ri => ri.RowIndex == RowIndex).First();
// we will assume the cell's CellReference is consistent with the given RowIndex
if (r.Elements<Cell>().Count(cr => cr.CellReference.Value == CellData.CellReference.Value) > 0)
{
// there's an existing cell
Cell c = (Cell)r.Elements<Cell>().Where(cr => cr.CellReference.Value == CellData.CellReference.Value).First();
c.DataType = CellData.DataType;
// reset to "normal" string value if it's a shared string.
// Otherwise, we'll have to go change the value in the SharedStringTable class,
// and that's out of scope for now. Let's focus on updating cells, shall we?
// We are also assuming we're not stupid enough to set the given cell's
// data type to be SharedString...
if (c.DataType == CellValues.SharedString)
{
c.DataType = CellValues.String;
}
c.CellValue = new CellValue(CellData.CellValue.Text);
// set any other additional properties you want
}
else
{
// no such cell
IEnumerable<Cell> iec = r.Elements<Cell>();
// in case there are no cells in the row
if (iec.Count() == 0)
{
r.Append(CellData);
}
else
{
bool bFound = false;
// cells need to be in order of their CellReference values
foreach (Cell c in iec)
{
// We assume the existing Cells are already in order.
// This means the moment our CellData's CellReference is before
// a particular existing Cell's CellReference, we can insert before
// that particular existing Cell.
if (string.Compare(CellData.CellReference.Value, c.CellReference.Value) < 0)
{
bFound = true;
r.InsertBefore(CellData, c);
break;
}
}
// well, we have to insert our CellData *somewhere*.
// If not found in the loop above, then it must have the highest CellReference.
// So we just append it at the end of the row.
if (!bFound)
{
r.Append(CellData);
}
}
// we don't append the row to the SheetData variable because
// the row already exists. We just need to append the cell.
}
}
}
}
}
Vincent的例子是使用工作簿设置为模板并且不包括清除单元格,但他确实展示了如何覆盖单元格。对我而言,看起来他涵盖了很多可能出错的事情。我会首先尝试覆盖没有任何内容的单元格,看看是否可行。
答案 1 :(得分:0)
首先,EPPlus是OpenXML的包装器。因此,以下概念适用于两者。学习曲线可能很陡峭,所以我将从Vincent Tan计划开始,供您检查:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Drawing;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml.Drawing.Spreadsheet;
namespace ExcelOpenXmlSetCellValue
{
class Program
{
static void Main(string[] args)
{
string sFile = "ExcelOpenXmlSetCellValue.xlsx";
if (File.Exists(sFile))
{
File.Delete(sFile);
}
try
{
BuildWorkbook(sFile);
Console.WriteLine("Program end");
Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
Console.ReadLine();
}
}
private static void BuildWorkbook(string filename)
{
using (SpreadsheetDocument xl = SpreadsheetDocument.Create(filename, SpreadsheetDocumentType.Workbook))
{
WorkbookPart wbp = xl.AddWorkbookPart();
WorksheetPart wsp = wbp.AddNewPart<WorksheetPart>();
Workbook wb = new Workbook();
FileVersion fv = new FileVersion();
fv.ApplicationName = "Microsoft Office Excel";
Worksheet ws = new Worksheet();
SheetData sd = new SheetData();
Row r;
Cell c;
r = new Row();
// For the 2nd row. I know, it's obvious, but it's recommended
// that you assign a value. While Excel might be able to handle
// a Row class without the RowIndex assigned (in a certain case),
// other applications might find that difficult, in particular,
// your own application.
r.RowIndex = 2;
c = new Cell();
// "D" for 4th column. The "2" is equal to the RowIndex of the Row
// that the Cell class is in. Note that the number part has to be
// equal to the RowIndex or disaster will happen. Or worse, Excel
// spits out the "file corrupted" error and your user sees it.
c.CellReference = "D2";
// by default, an unassigned DataType means CellValues.Number
//c.DataType = CellValues.Number;
c.CellValue = new CellValue("9.81");
r.Append(c);
sd.Append(r);
// What if you need more Cell's in a Row? Blank lines added for readability.
r = new Row();
r.RowIndex = 5;
c = new Cell();
// this way, the number part is always right, but you need to
// assign the RowIndex first. This will make sense when you have
// to append many Cell classes. This is just an alternative to
// assigning the cell reference.
c.CellReference = "F" + r.RowIndex;
// There are 2 other string types, the SharedString and InlineString.
// They are discussed in another chapter. The CellValues.String type
// provides the most straightforward way of including text strings.
c.DataType = CellValues.String;
c.CellValue = new CellValue("Is");
r.Append(c);
c = new Cell();
c.CellReference = "G" + r.RowIndex;
c.DataType = CellValues.String;
c.CellValue = new CellValue("that in");
r.Append(c);
c = new Cell();
c.CellReference = "H" + r.RowIndex;
c.DataType = CellValues.String;
c.CellValue = new CellValue("metres per second squared?");
r.Append(c);
// This is for row 5
sd.Append(r);
// And now, to show you sometimes the code can be rearranged a little.
// But because you need to append the Cell class to the Row class,
// it makes sense (logically and conceptually) to initialise a new Row first.
c = new Cell();
c.CellReference = "F7";
c.DataType = CellValues.String;
c.CellValue = new CellValue("It'd better be. Just got used to the metric system.");
r = new Row();
r.RowIndex = 7;
r.Append(c);
sd.Append(r);
ws.Append(sd);
wsp.Worksheet = ws;
wsp.Worksheet.Save();
Sheets sheets = new Sheets();
Sheet sheet = new Sheet();
sheet.Name = "Sheet1";
sheet.SheetId = 1;
sheet.Id = wbp.GetIdOfPart(wsp);
sheets.Append(sheet);
wb.Append(fv);
wb.Append(sheets);
xl.WorkbookPart.Workbook = wb;
xl.WorkbookPart.Workbook.Save();
xl.Close();
}
}
}
}
然后,他的书摘录可能在概念上有所帮助:
因此SheetData类包含Row类作为子类。每个Row类都包含Cell类作为子类。 Row类表示一行 在电子表格中。 Cell类表示电子表格中的单元格。 有时候,仍然需要说明显而易见的事情。
建议您为RowIndex属性赋值 Row类。这基本上是该Row类的行号。如果 它是第4行,分配4. RowIndex属性是可选的 打开XML规范,但电子表格应用程序可能没有 处理这个问题的方法。甚至Excel处理空白的RowIndex属性 正确地在特殊条件下(我们将在稍后讨论) 本章)。
对于Cell类,您需要注意以下属性:
- DataType - 单元格值的数据类型。你很可能会处理CellValues.Number和CellValues.String。
- CellReference - “A1格式”中的单元格引用。这意味着列字母后跟行索引。例如,“C5”表示 第3栏,第5行。有“R1C1格式”(“R”然后是行索引, 然后“C”然后列索引,给我们前面的“R5C3” 例子),但我们没有涵盖那个。 “A1格式”是默认值 Excel中。
- CellValue - 行动发生的地方。您可能会注意到CellValue实际上是一个类,其主要值是一个字符串。这个 表示您也以字符串形式存储数字值(DataType property确定了数据类型。
- CellFormula - 您有细胞配方的地方。但你可以在另一章中读到它。以上3个属性是通常的属性 处理
还有一件事。 Row类必须附加到SheetData 以递增的RowIndex顺序排序。必须附加Cell类 以升序列顺序到其Row类。这是不容谈判的。 否则Excel会呕吐其错误呕吐。
希望这足以让你走上正轨。如果没有,我会回来查看更多问题。
在回答您的其他问题时,对于计算列,请在右侧为您为计算列构建的单元格构建计算。