Files
QWERTYkez.OpenXmlProcessors/QWERTYkez.ExcelProcessor/Editors/RangeBorderExtensions.cs
melekhin e373d4108a
All checks were successful
Publish NuGet packages / publish (push) Successful in 28s
many debugs
2026-06-19 15:06:40 +07:00

149 lines
7.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
}
}