スプレッドシート ドキュメントにある 2 つの隣接するセルを結合する
適用対象: Excel 2010 | Office 2010 | PowerPoint 2010 | Word 2010
この記事の内容
SpreadsheetDocument オブジェクトを取得する
SpreadsheetML ドキュメントの基本構造
サンプル コードの動作のしくみ
サンプル コード
このトピックでは、Open XML SDK 2.0 for Microsoft Office のクラスを使用して、プログラムによって、スプレッドシート ドキュメントの隣接する 2 つのセルを結合する方法について説明します。
このトピックのコードをコンパイルするには、次のアセンブリ ディレクティブが必要です。
using System;
using System.Collections.Generic;
using System.Linq;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using System.Text.RegularExpressions;
Imports System
Imports System.Collections.Generic
Imports System.Linq
Imports DocumentFormat.OpenXml
Imports DocumentFormat.OpenXml.Packaging
Imports DocumentFormat.OpenXml.Spreadsheet
Imports System.Text.RegularExpressions
SpreadsheetDocument オブジェクトを取得する
Open XML SDK では、SpreadsheetDocument クラスが Excel ドキュメント パッケージを表します。Excel ドキュメントを開いて操作するには、ドキュメントから SpreadsheetDocument クラスのインスタンスを作成します。ドキュメントからインスタンスを作成した後、ワークシートが入っているメインのブック パーツにアクセスできます。パッケージ内では、SpreadsheetML マークアップを使用してドキュメントのテキストを XML で表現します。
ドキュメントからクラス インスタンスを作成するには、いずれかの Open() オーバーロード メソッドを呼び出します。このメソッドは複数提供されており、それぞれシグネチャが異なります。このトピックのサンプル コードで使用している Open(String, Boolean) メソッドのシグネチャは、2 つのパラメーターを必要とします。1 番目のパラメーターは、開こうとするドキュメントを表す完全なパスの文字列を受け取ります。2 番目のパラメーターは true または false であり、ファイルを編集用に開くかどうかを表します。このパラメーターが false の場合、ドキュメントに加えられた変更は保存されません。
Open メソッドを呼び出すコードは、次の using ステートメント内に記述します。
// Open the document for editing.
using (SpreadsheetDocument document = SpreadsheetDocument.Open(docName, true))
{
// Insert other code here.
}
' Open the document for editing.
Using document As SpreadsheetDocument = SpreadsheetDocument.Open(docName, True)
' Insert other code here.
End Using
using ステートメントは、一般的な .Open, .Save, .Close シーケンスに代わる手段として推奨されます。このステートメントでは、閉じかっこに達すると、Dispose メソッド (リソースをクリーンアップする Open XML SDK の内部メソッド) が自動的に呼び出されます。using ステートメントに続くブロックは、using ステートメントで作成または指定されたオブジェクト (この場合は "document") のスコープを設定します。
SpreadsheetML ドキュメントの基本構造
SpreadsheetML ドキュメントの基本構造は、Sheets 要素と Sheet 要素で構成されます。これらの要素は、Workbook 内のワークシートを参照します。Worksheet ごとに個別の 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 |
セルの値。 |
サンプル コードの動作のしくみ
スプレッドシート ファイルを編集用として開いた後、指定されたセルが存在するかどうかを確認します。指定されたセルが存在しない場合は、CreateSpreadsheetCellIfNotExist メソッドを呼び出してセルを作成し、それらを適切な Row オブジェクトに追加します。
// Given a Worksheet and a cell name, verifies that the specified cell exists.
// If it does not exist, creates a new cell.
private static void CreateSpreadsheetCellIfNotExist(Worksheet worksheet, string cellName)
{
string columnName = GetColumnName(cellName);
uint rowIndex = GetRowIndex(cellName);
IEnumerable<Row> rows = worksheet.Descendants<Row>().Where(r => r.RowIndex.Value == rowIndex);
// If the Worksheet does not contain the specified row, create the specified row.
// Create the specified cell in that row, and insert the row into the Worksheet.
if (rows.Count() == 0)
{
Row row = new Row() { RowIndex = new UInt32Value(rowIndex) };
Cell cell = new Cell() { CellReference = new StringValue(cellName) };
row.Append(cell);
worksheet.Descendants<SheetData>().First().Append(row);
worksheet.Save();
}
else
{
Row row = rows.First();
IEnumerable<Cell> cells = row.Elements<Cell>().Where(c => c.CellReference.Value == cellName);
// If the row does not contain the specified cell, create the specified cell.
if (cells.Count() == 0)
{
Cell cell = new Cell() { CellReference = new StringValue(cellName) };
row.Append(cell);
worksheet.Save();
}
}
}
' Given a Worksheet and a cell name, verifies that the specified cell exists.
' If it does not exist, creates a new cell.
Private Sub CreateSpreadsheetCellIfNotExist(ByVal worksheet As Worksheet, ByVal cellName As String)
Dim columnName As String = GetColumnName(cellName)
Dim rowIndex As UInteger = GetRowIndex(cellName)
Dim rows As IEnumerable(Of Row) = worksheet.Descendants(Of Row)().Where(Function(r) r.RowIndex.Value = rowIndex.ToString())
' If the worksheet does not contain the specified row, create the specified row.
' Create the specified cell in that row, and insert the row into the worksheet.
If (rows.Count = 0) Then
Dim row As Row = New Row()
row.RowIndex = New UInt32Value(rowIndex)
Dim cell As Cell = New Cell()
cell.CellReference = New StringValue(cellName)
row.Append(cell)
worksheet.Descendants(Of SheetData)().First().Append(row)
worksheet.Save()
Else
Dim row As Row = rows.First()
Dim cells As IEnumerable(Of Cell) = row.Elements(Of Cell)().Where(Function(c) c.CellReference.Value = cellName)
' If the row does not contain the specified cell, create the specified cell.
If (cells.Count = 0) Then
Dim cell As Cell = New Cell
cell.CellReference = New StringValue(cellName)
row.Append(cell)
worksheet.Save()
End If
End If
End Sub
列の名前を取得するために、セル名の列名部分を照合する新しい正規表現を作成します。この正規表現は、大文字または小文字の英字の任意の組み合わせ部分を照合します。正規表現の詳細については、「正規表現言語要素」を参照してください。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 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
行のインデックスを取得するために、セル名の行インデックス部分を照合する新しい正規表現を作成します。この正規表現は、10 進数の組み合わせ部分を照合します。次のコードは、10 進数で構成される、セル名の行インデックス部分を照合する正規表現を作成します。
// 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 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
サンプル コード
以下のコードでは、SpreadsheetDocument ドキュメント パッケージにある 2 つの隣接するセルを結合します。2 つのセルを結合するときは、一方のセルの内容のみ保持されます。左から右方向の言語では、左上のセルの内容が保持されます。右から左方向の言語では、右上のセルの内容が保持されます。次のコード例を使用して、MergeTwoCells メソッドを呼び出すことができます。この例では、"Sheet9.xlsx" という名前のファイルの "Jane" という名前のシートのセル B2 と C2 を結合します。
string docName = @"C:\Users\Public\Documents\Sheet9.xlsx";
string sheetName = "Jane";
string cell1Name = "B2";
string cell2Name = "C2";
MergeTwoCells(docName, sheetName, cell1Name, cell2Name);
Dim docName As String = "C:\Users\Public\Documents\Sheet9.xlsx"
Dim sheetName As String = "Jane"
Dim cell1Name As String = "B2"
Dim cell2Name As String = "C2"
MergeTwoCells(docName, sheetName, cell1Name, cell2Name)
以下に、C# と Visual Basic による完全なサンプル コードを示します。
// Given a document name, a worksheet name, and the names of two adjacent cells, merges the two cells.
// When two cells are merged, only the content from one cell is preserved:
// the upper-left cell for left-to-right languages or the upper-right cell for right-to-left languages.
private static void MergeTwoCells(string docName, string sheetName, string cell1Name, string cell2Name)
{
// Open the document for editing.
using (SpreadsheetDocument document = SpreadsheetDocument.Open(docName, true))
{
Worksheet worksheet = GetWorksheet(document, sheetName);
if (worksheet == null || string.IsNullOrEmpty(cell1Name) || string.IsNullOrEmpty(cell2Name))
{
return;
}
// Verify if the specified cells exist, and if they do not exist, create them.
CreateSpreadsheetCellIfNotExist(worksheet, cell1Name);
CreateSpreadsheetCellIfNotExist(worksheet, cell2Name);
MergeCells mergeCells;
if (worksheet.Elements<MergeCells>().Count() > 0)
{
mergeCells = worksheet.Elements<MergeCells>().First();
}
else
{
mergeCells = new MergeCells();
// Insert a MergeCells object into the specified position.
if (worksheet.Elements<CustomSheetView>().Count() > 0)
{
worksheet.InsertAfter(mergeCells, worksheet.Elements<CustomSheetView>().First());
}
else if (worksheet.Elements<DataConsolidate>().Count() > 0)
{
worksheet.InsertAfter(mergeCells, worksheet.Elements<DataConsolidate>().First());
}
else if (worksheet.Elements<SortState>().Count() > 0)
{
worksheet.InsertAfter(mergeCells, worksheet.Elements<SortState>().First());
}
else if (worksheet.Elements<AutoFilter>().Count() > 0)
{
worksheet.InsertAfter(mergeCells, worksheet.Elements<AutoFilter>().First());
}
else if (worksheet.Elements<Scenarios>().Count() > 0)
{
worksheet.InsertAfter(mergeCells, worksheet.Elements<Scenarios>().First());
}
else if (worksheet.Elements<ProtectedRanges>().Count() > 0)
{
worksheet.InsertAfter(mergeCells, worksheet.Elements<ProtectedRanges>().First());
}
else if (worksheet.Elements<SheetProtection>().Count() > 0)
{
worksheet.InsertAfter(mergeCells, worksheet.Elements<SheetProtection>().First());
}
else if (worksheet.Elements<SheetCalculationProperties>().Count() > 0)
{
worksheet.InsertAfter(mergeCells, worksheet.Elements<SheetCalculationProperties>().First());
}
else
{
worksheet.InsertAfter(mergeCells, worksheet.Elements<SheetData>().First());
}
}
// Create the merged cell and append it to the MergeCells collection.
MergeCell mergeCell = new MergeCell() { Reference = new StringValue(cell1Name + ":" + cell2Name) };
mergeCells.Append(mergeCell);
worksheet.Save();
}
}
// Given a Worksheet and a cell name, verifies that the specified cell exists.
// If it does not exist, creates a new cell.
private static void CreateSpreadsheetCellIfNotExist(Worksheet worksheet, string cellName)
{
string columnName = GetColumnName(cellName);
uint rowIndex = GetRowIndex(cellName);
IEnumerable<Row> rows = worksheet.Descendants<Row>().Where(r => r.RowIndex.Value == rowIndex);
// If the Worksheet does not contain the specified row, create the specified row.
// Create the specified cell in that row, and insert the row into the Worksheet.
if (rows.Count() == 0)
{
Row row = new Row() { RowIndex = new UInt32Value(rowIndex) };
Cell cell = new Cell() { CellReference = new StringValue(cellName) };
row.Append(cell);
worksheet.Descendants<SheetData>().First().Append(row);
worksheet.Save();
}
else
{
Row row = rows.First();
IEnumerable<Cell> cells = row.Elements<Cell>().Where(c => c.CellReference.Value == cellName);
// If the row does not contain the specified cell, create the specified cell.
if (cells.Count() == 0)
{
Cell cell = new Cell() { CellReference = new StringValue(cellName) };
row.Append(cell);
worksheet.Save();
}
}
}
// Given a SpreadsheetDocument and a worksheet name, get the specified worksheet.
private static Worksheet GetWorksheet(SpreadsheetDocument document, string worksheetName)
{
IEnumerable<Sheet> sheets = document.WorkbookPart.Workbook.Descendants<Sheet>().Where(s => s.Name == worksheetName);
WorksheetPart worksheetPart = (WorksheetPart)document.WorkbookPart.GetPartById(sheets.First().Id)
if (sheets.Count() == 0)
return null;
else
return worksheetPart.Worksheet;
}
// 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 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 document name, a worksheet name, and the names of two adjacent cells, merges the two cells.
' When two cells are merged, only the content from one cell is preserved:
' the upper-left cell for left-to-right languages or the upper-right cell for right-to-left languages.
Private Sub MergeTwoCells(ByVal docName As String, ByVal sheetName As String, ByVal cell1Name As String, ByVal cell2Name As String)
' Open the document for editing.
Dim document As SpreadsheetDocument = SpreadsheetDocument.Open(docName, True)
Using (document)
Dim worksheet As Worksheet = GetWorksheet(document, sheetName)
If ((worksheet Is Nothing) OrElse (String.IsNullOrEmpty(cell1Name) OrElse String.IsNullOrEmpty(cell2Name))) Then
Return
End If
' Verify if the specified cells exist, and if they do not exist, create them.
CreateSpreadsheetCellIfNotExist(worksheet, cell1Name)
CreateSpreadsheetCellIfNotExist(worksheet, cell2Name)
Dim mergeCells As MergeCells
If (worksheet.Elements(Of MergeCells)().Count() > 0) Then
mergeCells = worksheet.Elements(Of MergeCells).First()
Else
mergeCells = New MergeCells()
' Insert a MergeCells object into the specified position.
If (worksheet.Elements(Of CustomSheetView)().Count() > 0) Then
worksheet.InsertAfter(mergeCells, worksheet.Elements(Of CustomSheetView)().First())
ElseIf (worksheet.Elements(Of DataConsolidate)().Count() > 0) Then
worksheet.InsertAfter(mergeCells, worksheet.Elements(Of DataConsolidate)().First())
ElseIf (worksheet.Elements(Of SortState)().Count() > 0) Then
worksheet.InsertAfter(mergeCells, worksheet.Elements(Of SortState)().First())
ElseIf (worksheet.Elements(Of AutoFilter)().Count() > 0) Then
worksheet.InsertAfter(mergeCells, worksheet.Elements(Of AutoFilter)().First())
ElseIf (worksheet.Elements(Of Scenarios)().Count() > 0) Then
worksheet.InsertAfter(mergeCells, worksheet.Elements(Of Scenarios)().First())
ElseIf (worksheet.Elements(Of ProtectedRanges)().Count() > 0) Then
worksheet.InsertAfter(mergeCells, worksheet.Elements(Of ProtectedRanges)().First())
ElseIf (worksheet.Elements(Of SheetProtection)().Count() > 0) Then
worksheet.InsertAfter(mergeCells, worksheet.Elements(Of SheetProtection)().First())
ElseIf (worksheet.Elements(Of SheetCalculationProperties)().Count() > 0) Then
worksheet.InsertAfter(mergeCells, worksheet.Elements(Of SheetCalculationProperties)().First())
Else
worksheet.InsertAfter(mergeCells, worksheet.Elements(Of SheetData)().First())
End If
End If
' Create the merged cell and append it to the MergeCells collection.
Dim mergeCell As MergeCell = New MergeCell()
mergeCell.Reference = New StringValue((cell1Name + (":" + cell2Name)))
mergeCells.Append(mergeCell)
worksheet.Save()
End Using
End Sub
' Given a SpreadsheetDocument and a worksheet name, get the specified worksheet.
Private Function GetWorksheet(ByVal document As SpreadsheetDocument, ByVal worksheetName As String) As Worksheet
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 Nothing
End If
Dim worksheetPart As WorksheetPart = CType(document.WorkbookPart.GetPartById(sheets.First.Id), WorksheetPart)
Return worksheetPart.Worksheet
End Function
' Given a Worksheet and a cell name, verifies that the specified cell exists.
' If it does not exist, creates a new cell.
Private Sub CreateSpreadsheetCellIfNotExist(ByVal worksheet As Worksheet, ByVal cellName As String)
Dim columnName As String = GetColumnName(cellName)
Dim rowIndex As UInteger = GetRowIndex(cellName)
Dim rows As IEnumerable(Of Row) = worksheet.Descendants(Of Row)().Where(Function(r) r.RowIndex.Value = rowIndex.ToString())
' If the worksheet does not contain the specified row, create the specified row.
' Create the specified cell in that row, and insert the row into the worksheet.
If (rows.Count = 0) Then
Dim row As Row = New Row()
row.RowIndex = New UInt32Value(rowIndex)
Dim cell As Cell = New Cell()
cell.CellReference = New StringValue(cellName)
row.Append(cell)
worksheet.Descendants(Of SheetData)().First().Append(row)
worksheet.Save()
Else
Dim row As Row = rows.First()
Dim cells As IEnumerable(Of Cell) = row.Elements(Of Cell)().Where(Function(c) c.CellReference.Value = cellName)
' If the row does not contain the specified cell, create the specified cell.
If (cells.Count = 0) Then
Dim cell As Cell = New Cell
cell.CellReference = New StringValue(cellName)
row.Append(cell)
worksheet.Save()
End If
End If
End Sub
' 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 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