namespace QWERTYkez.ExcelProcessor; /// /// Методы расширения для установки границ диапазона. /// public static class RangeBorderExtensions { /// /// Устанавливает границы для диапазона с указанным стилем и цветом. /// /// Диапазон ячеек. /// Стиль линии границы. /// Цвет границы (необязательно). /// Какие границы применять (по умолчанию Outside). /// Тот же диапазон для цепочки вызовов. 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); /// /// Устанавливает границы для диапазона с указанными параметрами. /// /// Диапазон ячеек. /// Стиль и цвет границы. /// Какие границы применять (по умолчанию Outside). /// Тот же диапазон для цепочки вызовов. 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; } /// /// Очищает все границы в диапазоне (сбрасывает до None). /// 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 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); } }