Skip to content

Commit f5ced00

Browse files
committed
feat: Update CopySheet and RenameSheet methods to return boolean status
- Modified CopySheet method to return a boolean indicating whether the sheet was successfully copied. - Enhanced RenameSheet method to return a boolean indicating whether the sheet was successfully renamed. - Updated XML documentation to reflect the new return values for both methods. These changes provide better feedback on the operations' success when copying or renaming sheets.
1 parent 4a2d3cc commit f5ced00

File tree

2 files changed

+166
-3
lines changed

2 files changed

+166
-3
lines changed

FileFormat.Cells/Workbook.cs

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,11 @@ public bool RemoveSheet(string sheetName)
287287
/// Renames an existing sheet within the workbook.
288288
/// </summary>
289289
/// <param name="existingSheetName">The current name of the sheet to be renamed.</param>
290-
/// <param name="newSheetName">The new name to be assigned to the sheet.</param>
291-
public void RenameSheet(string existingSheetName, string newSheetName)
290+
/// <param name="newSheetName">The new name for the sheet.</param>
291+
/// <returns>
292+
/// Returns <c>true</c> if the sheet is successfully renamed; otherwise, <c>false</c>.
293+
/// </returns>
294+
public bool RenameSheet(string existingSheetName, string newSheetName)
292295
{
293296
// Find the sheet by its existing name
294297
var sheet = workbookpart.Workbook.Descendants<Sheet>().FirstOrDefault(s => s.Name == existingSheetName);
@@ -301,15 +304,20 @@ public void RenameSheet(string existingSheetName, string newSheetName)
301304

302305
// Synchronize the Worksheets property with the Sheets of the workbook
303306
SyncWorksheets();
307+
return true; // Sheet renamed successfully
304308
}
309+
return false; // Sheet not found, rename failed
305310
}
306311

307312
/// <summary>
308313
/// Copies an existing sheet within the workbook to a new sheet.
309314
/// </summary>
310315
/// <param name="sourceSheetName">The name of the sheet to be copied.</param>
311316
/// <param name="newSheetName">The name of the new sheet to be created.</param>
312-
public void CopySheet(string sourceSheetName, string newSheetName)
317+
/// <returns>
318+
/// Returns <c>true</c> if the sheet is successfully copied; otherwise, <c>false</c>.
319+
/// </returns>
320+
public bool CopySheet(string sourceSheetName, string newSheetName)
313321
{
314322
// Find the source sheet by its name
315323
Sheet sourceSheet = workbookpart.Workbook.Descendants<Sheet>().FirstOrDefault(s => s.Name == sourceSheetName);
@@ -335,7 +343,10 @@ public void CopySheet(string sourceSheetName, string newSheetName)
335343
// Append the new sheet to the workbook
336344
workbookpart.Workbook.Sheets.Append(newSheet);
337345
workbookpart.Workbook.Save(); // Save changes to the workbook
346+
347+
return true; // Sheet copied successfully
338348
}
349+
return false; // Source sheet not found, copy failed
339350
}
340351

341352

@@ -405,6 +416,50 @@ public void SetSheetVisibility(string sheetName, SheetVisibility visibility)
405416
}
406417
}
407418

419+
/// <summary>
420+
/// Retrieves a list of hidden or very hidden sheets from the workbook.
421+
/// </summary>
422+
/// <returns>A list of tuples where each tuple contains the sheet ID and sheet name for hidden sheets.</returns>
423+
/// <exception cref="InvalidOperationException">Thrown when the workbook part is not initialized.</exception>
424+
/// <exception cref="ArgumentNullException">Thrown when a sheet's ID or Name is null.</exception>
425+
public List<Tuple<string, string>> GetHiddenSheets()
426+
{
427+
if (workbookpart is null)
428+
throw new InvalidOperationException("WorkbookPart is not initialized.");
429+
430+
List<Tuple<string, string>> returnVal = new List<Tuple<string, string>>();
431+
432+
// Retrieves all sheets in the workbook.
433+
// Reference: DocumentFormat.OpenXml.Spreadsheet.Sheet
434+
var sheets = workbookpart.Workbook.Descendants<Sheet>();
435+
436+
// Look for sheets where there is a State attribute defined,
437+
// where the State has a value,
438+
// and where the value is either Hidden or VeryHidden.
439+
// Reference: DocumentFormat.OpenXml.Spreadsheet.SheetStateValues
440+
var hiddenSheets = sheets.Where(item => item.State is not null &&
441+
item.State.HasValue &&
442+
(item.State.Value == SheetStateValues.Hidden ||
443+
item.State.Value == SheetStateValues.VeryHidden));
444+
445+
// Populate the return list with the sheet ID and name as tuples.
446+
foreach (var sheet in hiddenSheets)
447+
{
448+
// Check if sheet ID or Name is null
449+
if (sheet.Id is null)
450+
throw new ArgumentNullException(nameof(sheet.Id), "Sheet ID cannot be null.");
451+
452+
if (sheet.Name is null)
453+
throw new ArgumentNullException(nameof(sheet.Name), "Sheet Name cannot be null.");
454+
455+
returnVal.Add(new Tuple<string, string>(
456+
sheet.Id, // The ID of the sheet, typically used to reference the sheet in the workbook.
457+
sheet.Name // The name of the sheet as displayed in the workbook.
458+
));
459+
}
460+
461+
return returnVal;
462+
}
408463

409464
/// <summary>
410465
/// Synchronize the Worksheets property with the actual sheets present in the workbook.

FileFormat.Cells/Worksheet.cs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1535,6 +1535,114 @@ public void InsertColumns(string startColumn, int numberOfColumns)
15351535
_worksheetPart.Worksheet.Save();
15361536
}
15371537

1538+
/// <summary>
1539+
/// Gets the column heading for a specified cell in the worksheet.
1540+
/// </summary>
1541+
/// <param name="cellName">The name of the cell (e.g., "A1").</param>
1542+
/// <returns>The text of the column heading, or null if the column does not exist.</returns>
1543+
/// <exception cref="ArgumentException">Thrown when the cellName is null or empty.</exception>
1544+
/// <exception cref="InvalidOperationException">Thrown when the WorkbookPart is not found, the sheet is not found,
1545+
/// the column name is invalid, no header cell is found, or the SharedStringTablePart is missing.</exception>
1546+
/// <exception cref="IndexOutOfRangeException">Thrown when the shared string index is out of range.</exception>
1547+
public string? GetColumnHeading(string cellName)
1548+
{
1549+
if (string.IsNullOrEmpty(cellName))
1550+
throw new ArgumentException("Cell name cannot be null or empty.", nameof(cellName));
1551+
1552+
var workbookPart = _worksheetPart.GetParentParts().OfType<WorkbookPart>().FirstOrDefault();
1553+
if (workbookPart == null)
1554+
throw new InvalidOperationException("No WorkbookPart found.");
1555+
1556+
var sheets = workbookPart.Workbook.Descendants<DocumentFormat.OpenXml.Spreadsheet.Sheet>();
1557+
var sheet = sheets.FirstOrDefault(s => workbookPart.GetPartById(s.Id) == _worksheetPart);
1558+
1559+
if (sheet == null)
1560+
throw new InvalidOperationException("No matching sheet found for the provided WorksheetPart.");
1561+
1562+
WorksheetPart worksheetPart = _worksheetPart;
1563+
1564+
// Get the column name for the specified cell.
1565+
string columnName = GetColumnName(cellName);
1566+
1567+
if (string.IsNullOrEmpty(columnName))
1568+
throw new InvalidOperationException("Unable to determine the column name from the provided cell name.");
1569+
1570+
// Get the cells in the specified column and order them by row.
1571+
IEnumerable<DocumentFormat.OpenXml.Spreadsheet.Cell> cells = worksheetPart.Worksheet.Descendants<DocumentFormat.OpenXml.Spreadsheet.Cell>()
1572+
.Where(c => string.Compare(GetColumnName(c.CellReference?.Value), columnName, true) == 0)
1573+
.OrderBy(r => GetRowIndexN(r.CellReference) ?? 0);
1574+
1575+
if (!cells.Any())
1576+
{
1577+
// The specified column does not exist.
1578+
return null;
1579+
}
1580+
1581+
// Get the first cell in the column.
1582+
DocumentFormat.OpenXml.Spreadsheet.Cell headCell = cells.First();
1583+
1584+
if (headCell == null)
1585+
throw new InvalidOperationException("No header cell found in the specified column.");
1586+
1587+
// If the content of the first cell is stored as a shared string, get the text of the first cell
1588+
// from the SharedStringTablePart and return it. Otherwise, return the string value of the cell.
1589+
if (headCell.DataType != null && headCell.DataType.Value == CellValues.SharedString && int.TryParse(headCell.CellValue?.Text, out int index))
1590+
{
1591+
var sharedStringPart = workbookPart.GetPartsOfType<SharedStringTablePart>().FirstOrDefault();
1592+
if (sharedStringPart == null)
1593+
throw new InvalidOperationException("No SharedStringTablePart found.");
1594+
1595+
var items = sharedStringPart.SharedStringTable.Elements<SharedStringItem>().ToArray();
1596+
if (index < 0 || index >= items.Length)
1597+
throw new IndexOutOfRangeException("Shared string index is out of range.");
1598+
1599+
return items[index].InnerText;
1600+
}
1601+
else
1602+
{
1603+
return headCell.CellValue?.Text;
1604+
}
1605+
}
1606+
1607+
1608+
/// <summary>
1609+
/// Gets the row index from the specified cell name.
1610+
/// </summary>
1611+
/// <param name="cellName">The cell name in A1 notation (e.g., "A1").</param>
1612+
/// <returns>The row index as a nullable unsigned integer, or null if the cell name is invalid.</returns>
1613+
/// <exception cref="FormatException">Thrown when the row index portion of the cell name cannot be parsed.</exception>
1614+
private uint? GetRowIndexN(string? cellName)
1615+
{
1616+
if (cellName is null)
1617+
{
1618+
return null;
1619+
}
1620+
1621+
// Create a regular expression to match the row index portion the cell name.
1622+
Regex regex = new Regex(@"\d+");
1623+
Match match = regex.Match(cellName);
1624+
1625+
return uint.Parse(match.Value);
1626+
}
1627+
1628+
/// <summary>
1629+
/// Given a cell name, parses the specified cell to get the column name.
1630+
/// </summary>
1631+
/// <param name="cellName">The cell name in A1 notation (e.g., "A1").</param>
1632+
/// <returns>The column name as a string, or an empty string if the cell name is invalid.</returns>
1633+
private string GetColumnName(string? cellName)
1634+
{
1635+
if (cellName is null)
1636+
{
1637+
return string.Empty;
1638+
}
1639+
1640+
// Create a regular expression to match the column name portion of the cell name.
1641+
Regex regex = new Regex("[A-Za-z]+");
1642+
Match match = regex.Match(cellName);
1643+
1644+
return match.Value;
1645+
}
15381646
private static string IncrementColumnReference(string reference, int columnCount)
15391647
{
15401648
var regex = new System.Text.RegularExpressions.Regex("([A-Za-z]+)(\\d+)");

0 commit comments

Comments
 (0)