many debugs
All checks were successful
Publish NuGet packages / publish (push) Successful in 28s

This commit is contained in:
melekhin
2026-06-19 15:06:40 +07:00
parent 08b39b7bfe
commit e373d4108a
24 changed files with 1569 additions and 632 deletions

View File

@@ -5,10 +5,10 @@
/// </summary>
internal sealed class ExcelRange : IRange
{
private readonly ExcelWriter _writer;
private readonly ExcelSheet _sheet;
private uint _rowStart, _rowEnd;
private uint _colStart, _colEnd;
internal readonly ExcelWriter _writer;
internal readonly ExcelSheet _sheet;
internal uint _rowStart, _rowEnd;
internal uint _colStart, _colEnd;
internal ExcelRange(ExcelWriter writer, ExcelSheet sheet, uint rowStart, uint colStart, uint rowEnd, uint colEnd)
{
@@ -55,17 +55,90 @@ internal sealed class ExcelRange : IRange
void ForEachCell(Action<ICell> action)
{
for (uint r = _rowStart; r <= _rowEnd; r++)
for (uint c = _colStart; c <= _colEnd; c++)
{
var cell = new ExcelCell(_writer, _sheet, r, c);
action(cell);
}
}
public IRange Set(NumberFormatPattern format)
{
if (format == null) return this;
_writer.ThrowIfDisposed();
lock (_writer._syncLock)
{
ForEachCell(cell => cell.Set(format));
return this;
}
}
public IRange Set(CellAlign align)
{
_writer.ThrowIfDisposed();
lock (_writer._syncLock)
{
ForEachCell(cell => cell.Set(align));
return this;
}
}
public IRange Set(CellBorder border)
{
_writer.ThrowIfDisposed();
lock (_writer._syncLock)
{
ForEachCell(cell => cell.Set(border));
return this;
}
}
public IRange Set(CellFill fill)
{
_writer.ThrowIfDisposed();
lock (_writer._syncLock)
{
ForEachCell(cell => cell.Set(fill));
return this;
}
}
public IRange Set(CellFont font)
{
_writer.ThrowIfDisposed();
lock (_writer._syncLock)
{
ForEachCell(cell => cell.Set(font));
return this;
}
}
public IRange Set(CellStyle style)
{
if (style == null) return this;
_writer.ThrowIfDisposed();
lock (_writer._syncLock)
{
ForEachCell(cell => cell.Set(style));
return this;
}
}
/// <summary>
/// Перемещает текущий диапазон в новую позицию (как "вырезать-вставить").
/// Исходный диапазон очищается, а текущий объект IRange перемещается на новое место.
/// </summary>
/// <param name="newRow">Номер строки для нового верхнего левого угла.</param>
/// <param name="newCol">Номер столбца для нового верхнего левого угла.</param>
/// <returns>Тот же объект IRange с новыми координатами.</returns>
public IRange MoveTo(uint newRow, uint newCol)
/// <summary>
/// Перемещает текущий диапазон в новую позицию (как "вырезать-вставить").
/// Исходный диапазон очищается, а текущий объект IRange перемещается на новое место.
/// </summary>
/// <param name="newRow">Номер строки для нового верхнего левого угла.</param>
/// <param name="newCol">Номер столбца для нового верхнего левого угла.</param>
/// <returns>Тот же объект IRange с новыми координатами.</returns>
public IRange MoveTo(uint newRow, uint newCol)
{
if (newRow == _rowStart && newCol == _colStart) return this;
_writer.ThrowIfDisposed();
@@ -115,7 +188,7 @@ internal sealed class ExcelRange : IRange
return MoveTo(rowIndex, CellAddressHelper.ColumnLetterToIndex(colIndex));
}
private enum CopyOrder
enum CopyOrder
{
Any,
LeftToRight,
@@ -125,7 +198,7 @@ internal sealed class ExcelRange : IRange
}
/// <summary>Копирует ячейки из исходного диапазона в целевой, поддерживая различные порядки обхода.</summary>
private void CopyCells(uint srcRowStart, uint srcColStart, uint srcRowEnd, uint srcColEnd,
void CopyCells(uint srcRowStart, uint srcColStart, uint srcRowEnd, uint srcColEnd,
uint dstRowStart, uint dstColStart, CopyOrder order)
{
// Определяем все строки, которые могут понадобиться (исходные и целевые)
@@ -172,7 +245,7 @@ internal sealed class ExcelRange : IRange
}
/// <summary>Строит словарь строк для указанных индексов строк.</summary>
private Dictionary<uint, Row> GetRowDictionary(HashSet<uint> rowIndices)
Dictionary<uint, Row> GetRowDictionary(HashSet<uint> rowIndices)
{
var dict = new Dictionary<uint, Row>();
var sheetData = _sheet.GetSheetData();
@@ -185,7 +258,7 @@ internal sealed class ExcelRange : IRange
}
/// <summary>Быстрый поиск ячейки в строке (линейный, подходит для типичного количества ячеек в строке).</summary>
private Cell? FindCellInRowFast(Row row, uint colIndex)
Cell? FindCellInRowFast(Row row, uint colIndex)
{
foreach (var cell in row.Elements<Cell>())
{
@@ -196,14 +269,14 @@ internal sealed class ExcelRange : IRange
}
/// <summary>Проверяет, пересекаются ли два прямоугольных диапазона.</summary>
private static bool RangesOverlap(uint r1s, uint c1s, uint r1e, uint c1e,
static bool RangesOverlap(uint r1s, uint c1s, uint r1e, uint c1e,
uint r2s, uint c2s, uint r2e, uint c2e)
{
return !(r1e < r2s || r2e < r1s || c1e < c2s || c2e < c1s);
}
/// <summary>Вставляет ячейку в указанную позицию, предварительно удаляя существующую.</summary>
private void InsertCellAt(uint row, uint col, Cell cell)
void InsertCellAt(uint row, uint col, Cell cell)
{
DeleteCellAt(row, col);
var sheetData = _sheet.GetSheetData();
@@ -212,7 +285,7 @@ internal sealed class ExcelRange : IRange
}
/// <summary>Удаляет ячейку, если она существует.</summary>
private void DeleteCellAt(uint row, uint col)
void DeleteCellAt(uint row, uint col)
{
var sheetData = _sheet.GetSheetData();
var rowElement = FindRowElement(sheetData, row);
@@ -222,7 +295,7 @@ internal sealed class ExcelRange : IRange
}
/// <summary>Получает или создаёт строку с указанным индексом.</summary>
private Row GetOrCreateRowElement(SheetData sheetData, uint rowIndex)
Row GetOrCreateRowElement(SheetData sheetData, uint rowIndex)
{
var existing = FindRowElement(sheetData, rowIndex);
if (existing != null) return existing;
@@ -232,7 +305,7 @@ internal sealed class ExcelRange : IRange
}
/// <summary>Вставляет ячейку в строку с сохранением порядка столбцов.</summary>
private void InsertCellInRow(Row row, Cell cell, uint colIndex)
void InsertCellInRow(Row row, Cell cell, uint colIndex)
{
string newRef = $"{CellAddressHelper.ColumnIndexToLetter(colIndex)}{row.RowIndex?.Value ?? 1}";
cell.CellReference = newRef;
@@ -251,7 +324,7 @@ internal sealed class ExcelRange : IRange
}
/// <summary>Очищает данные (содержимое) в указанном диапазоне.</summary>
private void ClearRangeData(uint rowStart, uint colStart, uint rowEnd, uint colEnd)
void ClearRangeData(uint rowStart, uint colStart, uint rowEnd, uint colEnd)
{
for (uint r = rowStart; r <= rowEnd; r++)
{
@@ -334,103 +407,12 @@ internal sealed class ExcelRange : IRange
return merged.Equals(range);
}
public IRange SetNumberFormat(NumberFormatPattern format)
{
if (format == null) return this;
_writer.ThrowIfDisposed();
lock (_writer._syncLock)
{
int fmtId = GetOrCreateNumberFormatId(format);
for (uint r = _rowStart; r <= _rowEnd; r++)
{
for (uint c = _colStart; c <= _colEnd; c++)
{
var cell = GetCellInternal(r, c);
cell?.StyleIndex = (uint)fmtId;
}
}
}
return this;
}
/// <inheritdoc />
public IRange SetCellAlign(CellAlign align)
{
_writer.ThrowIfDisposed();
lock (_writer._syncLock)
{
int styleIndex = _writer.GetOrCreateCellFormatId(
numberFormat: null,
font: null,
fill: null,
border: null,
align: align
);
ApplyStyleToRange((uint)styleIndex);
return this;
}
}
/// <inheritdoc />
public IRange SetCellBorder(CellBorder border)
{
_writer.ThrowIfDisposed();
lock (_writer._syncLock)
{
int styleIndex = _writer.GetOrCreateCellFormatId(
numberFormat: null,
font: null,
fill: null,
border: border,
align: null
);
ApplyStyleToRange((uint)styleIndex);
return this;
}
}
/// <inheritdoc />
public IRange SetCellFill(CellFill fill)
{
_writer.ThrowIfDisposed();
lock (_writer._syncLock)
{
int styleIndex = _writer.GetOrCreateCellFormatId(
numberFormat: null,
font: null,
fill: fill,
border: null,
align: null
);
ApplyStyleToRange((uint)styleIndex);
return this;
}
}
/// <inheritdoc />
public IRange SetCellFont(CellFont font)
{
_writer.ThrowIfDisposed();
lock (_writer._syncLock)
{
int styleIndex = _writer.GetOrCreateCellFormatId(
numberFormat: null,
font: font,
fill: null,
border: null,
align: null
);
ApplyStyleToRange((uint)styleIndex);
return this;
}
}
/// <summary>
/// Применяет стиль ко всем ячейкам диапазона.
/// </summary>
/// <param name="styleIndex">Индекс стиля для применения.</param>
/// <param name="createIfMissing">Если true, создаёт недостающие ячейки (по умолчанию true).</param>
private void ApplyStyleToRange(uint styleIndex, bool createIfMissing = true)
void ApplyStyleToRange(uint styleIndex, bool createIfMissing = true)
{
for (uint row = _rowStart; row <= _rowEnd; row++)
{
@@ -517,7 +499,7 @@ internal sealed class ExcelRange : IRange
{
if (GetSubCell(row, col, out var cell))
{
cell.Set(formula, format);
cell.SetFormula(formula, format);
return true;
}
return false;
@@ -619,7 +601,7 @@ internal sealed class ExcelRange : IRange
{
if (GetSubCell(row, col, out var cell))
{
cell.Set(formula, format);
cell.SetFormula(formula, format);
return true;
}
return false;
@@ -727,7 +709,7 @@ internal sealed class ExcelRange : IRange
// Вспомогательные методы
private Cell? GetCellInternal(uint row, uint col)
Cell? GetCellInternal(uint row, uint col)
{
var sheetData = _sheet.GetSheetData();
var rowElement = FindRowElement(sheetData, row);
@@ -735,7 +717,7 @@ internal sealed class ExcelRange : IRange
return FindCellInRow(rowElement, col);
}
private void CopyData(uint srcRowStart, uint srcColStart, uint srcRowEnd, uint srcColEnd, uint dstRowStart, uint dstColStart)
void CopyData(uint srcRowStart, uint srcColStart, uint srcRowEnd, uint srcColEnd, uint dstRowStart, uint dstColStart)
{
// Сохраняем все значения и форматы из исходного диапазона
var cellsData = new List<(uint row, uint col, Cell cell)>();
@@ -784,7 +766,7 @@ internal sealed class ExcelRange : IRange
}
}
private Row GetOrCreateRowElement(uint rowIndex)
Row GetOrCreateRowElement(uint rowIndex)
{
var existing = FindRowElement(_sheet.GetSheetData(), rowIndex);
if (existing != null) return existing;
@@ -793,14 +775,14 @@ internal sealed class ExcelRange : IRange
return newRow;
}
private static Row? FindRowElement(SheetData sheetData, uint rowIndex)
static Row? FindRowElement(SheetData sheetData, uint rowIndex)
{
foreach (var row in sheetData.Elements<Row>())
if (row.RowIndex?.Value == rowIndex) return row;
return null;
}
private static void InsertRowElement(SheetData sheetData, Row row, uint rowIndex)
static void InsertRowElement(SheetData sheetData, Row row, uint rowIndex)
{
bool inserted = false;
foreach (var existing in sheetData.Elements<Row>().ToList())
@@ -815,7 +797,7 @@ internal sealed class ExcelRange : IRange
if (!inserted) sheetData.Append(row);
}
private Cell? FindCellInRow(Row row, uint colIndex)
Cell? FindCellInRow(Row row, uint colIndex)
{
foreach (var cell in row.Elements<Cell>())
{
@@ -825,7 +807,7 @@ internal sealed class ExcelRange : IRange
return null;
}
private Column? GetColumnElementForIndex(uint col)
Column? GetColumnElementForIndex(uint col)
{
var worksheet = _sheet.Worksheet;
var cols = worksheet.GetFirstChild<Columns>();
@@ -839,7 +821,7 @@ internal sealed class ExcelRange : IRange
return null;
}
private Column GetOrCreateColumnElementForIndex(uint col)
Column GetOrCreateColumnElementForIndex(uint col)
{
var existing = GetColumnElementForIndex(col);
if (existing != null) return existing;
@@ -861,10 +843,10 @@ internal sealed class ExcelRange : IRange
return newCol;
}
private MergeCells? GetMergeCells() =>
MergeCells? GetMergeCells() =>
_sheet.Worksheet.GetFirstChild<MergeCells>();
private bool TryParseRangeReference(string reference, out ExcelRange range)
bool TryParseRangeReference(string reference, out ExcelRange range)
{
range = null!;
if (string.IsNullOrEmpty(reference)) return false;
@@ -877,10 +859,4 @@ internal sealed class ExcelRange : IRange
range = new ExcelRange(_writer, _sheet, rowStart, colStart, rowEnd, colEnd);
return true;
}
// создаёт стиль только с числовым форматом
private int GetOrCreateNumberFormatId(NumberFormatPattern format)
{
return _writer.GetOrCreateCellFormatId(numberFormat: format);
}
}