Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/auto filter #69

Merged
merged 3 commits into from
Dec 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ var exportDefinition = new SpreadsheetConfiguration<SimpleExportData>
DocumentSubTitle = "Showing the full options",
ExportData = GetSampleExportData(100),
WorksheetName = "Sample",
FreezePanes = true
FreezePanes = true,
AutoFilterDataRows = true
};
var fileContent = exportGenerator.CreateSingleSheetSpreadsheet(exportDefinition);
System.IO.File.WriteAllBytes("Sample.xlsx", fileContent);
Expand Down Expand Up @@ -76,4 +77,6 @@ This package is primarily geared towards the exporting of lists of objects into
* Data type formatting for Date & Currency fields
* Auto-fit of all columns for display
* The ability to freeze the header columns into a freeze pane for single sheet, or multi-sheet exports
* Support for Curreny, Date, F0, F1, and F2 fixed date formats
* The ability to add "Auto Filter" behavior to the data table portion of a sheet, while still supporting all other items
* The ability to automatically add "simple formula" totals to columsn. (SUM, AVG, etc)
* Support for Curreny, Date, F0, F1, F2, and F3 fixed data formats
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace NetCore.Utilities.SpreadsheetExample.Models;

namespace NetCore.Utilities.SpreadsheetExample.Models
internal class SecondExportData
{
class SecondExportData
{
}
}

}
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
using System;
using ICG.NetCore.Utilities.Spreadsheet;

namespace NetCore.Utilities.SpreadsheetExample.Models
namespace NetCore.Utilities.SpreadsheetExample.Models;

public class SimpleExportData
{
public class SimpleExportData
{
public string Title { get; set; }
public string Title { get; set; }

[SpreadsheetColumn("Due Date", format: "D")]
public DateTime DueDate { get; set; }

[SpreadsheetColumn("Due Date", format:"D")]
public DateTime DueDate { get; set; }

[SpreadsheetColumn("Total Cost", format:"C", formula: "SUM")]
public decimal TotalCost { get; set; }
[SpreadsheetColumn("Total Cost", format: "C", formula: "SUM")]
public decimal TotalCost { get; set; }

[SpreadsheetColumn("Testing Numbers", format:"F3")]
public decimal TestingNumbers { get; set; }
[SpreadsheetColumn("Testing Numbers", format: "F3")]
public decimal TestingNumbers { get; set; }

public string Notes { get; set; }
}
public string Notes { get; set; }
}
93 changes: 48 additions & 45 deletions samples/NetCore.Utilities.SpreadsheetExample/Program.cs
Original file line number Diff line number Diff line change
@@ -1,61 +1,64 @@
using System;
using System.Collections.Generic;
using System.IO;
using ICG.NetCore.Utilities.Spreadsheet;
using Microsoft.Extensions.DependencyInjection;
using NetCore.Utilities.SpreadsheetExample.Models;

namespace NetCore.Utilities.SpreadsheetExample
namespace NetCore.Utilities.SpreadsheetExample;

internal class Program
{
class Program
private static void Main(string[] args)
{
static void Main(string[] args)
//Setup our DI Container
var services = new ServiceCollection();
services.UseIcgNetCoreUtilitiesSpreadsheet();
var provider = services.BuildServiceProvider();

//Get our generator and export
var exportGenerator = provider.GetRequiredService<ISpreadsheetGenerator>();
var exportDefinition = new SpreadsheetConfiguration<SimpleExportData>
{
//Setup our DI Container
var services = new ServiceCollection();
services.UseIcgNetCoreUtilitiesSpreadsheet();
var provider = services.BuildServiceProvider();
RenderTitle = true,
DocumentTitle = "Sample Export of 100 Records",
RenderSubTitle = true,
DocumentSubTitle = "Showing the full options",
ExportData = GetSampleExportData(100),
WorksheetName = "Sample",
FreezeHeaders = true,
AutoFilterDataRows = true
};
var fileContent = exportGenerator.CreateSingleSheetSpreadsheet(exportDefinition);
File.WriteAllBytes("Sample.xlsx", fileContent);

//Get our generator and export
var exportGenerator = provider.GetRequiredService<ISpreadsheetGenerator>();
var exportDefinition = new SpreadsheetConfiguration<SimpleExportData>
//Sample 2 sheet export
var multiSheetDefinition = new MultisheetConfiguration()
.WithSheet("Sheet 1", GetSampleExportData(100))
.WithSheet("Additional Sheet", GetSampleExportData(500), config =>
{
RenderTitle = true,
DocumentTitle = "Sample Export of 100 Records",
RenderSubTitle = true,
DocumentSubTitle = "Showing the full options",
ExportData = GetSampleExportData(100),
WorksheetName = "Sample",
FreezeHeaders = true
};
var fileContent = exportGenerator.CreateSingleSheetSpreadsheet(exportDefinition);
System.IO.File.WriteAllBytes("Sample.xlsx", fileContent);

//Sample 2 sheet export
var multiSheetDefinition = new MultisheetConfiguration()
.WithSheet("Sheet 1", GetSampleExportData(100))
.WithSheet("Additional Sheet", GetSampleExportData(500), config =>
{
config.DocumentTitle = "Lots of data";
config.RenderTitle = true;
config.FreezeHeaders = true;
});
config.DocumentTitle = "Lots of data";
config.RenderTitle = true;
config.FreezeHeaders = true;
config.AutoFilterDataRows = true;
});

var multiFileContent = exportGenerator.CreateMultiSheetSpreadsheet(multiSheetDefinition);
System.IO.File.WriteAllBytes("Sample-Multi.xlsx", multiFileContent);
Console.WriteLine("Files Created");
Console.ReadLine();
}
var multiFileContent = exportGenerator.CreateMultiSheetSpreadsheet(multiSheetDefinition);
File.WriteAllBytes("Sample-Multi.xlsx", multiFileContent);
Console.WriteLine("Files Created");
Console.ReadLine();
}

private static List<SimpleExportData> GetSampleExportData(int numberOfRecords)
{
var listData = new List<SimpleExportData>();
for (var i = 0; i < numberOfRecords; i++)
private static List<SimpleExportData> GetSampleExportData(int numberOfRecords)
{
var listData = new List<SimpleExportData>();
for (var i = 0; i < numberOfRecords; i++)
listData.Add(new SimpleExportData
{
listData.Add(new SimpleExportData
{DueDate = DateTime.Now.AddDays(i), Notes = $"Record {i} notes", TotalCost = 15m, TestingNumbers = 1234.4567289m, Title = $"Sample Data Row #{i}"});
}
DueDate = DateTime.Now.AddDays(i), Notes = $"Record {i} notes", TotalCost = 15m,
TestingNumbers = 1234.4567289m, Title = $"Sample Data Row #{i}"
});

return listData;
}
return listData;
}
}
}
40 changes: 32 additions & 8 deletions src/NetCore.Utilities.Spreadsheet/OpenXmlSpreadsheetGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,19 +58,24 @@
stylesPart.Stylesheet = CreateStylesheet();
stylesPart.Stylesheet.Save();

var data = CreateExportSheet(exportConfiguration, out var columns);
var data = CreateExportSheet(exportConfiguration, out var columns, out var filter);

//Add a worksheet to our document
var worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet();

//If we are freezing panes add the sheet views
//If we are freezing panes add the sheet views, this is done BEFORE the data is loaded
if (exportConfiguration.FreezeHeaders)
worksheetPart.Worksheet.Append(CreateFreezePane(exportConfiguration));

//Load the actual data
worksheetPart.Worksheet.Append(columns);
worksheetPart.Worksheet.Append(data);

//If Filtering, add it after we have added the data
if (exportConfiguration.AutoFilterDataRows)
worksheetPart.Worksheet.Append(filter);

//Add the sheet to the workbook
var sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild(new Sheets());
var sheet = new Sheet
Expand Down Expand Up @@ -155,7 +160,7 @@
var spreadsheetDocument = SpreadsheetDocument.Create(output, SpreadsheetDocumentType.Workbook);
var workbookPart = spreadsheetDocument.AddWorkbookPart();
workbookPart.Workbook = new Workbook();
var sheets = spreadsheetDocument.WorkbookPart.Workbook.AppendChild(new Sheets());

Check warning on line 163 in src/NetCore.Utilities.Spreadsheet/OpenXmlSpreadsheetGenerator.cs

View workflow job for this annotation

GitHub Actions / Validate Build

Dereference of a possibly null reference.

Check warning on line 163 in src/NetCore.Utilities.Spreadsheet/OpenXmlSpreadsheetGenerator.cs

View workflow job for this annotation

GitHub Actions / Validate Build

Dereference of a possibly null reference.

//Setup our styles
var stylesPart = spreadsheetDocument.WorkbookPart.AddNewPart<WorkbookStylesPart>();
Expand All @@ -166,17 +171,24 @@
var sheetId = 1u;
foreach (var item in exportSheets)
{
var data = CreateExportSheet(item, out var columns);
var data = CreateExportSheet(item, out var columns, out var filter);

//Add a worksheet to our document
var worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
worksheetPart.Worksheet = new Worksheet();
//If we are freezing panes add the sheet views

//If we are freezing panes add the sheet views before loading the data
if (item.FreezeHeaders)
worksheetPart.Worksheet.Append(CreateFreezePane(item));

//Load the data
worksheetPart.Worksheet.Append(columns);
worksheetPart.Worksheet.Append(data);

//If we are filtering, add the filter ater the data
if (item.AutoFilterDataRows)
worksheetPart.Worksheet.Append(filter);

//Add the sheet to the workbook
var sheet = new Sheet
{
Expand Down Expand Up @@ -222,7 +234,7 @@

private sealed record OutputPropMap(Column Column, List<Cell> Cells);

private static SheetData CreateExportSheet(ISpreadsheetConfiguration exportConfiguration, out Columns columns)
private static SheetData CreateExportSheet(ISpreadsheetConfiguration exportConfiguration, out Columns columns, out AutoFilter filter)
{
//Build out our sheet information
var data = new SheetData();
Expand Down Expand Up @@ -291,7 +303,8 @@
data.Append(headerRow);
currentRow++;

uint? firstDataRow = headerProperties.Any(d => string.IsNullOrWhiteSpace(d.Formula) == false) ? currentRow : null;
var dataRowIndex = currentRow;
var requiresFormula = headerProperties.Any(d => string.IsNullOrWhiteSpace(d.Formula) == false);

//Run the data
foreach (var item in exportConfiguration.ExportData)
Expand Down Expand Up @@ -320,7 +333,8 @@
currentRow++;
}

if (firstDataRow != null)
//Add the formula(s) as needed
if (requiresFormula)
{
var dataRow = new Row { RowIndex = currentRow };
foreach (var prop in headerProperties)
Expand All @@ -331,7 +345,7 @@
{
CellReference = GetCellReferenceByRowAndColumn(currentRow, prop.Order),
DataType = CellValues.Number,
CellFormula = new CellFormula($"{prop.Formula}({GetCellReferenceByRowAndColumn(firstDataRow.Value, prop.Order)}:{GetCellReferenceByRowAndColumn(currentRow - 1, prop.Order)})")
CellFormula = new CellFormula($"{prop.Formula}({GetCellReferenceByRowAndColumn(dataRowIndex, prop.Order)}:{GetCellReferenceByRowAndColumn(currentRow - 1, prop.Order)})")
};

//Match the formatting of the column for this
Expand All @@ -358,6 +372,16 @@
{
CalculateSizes(outputMap.Values.ToList());
}

filter = new AutoFilter();
if (exportConfiguration.AutoFilterDataRows)
{
//Start 1 row up from data row start (to include header)
var startPosition = GetCellReferenceByRowAndColumn(dataRowIndex -1, 1);
var endPosition = GetCellReferenceByRowAndColumn(currentRow - 1, headerProperties.Max(p => p.Order));
filter.Reference = $"{startPosition}:{endPosition}";
}

return data;
}

Expand Down
8 changes: 8 additions & 0 deletions src/NetCore.Utilities.Spreadsheet/SpreadsheetConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ public interface ISpreadsheetConfiguration
/// if set to true the header(s) will be frozen
/// </summary>
public bool FreezeHeaders { get; set; }

/// <summary>
/// If set to true the data section of the sheet will have auto-filter turned on
/// </summary>
public bool AutoFilterDataRows { get; set; }
}
/// <inheritdoc />
/// <typeparam name="TRecord">The type to be exported</typeparam>
Expand Down Expand Up @@ -130,6 +135,9 @@ public class SpreadsheetConfiguration<T> : ISpreadsheetConfiguration<T> where T
/// if set to true the header(s) will be frozen
/// </summary>
public bool FreezeHeaders { get; set; }

/// <inheritdoc />
public bool AutoFilterDataRows { get; set; }
}

/// <summary>
Expand Down
Loading