Add Excel Table classes

This commit is contained in:
Sergey Chebotar 2023-03-31 15:27:31 +03:00
parent cdb153c988
commit f01228d945
16 changed files with 527 additions and 7 deletions

View File

@ -0,0 +1,47 @@
using System.Collections;
namespace RhSolutions.ExcelTable;
public sealed class ExcelColumn : ExcelTable, IEnumerable<ExcelTableCell>
{
public string Header
{
get => Range.Cells[1, 1].Value.ToString();
}
public int Index
{
get => Range.Column - ParentTable.Range.Column;
}
public int Length
{
get => Range.Rows.Count;
}
public ExcelColumn(Range range, ExcelTable table) : base(range, table)
{
Range = range;
ParentTable = table;
}
public ExcelTableCell this[int index]
{
get => new(Range.Cells[index + 1, 1], ParentTable);
}
public IEnumerator<ExcelTableCell> GetEnumerator()
{
return new ExcelColumnEnumerator(Range, ParentTable);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public ExcelColumn AddLeft()
{
Range.EntireColumn
.Insert(XlInsertShiftDirection.xlShiftToRight,
XlInsertFormatOrigin.xlFormatFromRightOrBelow);
return ParentTable.Columns[this.Index - 1];
}
}

View File

@ -0,0 +1,54 @@
using System.Collections;
namespace RhSolutions.ExcelTable;
public class ExcelColumnEnumerator : IEnumerator<ExcelTableCell>
{
public Range Range { get; }
public ExcelTable ParentTable { get; }
private int position = 0;
object IEnumerator.Current
{
get
{
return Current;
}
}
public ExcelTableCell Current
{
get
{
try
{
return new ExcelTableCell(Range.Cells[position, 1], ParentTable);
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
public ExcelColumnEnumerator(Range range, ExcelTable table)
{
Range = range;
ParentTable = table;
}
public bool MoveNext()
{
position++;
return (position <= Range.Rows.Count);
}
public void Reset()
{
position = 0;
}
public void Dispose()
{
}
}

View File

@ -0,0 +1,39 @@
using System.Collections;
namespace RhSolutions.ExcelTable;
public class ExcelColumns : IEnumerable<ExcelColumn>
{
public Range Range { get; }
public ExcelTable ParentTable { get; }
public int Length
{
get => Range.Columns.Count;
}
public ExcelColumns(Range range, ExcelTable parentTable)
{
Range = range;
ParentTable = parentTable;
}
public ExcelColumn this[int index]
{
get
{
if (index < 0 || index + 1 > Range.Columns.Count)
{
throw new IndexOutOfRangeException();
}
return new ExcelColumn(Range.Columns[index + 1], ParentTable);
}
}
public IEnumerator<ExcelColumn> GetEnumerator()
{
return new ExcelColumnsEnumerator(Range, ParentTable);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

View File

@ -0,0 +1,54 @@
using System.Collections;
namespace RhSolutions.ExcelTable;
public class ExcelColumnsEnumerator: IEnumerator<ExcelColumn>
{
public Range Range { get; }
public ExcelTable ParentTable { get; }
private int position = 0;
object IEnumerator.Current
{
get
{
return Current;
}
}
public ExcelColumn Current
{
get
{
try
{
return new ExcelColumn(Range.Columns[position], ParentTable);
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
public ExcelColumnsEnumerator(Range range, ExcelTable table)
{
Range = range;
ParentTable = table;
}
public bool MoveNext()
{
position++;
return (position <= Range.Columns.Count);
}
public void Reset()
{
position = 0;
}
public void Dispose()
{
}
}

View File

@ -0,0 +1,33 @@
using System.Collections;
namespace RhSolutions.ExcelTable;
public sealed class ExcelRow : ExcelTable, IEnumerable<ExcelTableCell>
{
public int Index
{
get => Range.Row - ParentTable.Range.Row;
}
public int Length
{
get => Range.Columns.Count;
}
public ExcelRow(Range range, ExcelTable table) : base(range, table)
{
Range = range;
ParentTable = table;
}
public ExcelTableCell this[int index]
{
get => new(Range.Cells[1, index + 1], ParentTable);
}
public IEnumerator<ExcelTableCell> GetEnumerator()
{
return new ExcelRowEnumerator(Range, ParentTable);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

View File

@ -0,0 +1,54 @@
using System.Collections;
namespace RhSolutions.ExcelTable;
public class ExcelRowEnumerator : IEnumerator<ExcelTableCell>
{
public Range Range { get; }
public ExcelTable ParentTable { get; }
private int position = 0;
object IEnumerator.Current
{
get
{
return Current;
}
}
public ExcelTableCell Current
{
get
{
try
{
return new ExcelTableCell(Range.Cells[1, position], ParentTable);
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
public ExcelRowEnumerator(Range range, ExcelTable parentTable)
{
Range = range;
ParentTable = parentTable;
}
public bool MoveNext()
{
position++;
return (position <= Range.Columns.Count);
}
public void Reset()
{
position = 0;
}
public void Dispose()
{
}
}

View File

@ -0,0 +1,39 @@
using System.Collections;
namespace RhSolutions.ExcelTable;
public class ExcelRows : IEnumerable<ExcelRow>
{
public Range Range { get; }
public ExcelTable ParentTable { get; }
public int Length
{
get => Range.Rows.Count;
}
public ExcelRows(Range range, ExcelTable parentTable)
{
Range = range;
ParentTable = parentTable;
}
public ExcelRow this[int index]
{
get
{
if (index < 0 || index + 1 > Range.Rows.Count)
{
throw new IndexOutOfRangeException();
}
return new ExcelRow(Range.Rows[index + 1], ParentTable);
}
}
public IEnumerator<ExcelRow> GetEnumerator()
{
return new ExcelRowsEnumerator(Range, ParentTable);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

View File

@ -0,0 +1,55 @@
using System.Collections;
namespace RhSolutions.ExcelTable;
public class ExcelRowsEnumerator : IEnumerator<ExcelRow>
{
public Range Range { get; }
public ExcelTable ParentTable { get; }
private int position = 0;
object IEnumerator.Current
{
get
{
return Current;
}
}
public ExcelRow Current
{
get
{
try
{
return new ExcelRow(Range.Rows[position], ParentTable);
}
catch (IndexOutOfRangeException)
{
throw new InvalidOperationException();
}
}
}
public ExcelRowsEnumerator(Range range, ExcelTable table)
{
Range = range;
ParentTable = table;
}
public bool MoveNext()
{
position++;
return (position <= Range.Rows.Count);
}
public void Reset()
{
position = 0;
}
public void Dispose()
{
}
}

View File

@ -0,0 +1,53 @@
namespace RhSolutions.ExcelTable;
public class ExcelTable
{
public Range Range { get; protected set; }
public ExcelTable ParentTable { get; protected set; }
public ExcelRows Rows { get; }
public ExcelColumns Columns { get; }
public ExcelTable(Range range)
{
Range = range;
ParentTable = null;
Rows = new ExcelRows(Range, this);
Columns = new ExcelColumns(Range, this);
}
public ExcelTable(Range range, ExcelTable table)
{
Range = range;
ParentTable = table;
Rows = new ExcelRows(Range, this);
Columns = new ExcelColumns(Range, this);
}
public ExcelTableCell this[int row, int column]
{
get => new(Range.Cells[row + 1, column + 1], this);
}
public IEnumerable<ExcelTableCell> Find(object item)
{
Range firstFound = Range.Find(item);
if (firstFound == null)
{
yield break;
}
Range nextFound = firstFound;
while (true)
{
yield return new ExcelTableCell(nextFound, ParentTable ?? this);
nextFound = Range.FindNext(nextFound);
if (nextFound.Row == firstFound.Row
&& nextFound.Column == firstFound.Column)
{
yield break;
}
}
}
}

View File

@ -0,0 +1,24 @@
namespace RhSolutions.ExcelTable;
public sealed class ExcelTableCell : ExcelTable
{
public ExcelRow ParentRow
{
get => ParentTable.Rows[ParentTable.Range.Row - Range.Row];
}
public ExcelColumn ParentColumn
{
get => ParentTable.Columns[ParentTable.Range.Column - Range.Column];
}
public object Value
{
get => Range.Cells[1, 1].Value2;
set => Range.Cells[1, 1].Value2 = value;
}
public ExcelTableCell(Range range, ExcelTable table) : base(range, table)
{
Range = range;
ParentTable = table;
}
}

View File

@ -3,7 +3,7 @@
<TargetFrameworks>net472;net6.0-windows7.0</TargetFrameworks>
<LangVersion>10</LangVersion>
<OutputType>Library</OutputType>
<RootNamespace>RhSolutions.AddIn</RootNamespace>
<RootNamespace>RhSolutions</RootNamespace>
<AssemblyName>RhSolutions.AddIn</AssemblyName>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<UseWindowsForms>true</UseWindowsForms>

View File

@ -0,0 +1,65 @@
namespace RhSolutions.Tests;
[ExcelTestSettings(OutOfProcess = true, Workbook = @"TestWorkbooks\ExcelTableTest.xlsx")]
public class ExcelTablesTests : IDisposable
{
ExcelTable.ExcelTable table;
public ExcelTablesTests()
{
Util.Application.Workbooks.Add();
Worksheet worksheet = Util.Workbook.Sheets[1];
Range range = worksheet.Range["E7:G9"];
table = new(range);
}
[ExcelFact]
public void CanReadExcelTable()
{
Assert.Equal(3, table.Columns.Where(c => c.Header.StartsWith("Столбец")).Count());
Assert.Equal("Столбец 1", table.Rows.First()[0].Value);
Assert.Equal("Столбец 2", table[0, 1].Value);
Assert.Equal(123d, table[1, 1].Value);
Assert.Null(table[1, 2].Value);
}
[ExcelFact]
public void CanModifyTableCells()
{
table[2, 1].Value = 1;
table[1, 1].Value = (double)table[1, 1].Value + 123;
Assert.Equal(1d, table[2, 1].Value);
Assert.Equal(246d, table[1, 1].Value);
}
[ExcelFact]
public void CanFindInTable()
{
table[2, 0].Value = "Find!";
table[2, 1].Value = "Find this!";
table[2, 2].Value = "Find that!";
var cells = table.Find("Find");
Assert.Collection(cells, item => Assert.Equal("Find!", item.Value),
item => Assert.Equal("Find this!", item.Value),
item => Assert.Equal("Find that!", item.Value));
Assert.Equal(0, table.Find("Значение").First().ParentColumn.Index);
Assert.Equal(3, table.Rows[2].Find("Find").Count());
Assert.Empty(table.Columns[1].Find("Пусто"));
}
[ExcelFact]
public void CanAddColumns()
{
int originalCount = table.Columns.Count();
table.Columns[1].AddLeft();
Assert.Equal(originalCount + 1, table.Columns.Count());
}
public void Dispose()
{
Util.Application.ActiveWindow.Close(SaveChanges: false);
}
}

View File

@ -24,6 +24,9 @@
<None Update="TestWorkbooks\EmptyWorkbook.xlsx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestWorkbooks\ExcelTableTest.xlsx">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

Binary file not shown.

View File

@ -1,5 +1,5 @@
global using Xunit;
global using Microsoft.Office.Interop.Excel;
global using ExcelDna.Testing;
global using RhSolutions.Models;
global using RhSolutions.ExcelTable;
global using RhSolutions.Services;

View File

@ -1,9 +1,9 @@
namespace RhSolutions.Tests;
[ExcelTestSettings(OutOfProcess = true)]
public class WorkbookCheck : IDisposable
public class WorkbookValidationTests : IDisposable
{
public WorkbookCheck()
public WorkbookValidationTests()
{
Util.Application.Workbooks.Add();
}
@ -11,7 +11,7 @@ public class WorkbookCheck : IDisposable
[ExcelFact(Workbook = @"TestWorkbooks\EmptyTestTable.xlsx")]
public void WorksheetIsCorrect()
{
Worksheet worksheet= Util.Workbook.Sheets[1];
Worksheet worksheet = Util.Workbook.Sheets[1];
Assert.True(worksheet.IsRehauSource());
}
@ -24,6 +24,6 @@ public class WorkbookCheck : IDisposable
public void Dispose()
{
Util.Application.ActiveWorkbook.Close(SaveChanges: false);
Util.Application.ActiveWindow.Close(SaveChanges: false);
}
}
}