149 lines
7.6 KiB
C#
149 lines
7.6 KiB
C#
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);
|
||
}
|
||
} |