Skip to content

Commit

Permalink
Dynamic Properties (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
jpare authored Dec 6, 2019
1 parent d41a11c commit e9dda4b
Show file tree
Hide file tree
Showing 9 changed files with 282 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@
<Compile Include="..\Dexiom.EPPlusExporter\ColumnHeaderConfiguration.cs">
<Link>ColumnHeaderConfiguration.cs</Link>
</Compile>
<Compile Include="..\Dexiom.EPPlusExporter\DisplayField.cs">
<Link>DisplayField.cs</Link>
</Compile>
<Compile Include="..\Dexiom.EPPlusExporter\DynamicProperty.cs">
<Link>DynamicProperty.cs</Link>
</Compile>
<Compile Include="..\Dexiom.EPPlusExporter\EnumerableExporter.cs">
<Link>EnumerableExporter.cs</Link>
</Compile>
Expand Down
2 changes: 2 additions & 0 deletions Dexiom.EPPlusExporter/Dexiom.EPPlusExporter.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="DisplayField.cs" />
<Compile Include="DynamicProperty.cs" />
<Compile Include="ColumnContentConfiguration.cs" />
<Compile Include="ColumnHeaderConfiguration.cs" />
<Compile Include="Extensions\IDictionaryExtensions.cs" />
Expand Down
52 changes: 52 additions & 0 deletions Dexiom.EPPlusExporter/DisplayField.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using System.ComponentModel;
using System.Reflection;
using Dexiom.EPPlusExporter.Helpers;

namespace Dexiom.EPPlusExporter
{
public class DisplayField<T> where T : class
{
private readonly PropertyInfo _propertyInfo;
private readonly DynamicProperty<T> _dynamicProperty;

public DisplayField(PropertyInfo propertyInfo)
{
_propertyInfo = propertyInfo;

Name = _propertyInfo.Name;
DisplayName = ReflectionHelper.GetPropertyDisplayName(_propertyInfo);
Type = _propertyInfo.PropertyType;
}

public DisplayField(DynamicProperty<T> dynamicProperty)
{
_dynamicProperty = dynamicProperty;

Name = _dynamicProperty.Name;
DisplayName = _dynamicProperty.DisplayName;
Type = _dynamicProperty.ValueType;
}

#region Properties
public string Name { get; set; }
public string DisplayName { get; set; }
public Type Type { get; set; }
#endregion

public object GetValue(T item)
{
if (_propertyInfo != null)
{
#if NET4
return _propertyInfo.GetValue(item, null);
#endif
#if NET45 || NET46
return _propertyInfo.GetValue(item);
#endif
}

return _dynamicProperty.GetValue(item);
}
}
}
22 changes: 22 additions & 0 deletions Dexiom.EPPlusExporter/DynamicProp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;

namespace Dexiom.EPPlusExporter
{
public static class DynamicProperty
{
public static DynamicProp<T> Create<T>(Func<T, object> getValue, Type valueType) where T : class => new DynamicProp<T>(getValue, valueType);
}

public class DynamicProp<T>
{
public DynamicProp(Func<T, object> getValue, Type valueType)
{
ValueType = valueType;
GetValue = getValue;
}


public Type ValueType { get; set; }
public Func<T, object> GetValue { get; set; }
}
}
30 changes: 30 additions & 0 deletions Dexiom.EPPlusExporter/DynamicProperty.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;

namespace Dexiom.EPPlusExporter
{
#region Create Method (using type inference)
public static class DynamicProperty
{
public static DynamicProperty<T> Create<T>(IEnumerable<T> data, string name, string displayName, Type valueType, Func<T, object> getValue) where T : class
{
return new DynamicProperty<T>()
{
Name = name,
DisplayName = displayName,
ValueType = valueType,
GetValue = getValue
};
}
}
#endregion

public class DynamicProperty<T>
where T : class
{
public string Name { get; set; }
public string DisplayName { get; set; }
public Type ValueType { get; set; }
public Func<T, object> GetValue { get; set; }
}
}
113 changes: 75 additions & 38 deletions Dexiom.EPPlusExporter/EnumerableExporter.cs
Original file line number Diff line number Diff line change
@@ -1,40 +1,43 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Net.Configuration;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Dexiom.EPPlusExporter.Extensions;
using Dexiom.EPPlusExporter.Helpers;
using OfficeOpenXml;
using OfficeOpenXml.Style;
using OfficeOpenXml.Table;

namespace Dexiom.EPPlusExporter
{
#region Create Method (using type inference)
public class EnumerableExporter
#region Create Method (using type inference)
public static class EnumerableExporter
{
public static EnumerableExporter<T> Create<T>(IEnumerable<T> data, TableStyles tableStyles = TableStyles.Medium4) where T : class => new EnumerableExporter<T>(data) { TableStyle = tableStyles };
public static EnumerableExporter<T> Create<T>(IEnumerable<T> data, IEnumerable<DynamicProperty<T>> dynamicProperties, TableStyles tableStyles = TableStyles.Medium4) where T : class => new EnumerableExporter<T>(data, dynamicProperties) { TableStyle = tableStyles };
}
#endregion
#endregion

public class EnumerableExporter<T> : TableExporter<T>
where T : class
{
public IEnumerable<T> Data { get; set; }
public IEnumerable<DynamicProperty<T>> DynamicProperties { get; set; }

#region Constructors
#region Constructors
public EnumerableExporter(IEnumerable<T> data)
{
Data = data;
}
#endregion

#region Protected
public EnumerableExporter(IEnumerable<T> data, IEnumerable<DynamicProperty<T>> dynamicProperties)
{
Data = data;
DynamicProperties = dynamicProperties;
}
#endregion

#region Protected
protected override ExcelRange AddWorksheet(ExcelPackage package)
{
const int headerFirstRow = 1;
Expand All @@ -46,29 +49,63 @@ protected override ExcelRange AddWorksheet(ExcelPackage package)
return null;

//let's avoid multiple enumeration
var myData = Data as IList<T> ?? Data.ToList();
var data = Data as IList<T> ?? Data.ToList();
IList<DynamicProperty<T>> dynamicProperties = null;
if (DynamicProperties != null)
dynamicProperties = DynamicProperties as IList<DynamicProperty<T>> ?? DynamicProperties.ToList();

//get available properties
var properties = ReflectionHelper.GetBaseTypeOfEnumerable(Data).GetProperties()
.Where(p => !IgnoredProperties.Contains(p.Name));
var properties = ReflectionHelper.GetBaseTypeOfEnumerable(data).GetProperties()
.Where(p => !IgnoredProperties.Contains(p.Name))
.ToList();

//resolve displayed properties
var displayedProperties = DisplayedProperties?.Select(propName => properties.FirstOrDefault(n => n.Name == propName)).Where(propInfo => propInfo != null).ToList() ?? properties.ToList();
HashSet<string> allPropertyNames;
if (DisplayedProperties != null)
{
allPropertyNames = DisplayedProperties;
}
else
{
allPropertyNames = new HashSet<string>(properties.Select(n => n.Name));
if (dynamicProperties != null)
{
foreach (var dynamicPropertyName in dynamicProperties.Select(n => n.Name))
allPropertyNames.Add(dynamicPropertyName);
}
}

var displayFields = new List<DisplayField<T>>();
foreach (var propertyName in allPropertyNames)
{
var property = properties.FirstOrDefault(n => n.Name == propertyName);
if (property != null)
{
//displayedProperties.Add(property); //todo: delete me
displayFields.Add(new DisplayField<T>(property));
}
else
{
var dynamicProperty = DynamicProperties?.FirstOrDefault(n => n.Name == propertyName);
if (dynamicProperty != null)
displayFields.Add(new DisplayField<T>(dynamicProperty));
}
}

//init the configurations
var columnConfigurations = GetColumnConfigurations(displayedProperties.Select(n => n.Name));
var columnConfigurations = GetColumnConfigurations(displayFields.Select(n => n.Name));

//create the worksheet
var worksheet = package.Workbook.Worksheets.Add(WorksheetName);

//Create table header
{
var col = headerFirstCol;
foreach (var property in displayedProperties)
foreach (var displayField in displayFields)
{
var colConfig = columnConfigurations[property.Name];
var colConfig = columnConfigurations[displayField.Name];
var cell = worksheet.Cells[headerFirstRow, col];
cell.Value = string.IsNullOrEmpty(colConfig.Header.Text) ? ReflectionHelper.GetPropertyDisplayName(property) : colConfig.Header.Text;
cell.Value = string.IsNullOrEmpty(colConfig.Header.Text) ? displayField.DisplayName : colConfig.Header.Text;
colConfig.Header.SetStyle(cell.Style);

col++;
Expand All @@ -77,37 +114,37 @@ protected override ExcelRange AddWorksheet(ExcelPackage package)

//Add rows
var row = dataFirstRow;
foreach (var item in myData)
foreach (var item in data)
{
var iCol = dataFirstCol;
foreach (var property in displayedProperties)
foreach (var displayField in displayFields)
{
var cell = worksheet.Cells[row, iCol];
cell.Value = GetPropertyValue(property, item);
cell.Value = ApplyTextFormat(displayField.Name, displayField.GetValue(item));

iCol++;
}
row++;
}

//get bottom & right bounds
var dataLastCol = dataFirstCol + displayedProperties.Count - 1;
var dataLastRow = dataFirstRow + Math.Max(myData.Count, 1) - 1; //make sure to have at least 1 data line (for table format)
var dataLastCol = dataFirstCol + displayFields.Count - 1;
var dataLastRow = dataFirstRow + Math.Max(data.Count, 1) - 1; //make sure to have at least 1 data line (for table format)
var tableRange = worksheet.Cells[headerFirstRow, headerFirstCol, dataLastRow, dataLastCol];

WorksheetHelper.FormatAsTable(tableRange, TableStyle, WorksheetName, false);

//apply configurations
{
var iCol = dataFirstCol;
foreach (var property in displayedProperties)
foreach (var displayField in displayFields)
{
var colConfig = columnConfigurations[property.Name];
var colConfig = columnConfigurations[displayField.Name];
var columnRange = worksheet.Cells[dataFirstRow, iCol, dataLastRow, iCol];

//apply default number format
if (DefaultNumberFormats.ContainsKey(property.PropertyType))
columnRange.Style.Numberformat.Format = DefaultNumberFormats[property.PropertyType];
if (DefaultNumberFormats.ContainsKey(displayField.Type))
columnRange.Style.Numberformat.Format = DefaultNumberFormats[displayField.Type];

//apply number format
if (colConfig.Content.NumberFormat != null)
Expand All @@ -129,14 +166,14 @@ protected override ExcelRange AddWorksheet(ExcelPackage package)
//apply conditional styles
{
var iCol = dataFirstCol;
foreach (var property in displayedProperties)
foreach (var displayField in displayFields)
{
if (ConditionalStyles.ContainsKey(property.Name))
if (ConditionalStyles.ContainsKey(displayField.Name))
{
var conditionalStyle = ConditionalStyles[property.Name];
var conditionalStyle = ConditionalStyles[displayField.Name];

var iRow = dataFirstRow;
foreach (var item in myData)
foreach (var item in data)
{
var cell = worksheet.Cells[iRow, iCol];
conditionalStyle(item, cell.Style); //apply style on cell
Expand All @@ -151,14 +188,14 @@ protected override ExcelRange AddWorksheet(ExcelPackage package)
//apply conditional styles
{
var iCol = dataFirstCol;
foreach (var property in displayedProperties)
foreach (var displayField in displayFields)
{
if (Formulas.ContainsKey(property.Name))
if (Formulas.ContainsKey(displayField.Name))
{
var formulaFormat = Formulas[property.Name];
var formulaFormat = Formulas[displayField.Name];

var iRow = dataFirstRow;
foreach (var item in myData)
foreach (var item in data)
{
var cell = worksheet.Cells[iRow, iCol];
var formula = formulaFormat(item, cell.Value); //apply style on cell
Expand All @@ -176,6 +213,6 @@ protected override ExcelRange AddWorksheet(ExcelPackage package)
return tableRange;
}

#endregion
#endregion
}
}
Loading

0 comments on commit e9dda4b

Please sign in to comment.