Files
QWERTYkez.OpenXmlProcessors/QWERTYkez.ExcelProcessor/Editors/ExcelSheet.cs
melekhin 08b39b7bfe
All checks were successful
Publish NuGet packages / publish (push) Successful in 23s
Sheet.TryMergeBy...()
2026-06-17 10:39:00 +07:00

329 lines
11 KiB
C#

namespace QWERTYkez.ExcelProcessor;
/// <summary>
/// Внутренняя реализация <see cref="ISheet"/>.
/// </summary>
internal sealed class ExcelSheet : ISheet
{
internal readonly ExcelWriter Book;
internal readonly Sheet SheetElement;
internal readonly Worksheet Worksheet;
internal readonly uint SheetId;
internal ExcelSheet(ExcelWriter book, Sheet sheetElement, uint sheetId)
{
Book = book;
SheetElement = sheetElement;
SheetId = sheetId;
string partId = SheetElement.Id?.Value ?? string.Empty;
if (string.IsNullOrEmpty(partId))
throw new InvalidOperationException("Sheet has no relationship ID");
if (Book._doc.WorkbookPart?.GetPartById(partId) is not WorksheetPart part)
throw new InvalidOperationException("WorksheetPart not found");
if (part.Worksheet is not Worksheet worksheet)
throw new InvalidOperationException("WorksheetPart not found");
Worksheet = worksheet;
}
public int Index => (int)SheetId;
public string Name => SheetElement.Name?.Value ?? string.Empty;
public bool TrySetName(string name)
{
if (string.IsNullOrEmpty(name)) return false;
Book.ThrowIfDisposed();
SheetElement.Name = name;
return true;
}
public IRow Row(uint row) => new ExcelRow(Book, this, row);
public ISheet Row(uint row, Action<IRow> edit)
{
edit(Row(row));
return this;
}
public IColumn Col(uint col) => new ExcelColumn(Book, this, col);
public ISheet Col(uint col, Action<IColumn> edit)
{
edit(Col(col));
return this;
}
public IColumn Col(string col) => Col(CellAddressHelper.ColumnLetterToIndex(col));
public ISheet Col(string col, Action<IColumn> edit)
{
edit(Col(col));
return this;
}
public ICell Cell(uint row, uint col) => new ExcelCell(Book, this, row, col);
public ISheet Cell(uint row, uint col, Action<ICell> edit)
{
edit(Cell(row, col));
return this;
}
public ISheet Cell(uint row, uint col, string value)
{
Cell(row, col).Set(value); return this;
}
public ISheet Cell(uint row, uint col, bool value)
{
Cell(row, col).Set(value); return this;
}
public ISheet Cell(uint row, uint col, string formula, NumberFormatPattern? format = null)
{
Cell(row, col).Set(formula, format); return this;
}
public ISheet Cell(uint row, uint col, DateTime value, NumberFormatPattern? format = null)
{
Cell(row, col).Set(value, format); return this;
}
public ISheet Cell(uint row, uint col, decimal value, NumberFormatPattern? format = null)
{
Cell(row, col).Set(value, format); return this;
}
public ISheet Cell(uint row, uint col, double value, NumberFormatPattern? format = null)
{
Cell(row, col).Set(value, format); return this;
}
public ISheet Cell(uint row, uint col, float value, NumberFormatPattern? format = null)
{
Cell(row, col).Set(value, format); return this;
}
public ISheet Cell(uint row, uint col, int value, NumberFormatPattern? format = null)
{
Cell(row, col).Set(value, format); return this;
}
public ISheet Cell(uint row, uint col, long value, NumberFormatPattern? format = null)
{
Cell(row, col).Set(value, format); return this;
}
public ICell Cell(uint row, string col) => Cell(row, CellAddressHelper.ColumnLetterToIndex(col));
public ISheet Cell(uint row, string col, Action<ICell> edit)
{
edit(Cell(row, col));
return this;
}
public ISheet Cell(uint row, string col, string value)
{
Cell(row, col).Set(value); return this;
}
public ISheet Cell(uint row, string col, bool value)
{
Cell(row, col).Set(value); return this;
}
public ISheet Cell(uint row, string col, string formula, NumberFormatPattern? format = null)
{
Cell(row, col).Set(formula, format); return this;
}
public ISheet Cell(uint row, string col, DateTime value, NumberFormatPattern? format = null)
{
Cell(row, col).Set(value, format); return this;
}
public ISheet Cell(uint row, string col, decimal value, NumberFormatPattern? format = null)
{
Cell(row, col).Set(value, format); return this;
}
public ISheet Cell(uint row, string col, double value, NumberFormatPattern? format = null)
{
Cell(row, col).Set(value, format); return this;
}
public ISheet Cell(uint row, string col, float value, NumberFormatPattern? format = null)
{
Cell(row, col).Set(value, format); return this;
}
public ISheet Cell(uint row, string col, int value, NumberFormatPattern? format = null)
{
Cell(row, col).Set(value, format); return this;
}
public ISheet Cell(uint row, string col, long value, NumberFormatPattern? format = null)
{
Cell(row, col).Set(value, format); return this;
}
public void ClearContents()
{
var sheetData = GetSheetData();
foreach (var row in sheetData.Elements<Row>().ToList())
{
foreach (var cell in row.Elements<Cell>().ToList())
cell.Remove();
}
}
public void ClearFormats()
{
var sheetData = GetSheetData();
foreach (var row in sheetData.Elements<Row>())
{
foreach (var cell in row.Elements<Cell>())
cell.StyleIndex = null;
}
}
public void Clear()
{
ClearContents();
ClearFormats();
}
public void Remove() => Book.TryRemoveSheet(this);
// Вспомогательные методы
internal SheetData GetSheetData()
{
var worksheet = Worksheet;
var sheetData = worksheet.GetFirstChild<SheetData>();
if (sheetData == null)
{
sheetData = new SheetData();
worksheet.Append(sheetData);
}
return sheetData;
}
#region Range Operations
/// <inheritdoc />
public IRange RangeByIndexes(uint startRow, uint startCol, uint endRow, uint endCol)
{
if (startRow == 0) throw new ArgumentException("startRow must be >= 1", nameof(startRow));
if (startCol == 0) throw new ArgumentException("startCol must be >= 1", nameof(startCol));
if (endRow == 0) throw new ArgumentException("endRow must be >= 1", nameof(endRow));
if (endCol == 0) throw new ArgumentException("endCol must be >= 1", nameof(endCol));
// Приводим к корректному порядку (пользователь мог передать start > end)
uint rowStart = Math.Min(startRow, endRow);
uint rowEnd = Math.Max(startRow, endRow);
uint colStart = Math.Min(startCol, endCol);
uint colEnd = Math.Max(startCol, endCol);
return new ExcelRange(Book, this, rowStart, colStart, rowEnd, colEnd);
}
/// <inheritdoc />
public IRange RangeByIndexes(uint startRow, string startCol, uint endRow, string endCol)
{
if (string.IsNullOrEmpty(startCol)) throw new ArgumentException("startCol cannot be null or empty", nameof(startCol));
if (string.IsNullOrEmpty(endCol)) throw new ArgumentException("endCol cannot be null or empty", nameof(endCol));
uint startColIdx = CellAddressHelper.ColumnLetterToIndex(startCol);
uint endColIdx = CellAddressHelper.ColumnLetterToIndex(endCol);
if (startColIdx == 0) throw new ArgumentException($"Invalid column letter: '{startCol}'", nameof(startCol));
if (endColIdx == 0) throw new ArgumentException($"Invalid column letter: '{endCol}'", nameof(endCol));
return RangeByIndexes(startRow, startColIdx, endRow, endColIdx);
}
/// <inheritdoc />
public IRange RangeByLength(uint startRow, uint startCol, uint rows, uint cols)
{
if (startRow == 0) throw new ArgumentException("startRow must be >= 1", nameof(startRow));
if (startCol == 0) throw new ArgumentException("startCol must be >= 1", nameof(startCol));
if (rows == 0) throw new ArgumentException("rows must be > 0", nameof(rows));
if (cols == 0) throw new ArgumentException("cols must be > 0", nameof(cols));
checked
{
uint endRow = startRow + rows - 1;
uint endCol = startCol + cols - 1;
return new ExcelRange(Book, this, startRow, startCol, endRow, endCol);
}
}
/// <inheritdoc />
public IRange RangeByLength(uint startRow, string startCol, uint rows, uint cols)
{
if (string.IsNullOrEmpty(startCol)) throw new ArgumentException("startCol cannot be null or empty", nameof(startCol));
uint startColIdx = CellAddressHelper.ColumnLetterToIndex(startCol);
if (startColIdx == 0) throw new ArgumentException($"Invalid column letter: '{startCol}'", nameof(startCol));
return RangeByLength(startRow, startColIdx, rows, cols);
}
/// <inheritdoc />
public ISheet RangeByIndexes(uint startRow, uint startCol, uint endRow, uint endCol, Action<IRange> edit)
{
if (edit is null) throw new ArgumentNullException(nameof(edit));
var range = RangeByIndexes(startRow, startCol, endRow, endCol);
edit(range);
return this;
}
/// <inheritdoc />
public ISheet RangeByIndexes(uint startRow, string startCol, uint endRow, string endCol, Action<IRange> edit)
{
if (edit is null) throw new ArgumentNullException(nameof(edit));
var range = RangeByIndexes(startRow, startCol, endRow, endCol);
edit(range);
return this;
}
/// <inheritdoc />
public ISheet RangeByLength(uint startRow, uint startCol, uint rows, uint cols, Action<IRange> edit)
{
if (edit is null) throw new ArgumentNullException(nameof(edit));
var range = RangeByLength(startRow, startCol, rows, cols);
edit(range);
return this;
}
/// <inheritdoc />
public ISheet RangeByLength(uint startRow, string startCol, uint rows, uint cols, Action<IRange> edit)
{
if (edit is null) throw new ArgumentNullException(nameof(edit));
var range = RangeByLength(startRow, startCol, rows, cols);
edit(range);
return this;
}
#endregion
#region Merge Operations
/// <inheritdoc />
public bool TryMergeByIndexes(uint startRow, uint startCol, uint endRow, uint endCol) =>
RangeByIndexes(startRow, startCol, endRow, endCol).TryMerge();
/// <inheritdoc />
public bool TryMergeByIndexes(uint startRow, string startCol, uint endRow, string endCol) =>
RangeByIndexes(startRow, startCol, endRow, endCol).TryMerge();
/// <inheritdoc />
public bool TryMergeByLength(uint startRow, uint startCol, uint rows, uint cols) =>
RangeByLength(startRow, startCol, rows, cols).TryMerge();
/// <inheritdoc />
public bool TryMergeByLength(uint startRow, string startCol, uint rows, uint cols) =>
RangeByLength(startRow, startCol, rows, cols).TryMerge();
#endregion
}