Files

149 lines
7.6 KiB
C#
Raw Permalink Normal View History

2026-06-19 15:06:40 +07:00
namespace QWERTYkez.ExcelProcessor;
/// <summary>
/// Методы расширения для установки границ диапазона.
/// </summary>
public static class RangeBorderExtensions
{
/// <summary>
/// Устанавливает границы для диапазона с указанным стилем и цветом.
/// </summary>
/// <param name="range">Диапазон ячеек.</param>
/// <param name="style">Стиль линии границы.</param>
/// <param name="color">Цвет границы (необязательно).</param>
/// <param name="target">Какие границы применять (по умолчанию Outside).</param>
/// <returns>Тот же диапазон для цепочки вызовов.</returns>
public static IRange Set(this IRange range, BorderStyle style, BorderTarget target = BorderTarget.Outside,
System.Drawing.Color? color = null) => range.Set(new() { Style = style, Color = color ?? System.Drawing.Color.Black }, target);
/// <summary>
/// Устанавливает границы для диапазона с указанными параметрами.
/// </summary>
/// <param name="range">Диапазон ячеек.</param>
/// <param name="borderSide">Стиль и цвет границы.</param>
/// <param name="target">Какие границы применять (по умолчанию Outside).</param>
/// <returns>Тот же диапазон для цепочки вызовов.</returns>
public static IRange Set(this IRange range, BorderSide borderSide, BorderTarget target = BorderTarget.Outside)
{
if (range is null) throw new ArgumentNullException(nameof(range));
if (range is not ExcelRange excelRange)
throw new ArgumentException("Range must be of type ExcelRange", nameof(range));
var writer = excelRange._writer;
var sheet = excelRange._sheet;
uint rowStart = range.RowStart;
uint rowEnd = range.RowEnd;
uint colStart = range.ColStart;
uint colEnd = range.ColEnd;
writer.ThrowIfDisposed();
lock (writer._syncLock)
{
// Определяем, какие стороны нужны для каждой ячейки
for (uint r = rowStart; r <= rowEnd; r++)
{
for (uint c = colStart; c <= colEnd; c++)
{
var cell = new ExcelCell(writer, sheet, r, c);
var newBorder = new CellBorder();
bool isTopRow = (r == rowStart);
bool isBottomRow = (r == rowEnd);
bool isLeftCol = (c == colStart);
bool isRightCol = (c == colEnd);
// В зависимости от target, определяем, какие стороны устанавливать
if (target == BorderTarget.All || target == BorderTarget.Outside)
{
if (isTopRow) newBorder = newBorder with { TopBorder = borderSide };
if (isBottomRow) newBorder = newBorder with { BottomBorder = borderSide };
if (isLeftCol) newBorder = newBorder with { LeftBorder = borderSide };
if (isRightCol) newBorder = newBorder with { RightBorder = borderSide };
}
if (target == BorderTarget.All || target == BorderTarget.Inside)
{
// Внутренние границы: для каждой ячейки устанавливаем правую и нижнюю,
// если есть сосед справа/снизу внутри диапазона
if (c < colEnd) newBorder = newBorder with { RightBorder = borderSide };
if (r < rowEnd) newBorder = newBorder with { BottomBorder = borderSide };
}
// Отдельные стороны
if (target == BorderTarget.Top && isTopRow)
newBorder = newBorder with { TopBorder = borderSide };
if (target == BorderTarget.Bottom && isBottomRow)
newBorder = newBorder with { BottomBorder = borderSide };
if (target == BorderTarget.Left && isLeftCol)
newBorder = newBorder with { LeftBorder = borderSide };
if (target == BorderTarget.Right && isRightCol)
newBorder = newBorder with { RightBorder = borderSide };
// Применяем границу к ячейке (изолированно, без каскада)
cell.SetBorderIsolate(newBorder);
// Если ячейка находится на внешней границе диапазона, очищаем соответствующую сторону у соседа вне диапазона
if (isTopRow && (target == BorderTarget.Outside || target == BorderTarget.All || target == BorderTarget.Top))
ClearNeighborBorder(cell, -1, 0, b => b with { BottomBorder = null });
if (isBottomRow && (target == BorderTarget.Outside || target == BorderTarget.All || target == BorderTarget.Bottom))
ClearNeighborBorder(cell, 1, 0, b => b with { TopBorder = null });
if (isLeftCol && (target == BorderTarget.Outside || target == BorderTarget.All || target == BorderTarget.Left))
ClearNeighborBorder(cell, 0, -1, b => b with { RightBorder = null });
if (isRightCol && (target == BorderTarget.Outside || target == BorderTarget.All || target == BorderTarget.Right))
ClearNeighborBorder(cell, 0, 1, b => b with { LeftBorder = null });
}
}
}
return range;
}
/// <summary>
/// Очищает все границы в диапазоне (сбрасывает до None).
/// </summary>
public static IRange ClearBorders(this IRange range)
{
if (range is null) throw new ArgumentNullException(nameof(range));
if (range is not ExcelRange excelRange)
throw new ArgumentException("Range must be of type ExcelRange", nameof(range));
var writer = excelRange._writer;
var sheet = excelRange._sheet;
uint rowStart = range.RowStart;
uint rowEnd = range.RowEnd;
uint colStart = range.ColStart;
uint colEnd = range.ColEnd;
writer.ThrowIfDisposed();
lock (writer._syncLock)
{
for (uint r = rowStart; r <= rowEnd; r++)
{
for (uint c = colStart; c <= colEnd; c++)
{
var cell = new ExcelCell(writer, sheet, r, c);
// Удаляем все границы
cell.SetBorderIsolate(new CellBorder()); // пустая граница
}
}
}
return range;
}
// Вспомогательный метод для очистки конкретной стороны у соседа
static void ClearNeighborBorder(ExcelCell cell, int rowOffset, int colOffset, Func<CellBorder, CellBorder> clearFunc)
{
var neighbor = cell.GetNeighbor(rowOffset, colOffset);
if (neighbor is null)
return;
var neighborBorder = neighbor.GetCellBorder();
var newBorder = clearFunc(neighborBorder);
if (!neighborBorder.Equals(newBorder))
neighbor.SetBorderIsolate(newBorder);
}
}