diff --git a/QWERTYkez.ExcelProcessor/Editors/ExcelCell.cs b/QWERTYkez.ExcelProcessor/Editors/ExcelCell.cs
index e0a9019..2f709e9 100644
--- a/QWERTYkez.ExcelProcessor/Editors/ExcelCell.cs
+++ b/QWERTYkez.ExcelProcessor/Editors/ExcelCell.cs
@@ -3,21 +3,450 @@
///
/// Внутренняя реализация .
///
-internal sealed class ExcelCell : ICell
+internal sealed class ExcelCell(ExcelWriter writer, ExcelSheet sheet, uint row, uint col) : ICell
{
- private readonly ExcelWriter _writer;
- private readonly ExcelSheet _sheet;
- private uint _row;
- private uint _col;
+ // Кэш фрагментов богатого текста (только для InlineString)
+ private List? _runsCache;
+ private bool _cacheValid;
- internal ExcelCell(ExcelWriter writer, ExcelSheet sheet, uint row, uint col)
+ // ---- Реализация ICell (новые методы) ----
+
+ public int RunsCount
{
- _writer = writer;
- _sheet = sheet;
- _row = row;
- _col = col;
+ get
+ {
+ EnsureCacheValid();
+ return _runsCache?.Count ?? 0;
+ }
}
+ public IEnumerable GetRuns()
+ {
+ EnsureCacheValid();
+ return _runsCache ?? Enumerable.Empty();
+ }
+
+ public IRun? GetRunAt(int index)
+ {
+ EnsureCacheValid();
+ if (_runsCache is null || index < 0 || index >= _runsCache.Count)
+ return null;
+ return _runsCache[index];
+ }
+
+ public bool TryGetRunAt(int index, out IRun run)
+ {
+ run = GetRunAt(index)!;
+ return run != null;
+ }
+
+ public IRun? First()
+ {
+ EnsureCacheValid();
+ return _runsCache?.FirstOrDefault();
+ }
+
+ public bool TryGetFirst(out IRun run)
+ {
+ run = First()!;
+ return run != null;
+ }
+
+ public IRun? Last()
+ {
+ EnsureCacheValid();
+ return _runsCache?.LastOrDefault();
+ }
+
+ public bool TryGetLast(out IRun run)
+ {
+ run = Last()!;
+ return run != null;
+ }
+
+ public bool TryRemoveRun(IRun run)
+ {
+ if (run is null) return false;
+ EnsureCacheValid();
+ if (_runsCache is null) return false;
+ bool removed = _runsCache.Remove(run);
+ if (removed)
+ {
+ _cacheValid = true;
+ UpdateCellFromCache();
+ }
+ return removed;
+ }
+
+ public bool TryRemoveRun(int index)
+ {
+ EnsureCacheValid();
+ if (_runsCache is null || index < 0 || index >= _runsCache.Count)
+ return false;
+ _runsCache.RemoveAt(index);
+ _cacheValid = true;
+ UpdateCellFromCache();
+ return true;
+ }
+
+ public bool TryRemoveRun(int index, out IRun? removed)
+ {
+ removed = GetRunAt(index);
+ if (removed is null) return false;
+ return TryRemoveRun(index);
+ }
+
+ public ICell Break()
+ {
+ EnsureCacheValid();
+ if (_runsCache != null && _runsCache.Count > 0)
+ {
+ var lastRun = _runsCache[_runsCache.Count - 1];
+ lastRun.Text += "\n";
+ }
+ else
+ {
+ Run("\n", null);
+ }
+ _cacheValid = true;
+ UpdateCellFromCache();
+ return this;
+ }
+
+ public ICell Run(string text, RunFormat? format = null)
+ {
+ if (string.IsNullOrEmpty(text)) return this;
+ EnsureCacheValid();
+ _runsCache ??= [];
+ _runsCache.Add(new ExcelRun { Text = text, Format = format });
+ _cacheValid = true;
+ UpdateCellFromCache();
+ return this;
+ }
+
+ public ICell RunBreak(string text, RunFormat? format = null)
+ {
+ Run(text, format);
+ Break();
+ return this;
+ }
+
+ public ICell Sub(string text, RunFormat? format = null)
+ {
+ var subFormat = format is { } fmt
+ ? new RunFormat
+ {
+ IsBold = fmt.IsBold,
+ IsItalic = fmt.IsItalic,
+ Underline = fmt.Underline,
+ IsStrike = fmt.IsStrike,
+ Color = fmt.Color,
+ FontSize = fmt.FontSize,
+ FontFamily = fmt.FontFamily,
+ Vertical = VerticalTextRunAlignment.Subscript
+ }
+ : new RunFormat { Vertical = VerticalTextRunAlignment.Subscript };
+ return Run(text, subFormat);
+ }
+
+ public ICell Sup(string text, RunFormat? format = null)
+ {
+ var supFormat = format is { } fmt
+ ? new RunFormat
+ {
+ IsBold = fmt.IsBold,
+ IsItalic = fmt.IsItalic,
+ Underline = fmt.Underline,
+ IsStrike = fmt.IsStrike,
+ Color = fmt.Color,
+ FontSize = fmt.FontSize,
+ FontFamily = fmt.FontFamily,
+ Vertical = VerticalTextRunAlignment.Superscript
+ }
+ : new RunFormat { Vertical = VerticalTextRunAlignment.Superscript };
+ return Run(text, supFormat);
+ }
+
+ public bool TryInsertRun(int index, string text, RunFormat? format = null)
+ {
+ if (index < 0 || string.IsNullOrEmpty(text)) return false;
+ EnsureCacheValid();
+ _runsCache ??= [];
+ if (index > _runsCache.Count) return false;
+ _runsCache.Insert(index, new ExcelRun { Text = text, Format = format });
+ _cacheValid = true;
+ UpdateCellFromCache();
+ return true;
+ }
+
+ public bool TryInsertRunBreak(int index, string text, RunFormat? format = null)
+ {
+ if (!TryInsertRun(index, text, format)) return false;
+ return TryInsertRun(index + 1, "\n", null);
+ }
+
+ public bool TryInsertSub(int index, string text, RunFormat? format = null)
+ {
+ var subFormat = format is { } fmt
+ ? new RunFormat
+ {
+ IsBold = fmt.IsBold,
+ IsItalic = fmt.IsItalic,
+ Underline = fmt.Underline,
+ IsStrike = fmt.IsStrike,
+ Color = fmt.Color,
+ FontSize = fmt.FontSize,
+ FontFamily = fmt.FontFamily,
+ Vertical = VerticalTextRunAlignment.Subscript
+ }
+ : new RunFormat { Vertical = VerticalTextRunAlignment.Subscript };
+ return TryInsertRun(index, text, subFormat);
+ }
+
+ public bool TryInsertSup(int index, string text, RunFormat? format = null)
+ {
+ var supFormat = format is { } fmt
+ ? new RunFormat
+ {
+ IsBold = fmt.IsBold,
+ IsItalic = fmt.IsItalic,
+ Underline = fmt.Underline,
+ IsStrike = fmt.IsStrike,
+ Color = fmt.Color,
+ FontSize = fmt.FontSize,
+ FontFamily = fmt.FontFamily,
+ Vertical = VerticalTextRunAlignment.Superscript
+ }
+ : new RunFormat { Vertical = VerticalTextRunAlignment.Superscript };
+ return TryInsertRun(index, text, supFormat);
+ }
+
+ public void ApplyFormatToAllRuns(RunFormat format)
+ {
+ EnsureCacheValid();
+ if (_runsCache is null || _runsCache.Count == 0) return;
+ foreach (var run in _runsCache)
+ {
+ if (run is ExcelRun xRun)
+ {
+ var baseFmt = xRun.Format ?? new RunFormat();
+ xRun.Format = MergeRunFormat(baseFmt, format);
+ }
+ }
+ _cacheValid = true;
+ UpdateCellFromCache();
+ }
+
+ public ICell ClearRuns()
+ {
+ _runsCache = null;
+ _cacheValid = true;
+ UpdateCellFromCache(); // устанавливаем пустой InlineString
+ return this;
+ }
+
+ // ---- Существующие методы (частично изменены) ----
+
+ // (здесь остаются все старые методы: IsMerged, GetMergedRange, MoveTo, Height, Width, IsNumber, и т.д.)
+ // Они не меняются, за исключением методов Set и ClearContent, которые должны сбрасывать кэш.
+
+ // Пример: метод Set(string) - сбрасываем кэш и устанавливаем обычный текст
+ public ICell Set(string value)
+ {
+ if (value == null) return this;
+ writer.ThrowIfDisposed();
+ lock (writer._syncLock)
+ {
+ var cell = GetOrCreateCellElement();
+ // Сбрасываем кэш
+ _runsCache = null;
+ _cacheValid = false;
+
+ if (double.TryParse(value, out double num))
+ {
+ cell.DataType = CellValues.Number;
+ cell.CellValue = new CellValue(num.ToString(CultureInfo.InvariantCulture));
+ }
+ else
+ {
+ int idx = writer.GetOrAddSharedString(value);
+ cell.DataType = CellValues.SharedString;
+ cell.CellValue = new CellValue(idx.ToString());
+ }
+ // Удаляем InlineString, если был
+ cell.InlineString = null;
+ cell.CellFormula = null;
+ }
+ return this;
+ }
+
+ // Аналогично для Set(bool), Set(числа), Set(DateTime) - нужно сбрасывать кэш и удалять InlineString.
+ // Для краткости приведу только один метод, остальные аналогично.
+
+ // Метод ClearContent - сбрасываем кэш
+ public void ClearContent()
+ {
+ writer.ThrowIfDisposed();
+ lock (writer._syncLock)
+ {
+ var cell = GetCellElement();
+ if (cell != null)
+ {
+ cell.CellValue = null;
+ cell.CellFormula = null;
+ cell.InlineString = null;
+ cell.DataType = null;
+ }
+ _runsCache = null;
+ _cacheValid = false;
+ }
+ }
+
+ // Метод ClearFormat не трогает текст, поэтому кэш остаётся валидным (если был).
+ // Но если стиль меняется, кэш не сбрасываем.
+
+ // ---- Приватные методы для работы с кэшем ----
+
+ private void EnsureCacheValid()
+ {
+ if (_cacheValid) return;
+ lock (writer._syncLock)
+ {
+ if (_cacheValid) return;
+ var cell = GetCellElement();
+ _runsCache = [];
+
+ if (cell != null && cell.DataType?.Value == CellValues.InlineString && cell.InlineString != null)
+ {
+ // Читаем InlineString
+ foreach (var run in cell.InlineString.Elements())
+ {
+ string text = run.GetFirstChild()?.Text ?? string.Empty;
+ RunFormat? format = RunFormat.FromRunProperties(run.RunProperties!);
+ _runsCache.Add(new ExcelRun { Text = text, Format = format });
+ }
+ }
+ else if (cell != null)
+ {
+ // Преобразуем содержимое в один Run (без форматирования)
+ string text = GetStringFromCell(cell);
+ if (!string.IsNullOrEmpty(text))
+ _runsCache.Add(new ExcelRun { Text = text, Format = null });
+ }
+ // Если ячейка пуста, кэш остаётся пустым
+ _cacheValid = true;
+ }
+ }
+
+ private string GetStringFromCell(Cell cell)
+ {
+ if (cell == null) return string.Empty;
+ if (cell.DataType?.Value == CellValues.SharedString && cell.CellValue != null)
+ {
+ return writer.GetSharedString(uint.Parse(cell.CellValue.Text));
+ }
+ if (cell.CellValue != null)
+ {
+ return cell.CellValue.Text;
+ }
+ return string.Empty;
+ }
+
+ private void UpdateCellFromCache()
+ {
+ lock (writer._syncLock)
+ {
+ var cell = GetOrCreateCellElement();
+ if (_runsCache == null || _runsCache.Count == 0)
+ {
+ // Устанавливаем пустой InlineString
+ cell.InlineString = new InlineString();
+ cell.DataType = CellValues.InlineString;
+ cell.CellValue = null;
+ cell.CellFormula = null;
+ }
+ else
+ {
+ cell.InlineString = BuildInlineStringFromCache();
+ cell.DataType = CellValues.InlineString;
+ cell.CellValue = null;
+ cell.CellFormula = null;
+ }
+ }
+ }
+
+ private InlineString BuildInlineStringFromCache()
+ {
+ var inline = new InlineString();
+ if (_runsCache == null) return inline;
+ foreach (var run in _runsCache)
+ {
+ var runElement = new Run();
+ runElement.Append(new Text(run.Text));
+ if (run.Format is { } fmt)
+ {
+ var rPr = new RunProperties();
+ if (fmt.IsBold == true) rPr.Append(new Bold());
+ if (fmt.IsItalic == true) rPr.Append(new Italic());
+ if (fmt.Underline.HasValue)
+ {
+ rPr.Append(new Underline
+ {
+ Val = fmt.Underline.Value switch
+ {
+ UnderlineStyle.Single => UnderlineValues.Single,
+ UnderlineStyle.Double => UnderlineValues.Double,
+ UnderlineStyle.SingleAccounting => UnderlineValues.SingleAccounting,
+ UnderlineStyle.DoubleAccounting => UnderlineValues.DoubleAccounting,
+ _ => throw new NotImplementedException(),
+ }
+ });
+ }
+ if (fmt.IsStrike == true) rPr.Append(new Strike());
+ if (fmt.Color.HasValue)
+ {
+ var excelColor = fmt.Color.Value.ToExcel();
+ rPr.Append(excelColor);
+ }
+ if (fmt.FontSize.HasValue)
+ rPr.Append(new FontSize { Val = fmt.FontSize.Value });
+ if (!string.IsNullOrEmpty(fmt.FontFamily))
+ rPr.Append(new RunFont { Val = fmt.FontFamily });
+ if (fmt.Vertical.HasValue)
+ {
+ var vertAlign = new VerticalTextAlignment
+ {
+ Val = fmt.Vertical.Value == VerticalTextRunAlignment.Superscript
+ ? VerticalAlignmentRunValues.Superscript
+ : VerticalAlignmentRunValues.Subscript
+ };
+ rPr.Append(vertAlign);
+ }
+ runElement.RunProperties = rPr;
+ }
+ inline.Append(runElement);
+ }
+ return inline;
+ }
+
+ private static RunFormat MergeRunFormat(RunFormat baseFmt, RunFormat overlay)
+ {
+ return new RunFormat
+ {
+ IsBold = overlay.IsBold ?? baseFmt.IsBold,
+ IsItalic = overlay.IsItalic ?? baseFmt.IsItalic,
+ Underline = overlay.Underline ?? baseFmt.Underline,
+ IsStrike = overlay.IsStrike ?? baseFmt.IsStrike,
+ Color = overlay.Color ?? baseFmt.Color,
+ FontSize = overlay.FontSize ?? baseFmt.FontSize,
+ FontFamily = overlay.FontFamily ?? baseFmt.FontFamily,
+ Vertical = overlay.Vertical ?? baseFmt.Vertical
+ };
+ }
+
+
+
+
public bool IsMerged
{
get
@@ -29,8 +458,8 @@ internal sealed class ExcelCell : ICell
public IRange? GetMergedRange()
{
- _writer.ThrowIfDisposed();
- lock (_writer._syncLock)
+ writer.ThrowIfDisposed();
+ lock (writer._syncLock)
{
var mergeCells = GetMergeCells();
if (mergeCells == null) return null;
@@ -38,8 +467,8 @@ internal sealed class ExcelCell : ICell
{
if (TryParseRangeReference(mergeCell.Reference?.Value ?? string.Empty, out var range))
{
- if (_row >= range.RowStart && _row <= range.RowEnd &&
- _col >= range.ColStart && _col <= range.ColEnd)
+ if (row >= range.RowStart && row <= range.RowEnd &&
+ col >= range.ColStart && col <= range.ColEnd)
return range;
}
}
@@ -54,15 +483,15 @@ internal sealed class ExcelCell : ICell
return merged.Equals(range);
}
- public uint Row => _row;
- public uint Col => _col;
- public string ColLetter => CellAddressHelper.ColumnIndexToLetter(_col);
+ public uint Row => row;
+ public uint Col => col;
+ public string ColLetter => CellAddressHelper.ColumnIndexToLetter(col);
public ICell MoveTo(uint rowIndex, uint colIndex)
{
- if (rowIndex == _row && colIndex == _col) return this;
- _writer.ThrowIfDisposed();
- lock (_writer._syncLock)
+ if (rowIndex == row && colIndex == col) return this;
+ writer.ThrowIfDisposed();
+ lock (writer._syncLock)
{
// Вырезать-вставить: копируем данные в новую позицию, очищаем старую
var srcCell = GetCellElement();
@@ -72,8 +501,8 @@ internal sealed class ExcelCell : ICell
InsertCellAt(rowIndex, colIndex, cloned);
srcCell.Remove();
}
- _row = rowIndex;
- _col = colIndex;
+ row = rowIndex;
+ col = colIndex;
}
return this;
}
@@ -82,14 +511,14 @@ internal sealed class ExcelCell : ICell
public RowHeight Height
{
- get => ExcelRow.GetHeight(_writer, _sheet, _row);
- set => ExcelRow.SetHeight(_writer, _sheet, _row, value);
+ get => ExcelRow.GetHeight(writer, sheet, row);
+ set => ExcelRow.SetHeight(writer, sheet, row, value);
}
public ColumnWidth Width
{
- get => ExcelColumn.GetWidth(_writer, _sheet, _col);
- set => ExcelColumn.SetWidth(_writer, _sheet, _col, value);
+ get => ExcelColumn.GetWidth(writer, sheet, col);
+ set => ExcelColumn.SetWidth(writer, sheet, col, value);
}
public bool IsNumber
@@ -157,7 +586,7 @@ internal sealed class ExcelCell : ICell
var cell = GetCellElement();
if (cell?.StyleIndex?.Value is not uint styleIndex)
return false;
- return _writer.IsCellLocked(styleIndex);
+ return writer.IsCellLocked(styleIndex);
}
}
public bool HasFormula => GetCellElement()?.CellFormula != null;
@@ -192,7 +621,7 @@ internal sealed class ExcelCell : ICell
var cell = GetCellElement();
if (cell?.StyleIndex?.Value is not uint styleIndex)
return null;
- return _writer.GetNumberFormat(styleIndex);
+ return writer.GetNumberFormat(styleIndex);
}
///
@@ -201,7 +630,7 @@ internal sealed class ExcelCell : ICell
var cell = GetCellElement();
if (cell?.StyleIndex?.Value is not uint styleIndex)
return default;
- return _writer.GetCellAlign(styleIndex);
+ return writer.GetCellAlign(styleIndex);
}
///
@@ -210,7 +639,7 @@ internal sealed class ExcelCell : ICell
var cell = GetCellElement();
if (cell?.StyleIndex?.Value is not uint styleIndex)
return default;
- return _writer.GetCellBorder(styleIndex);
+ return writer.GetCellBorder(styleIndex);
}
///
@@ -219,7 +648,7 @@ internal sealed class ExcelCell : ICell
var cell = GetCellElement();
if (cell?.StyleIndex?.Value is not uint styleIndex)
return default;
- return _writer.GetCellFill(styleIndex);
+ return writer.GetCellFill(styleIndex);
}
///
@@ -228,7 +657,7 @@ internal sealed class ExcelCell : ICell
var cell = GetCellElement();
if (cell?.StyleIndex?.Value is not uint styleIndex)
return default;
- return _writer.GetCellFont(styleIndex);
+ return writer.GetCellFont(styleIndex);
}
public bool TryGetBoolean(out bool value)
@@ -283,8 +712,8 @@ internal sealed class ExcelCell : ICell
public bool TrySet(string formula, NumberFormatPattern? format = null)
{
if (string.IsNullOrEmpty(formula)) return false;
- _writer.ThrowIfDisposed();
- lock (_writer._syncLock)
+ writer.ThrowIfDisposed();
+ lock (writer._syncLock)
{
var cell = GetOrCreateCellElement();
cell.CellFormula = new CellFormula(formula);
@@ -305,8 +734,8 @@ internal sealed class ExcelCell : ICell
public ICell Set(NumberFormatPattern format)
{
if (format == null) return this;
- _writer.ThrowIfDisposed();
- lock (_writer._syncLock)
+ writer.ThrowIfDisposed();
+ lock (writer._syncLock)
{
var cell = GetOrCreateCellElement();
SetNumberFormatInternal(cell, format);
@@ -317,11 +746,11 @@ internal sealed class ExcelCell : ICell
///
public ICell Set(CellAlign align)
{
- _writer.ThrowIfDisposed();
- lock (_writer._syncLock)
+ writer.ThrowIfDisposed();
+ lock (writer._syncLock)
{
var cell = GetOrCreateCellElement();
- int styleIndex = _writer.GetOrCreateCellFormatId(
+ int styleIndex = writer.GetOrCreateCellFormatId(
numberFormat: null,
font: null,
fill: null,
@@ -336,11 +765,11 @@ internal sealed class ExcelCell : ICell
///
public ICell Set(CellBorder border)
{
- _writer.ThrowIfDisposed();
- lock (_writer._syncLock)
+ writer.ThrowIfDisposed();
+ lock (writer._syncLock)
{
var cell = GetOrCreateCellElement();
- int styleIndex = _writer.GetOrCreateCellFormatId(
+ int styleIndex = writer.GetOrCreateCellFormatId(
numberFormat: null,
font: null,
fill: null,
@@ -355,11 +784,11 @@ internal sealed class ExcelCell : ICell
///
public ICell Set(CellFill fill)
{
- _writer.ThrowIfDisposed();
- lock (_writer._syncLock)
+ writer.ThrowIfDisposed();
+ lock (writer._syncLock)
{
var cell = GetOrCreateCellElement();
- int styleIndex = _writer.GetOrCreateCellFormatId(
+ int styleIndex = writer.GetOrCreateCellFormatId(
numberFormat: null,
font: null,
fill: fill,
@@ -374,11 +803,11 @@ internal sealed class ExcelCell : ICell
///
public ICell Set(CellFont font)
{
- _writer.ThrowIfDisposed();
- lock (_writer._syncLock)
+ writer.ThrowIfDisposed();
+ lock (writer._syncLock)
{
var cell = GetOrCreateCellElement();
- int styleIndex = _writer.GetOrCreateCellFormatId(
+ int styleIndex = writer.GetOrCreateCellFormatId(
numberFormat: null,
font: font,
fill: null,
@@ -390,126 +819,10 @@ internal sealed class ExcelCell : ICell
}
}
- public ICell Text(Action value)
- {
- if (value == null) return this;
- _writer.ThrowIfDisposed();
- lock (_writer._syncLock)
- {
- var cell = GetOrCreateCellElement();
- var textObj = new ExcelCellText();
- value(textObj);
- cell.InlineString = BuildInlineString(textObj);
- cell.DataType = CellValues.InlineString;
- cell.CellValue = null;
- cell.CellFormula = null;
-
- // Если есть разрыв строки (символ \n), включаем перенос текста для ячейки
- bool hasNewline = textObj.GetRuns().Any(r => r.Text != null && r.Text.Contains('\n'));
- if (hasNewline)
- {
- var currentAlign = GetCellAlign();
- if (currentAlign.WrapText != true)
- {
- var newAlign = new CellAlign
- {
- Horizontal = currentAlign.Horizontal,
- Vertical = currentAlign.Vertical,
- WrapText = true,
- ShrinkToFit = currentAlign.ShrinkToFit
- };
- Set(newAlign);
- }
- }
- }
- return this;
- }
-
- ///
- public ICellText Text()
- {
- _writer.ThrowIfDisposed();
- lock (_writer._syncLock)
- {
- // 1. Получаем или создаём элемент ячейки
- var cell = GetOrCreateCellElement();
-
- // 2. Убедимся, что у ячейки правильный тип и есть InlineString
- if (cell.DataType == null || cell.DataType.Value != CellValues.InlineString)
- {
- // Если был другой тип (например, число) — меняем на InlineString
- cell.DataType = CellValues.InlineString;
- // Удаляем старый InlineString, если был
- cell.InlineString?.Remove();
- // Создаём новый пустой InlineString
- cell.InlineString = new InlineString();
- // Добавляем пустой Text (обязательно для Open XML)
- cell.InlineString.AppendChild(new Text());
- }
- else if (cell.InlineString == null)
- {
- // Если тип InlineString, но сам элемент отсутствует
- cell.InlineString = new InlineString();
- cell.InlineString.AppendChild(new Text());
- }
-
- // 3. Строим объект ICellText на основе содержимого InlineString
- var textObj = new ExcelCellText();
- foreach (var run in cell.InlineString.Elements())
- {
- string text = run.GetFirstChild()?.Text ?? string.Empty;
- RunFormat? format = null;
- if (run.RunProperties != null)
- {
- format = RunFormat.FromRunProperties(run.RunProperties);
- }
- textObj.Run(text, format);
- }
-
- // Если не было ни одного Run, текст всё равно должен быть доступен (пустой)
- // Можно оставить textObj пустым, а можно сразу добавить пустой Run
- // (но это не обязательно – пользователь может добавить run позже)
- return textObj;
- }
- }
-
- ///
- public bool TryText(out ICellText cellText)
- {
- cellText = Text()!;
- return cellText is not null;
- }
-
- public ICell Set(string value)
- {
- if (value == null) return this;
- _writer.ThrowIfDisposed();
- lock (_writer._syncLock)
- {
- var cell = GetOrCreateCellElement();
- // Проверяем, можно ли сохранить как число?
- if (double.TryParse(value, out double num))
- {
- cell.DataType = CellValues.Number;
- cell.CellValue = new CellValue(num.ToString());
- }
- else
- {
- // Используем общую таблицу строк
- int idx = GetOrAddSharedString(value);
- cell.DataType = CellValues.SharedString;
- cell.CellValue = new CellValue(idx.ToString());
- }
- cell.InlineString = null;
- cell.CellFormula = null;
- }
- return this;
- }
-
public ICell Set(bool value)
{
- _writer.ThrowIfDisposed();
- lock (_writer._syncLock)
+ writer.ThrowIfDisposed();
+ lock (writer._syncLock)
{
var cell = GetOrCreateCellElement();
cell.DataType = CellValues.Boolean;
@@ -530,8 +843,8 @@ internal sealed class ExcelCell : ICell
public ICell Set(decimal value, NumberFormatPattern? format = null) => Set((double)value, format);
public ICell Set(double value, NumberFormatPattern? format = null)
{
- _writer.ThrowIfDisposed();
- lock (_writer._syncLock)
+ writer.ThrowIfDisposed();
+ lock (writer._syncLock)
{
var cell = GetOrCreateCellElement();
cell.DataType = CellValues.Number;
@@ -545,26 +858,10 @@ internal sealed class ExcelCell : ICell
public ICell Set(int value, NumberFormatPattern? format = null) => Set((double)value, format);
public ICell Set(long value, NumberFormatPattern? format = null) => Set((double)value, format);
- public void ClearContent()
- {
- _writer.ThrowIfDisposed();
- lock (_writer._syncLock)
- {
- var cell = GetCellElement();
- if (cell != null)
- {
- cell.CellValue = null;
- cell.CellFormula = null;
- cell.InlineString = null;
- cell.DataType = null;
- }
- }
- }
-
public void ClearFormat()
{
- _writer.ThrowIfDisposed();
- lock (_writer._syncLock)
+ writer.ThrowIfDisposed();
+ lock (writer._syncLock)
{
var cell = GetCellElement();
cell?.StyleIndex = null;
@@ -579,19 +876,19 @@ internal sealed class ExcelCell : ICell
public ICell CopyTo(uint rowIndex, uint colIndex, out ICell copiedCell)
{
- _writer.ThrowIfDisposed();
- lock (_writer._syncLock)
+ writer.ThrowIfDisposed();
+ lock (writer._syncLock)
{
var srcCell = GetCellElement();
if (srcCell != null)
{
var cloned = (Cell)srcCell.CloneNode(true);
InsertCellAt(rowIndex, colIndex, cloned);
- copiedCell = new ExcelCell(_writer, _sheet, rowIndex, colIndex);
+ copiedCell = new ExcelCell(writer, sheet, rowIndex, colIndex);
}
else
{
- copiedCell = new ExcelCell(_writer, _sheet, rowIndex, colIndex);
+ copiedCell = new ExcelCell(writer, sheet, rowIndex, colIndex);
}
return this;
}
@@ -604,29 +901,29 @@ internal sealed class ExcelCell : ICell
private Cell? GetCellElement()
{
- var sheetData = _sheet.GetSheetData();
- var row = FindRowElement(sheetData, _row);
- if (row == null) return null;
- return FindCellInRow(row, _col);
+ var sheetData = sheet.GetSheetData();
+ var eRow = FindRowElement(sheetData, row);
+ if (eRow == null) return null;
+ return FindCellInRow(eRow, col);
}
private Cell GetOrCreateCellElement()
{
- var sheetData = _sheet.GetSheetData();
- var row = GetOrCreateRowElement(sheetData, _row);
- var cell = FindCellInRow(row, _col);
+ var sheetData = sheet.GetSheetData();
+ var eRow = GetOrCreateRowElement(sheetData, row);
+ var cell = FindCellInRow(eRow, col);
if (cell != null) return cell;
cell = new Cell();
- string cellRef = CellAddressHelper.ColumnIndexToLetter(_col) + _row.ToString();
+ string cellRef = CellAddressHelper.ColumnIndexToLetter(col) + row.ToString();
cell.CellReference = cellRef;
- InsertCellInRow(row, cell, _col);
+ InsertCellInRow(eRow, cell, col);
return cell;
}
private static Row? FindRowElement(SheetData sheetData, uint rowIndex)
{
- foreach (var row in sheetData.Elements())
- if (row.RowIndex?.Value == rowIndex) return row;
+ foreach (var eRow in sheetData.Elements())
+ if (eRow.RowIndex?.Value == rowIndex) return eRow;
return null;
}
@@ -639,24 +936,24 @@ internal sealed class ExcelCell : ICell
return newRow;
}
- private static void InsertRowElement(SheetData sheetData, Row row, uint rowIndex)
+ private static void InsertRowElement(SheetData sheetData, Row eRow, uint rowIndex)
{
bool inserted = false;
foreach (var existing in sheetData.Elements().ToList())
{
if (existing.RowIndex?.Value > rowIndex)
{
- existing.InsertBeforeSelf(row);
+ existing.InsertBeforeSelf(eRow);
inserted = true;
break;
}
}
- if (!inserted) sheetData.Append(row);
+ if (!inserted) sheetData.Append(eRow);
}
- private static Cell? FindCellInRow(Row row, uint colIndex)
+ private static Cell? FindCellInRow(Row eRow, uint colIndex)
{
- foreach (var cell in row.Elements())
+ foreach (var cell in eRow.Elements())
{
if (CellAddressHelper.TryParseCellReference(cell.CellReference?.Value ?? string.Empty, out _, out uint col) && col == colIndex)
return cell;
@@ -664,12 +961,12 @@ internal sealed class ExcelCell : ICell
return null;
}
- private static void InsertCellInRow(Row row, Cell cell, uint colIndex)
+ private static void InsertCellInRow(Row eRow, Cell cell, uint colIndex)
{
- string newRef = CellAddressHelper.ColumnIndexToLetter(colIndex) + (row.RowIndex?.Value ?? 1).ToString();
+ string newRef = CellAddressHelper.ColumnIndexToLetter(colIndex) + (eRow.RowIndex?.Value ?? 1).ToString();
cell.CellReference = newRef;
bool inserted = false;
- foreach (var existing in row.Elements().ToList())
+ foreach (var existing in eRow.Elements| ().ToList())
{
if (CellAddressHelper.TryParseCellReference(existing.CellReference?.Value ?? string.Empty, out _, out uint existingCol) && existingCol > colIndex)
{
@@ -678,12 +975,12 @@ internal sealed class ExcelCell : ICell
break;
}
}
- if (!inserted) row.Append(cell);
+ if (!inserted) eRow.Append(cell);
}
private void InsertCellAt(uint rowIndex, uint colIndex, Cell cell)
{
- var sheetData = _sheet.GetSheetData();
+ var sheetData = sheet.GetSheetData();
var row = GetOrCreateRowElement(sheetData, rowIndex);
InsertCellInRow(row, cell, colIndex);
}
@@ -691,7 +988,7 @@ internal sealed class ExcelCell : ICell
private void SetNumberFormatInternal(Cell cell, NumberFormatPattern format)
{
if (format == null) return;
- int styleIndex = _writer.GetOrCreateCellFormatId(
+ int styleIndex = writer.GetOrCreateCellFormatId(
numberFormat: format,
font: null,
fill: null,
@@ -703,12 +1000,12 @@ internal sealed class ExcelCell : ICell
private string GetSharedString(uint index)
{
- return _writer.GetSharedString(index);
+ return writer.GetSharedString(index);
}
private int GetOrAddSharedString(string value)
{
- return _writer.GetOrAddSharedString(value);
+ return writer.GetOrAddSharedString(value);
}
private string ExtractTextFromInlineString(InlineString? inlineString)
@@ -723,63 +1020,8 @@ internal sealed class ExcelCell : ICell
return sb.ToString();
}
- private InlineString BuildInlineString(ExcelCellText textObj)
- {
- var inline = new InlineString();
- foreach (var run in textObj.GetRuns())
- {
- var runElement = new Run();
- // Всегда создаём элемент Text, даже если строка состоит из "\n"
- runElement.Append(new Text(run.Text));
-
- if (run.Format is { } fmt)
- {
- var rPr = new RunProperties();
- if (fmt.IsBold == true) rPr.Append(new Bold());
- if (fmt.IsItalic == true) rPr.Append(new Italic());
- if (fmt.Underline.HasValue)
- {
- rPr.Append(new Underline
- {
- Val = fmt.Underline.Value switch
- {
- UnderlineStyle.Single => UnderlineValues.Single,
- UnderlineStyle.Double => UnderlineValues.Double,
- UnderlineStyle.SingleAccounting => UnderlineValues.SingleAccounting,
- UnderlineStyle.DoubleAccounting => UnderlineValues.DoubleAccounting,
- _ => throw new NotImplementedException(),
- }
- });
- }
- if (fmt.IsStrike == true) rPr.Append(new Strike());
- if (fmt.Color.HasValue)
- {
- var excelColor = fmt.Color.Value.ToExcel();
- rPr.Append(excelColor);
- }
- if (fmt.FontSize.HasValue)
- rPr.Append(new FontSize { Val = fmt.FontSize.Value });
- if (!string.IsNullOrEmpty(fmt.FontFamily))
- rPr.Append(new RunFont { Val = fmt.FontFamily });
- if (fmt.Vertical.HasValue)
- {
- var vertAlign = new VerticalTextAlignment
- {
- Val = fmt.Vertical.Value == VerticalTextRunAlignment.Superscript
- ? VerticalAlignmentRunValues.Superscript
- : VerticalAlignmentRunValues.Subscript
- };
- rPr.Append(vertAlign);
- }
- runElement.RunProperties = rPr;
- }
- inline.Append(runElement);
- }
- return inline;
- }
-
private MergeCells? GetMergeCells() =>
- _sheet.Worksheet.GetFirstChild();
+ sheet.Worksheet.GetFirstChild();
private bool TryParseRangeReference(string reference, out ExcelRange range)
{
@@ -791,7 +1033,7 @@ internal sealed class ExcelCell : ICell
if (!CellAddressHelper.TryParseCellReference(parts[1] + "1", out uint row2, out uint col2)) return false;
uint rowStart = Math.Min(row1, row2), rowEnd = Math.Max(row1, row2);
uint colStart = Math.Min(col1, col2), colEnd = Math.Max(col1, col2);
- range = new ExcelRange(_writer, _sheet, rowStart, colStart, rowEnd, colEnd);
+ range = new ExcelRange(writer, sheet, rowStart, colStart, rowEnd, colEnd);
return true;
}
}
\ No newline at end of file
diff --git a/QWERTYkez.ExcelProcessor/Editors/ExcelCellText.cs b/QWERTYkez.ExcelProcessor/Editors/ExcelCellText.cs
deleted file mode 100644
index 0178834..0000000
--- a/QWERTYkez.ExcelProcessor/Editors/ExcelCellText.cs
+++ /dev/null
@@ -1,270 +0,0 @@
-namespace QWERTYkez.ExcelProcessor;
-
-///
-/// Внутренняя реализация для работы с богатым текстом ячейки.
-/// Хранит коллекцию фрагментов .
-/// Минимизирует аллокации, не использует рефлексию.
-///
-internal sealed class ExcelCellText : ICellText
-{
- private List? _runs;
-
- ///
- public int Count => _runs?.Count ?? 0;
-
- ///
- public IEnumerable GetRuns()
- {
- if (_runs is null)
- return [];
- // Возвращаем сам список, чтобы избежать копирования.
- // Вызывающий не должен модифицировать коллекцию.
- return _runs;
- }
-
- ///
- public IRun? GetRunAt(int index)
- {
- if (_runs is null || index < 0 || index >= _runs.Count)
- return null;
- return _runs[index];
- }
-
- ///
- public bool TryGetRunAt(int index, out IRun run)
- {
- run = GetRunAt(index)!;
- return run != null;
- }
-
- ///
- public IRun? First()
- {
- if (_runs is null || _runs.Count == 0)
- return null;
- return _runs[0];
- }
-
- ///
- public bool TryGetFirst(out IRun run)
- {
- run = First()!;
- return run != null;
- }
-
- ///
- public IRun? Last()
- {
- if (_runs is null || _runs.Count == 0)
- return null;
- return _runs[_runs.Count - 1];
- }
-
- ///
- public bool TryGetLast(out IRun run)
- {
- run = Last()!;
- return run != null;
- }
-
- ///
- public bool TryRemoveRun(IRun run)
- {
- if (run is null || _runs is null)
- return false;
- return _runs.Remove(run);
- }
-
- ///
- public bool TryRemoveRun(int index)
- {
- if (_runs is null || index < 0 || index >= _runs.Count)
- return false;
- _runs.RemoveAt(index);
- return true;
- }
-
- ///
- public bool TryRemoveRun(int index, out IRun? removed)
- {
- removed = GetRunAt(index);
- if (removed is null)
- return false;
- return TryRemoveRun(index);
- }
-
- ///
- public ICellText Break()
- {
- // Добавляем символ переноса строки в последний существующий Run
- if (_runs != null && _runs.Count > 0)
- {
- var lastRun = _runs[_runs.Count - 1];
- lastRun.Text += "\n";
- }
- else
- {
- // Если нет ни одного Run, создаём новый с символом переноса
- Run("\n", null);
- }
- return this;
- }
-
- ///
- public ICellText Run(string text, RunFormat? format = null)
- {
- if (string.IsNullOrEmpty(text))
- return this;
- _runs ??= [];
- var run = new ExcelRun { Text = text, Format = format };
- _runs.Add(run);
- return this;
- }
-
- ///
- public ICellText RunBreak(string text, RunFormat? format = null)
- {
- Run(text, format);
- Break();
- return this;
- }
-
- ///
- public ICellText Sub(string text, RunFormat? format = null)
- {
- var subFormat = format is { } fmt
- ? new RunFormat
- {
- IsBold = fmt.IsBold,
- IsItalic = fmt.IsItalic,
- Underline = fmt.Underline,
- IsStrike = fmt.IsStrike,
- Color = fmt.Color,
- FontSize = fmt.FontSize,
- FontFamily = fmt.FontFamily,
- Vertical = VerticalTextRunAlignment.Subscript
- }
- : new RunFormat { Vertical = VerticalTextRunAlignment.Subscript };
- return Run(text, subFormat);
- }
-
- ///
- public ICellText Sup(string text, RunFormat? format = null)
- {
- var supFormat = format is { } fmt
- ? new RunFormat
- {
- IsBold = fmt.IsBold,
- IsItalic = fmt.IsItalic,
- Underline = fmt.Underline,
- IsStrike = fmt.IsStrike,
- Color = fmt.Color,
- FontSize = fmt.FontSize,
- FontFamily = fmt.FontFamily,
- Vertical = VerticalTextRunAlignment.Subscript
- }
- : new RunFormat { Vertical = VerticalTextRunAlignment.Superscript };
- return Run(text, supFormat);
- }
-
- ///
- public bool TryInsertRun(int index, string text, RunFormat? format = null)
- {
- if (index < 0 || string.IsNullOrEmpty(text))
- return false;
- _runs ??= [];
- if (index > _runs.Count)
- return false;
- var run = new ExcelRun { Text = text, Format = format };
- _runs.Insert(index, run);
- return true;
- }
-
- ///
- public bool TryInsertRunBreak(int index, string text, RunFormat? format = null)
- {
- if (!TryInsertRun(index, text, format))
- return false;
- // После вставленного run добавляем break на следующей позиции
- Break();
- // Сдвигаем? Просто добавляем break в конец – неверно. Break должен быть сразу после вставленного.
- // Но AddBreak добавляет в конец. Нужно вставить break на index+1.
- return TryInsertRun(index + 1, "\n", null);
- }
-
- ///
- public bool TryInsertSub(int index, string text, RunFormat? format = null)
- {
- var subFormat = format is { } fmt
- ? new RunFormat
- {
- IsBold = fmt.IsBold,
- IsItalic = fmt.IsItalic,
- Underline = fmt.Underline,
- IsStrike = fmt.IsStrike,
- Color = fmt.Color,
- FontSize = fmt.FontSize,
- FontFamily = fmt.FontFamily,
- Vertical = VerticalTextRunAlignment.Subscript
- }
- : new RunFormat { Vertical = VerticalTextRunAlignment.Subscript };
- return TryInsertRun(index, text, subFormat);
- }
-
- ///
- public bool TryInsertSup(int index, string text, RunFormat? format = null)
- {
- var supFormat = format is { } fmt
- ? new RunFormat
- {
- IsBold = fmt.IsBold,
- IsItalic = fmt.IsItalic,
- Underline = fmt.Underline,
- IsStrike = fmt.IsStrike,
- Color = fmt.Color,
- FontSize = fmt.FontSize,
- FontFamily = fmt.FontFamily,
- Vertical = VerticalTextRunAlignment.Subscript
- }
- : new RunFormat { Vertical = VerticalTextRunAlignment.Superscript };
- return TryInsertRun(index, text, supFormat);
- }
-
- ///
- public void ApplyFormatToAllRuns(RunFormat format)
- {
- if (_runs is null || _runs.Count == 0)
- return;
- foreach (var run in _runs)
- {
- if (run is ExcelRun xRun)
- {
- // Объединение форматов: ненулевые свойства overlay заменяют значения в base.
- var baseFmt = xRun.Format ?? new RunFormat();
- xRun.Format = MergeRunFormat(baseFmt, format);
- }
- }
- }
-
- ///
- public void Clear()
- {
- _runs?.Clear();
- _runs = null;
- }
-
- private static RunFormat MergeRunFormat(RunFormat baseFmt, RunFormat overlay)
- {
- return new RunFormat
- {
- IsBold = overlay.IsBold ?? baseFmt.IsBold,
- IsItalic = overlay.IsItalic ?? baseFmt.IsItalic,
- Underline = overlay.Underline ?? baseFmt.Underline,
- IsStrike = overlay.IsStrike ?? baseFmt.IsStrike,
- Color = overlay.Color ?? baseFmt.Color,
- FontSize = overlay.FontSize ?? baseFmt.FontSize,
- FontFamily = overlay.FontFamily ?? baseFmt.FontFamily,
- Vertical = overlay.Vertical ?? baseFmt.Vertical
- };
- }
-}
\ No newline at end of file
diff --git a/QWERTYkez.ExcelProcessor/Editors/Interfaces.cs b/QWERTYkez.ExcelProcessor/Editors/Interfaces.cs
index 954f898..7d0abd0 100644
--- a/QWERTYkez.ExcelProcessor/Editors/Interfaces.cs
+++ b/QWERTYkez.ExcelProcessor/Editors/Interfaces.cs
@@ -560,6 +560,69 @@ public interface IRange
/// Представляет одну ячейку на листе.
public interface ICell
{
+ /// Количество фрагментов (Run) в тексте.
+ int RunsCount { get; }
+
+ /// Возвращает все фрагменты.
+ IEnumerable GetRuns();
+
+ /// Возвращает фрагмент по индексу или null.
+ IRun? GetRunAt(int index);
+
+ /// Пытается получить фрагмент по индексу.
+ bool TryGetRunAt(int index, out IRun run);
+
+ /// Первый фрагмент или null.
+ IRun? First();
+
+ /// Пытается получить первый фрагмент.
+ bool TryGetFirst(out IRun run);
+
+ /// Последний фрагмент или null.
+ IRun? Last();
+
+ /// Пытается получить последний фрагмент.
+ bool TryGetLast(out IRun run);
+
+ /// Пытается удалить фрагмент.
+ bool TryRemoveRun(IRun run);
+
+ /// Удаляет фрагмент по индексу.
+ bool TryRemoveRun(int index);
+
+ /// Удаляет фрагмент по индексу и возвращает удалённый.
+ bool TryRemoveRun(int index, out IRun? removed);
+
+ /// Добавляет разрыв строки (перенос внутри ячейки).
+ ICell Break();
+
+ /// Добавляет обычный текстовый фрагмент.
+ ICell Run(string text, RunFormat? format = null);
+
+ /// Добавляет фрагмент с последующим разрывом строки.
+ ICell RunBreak(string text, RunFormat? format = null);
+
+ /// Добавляет подстрочный фрагмент (эквивалентно AddRun с Vertical = Subscript).
+ ICell Sub(string text, RunFormat? format = null);
+
+ /// Добавляет надстрочный фрагмент.
+ ICell Sup(string text, RunFormat? format = null);
+
+ /// Вставляет фрагмент по индексу.
+ bool TryInsertRun(int index, string text, RunFormat? format = null);
+
+ /// Вставляет фрагмент с последующим разрывом строки по индексу.
+ bool TryInsertRunBreak(int index, string text, RunFormat? format = null);
+
+ /// Вставляет подстрочный фрагмент по индексу.
+ bool TryInsertSub(int index, string text, RunFormat? format = null);
+
+ /// Вставляет надстрочный фрагмент по индексу.
+ bool TryInsertSup(int index, string text, RunFormat? format = null);
+
+ /// Применяет заданный формат ко всем существующим фрагментам (поверх их текущего форматирования, заменяя неуказанные свойства).
+ void ApplyFormatToAllRuns(RunFormat format);
+
/// Проверяет, входит ли ячейка в объединённый диапазон.
bool IsMerged { get; }
@@ -699,15 +762,6 @@ public interface ICell
/// Устанавливает шрифт ячейки.
ICell Set(CellFont format);
- /// Устанавливает богатый текст (форматированный) с помощью делегата.
- ICell Text(Action value);
-
- /// Возвращает объект для редактирования богатого текста ячейки (если ячейка содержит InlineString).
- bool TryText(out ICellText cellText);
-
- /// Возвращает объект для редактирования богатого текста ячейки (если ячейка содержит InlineString).
- ICellText Text();
-
/// Устанавливает простое текстовое значение (без форматирования).
ICell Set(string value);
@@ -742,76 +796,6 @@ public interface ICell
void Clear();
}
-/// Представляет богатый текст внутри ячейки (несколько форматированных фрагментов).
-public interface ICellText
-{
- /// Количество фрагментов (Run) в тексте.
- int Count { get; }
-
- /// Возвращает все фрагменты.
- IEnumerable GetRuns();
-
- /// Возвращает фрагмент по индексу или null.
- IRun? GetRunAt(int index);
-
- /// Пытается получить фрагмент по индексу.
- bool TryGetRunAt(int index, out IRun run);
-
- /// Первый фрагмент или null.
- IRun? First();
-
- /// Пытается получить первый фрагмент.
- bool TryGetFirst(out IRun run);
-
- /// Последний фрагмент или null.
- IRun? Last();
-
- /// Пытается получить последний фрагмент.
- bool TryGetLast(out IRun run);
-
- /// Пытается удалить фрагмент.
- bool TryRemoveRun(IRun run);
-
- /// Удаляет фрагмент по индексу.
- bool TryRemoveRun(int index);
-
- /// Удаляет фрагмент по индексу и возвращает удалённый.
- bool TryRemoveRun(int index, out IRun? removed);
-
- /// Добавляет разрыв строки (перенос внутри ячейки).
- ICellText Break();
-
- /// Добавляет обычный текстовый фрагмент.
- ICellText Run(string text, RunFormat? format = null);
-
- /// Добавляет фрагмент с последующим разрывом строки.
- ICellText RunBreak(string text, RunFormat? format = null);
-
- /// Добавляет подстрочный фрагмент (эквивалентно AddRun с Vertical = Subscript).
- ICellText Sub(string text, RunFormat? format = null);
-
- /// Добавляет надстрочный фрагмент.
- ICellText Sup(string text, RunFormat? format = null);
-
- /// Вставляет фрагмент по индексу.
- bool TryInsertRun(int index, string text, RunFormat? format = null);
-
- /// Вставляет фрагмент с последующим разрывом строки по индексу.
- bool TryInsertRunBreak(int index, string text, RunFormat? format = null);
-
- /// Вставляет подстрочный фрагмент по индексу.
- bool TryInsertSub(int index, string text, RunFormat? format = null);
-
- /// Вставляет надстрочный фрагмент по индексу.
- bool TryInsertSup(int index, string text, RunFormat? format = null);
-
- /// Применяет заданный формат ко всем существующим фрагментам (поверх их текущего форматирования, заменяя неуказанные свойства).
- void ApplyFormatToAllRuns(RunFormat format);
-
- /// Удаляет все фрагменты, очищая текст ячейки.
- void Clear();
-}
-
/// Представляет один форматированный фрагмент текста внутри ячейки.
public interface IRun
{
| | | |