スプレッドシート ドキュメントのセル範囲の合計を計算する
適用対象: Excel 2010 | Office 2010 | PowerPoint 2010 | Word 2010
この記事の内容
SpreadsheetDocument オブジェクトを取得する
SpreadsheetML ドキュメントの基本構造
サンプル コードの動作のしくみ
サンプル コード
ここでは、Open XML SDK 2.0 for Microsoft Office のクラスを使用して、プログラムによってスプレッドシート ドキュメントの連続したセル範囲の合計を計算する方法を説明します。
このトピックのコードをコンパイルするには、次のアセンブリ ディレクティブが必要です。
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text.RegularExpressions
Imports DocumentFormat.OpenXml
Imports DocumentFormat.OpenXml.Packaging
Imports DocumentFormat.OpenXml.Spreadsheet
SpreadsheetDocument オブジェクトを取得する
Open XML SDK では、SpreadsheetDocument クラスが Excel ドキュメント パッケージを表します。Excel ドキュメントを開いて操作するには、ドキュメントから SpreadsheetDocument クラスのインスタンスを作成します。ドキュメントからインスタンスを作成した後、ワークシートが含まれているメインの Workbook パーツにアクセスできます。ドキュメント内のテキストは、パッケージ内では SpreadsheetML マークアップを使用して XML として表されます。
ドキュメントからクラス インスタンスを作成するには、Open のいずれかのメソッドを呼び出します。メソッドにはいくつかの種類があり、それぞれシグネチャが異なります。このトピックのサンプル コードでは、2 つのパラメーターを必要とするシグネチャを持つ Open(String, Boolean) メソッドを使用します。最初のパラメーターには、開くドキュメントを表す完全なパスの文字列を指定します。2 番目のパラメーターは true または false で、開いたファイルを編集するかどうかを指定します。このパラメーターを false に指定した場合は、ドキュメントに対する変更が保存されません。
Open メソッドを呼び出すコードは、次の using ステートメント内に記述します。
// Open the document for editing.
using (SpreadsheetDocument document = SpreadsheetDocument.Open(docName, true))
{
// Other code goes here.
}
' Open the document for editing.
Using document As SpreadsheetDocument = SpreadsheetDocument.Open(docName, True)
' Other code goes here.
End Using
using ステートメントは、典型的な .Open, .Save, .Close シーケンスの代わりに使える推奨手段です。これを使用すると、対応する右かっこに達したときに、Dispose メソッド (Open XML SDK でリソースのクリーンアップに使われる内部メソッド) が自動的に呼び出されます。using ステートメントに続くブロックが、using ステートメントで作成または指定したオブジェクト (このケースでは document) のスコープとして設定されます。
SpreadsheetML ドキュメントの基本構造
SpreadsheetML ドキュメントの基本構造は、Sheets 要素と Sheet 要素で構成されます。これらの要素は、ブック内のワークシートを参照します。ワークシートごとに個別の XML ファイルが作成されます。たとえば、MySheet1 と MySheet2 という名前の 2 つのワークシートがあるブックの SpreadsheetML は Workbook.xml ファイル内にあり、次のコード例のように記述されます。
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<workbook xmlns=https://schemas.openxmlformats.org/spreadsheetml/2006/main xmlns:r="https://schemas.openxmlformats.org/officeDocument/2006/relationships">
<sheets>
<sheet name="MySheet1" sheetId="1" r:id="rId1" />
<sheet name="MySheet2" sheetId="2" r:id="rId2" />
</sheets>
</workbook>
ワークシート XML ファイルには SheetData などのブロック レベル要素が 1 つ以上含まれています。セルの表を表す sheetData には Row 要素が 1 つ以上含まれています。row には Cell が 1 つ以上含まれています。セルにはそれぞれ、セルの値を表す CellValue 要素が含まれています。たとえば、Sheet1.xml には、ブックにある 1 番目のワークシートの SpreadsheetML があります。このワークシートにはセル A1 に値 100 があるだけです。Sheet1.xml を以下のコード例に示します。
<?xml version="1.0" encoding="UTF-8" ?>
<worksheet xmlns="https://schemas.openxmlformats.org/spreadsheetml/2006/main">
<sheetData>
<row r="1">
<c r="A1">
<v>100</v>
</c>
</row>
</sheetData>
</worksheet>
Open XML SDK 2.0 を使用すると、SpreadsheetML 要素に対応する厳密に型指定されたクラスを使用したドキュメント構造とコンテンツを作成できます。これらのクラスは DocumentFormat.OpenXML.Spreadsheet 名前空間にあります。次の表は、workbook、sheets、sheet、worksheet、sheetData の各要素に対応するクラスのクラス名の一覧です。
SpreadsheetML の要素 |
Open XML SDK 2.0 のクラス |
説明 |
---|---|---|
workbook |
DocumentFormat.OpenXml.Spreadsheet.Workbook |
メイン ドキュメント パーツのルート要素。 |
sheets |
DocumentFormat.OpenXml.Spreadsheet.Sheets |
ISO/IEC 29500 (英語) の仕様で規定されている、シート、ファイル バージョン、その他のブロック レベル構造のコンテナー。 |
sheet |
DocumentFormat.OpenXml.Spreadsheet.Sheet |
シート定義ファイルを指し示すシート。 |
worksheet |
DocumentFormat.OpenXml.Spreadsheet.Worksheet |
シート データが含まれているシート定義ファイル。 |
sheetData |
DocumentFormat.OpenXml.Spreadsheet.SheetData |
セル テーブル。行ごとにグループ化されています。 |
row |
DocumentFormat.OpenXml.Spreadsheet.Row |
セル テーブル内の行。 |
c |
DocumentFormat.OpenXml.Spreadsheet.Cell |
行内のセル。 |
v |
DocumentFormat.OpenXml.Spreadsheet.CellValue |
セルの値。 |
サンプル コードの動作のしくみ
サンプル コードでは最初に、CalculateSumOfCellRange メソッドに、ソース SpreadsheetML ファイルへの完全なパス名を表すパラメーター、セルを含むワークシートの名前を表すパラメーター、連続した範囲の最初のセルの名前を表すパラメーター、連続した範囲の最後のセルの名前を表すパラメーター、および結果を表示するセルの名前を表すパラメーターを渡します。
コードは、次に読み取り/書き込みアクセス用の SpreadsheetDocument ドキュメント パッケージとして編集用にファイルを開き、指定された Worksheet オブジェクトを取得します。それから、GetRowIndex メソッドを呼び出して、連続した範囲にある最初のセルと最後のセルの行インデックスを取得します。そして、GetColumnName メソッドを呼び出し、連続した範囲にある最初のセルと最後のセルの列の名前を取得します。
連続した範囲内の各 Row オブジェクトに対して、Cell オブジェクトにそれぞれ反復処理を行い、CompareColumn メソッドを呼び出して、セルの列が連続した範囲内にあるかどうかを確認します。セルが連続した範囲内にある場合、そのセルの値を合計に追加します。それから、SharedStringTablePart オブジェクトが存在する場合はそれを取得します。存在しない場合は、AddNewPart メソッドを使用して作成します。そして、InsertSharedStringItem メソッドを呼び出して、結果を SharedStringTablePart オブジェクトに挿入します。
次に、InsertCellInWorksheet メソッドを呼び出して、結果用の新しいセルをワークシートに挿入します。詳細については、「cc861607(v=office.14).md」を参照してください。それから、ワークシートを保存します。
// Given a document name, a worksheet name, the name of the first cell in the contiguous range,
// the name of the last cell in the contiguous range, and the name of the results cell,
// calculates the sum of the cells in the contiguous range and inserts the result into the results cell.
// Note: All cells in the contiguous range must contain numbers.
private static void CalculateSumOfCellRange(string docName, string worksheetName, string firstCellName, string lastCellName, string resultCell)
{
// Open the document for editing.
using (SpreadsheetDocument document = SpreadsheetDocument.Open(docName, true))
{
IEnumerable<Sheet> sheets = document.WorkbookPart.Workbook.Descendants<Sheet>().Where(s => s.Name == worksheetName);
if (sheets.Count() == 0)
{
// The specified worksheet does not exist.
return;
}
WorksheetPart worksheetPart = (WorksheetPart)document.WorkbookPart.GetPartById(sheets.First().Id);
Worksheet worksheet = worksheetPart.Worksheet;
// Get the row number and column name for the first and last cells in the range.
uint firstRowNum = GetRowIndex(firstCellName);
uint lastRowNum = GetRowIndex(lastCellName);
string firstColumn = GetColumnName(firstCellName);
string lastColumn = GetColumnName(lastCellName);
double sum = 0;
// Iterate through the cells within the range and add their values to the sum.
foreach (Row row in worksheet.Descendants<Row>().Where(r => r.RowIndex.Value >= firstRowNum && r.RowIndex.Value <= lastRowNum))
{
foreach (Cell cell in row)
{
string columnName = GetColumnName(cell.CellReference.Value);
if (CompareColumn(columnName, firstColumn) >= 0 && CompareColumn(columnName, lastColumn) <= 0)
{
sum += double.Parse(cell.CellValue.Text);
}
}
}
// Get the SharedStringTablePart and add the result to it.
// If the SharedStringPart does not exist, create a new one.
SharedStringTablePart shareStringPart;
if (document.WorkbookPart.GetPartsOfType<SharedStringTablePart>().Count() > 0)
{
shareStringPart = document.WorkbookPart.GetPartsOfType<SharedStringTablePart>().First();
}
else
{
shareStringPart = document.WorkbookPart.AddNewPart<SharedStringTablePart>();
}
// Insert the result into the SharedStringTablePart.
int index = InsertSharedStringItem("Result:" + sum, shareStringPart);
Cell result = InsertCellInWorksheet(GetColumnName(resultCell), GetRowIndex(resultCell), worksheetPart);
// Set the value of the cell.
result.CellValue = new CellValue(index.ToString());
result.DataType = new EnumValue<CellValues>(CellValues.SharedString);
worksheetPart.Worksheet.Save();
}
}
' Given a document name, a worksheet name, the name of the first cell in the contiguous range,
' the name of the last cell in the contiguous range, and the name of the results cell,
' calculates the sum of the cells in the contiguous range and inserts the result into the results cell.
' Note: All cells in the contiguous range must contain numbers.
Private Shared Sub CalculateSumOfCellRange(ByVal docName As String, ByVal worksheetName As String, ByVal firstCellName As String, ByVal lastCellName As String, ByVal resultCell As String)
' Open the document for editing.
Using document As SpreadsheetDocument = SpreadsheetDocument.Open(docName, True)
Dim sheets As IEnumerable(Of Sheet) = document.WorkbookPart.Workbook.Descendants(Of Sheet)().Where(Function(s) s.Name = worksheetName)
If sheets.Count() = 0 Then
' The specified worksheet does not exist.
Return
End If
Dim worksheetPart As WorksheetPart = CType(document.WorkbookPart.GetPartById(sheets.First().Id), WorksheetPart)
Dim worksheet As Worksheet = worksheetPart.Worksheet
' Get the row number and column name for the first and last cells in the range.
Dim firstRowNum As UInteger = GetRowIndex(firstCellName)
Dim lastRowNum As UInteger = GetRowIndex(lastCellName)
Dim firstColumn As String = GetColumnName(firstCellName)
Dim lastColumn As String = GetColumnName(lastCellName)
Dim sum As Double = 0
' Iterate through the cells within the range and add their values to the sum.
For Each row As Row In worksheet.Descendants(Of Row)().Where(Function(r) r.RowIndex.Value >= firstRowNum AndAlso r.RowIndex.Value <= lastRowNum)
For Each cell As Cell In row
Dim columnName As String = GetColumnName(cell.CellReference.Value)
If CompareColumn(columnName, firstColumn) >= 0 AndAlso CompareColumn(columnName, lastColumn) <= 0 Then
sum += Double.Parse(cell.CellValue.Text)
End If
Next cell
Next row
' Get the SharedStringTablePart and add the result to it.
' If the SharedStringPart does not exist, create a new one.
Dim shareStringPart As SharedStringTablePart
If document.WorkbookPart.GetPartsOfType(Of SharedStringTablePart)().Count() > 0 Then
shareStringPart = document.WorkbookPart.GetPartsOfType(Of SharedStringTablePart)().First()
Else
shareStringPart = document.WorkbookPart.AddNewPart(Of SharedStringTablePart)()
End If
' Insert the result into the SharedStringTablePart.
Dim index As Integer = InsertSharedStringItem("Result:" & sum, shareStringPart)
Dim result As Cell = InsertCellInWorksheet(GetColumnName(resultCell), GetRowIndex(resultCell), worksheetPart)
' Set the value of the cell.
result.CellValue = New CellValue(index.ToString())
result.DataType = New EnumValue(Of CellValues)(CellValues.SharedString)
worksheetPart.Worksheet.Save()
End Using
End Subxxxxxxx
行インデックスを取得するため、セルの名前を表すパラメーターを渡し、セル名の行インデックス部分と一致する新しい正規表現を作成します。正規表現の詳細については、「正規表現言語要素」を参照してください。Regex.Match メソッドを呼び出して行インデックスを取得した後、行インデックスを返します。
// Given a cell name, parses the specified cell to get the row index.
private static uint GetRowIndex(string cellName)
{
// Create a regular expression to match the row index portion the cell name.
Regex regex = new Regex(@"\d+");
Match match = regex.Match(cellName);
return uint.Parse(match.Value);
}
' Given a cell name, parses the specified cell to get the row index.
Private Shared Function GetRowIndex(ByVal cellName As String) As UInteger
' Create a regular expression to match the row index portion the cell name.
Dim regex As New Regex("\d+")
Dim match As Match = regex.Match(cellName)
Return UInteger.Parse(match.Value)
End Function
次に、セルの名前を表すパラメーターを渡して列名を取得し、セル名の列名部分と一致する新しい正規表現を作成します。この正規表現は、大文字または小文字の英字の任意の組み合わせ部分を照合します。Regex.Match メソッドを呼び出して列名を取得し、列名を返します。
// Given a cell name, parses the specified cell to get the column name.
private static string GetColumnName(string cellName)
{
// Create a regular expression to match the column name portion of the cell name.
Regex regex = new Regex("[A-Za-z]+");
Match match = regex.Match(cellName);
return match.Value;
}
' Given a cell name, parses the specified cell to get the column name.
Private Shared Function GetColumnName(ByVal cellName As String) As String
' Create a regular expression to match the column name portion of the cell name.
Dim regex As New Regex("[A-Za-z]+")
Dim match As Match = regex.Match(cellName)
Return match.Value
End Function
2 つの列を比較するため、比較する列を表す 2 つのパラメーターを渡します。1 つ目の列が 2 つ目の列より長い場合は、1 を返します。2 つ目の列が 1 つ目の列より長い場合は、-1 を返します。それ以外の場合は、Compare を使用して列の値を比較して、その結果を返します。
// Given two columns, compares the columns.
private static int CompareColumn(string column1, string column2)
{
if (column1.Length > column2.Length)
{
return 1;
}
else if (column1.Length < column2.Length)
{
return -1;
}
else
{
return string.Compare(column1, column2, true);
}
}
' Given two columns, compares the columns.
Private Shared Function CompareColumn(ByVal column1 As String, ByVal column2 As String) As Integer
If column1.Length > column2.Length Then
Return 1
ElseIf column1.Length < column2.Length Then
Return -1
Else
Return String.Compare(column1, column2, True)
End If
End Function
SharedStringItem を挿入するため、セルに挿入するテキストを表すパラメーター、およびスプレッドシートの SharedStringTablePart オブジェクトを表すパラメーターを渡します。ShareStringTablePart オブジェクトに SharedStringTable オブジェクトが含まれない場合は作成します。テキストが ShareStringTable オブジェクトに既に存在する場合は、テキストを表す SharedStringItem オブジェクトのインデックスを返します。テキストが存在しない場合は、テキストを表す新しい SharedStringItem オブジェクトを作成します。そして、テキストを表す SharedStringItem オブジェクトのインデックスを返します。
// Given text and a SharedStringTablePart, creates a SharedStringItem with the specified text
// and inserts it into the SharedStringTablePart. If the item already exists, returns its index.
private static int InsertSharedStringItem(string text, SharedStringTablePart shareStringPart)
{
// If the part does not contain a SharedStringTable, create it.
if (shareStringPart.SharedStringTable == null)
{
shareStringPart.SharedStringTable = new SharedStringTable();
}
int i = 0;
foreach (SharedStringItem item in shareStringPart.SharedStringTable.Elements<SharedStringItem>())
{
if (item.InnerText == text)
{
// The text already exists in the part. Return its index.
return i;
}
i++;
}
// The text does not exist in the part. Create the SharedStringItem.
shareStringPart.SharedStringTable.AppendChild(new SharedStringItem(new DocumentFormat.OpenXml.Spreadsheet.Text(text)));
shareStringPart.SharedStringTable.Save();
return i;
}
' Given text and a SharedStringTablePart, creates a SharedStringItem with the specified text
' and inserts it into the SharedStringTablePart. If the item already exists, returns its index.
Private Shared Function InsertSharedStringItem(ByVal text As String, ByVal shareStringPart As SharedStringTablePart) As Integer
' If the part does not contain a SharedStringTable, create it.
If shareStringPart.SharedStringTable Is Nothing Then
shareStringPart.SharedStringTable = New SharedStringTable()
End If
Dim i As Integer = 0
For Each item As SharedStringItem In shareStringPart.SharedStringTable.Elements(Of SharedStringItem)()
If item.InnerText = text Then
' The text already exists in the part. Return its index.
Return i
End If
i += 1
Next item
' The text does not exist in the part. Create the SharedStringItem.
shareStringPart.SharedStringTable.AppendChild(New SharedStringItem(New DocumentFormat.OpenXml.Spreadsheet.Text(text)))
shareStringPart.SharedStringTable.Save()
Return i
End Function
最後に、セルをワークシートに挿入します。そのためには、列の名前を表すパラメーター、セルの行数を表すパラメーター、およびセルを含むワークシートを表すパラメーターを渡します。指定された行が存在しない場合、行を作成して、その行をワークシートの末尾に追加します。指定された列が存在する場合、その列内で行に一致するセルを見つけて、そのセルを返します。指定された列が存在しない場合、列を作成してワークシートに挿入します。それから、新しいセルを列内のどこに挿入するかを決定します。これには、行要素を順番に反復処理して、指定された行の直後にあるセルを見つけます。この行を refCell 変数に保存します。InsertBefore メソッドを使用して、refCell で参照されているセルの前に新しいセルを挿入します。そして、新しい Cell オブジェクトを返します。
// Given a column name, a row index, and a WorksheetPart, inserts a cell into the worksheet.
// If the cell already exists, returns it.
private static Cell InsertCellInWorksheet(string columnName, uint rowIndex, WorksheetPart worksheetPart)
{
Worksheet worksheet = worksheetPart.Worksheet;
SheetData sheetData = worksheet.GetFirstChild<SheetData>();
string cellReference = columnName + rowIndex;
// If the worksheet does not contain a row with the specified row index, insert one.
Row row;
if (sheetData.Elements<Row>().Where(r => r.RowIndex == rowIndex).Count() != 0)
{
row = sheetData.Elements<Row>().Where(r => r.RowIndex == rowIndex).First();
}
else
{
row = new Row() { RowIndex = rowIndex };
sheetData.Append(row);
}
// If there is not a cell with the specified column name, insert one.
if (row.Elements<Cell>().Where(c => c.CellReference.Value == columnName + rowIndex).Count() > 0)
{
return row.Elements<Cell>().Where(c => c.CellReference.Value == cellReference).First();
}
else
{
// Cells must be in sequential order according to CellReference. Determine where to insert the new cell.
Cell refCell = null;
foreach (Cell cell in row.Elements<Cell>())
{
if (string.Compare(cell.CellReference.Value, cellReference, true) > 0)
{
refCell = cell;
break;
}
}
Cell newCell = new Cell() { CellReference = cellReference };
row.InsertBefore(newCell, refCell);
worksheet.Save();
return newCell;
}
}
' Given a column name, a row index, and a WorksheetPart, inserts a cell into the worksheet.
' If the cell already exists, returns it.
Private Shared Function InsertCellInWorksheet(ByVal columnName As String, ByVal rowIndex As UInteger, ByVal worksheetPart As WorksheetPart) As Cell
Dim worksheet As Worksheet = worksheetPart.Worksheet
Dim sheetData As SheetData = worksheet.GetFirstChild(Of SheetData)()
Dim cellReference As String = columnName & rowIndex
' If the worksheet does not contain a row with the specified row index, insert one.
Dim row As Row
If sheetData.Elements(Of Row)().Where(Function(r) r.RowIndex = rowIndex).Count() <> 0 Then
row = sheetData.Elements(Of Row)().Where(Function(r) r.RowIndex = rowIndex).First()
Else
row = New Row() With {.RowIndex = rowIndex}
sheetData.Append(row)
End If
' If there is not a cell with the specified column name, insert one.
If row.Elements(Of Cell)().Where(Function(c) c.CellReference.Value = columnName & rowIndex).Count() > 0 Then
Return row.Elements(Of Cell)().Where(Function(c) c.CellReference.Value = cellReference).First()
Else
' Cells must be in sequential order according to CellReference. Determine where to insert the new cell.
Dim refCell As Cell = Nothing
For Each cell As Cell In row.Elements(Of Cell)()
If String.Compare(cell.CellReference.Value, cellReference, True) > 0 Then
refCell = cell
Exit For
End If
Next cell
Dim newCell As New Cell() With {.CellReference = cellReference}
row.InsertBefore(newCell, refCell)
worksheet.Save()
Return newCell
End If
End Function
サンプル コード
次のコード サンプルは、スプレッドシート ドキュメントの連続したセル範囲の合計を計算します。結果は SharedStringTablePart オブジェクトに挿入され、指定された結果セルに挿入されます。次の例を使用して、CalculateSumOfCellRange メソッドを呼び出すことができます。
string docName = @"C:\Users\Public\Documents\Sheet1.xlsx";
string worksheetName = "John";
string firstCellName = "A1";
string lastCellName = "A3";
string resultCell = "A4";
CalculateSumOfCellRange(docName, worksheetName, firstCellName, lastCellName, resultCell);
Dim docName As String = "C:\Users\Public\Documents\Sheet1.xlsx"
Dim worksheetName As String = "John"
Dim firstCellName As String = "A1"
Dim lastCellName As String = "A3"
Dim resultCell As String = "A4"
CalculateSumOfCellRange(docName, worksheetName, firstCellName, lastCellName, resultCell)
プログラムを実行した後、"Sheet1.xlsx" という名前のファイルを調べて、"John" という名前のワークシートの指定したセルで、列の合計を確認できます。
以下に、C# と Visual Basic による完全なサンプル コードを示します。
private static void CalculateSumOfCellRange(string docName, string worksheetName, string firstCellName, string lastCellName, string resultCell)
{
// Open the document for editing.
using (SpreadsheetDocument document = SpreadsheetDocument.Open(docName, true))
{
IEnumerable<Sheet> sheets = document.WorkbookPart.Workbook.Descendants<Sheet>().Where(s => s.Name == worksheetName);
if (sheets.Count() == 0)
{
// The specified worksheet does not exist.
return;
}
WorksheetPart worksheetPart = (WorksheetPart)document.WorkbookPart.GetPartById(sheets.First().Id);
Worksheet worksheet = worksheetPart.Worksheet;
// Get the row number and column name for the first and last cells in the range.
uint firstRowNum = GetRowIndex(firstCellName);
uint lastRowNum = GetRowIndex(lastCellName);
string firstColumn = GetColumnName(firstCellName);
string lastColumn = GetColumnName(lastCellName);
double sum = 0;
// Iterate through the cells within the range and add their values to the sum.
foreach (Row row in worksheet.Descendants<Row>().Where(r => r.RowIndex.Value >= firstRowNum && r.RowIndex.Value <= lastRowNum))
{
foreach (Cell cell in row)
{
string columnName = GetColumnName(cell.CellReference.Value);
if (CompareColumn(columnName, firstColumn) >= 0 && CompareColumn(columnName, lastColumn) <= 0)
{
sum += double.Parse(cell.CellValue.Text);
}
}
}
// Get the SharedStringTablePart and add the result to it.
// If the SharedStringPart does not exist, create a new one.
SharedStringTablePart shareStringPart;
if (document.WorkbookPart.GetPartsOfType<SharedStringTablePart>().Count() > 0)
{
shareStringPart = document.WorkbookPart.GetPartsOfType<SharedStringTablePart>().First();
}
else
{
shareStringPart = document.WorkbookPart.AddNewPart<SharedStringTablePart>();
}
// Insert the result into the SharedStringTablePart.
int index = InsertSharedStringItem("Result: " + sum, shareStringPart);
Cell result = InsertCellInWorksheet(GetColumnName(resultCell), GetRowIndex(resultCell), worksheetPart);
// Set the value of the cell.
result.CellValue = new CellValue(index.ToString());
result.DataType = new EnumValue<CellValues>(CellValues.SharedString);
worksheetPart.Worksheet.Save();
}
}
// Given a cell name, parses the specified cell to get the row index.
private static uint GetRowIndex(string cellName)
{
// Create a regular expression to match the row index portion the cell name.
Regex regex = new Regex(@"\d+");
Match match = regex.Match(cellName);
return uint.Parse(match.Value);
}
// Given a cell name, parses the specified cell to get the column name.
private static string GetColumnName(string cellName)
{
// Create a regular expression to match the column name portion of the cell name.
Regex regex = new Regex("[A-Za-z]+");
Match match = regex.Match(cellName);
return match.Value;
}
// Given two columns, compares the columns.
private static int CompareColumn(string column1, string column2)
{
if (column1.Length > column2.Length)
{
return 1;
}
else if (column1.Length < column2.Length)
{
return -1;
}
else
{
return string.Compare(column1, column2, true);
}
}
// Given text and a SharedStringTablePart, creates a SharedStringItem with the specified text
// and inserts it into the SharedStringTablePart. If the item already exists, returns its index.
private static int InsertSharedStringItem(string text, SharedStringTablePart shareStringPart)
{
// If the part does not contain a SharedStringTable, create it.
if (shareStringPart.SharedStringTable == null)
{
shareStringPart.SharedStringTable = new SharedStringTable();
}
int i = 0;
foreach (SharedStringItem item in shareStringPart.SharedStringTable.Elements<SharedStringItem>())
{
if (item.InnerText == text)
{
// The text already exists in the part. Return its index.
return i;
}
i++;
}
// The text does not exist in the part. Create the SharedStringItem.
shareStringPart.SharedStringTable.AppendChild(new SharedStringItem(new DocumentFormat.OpenXml.Spreadsheet.Text(text)));
shareStringPart.SharedStringTable.Save();
return i;
}
// Given a column name, a row index, and a WorksheetPart, inserts a cell into the worksheet.
// If the cell already exists, returns it.
private static Cell InsertCellInWorksheet(string columnName, uint rowIndex, WorksheetPart worksheetPart)
{
Worksheet worksheet = worksheetPart.Worksheet;
SheetData sheetData = worksheet.GetFirstChild<SheetData>();
string cellReference = columnName + rowIndex;
// If the worksheet does not contain a row with the specified row index, insert one.
Row row;
if (sheetData.Elements<Row>().Where(r => r.RowIndex == rowIndex).Count() != 0)
{
row = sheetData.Elements<Row>().Where(r => r.RowIndex == rowIndex).First();
}
else
{
row = new Row() { RowIndex = rowIndex };
sheetData.Append(row);
}
// If there is not a cell with the specified column name, insert one.
if (row.Elements<Cell>().Where(c => c.CellReference.Value == columnName + rowIndex).Count() > 0)
{
return row.Elements<Cell>().Where(c => c.CellReference.Value == cellReference).First();
}
else
{
// Cells must be in sequential order according to CellReference. Determine where to insert the new cell.
Cell refCell = null;
foreach (Cell cell in row.Elements<Cell>())
{
if (string.Compare(cell.CellReference.Value, cellReference, true) > 0)
{
refCell = cell;
break;
}
}
Cell newCell = new Cell() { CellReference = cellReference };
row.InsertBefore(newCell, refCell);
worksheet.Save();
return newCell;
}
}
' Given a document name, a worksheet name, the name of the first cell in the contiguous range,
' the name of the last cell in the contiguous range, and the name of the results cell,
' calculates the sum of the cells in the contiguous range and inserts the result into the results cell.
' Note: All cells in the contiguous range must contain numbers.Private Sub CalculateSumOfCellRange(ByVal docName As String, ByVal worksheetName As String, ByVal firstCellName As String, _
Private Sub CalculateSumOfCellRange(ByVal docName As String, ByVal worksheetName As String, ByVal firstCellName As String, _
ByVal lastCellName As String, ByVal resultCell As String)
' Open the document for editing.
Dim document As SpreadsheetDocument = SpreadsheetDocument.Open(docName, True)
Using (document)
Dim sheets As IEnumerable(Of Sheet) = _
document.WorkbookPart.Workbook.Descendants(Of Sheet)().Where(Function(s) s.Name = worksheetName)
If (sheets.Count() = 0) Then
' The specified worksheet does not exist.
Return
End If
Dim worksheetPart As WorksheetPart = CType(document.WorkbookPart.GetPartById(Sheets.First().Id), WorksheetPart)
Dim worksheet As Worksheet = WorksheetPart.Worksheet
' Get the row number and column name for the first and last cells in the range.
Dim firstRowNum As UInteger = GetRowIndex(firstCellName)
Dim lastRowNum As UInteger = GetRowIndex(lastCellName)
Dim firstColumn As String = GetColumnName(firstCellName)
Dim lastColumn As String = GetColumnName(lastCellName)
Dim sum As Double = 0
' Iterate through the cells within the range and add their values to the sum.
For Each row As Row In worksheet.Descendants(Of Row)().Where(Function(r) r.RowIndex.Value >= firstRowNum _
AndAlso r.RowIndex.Value <= lastRowNum)
For Each cell As Cell In row
Dim columnName As String = GetColumnName(Cell.CellReference.Value)
If ((CompareColumn(columnName, firstColumn) >= 0) AndAlso (CompareColumn(columnName, lastColumn) <= 0)) Then
sum = (sum + Double.Parse(cell.CellValue.Text))
End If
Next
Next
' Get the SharedStringTablePart and add the result to it.
' If the SharedStringPart does not exist, create a new one.
Dim shareStringPart As SharedStringTablePart
If (document.WorkbookPart.GetPartsOfType(Of SharedStringTablePart)().Count() > 0) Then
shareStringPart = document.WorkbookPart.GetPartsOfType(Of SharedStringTablePart)().First()
Else
shareStringPart = document.WorkbookPart.AddNewPart(Of SharedStringTablePart)()
End If
' Insert the result into the SharedStringTablePart.
Dim index As Integer = InsertSharedStringItem(("Result:" + sum.ToString()), shareStringPart)
Dim result As Cell = InsertCellInWorksheet(GetColumnName(resultCell), GetRowIndex(resultCell), WorksheetPart)
' Set the value of the cell.
result.CellValue = New CellValue(index.ToString())
result.DataType = New EnumValue(Of CellValues)(CellValues.SharedString)
worksheetPart.Worksheet.Save()
End Using
End Sub
' Given a cell name, parses the specified cell to get the row index.
Private Function GetRowIndex(ByVal cellName As String) As UInteger
' Create a regular expression to match the row index portion the cell name.
Dim regex As Regex = New Regex("\d+")
Dim match As Match = regex.Match(cellName)
Return UInteger.Parse(match.Value)
End Function
' Given a cell name, parses the specified cell to get the column name.
Private Function GetColumnName(ByVal cellName As String) As String
' Create a regular expression to match the column name portion of the cell name.
Dim regex As Regex = New Regex("[A-Za-z]+")
Dim match As Match = regex.Match(cellName)
Return match.Value
End Function
' Given two columns, compares the columns.
Private Function CompareColumn(ByVal column1 As String, ByVal column2 As String) As Integer
If (column1.Length > column2.Length) Then
Return 1
ElseIf (column1.Length < column2.Length) Then
Return -1
Else
Return String.Compare(column1, column2, True)
End If
End Function
' Given text and a SharedStringTablePart, creates a SharedStringItem with the specified text
' and inserts it into the SharedStringTablePart. If the item already exists, returns its index.
Private Function InsertSharedStringItem(ByVal text As String, ByVal shareStringPart As SharedStringTablePart) As Integer
' If the part does not contain a SharedStringTable, create it.
If (shareStringPart.SharedStringTable Is Nothing) Then
shareStringPart.SharedStringTable = New SharedStringTable
End If
Dim i As Integer = 0
For Each item As SharedStringItem In shareStringPart.SharedStringTable.Elements(Of SharedStringItem)()
If (item.InnerText = text) Then
' The text already exists in the part. Return its index.
Return i
End If
i = (i + 1)
Next
' The text does not exist in the part. Create the SharedStringItem.
shareStringPart.SharedStringTable.AppendChild(New SharedStringItem(New DocumentFormat.OpenXml.Spreadsheet.Text(text)))
shareStringPart.SharedStringTable.Save()
Return i
End Function
' Given a column name, a row index, and a WorksheetPart, inserts a cell into the worksheet.
' If the cell already exists, return it.
Private Function InsertCellInWorksheet(ByVal columnName As String, ByVal rowIndex As UInteger, ByVal worksheetPart As WorksheetPart) As Cell
Dim worksheet As Worksheet = worksheetPart.Worksheet
Dim sheetData As SheetData = worksheet.GetFirstChild(Of SheetData)()
Dim cellReference As String = (columnName + rowIndex.ToString())
' If the worksheet does not contain a row with the specified row index, insert one.
Dim row As Row
If (sheetData.Elements(Of Row).Where(Function(r) r.RowIndex.Value = rowIndex).Count() <> 0) Then
row = sheetData.Elements(Of Row).Where(Function(r) r.RowIndex.Value = rowIndex).First()
Else
row = New Row()
row.RowIndex = rowIndex
sheetData.Append(row)
End If
' If there is not a cell with the specified column name, insert one.
If (row.Elements(Of Cell).Where(Function(c) c.CellReference.Value = columnName + rowIndex.ToString()).Count() > 0) Then
Return row.Elements(Of Cell).Where(Function(c) c.CellReference.Value = cellReference).First()
Else
' Cells must be in sequential order according to CellReference. Determine where to insert the new cell.
Dim refCell As Cell = Nothing
For Each cell As Cell In row.Elements(Of Cell)()
If (String.Compare(cell.CellReference.Value, cellReference, True) > 0) Then
refCell = cell
Exit For
End If
Next
Dim newCell As Cell = New Cell
newCell.CellReference = cellReference
row.InsertBefore(newCell, refCell)
worksheet.Save()
Return newCell
End If
End Function