[ELMA3] Добавление кастомных колонок при экспорте записей в Excel
В системе существует возможность экспортировать сведения из динамически формируемых таблиц в Excel.
Точка расширения
Чтобы экспортировать в Excel свойства, которые отсутствуют в сущности, следует использовать точку расширения IExportExcelCustomColumnsProvider, которая позволяет добавить при экспорте свои колонки в базовый набор колонок.
/// <summary>
/// Провайдер для кастомных столбцов при экспорте в Excel
/// </summary>
[ExtensionPoint(ServiceScope.Shell)]
public interface IExportExcelCustomColumnsProvider : IExportExcelValueProvider
{
/// <summary>
/// Получить расширенный список столбцов для экспорта
/// </summary>
/// <param name="type">Тип сущности</param>
/// <param name="baseColumns">Базовый список столбцов, необходим для корректного встраивания кастомных столбцов</param>
/// <returns>Новый список столбцов со встроенными кастомными</returns>
IEnumerable<ExportColumnDescription> GetColumns(Type type, IEnumerable<ExportColumnDescription> baseColumns);
}
Пример реализации
В отчете о трудозатратах выводятся колонки Название и План.
Однако, при экспорте в Excel данные колонки отсутствуют. Причина в том, что сущность трудозатрат WorkLogItem не содержит соответствующих свойств.
Чтобы экспортировать необходимые свойства, добавляется реализация точки расширения IExportExcelCustomColumnsProvider, которая для типа IWorkLogItem встраивает в нужном порядке недостающие колонки, а также позволяет вернуть значение свойства для кастомной ячейки и при необходимости конвертирует его (например, преобразует значение типа WorkTime в TimeSpan).
/// <summary>
/// Провайдер для кастомных столбцов трудозатрат при экспорте в Excel
/// </summary>
[Component]
internal sealed class WorkLogItemExportExcelCustomColumnsProvider : IExportExcelCustomColumnsProvider
{
#region Private members
/// <summary>
/// Максимальное число минут в типе Timespan
/// </summary>
private static readonly long MaxTotalMinutes = (long)TimeSpan.MaxValue.TotalMinutes;
/// <summary>
/// Гуид свойства Наименование
/// </summary>
private static readonly Guid NameUid = new Guid("26401CA9-3D5A-4231-9E8C-2EA0DD04D121");
/// <summary>
/// Гуид свойства Плановые трудозатраты
/// </summary>
private static readonly Guid PlanWorkTimeUid = new Guid("99334918-3FE6-465E-87C3-35363073B5AE");
/// <summary>
/// Гуид свойства Статус
/// </summary>
private static readonly Guid StatusUid = new Guid("d671f637-3a5a-4c08-8b55-a1f3cb622027");
/// <summary>
/// Гуид свойства Вид деятельности
/// </summary>
private static readonly Guid ActivityUid = new Guid("50417934-90c5-46b9-91be-d2935eceeabc");
/// <summary>
/// Описание кастомного столбца Название
/// </summary>
private readonly ExportColumnDescription nameColumn = new ExportColumnDescription()
{
ColumnName = SR.T("Название"),
TableColumnWidth = 200,
PropertyUid = NameUid
};
/// <summary>
/// Описание кастомного столбца Плановые трудозатраты
/// </summary>
private readonly ExportColumnDescription planWorkTimeColumn = new ExportColumnDescription()
{
ColumnName = SR.T("Плановые трудозатраты"),
TableColumnWidth = 200,
PropertyUid = PlanWorkTimeUid
};
#endregion
#region Implementation IExportExcelCustomColumnsProvider
/// <inheritdoc />
public IEnumerable<ExportColumnDescription> GetColumns(Type type, IEnumerable<ExportColumnDescription> baseColumns)
{
List<ExportColumnDescription> result = null;
if (type.IsInheritOrSame<IWorkLogItem>())
{
result = baseColumns.ToList();
// Название вставляем после статуса или первым.
var insertIndex = result.FindIndex(col => col.PropertyUid == StatusUid) + 1;
result.Insert(insertIndex, nameColumn);
// Плановые трудозатраты вставляем после вида деятельности или последним.
insertIndex = result.FindIndex(col => col.PropertyUid == ActivityUid) + 1;
if (insertIndex == 0)
{
result.Add(planWorkTimeColumn);
}
else
{
result.Insert(insertIndex, planWorkTimeColumn);
}
}
return result;
}
#endregion
#region Implementation IExportExcelValueProvider
/// <inheritdoc />
public object GetValue(IEntity entity, Guid propertyUid)
{
object result = null;
var item = (IWorkLogItem)entity;
if (propertyUid == NameUid)
{
result = item.Name;
}
else if (propertyUid == PlanWorkTimeUid)
{
result = ConvertWorkTime(item.PlanWorkTime);
}
else
{
// nothing to do.
}
return result;
}
/// <inheritdoc />
public bool Resolve(IEntity entity, Guid propertyUid)
{
return entity is IWorkLogItem && propertyUid.In(NameUid, PlanWorkTimeUid);
}
#endregion
/// <summary>
/// Конвертируем <see cref="WorkTime"/> в формат для выгрузки
/// </summary>
/// <param name="value">Конвертируемое значение</param>
/// <returns>Объект типа Timespan или string</returns>
private object ConvertWorkTime(WorkTime? value)
{
if (value != null)
{
var workTime = value.Value;
var totalMinutes = workTime.TotalMinutes;
if (totalMinutes <= MaxTotalMinutes)
{
return TimeSpan.FromMinutes(totalMinutes);
}
else
{
// В случае переполнения выведем хотя бы просто строку (пусть она в сумме и не учтется потом).
return string.Concat(workTime.TotalHours, ":", workTime.Minutes);
}
}
return TimeSpan.Zero;
}
}
В приведенном примере встроены колонки Название и Плановые трудозатраты.