diff --git a/RhSolutions.AddIn/ExcelTable/ExcelColumn.cs b/RhSolutions.AddIn/ExcelTable/ExcelColumn.cs new file mode 100644 index 0000000..7d72cd5 --- /dev/null +++ b/RhSolutions.AddIn/ExcelTable/ExcelColumn.cs @@ -0,0 +1,47 @@ +using System.Collections; + +namespace RhSolutions.ExcelTable; + +public sealed class ExcelColumn : ExcelTable, IEnumerable +{ + 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 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]; + } +} diff --git a/RhSolutions.AddIn/ExcelTable/ExcelColumnEnumerator.cs b/RhSolutions.AddIn/ExcelTable/ExcelColumnEnumerator.cs new file mode 100644 index 0000000..c809c18 --- /dev/null +++ b/RhSolutions.AddIn/ExcelTable/ExcelColumnEnumerator.cs @@ -0,0 +1,54 @@ +using System.Collections; + +namespace RhSolutions.ExcelTable; + +public class ExcelColumnEnumerator : IEnumerator +{ + 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() + { + + } +} diff --git a/RhSolutions.AddIn/ExcelTable/ExcelColumns.cs b/RhSolutions.AddIn/ExcelTable/ExcelColumns.cs new file mode 100644 index 0000000..4aacd6f --- /dev/null +++ b/RhSolutions.AddIn/ExcelTable/ExcelColumns.cs @@ -0,0 +1,39 @@ +using System.Collections; + +namespace RhSolutions.ExcelTable; + +public class ExcelColumns : IEnumerable +{ + 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 GetEnumerator() + { + return new ExcelColumnsEnumerator(Range, ParentTable); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); +} \ No newline at end of file diff --git a/RhSolutions.AddIn/ExcelTable/ExcelColumnsEnumerator.cs b/RhSolutions.AddIn/ExcelTable/ExcelColumnsEnumerator.cs new file mode 100644 index 0000000..8c351e9 --- /dev/null +++ b/RhSolutions.AddIn/ExcelTable/ExcelColumnsEnumerator.cs @@ -0,0 +1,54 @@ +using System.Collections; + +namespace RhSolutions.ExcelTable; + +public class ExcelColumnsEnumerator: IEnumerator +{ + 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() + { + + } +} \ No newline at end of file diff --git a/RhSolutions.AddIn/ExcelTable/ExcelRow.cs b/RhSolutions.AddIn/ExcelTable/ExcelRow.cs new file mode 100644 index 0000000..231fd7c --- /dev/null +++ b/RhSolutions.AddIn/ExcelTable/ExcelRow.cs @@ -0,0 +1,33 @@ +using System.Collections; + +namespace RhSolutions.ExcelTable; + +public sealed class ExcelRow : ExcelTable, IEnumerable +{ + 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 GetEnumerator() + { + return new ExcelRowEnumerator(Range, ParentTable); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); +} diff --git a/RhSolutions.AddIn/ExcelTable/ExcelRowEnumerator.cs b/RhSolutions.AddIn/ExcelTable/ExcelRowEnumerator.cs new file mode 100644 index 0000000..58f88d8 --- /dev/null +++ b/RhSolutions.AddIn/ExcelTable/ExcelRowEnumerator.cs @@ -0,0 +1,54 @@ +using System.Collections; + +namespace RhSolutions.ExcelTable; + +public class ExcelRowEnumerator : IEnumerator +{ + 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() + { + + } +} \ No newline at end of file diff --git a/RhSolutions.AddIn/ExcelTable/ExcelRows.cs b/RhSolutions.AddIn/ExcelTable/ExcelRows.cs new file mode 100644 index 0000000..44602f0 --- /dev/null +++ b/RhSolutions.AddIn/ExcelTable/ExcelRows.cs @@ -0,0 +1,39 @@ +using System.Collections; + +namespace RhSolutions.ExcelTable; + +public class ExcelRows : IEnumerable +{ + 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 GetEnumerator() + { + return new ExcelRowsEnumerator(Range, ParentTable); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); +} diff --git a/RhSolutions.AddIn/ExcelTable/ExcelRowsEnumerator.cs b/RhSolutions.AddIn/ExcelTable/ExcelRowsEnumerator.cs new file mode 100644 index 0000000..e5430d1 --- /dev/null +++ b/RhSolutions.AddIn/ExcelTable/ExcelRowsEnumerator.cs @@ -0,0 +1,55 @@ +using System.Collections; + +namespace RhSolutions.ExcelTable; + +public class ExcelRowsEnumerator : IEnumerator +{ + 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() + { + + } + +} \ No newline at end of file diff --git a/RhSolutions.AddIn/ExcelTable/ExcelTable.cs b/RhSolutions.AddIn/ExcelTable/ExcelTable.cs new file mode 100644 index 0000000..2788036 --- /dev/null +++ b/RhSolutions.AddIn/ExcelTable/ExcelTable.cs @@ -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 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; + } + } + } +} diff --git a/RhSolutions.AddIn/ExcelTable/ExcelTableCell.cs b/RhSolutions.AddIn/ExcelTable/ExcelTableCell.cs new file mode 100644 index 0000000..8f742d2 --- /dev/null +++ b/RhSolutions.AddIn/ExcelTable/ExcelTableCell.cs @@ -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; + } +} diff --git a/RhSolutions.AddIn/RhSolutions.AddIn.csproj b/RhSolutions.AddIn/RhSolutions.AddIn.csproj index 59173c5..81fcb6a 100644 --- a/RhSolutions.AddIn/RhSolutions.AddIn.csproj +++ b/RhSolutions.AddIn/RhSolutions.AddIn.csproj @@ -3,7 +3,7 @@ net472;net6.0-windows7.0 10 Library - RhSolutions.AddIn + RhSolutions RhSolutions.AddIn false true diff --git a/RhSolutions.Tests/ExcelTablesTests.cs b/RhSolutions.Tests/ExcelTablesTests.cs new file mode 100644 index 0000000..3ce71bc --- /dev/null +++ b/RhSolutions.Tests/ExcelTablesTests.cs @@ -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); + } +} diff --git a/RhSolutions.Tests/RhSolutions.Tests.csproj b/RhSolutions.Tests/RhSolutions.Tests.csproj index aeb9c9e..9410802 100644 --- a/RhSolutions.Tests/RhSolutions.Tests.csproj +++ b/RhSolutions.Tests/RhSolutions.Tests.csproj @@ -24,6 +24,9 @@ PreserveNewest + + PreserveNewest + diff --git a/RhSolutions.Tests/TestWorkbooks/ExcelTableTest.xlsx b/RhSolutions.Tests/TestWorkbooks/ExcelTableTest.xlsx new file mode 100644 index 0000000..da4cf0b Binary files /dev/null and b/RhSolutions.Tests/TestWorkbooks/ExcelTableTest.xlsx differ diff --git a/RhSolutions.Tests/Usings.cs b/RhSolutions.Tests/Usings.cs index 74130ca..4b95659 100644 --- a/RhSolutions.Tests/Usings.cs +++ b/RhSolutions.Tests/Usings.cs @@ -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; diff --git a/RhSolutions.Tests/RhSolutionsCheckTest.cs b/RhSolutions.Tests/WorkbookValidationTests.cs similarity index 72% rename from RhSolutions.Tests/RhSolutionsCheckTest.cs rename to RhSolutions.Tests/WorkbookValidationTests.cs index f4d1317..aec7139 100644 --- a/RhSolutions.Tests/RhSolutionsCheckTest.cs +++ b/RhSolutions.Tests/WorkbookValidationTests.cs @@ -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); } -} +} \ No newline at end of file