かなり明確です。あなたは私が個人的にコード化していない領域ですが、各シートのSheetDataはデータを消去する場所になります。以下は、既存のシートにデータを追加して変更する方法を示しヴィンセント・タンから別のプログラムには、次のとおりです。
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.
}
}
}
}
}
ヴィンセントの例は、テンプレートとして設定ワークブックを使用しており、セルをクリアカバーしていませんが、彼はどのように示してい
セルを上書きする。私には、彼は間違っている可能性のあるものをたくさんカバーしているように見えます。私は何もしていない細胞を上書きしようとして、それがうまくいくかどうかを見ます。
私は既存のXLSM SpreedSheetを削除したくありません。私はExcelシートのデータをクリアしたい、または上書きすることができます。それは複数のシートを持っています。 それは独自の報告アプリケーションであり、かなり大きいです。 私が作業しているシート上のデータは、他のシートやチャートにデータを送ります。 私はこの既存のファイルをテンプレートとして使用して、計算された列 と他の参照シートが、作業中のシートが作成されると自動的にデータを取得できるようにデータを挿入したいと思います。 – Nepsydaz