This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user