Fusionner deux cellules adjacentes dans un document de feuille de calcul
Cette rubrique montre comment utiliser les classes du Kit de développement logiciel (SDK) Open XML pour Office afin de fusionner par programmation deux cellules adjacentes dans un document de feuille de calcul.
Structure de base d’un document spreadsheetML
La structure de base d’un document SpreadsheetML comprend les éléments Sheets et Sheet faisant référence aux feuilles de calcul dans le classeur. Un fichier XML distinct est créé pour chaque feuille de calcul. Par exemple, l’élément SpreadsheetML d’un élément Workbook qui contient deux feuilles de calcul nommées « MySheet1 » et « MySheet2 » se trouve dans le fichier Workbook.xml et apparaît dans l’exemple de code suivant.
<?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>
Les fichiers XML de feuille de calcul contiennent un ou plusieurs éléments de niveau bloc tels que sheetData représente la table de cellules et contient un ou plusieurs éléments Row . Un élément Row contient un ou plusieurs éléments Cell (cellule). Chaque cellule contient un élément CellValue qui représente la valeur de la cellule. Par exemple, le SpreadsheetML de la première feuille de calcul d'un classeur, qui possède uniquement la valeur 100 dans la cellule A1, se trouve dans le fichier Sheet1.xml et est illustré dans l'exemple de code suivant.
<?xml version="1.0" encoding="UTF-8" ?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<sheetData>
<row r="1">
<c r="A1">
<v>100</v>
</c>
</row>
</sheetData>
</worksheet>
À l’aide du Kit de développement logiciel (SDK) Open XML, vous pouvez créer une structure de document et du contenu qui utilisent des classes fortement typées qui correspondent à des éléments SpreadsheetML . Vous pouvez trouver ces classes dans l'espace de noms DocumentFormat.OpenXML.Spreadsheet. La table suivante répertorie les noms des classes qui correspondent aux éléments workbook, sheets, sheet, worksheet et sheetData.
Élément SpreadsheetML | Classe du Kit de développement logiciel (SDK) Open XML | Description |
---|---|---|
workbook | DocumentFormat.OpenXML.Spreadsheet.Workbook | Élément racine du composant document principal. |
sheets | DocumentFormat.OpenXML.Spreadsheet.Sheets | Conteneur des structures de niveau bloc comme les éléments de feuille, fileVersion et autres spécifiés par la norme ISO/IEC 29500. |
sheet | DocumentFormat.OpenXml.Spreadsheet.Sheet | Feuille qui pointe vers un fichier de définition de feuille. |
feuille de calcul | Documentformat.openxml.spreadsheet. Feuilles de calcul | Fichier de définition de feuille qui contient les données de feuille. |
sheetData | DocumentFormat.OpenXML.Spreadsheet.SheetData | Tableau de cellules, regroupées par lignes. |
row | DocumentFormat.OpenXml.Spreadsheet.Row | Ligne dans le tableau de cellules. |
c | DocumentFormat.OpenXml.Spreadsheet.Cell | Cellule d'une ligne. |
v | DocumentFormat.OpenXml.Spreadsheet.CellValue | Valeur d'une cellule. |
Exemple de code
Le code suivant fusionne deux cellules adjacentes dans un package de document SpreadsheetDocument . Lors de la fusion de deux cellules, seul le contenu de l’une des cellules est conservé. Dans les langues qui s’écrivent de gauche à droite, le contenu de la cellule supérieure gauche est conservé. Dans les langues qui s’écrivent de droite à gauche, le contenu de la cellule supérieure droite est conservé.
Voici un exemple de code complet en C# et Visual Basic.
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
// 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.
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 is 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.
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.
static Worksheet? GetWorksheet(SpreadsheetDocument document, string worksheetName)
{
WorkbookPart workbookPart = document.WorkbookPart ?? document.AddWorkbookPart();
IEnumerable<Sheet> sheets = workbookPart.Workbook.Descendants<Sheet>().Where(s => s.Name == worksheetName);
string? id = sheets.First().Id;
WorksheetPart? worksheetPart = id is not null ? (WorksheetPart)workbookPart.GetPartById(id) : null;
return worksheetPart?.Worksheet;
}
// Given a cell name, parses the specified cell to get the column name.
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.
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);
}