diff --git a/components/treelist/export/events.md b/components/treelist/export/events.md new file mode 100644 index 000000000..e20664f6f --- /dev/null +++ b/components/treelist/export/events.md @@ -0,0 +1,348 @@ +--- +title: Export Events +page_title: TreeList - Export Events +description: Learn about the Blazor TreeList events related to exporting the component data. +slug: treelist-export-events +tags: telerik,blazor,treelist,export,events +published: True +position: 15 +components: ["treelist"] +--- + +# Export Events + +You can customize the files exported to Excel by using the [OnBeforeExport](#onbeforeexport) and the [OnAfterExport](#onafterexport) events exposed to the `TreeListExcelExport` tag. + +## OnBeforeExport + +The `OnBeforeExport` event fires after the user clicks the `ExcelExport` command button and before the export process starts. You can use the event to configure the exported TreeList columns or change the exported data. The event handler receives a `TreeListBeforeExcelExportEventArgs` object, which provides the following properties: + +* `Columns`—`List`—A collection of all exportable columns in the TreeList. These are all visible `TreeListColumn` instances. You can customize the following attributes of the TreeList column before exporting it into Excel: + + * `Width`—Define the width of the column **in pixels**. + * `Title`—Define the column title to be shown in the Excel file header. + * `NumberFormat`—Provide an Excel-compatible number/date format + * `Field`—Set the data bound field of the column. + +To export a hidden TreeList column that has its `Visible` parameter set to `false`, you can manually define an instance of the `TreeListExcelExportColumn` in the handler for the `OnBeforeExport` event and add that column to the `args.Columns` collection. + +* `Data`—`IEnumerable`—Assign a custom collection of data to be exported to Excel. + +* `IsCancelled`— `bool`—Cancel the `OnBeforeExcel` event by setting the `isCancelled` property to `true`. + +>caption Using the TreeList OnBeforeExport with Excel export + +````RAZOR +@using Telerik.Documents.SpreadsheetStreaming + + + + + + + Export to Excel + + + + + + + + + +@code { + private TelerikTreeList? TreeListRef; + private IEnumerable? TreeListData { get; set; } + + private EmployeeService TreeListEmployeeService { get; set; } = new(); + + private void OnTreeListBeforeExport(TreeListBeforeExcelExportEventArgs args) + { + // Export the hidden IsDriver column that has Visible="false" + var exportableHiddenColumn = new TreeListExcelExportColumn() + { + Title = "Is Driver", + Field = nameof(Employee.IsDriver) + }; + args.Columns.Add(exportableHiddenColumn); + + // Customize the Width of the first exported column + args.Columns[0].Width = "360px"; + + // Change the format of the Salary column + // BuiltInNumberFormats is part of the Telerik.Documents.SpreadsheetStreaming namespace + args.Columns[1].NumberFormat = BuiltInNumberFormats.GetCurrency2(); + + // Change the format and title of the HireDate column + args.Columns[2].NumberFormat = BuiltInNumberFormats.GetShortDate(); + args.Columns[2].Title = "Hired On"; + + // Set IsCancelled to true if you want to prevent exporting + // args.IsCancelled = false; + } + + protected override async Task OnInitializedAsync() + { + TreeListData = await TreeListEmployeeService.Read(); + } + + public class Employee + { + public int Id { get; set; } + public bool HasChildren { get; set; } + public List? Items { get; set; } + public string Name { get; set; } = string.Empty; + public string Notes { get; set; } = string.Empty; + public decimal? Salary { get; set; } + public DateTime? HireDate { get; set; } + public bool IsDriver { get; set; } + + public override bool Equals(object? obj) + { + return obj is Employee && ((Employee)obj).Id == Id; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + + #region Data Service + + public class EmployeeService + { + private List Items { get; set; } = new(); + + private readonly int TreeLevelCount; + private readonly int RootItemCount; + private readonly int ChildItemCount; + + private int LastId { get; set; } + private readonly Random Rnd = Random.Shared; + + public async Task> Read() + { + await SimulateAsyncOperation(); + + return Items; + } + + private async Task SimulateAsyncOperation() + { + await Task.Delay(100); + } + + private void PopulateItems(List items, int level) + { + for (int i = 1; i <= (level == 1 ? RootItemCount : ChildItemCount); i++) + { + var itemId = ++LastId; + + Employee newItem = new Employee() + { + Id = itemId, + HasChildren = level < TreeLevelCount, + Name = $"Employee Name {itemId} ({level}-{i})", + Notes = $"Multi-line\nnotes {itemId}", + Salary = Rnd.Next(1_000, 10_000) * 1.23m, + HireDate = DateTime.Today.AddDays(-Rnd.Next(365, 3650)), + IsDriver = itemId % 2 == 0 + }; + + items.Add(newItem); + } + + if (level < TreeLevelCount) + { + PopulateChildren(items, level + 1); + } + } + + private void PopulateChildren(List items, int level) + { + foreach (var item in items) + { + item.Items = new List(); + + PopulateItems(item.Items, level); + } + } + + public EmployeeService(int treeLevelCount = 3, int rootItemCount = 5, int childItemCount = 3) + { + TreeLevelCount = treeLevelCount; + RootItemCount = rootItemCount; + ChildItemCount = childItemCount; + + List items = new(); + PopulateItems(items, 1); + + Items = items; + } + } + + #endregion Data Service +} +```` + +## OnAfterExport + +The `OnAfterExport` event fires after [OnBeforeExport](#onbeforeexport) and before the generated file is provided to the user. You can use the event to make changes to the exported file. The event handler receives a `TreeListAfterExcelExportEventArgs` object, which provides the following properties: + +* `Stream`—`MemoryStream`—The output of the Excel export as a memory stream. The stream itself is finalized, so that the resource does not leak. To read and work with the stream, clone its available binary data to a new `MemoryStream` instance. Then, use [Telerik Document Processing](slug:dpl-in-blazor) to make changes to the exported file, for example, [apply custom cell formatting with RadSpreadProcessing](slug:grid-kb-custom-cell-formatting-with-radspreadprocessing). + +>caption Get the stream of the exported Excel file + +````RAZOR Excel + + + + + + Export to Excel + + + + + + + + + +@code { + private TelerikTreeList? TreeListRef; + private IEnumerable? TreeListData { get; set; } + + private EmployeeService TreeListEmployeeService { get; set; } = new(); + + private void OnTreeListAfterExport(TreeListAfterExcelExportEventArgs args) + { + byte[] streamBytes = args.Stream.ToArray(); + MemoryStream excelStream = new MemoryStream(streamBytes); + } + + protected override async Task OnInitializedAsync() + { + TreeListData = await TreeListEmployeeService.Read(); + } + + public class Employee + { + public int Id { get; set; } + public bool HasChildren { get; set; } + public List? Items { get; set; } + public string Name { get; set; } = string.Empty; + public string Notes { get; set; } = string.Empty; + public decimal? Salary { get; set; } + public DateTime? HireDate { get; set; } + public bool IsDriver { get; set; } + + public override bool Equals(object? obj) + { + return obj is Employee && ((Employee)obj).Id == Id; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + + #region Data Service + + public class EmployeeService + { + private List Items { get; set; } = new(); + + private readonly int TreeLevelCount; + private readonly int RootItemCount; + private readonly int ChildItemCount; + + private int LastId { get; set; } + private readonly Random Rnd = Random.Shared; + + public async Task> Read() + { + await SimulateAsyncOperation(); + + return Items; + } + + private async Task SimulateAsyncOperation() + { + await Task.Delay(100); + } + + private void PopulateItems(List items, int level) + { + for (int i = 1; i <= (level == 1 ? RootItemCount : ChildItemCount); i++) + { + var itemId = ++LastId; + + Employee newItem = new Employee() + { + Id = itemId, + HasChildren = level < TreeLevelCount, + Name = $"Employee Name {itemId} ({level}-{i})", + Notes = $"Multi-line\nnotes {itemId}", + Salary = Rnd.Next(1_000, 10_000) * 1.23m, + HireDate = DateTime.Today.AddDays(-Rnd.Next(365, 3650)), + IsDriver = itemId % 2 == 0 + }; + + items.Add(newItem); + } + + if (level < TreeLevelCount) + { + PopulateChildren(items, level + 1); + } + } + + private void PopulateChildren(List items, int level) + { + foreach (var item in items) + { + item.Items = new List(); + + PopulateItems(item.Items, level); + } + } + + public EmployeeService(int treeLevelCount = 3, int rootItemCount = 5, int childItemCount = 3) + { + TreeLevelCount = treeLevelCount; + RootItemCount = rootItemCount; + ChildItemCount = childItemCount; + + List items = new(); + PopulateItems(items, 1); + + Items = items; + } + } + + #endregion Data Service +} +```` + +## See Also + +* [TreeList Excel Export](slug:treelist-export-excel) +* [Custom cell formatting of the exported file with RadSpreadProcessing](slug:grid-kb-custom-cell-formatting-with-radspreadprocessing) +* [Custom cell formatting of the exported file with RadSpreadStreamProcessing](slug:grid-kb-custom-cell-formatting-with-radspreadstreamprocessing) diff --git a/components/treelist/export/excel.md b/components/treelist/export/excel.md new file mode 100644 index 000000000..3a5b74830 --- /dev/null +++ b/components/treelist/export/excel.md @@ -0,0 +1,407 @@ +--- +title: Excel +page_title: TreeList - Excel Export +description: Export to Excel the TreeList for Blazor. +slug: treelist-export-excel +tags: telerik,blazor,treelist,export,excel +published: True +position: 5 +components: ["treelist"] +--- + +# TreeList Excel Export + +You can export the grid to Excel with a click of a button. The current filter, sort, page, grouping, column order and column size are applied to the `xlsx` document. + +When you click the Export button, your browser will receive the resulting file. + +>tip Make sure to get familiar with all the [general export documentation first](slug:treelist-export-overview). + +#### In This Article + +* [Basics](#basics) +* [Programmatic Export](#programmatic-export) +* [Customization](#customization) + +## Basics + +To enable the Excel export in the TreeList: + +1. [Add the Export Tool](#add-the-export-tool) +1. [Configure the Export Settings](#configure-the-export-settings) +1. [Set the Columns Width in Pixels](#set-the-columns-width-in-pixels) + +### Add the Export Tool + +Add a `TreeListCommandButton` with `Command="ExcelExport"` and optional `Icon` inside the [``](slug:treelist-toolbar): + +````RAZOR.skip-repl + + Export to Excel + +```` + +### Configure the Export Settings + +To configure the Excel export settings, add the `TreeListExcelExport` tag under the `TreeListExport` tag. You can set the following options: + +@[template](/_contentTemplates/common/parameters-table-styles.md#table-layout) + +| Parameter | Type and Default Value | Description | +| --- | --- | --- | +| `AllPages` | `bool` | Whether to export the current page only, or the entire data from the data source. | +| `ExpandAll` | `bool` | Whether to expand all parent items, so that the children are exported too. | +| `FileName` | `string` | The name of the file. The grid will add the `.xslx` extension for you. | + +>caption Using TreeList Excel Export Settings + +````RAZOR.skip-repl + + + +```` + +For further customizations, use the `TreeListExcelExport` tag to subscribe to the [TreeList export events](slug:treelist-export-events). + +### Set the Columns Width in Pixels + +The export to Excel does not require that all columns have explicit widths set. However, if you do set the column widths, ensure you use only `px`. + +Excel cannot parse units different than `px` (e.g., `rem` or `%`) and renders a collapsed (hidden) column with zero width. This is an Excel limitation. If you prefer to use different than `px` units in the UI, handle the [`OnBeforeExport` event to provide the column width in pixels for the proper export](slug:treelist-export-events). + +>caption Export the TreeList to Excel + +````RAZOR + + + + + + Export to Excel + + + + + + + + + +@code { + private IEnumerable? TreeListData { get; set; } + + private EmployeeService TreeListEmployeeService { get; set; } = new(); + + protected override async Task OnInitializedAsync() + { + TreeListData = await TreeListEmployeeService.Read(); + } + + public class Employee + { + public int Id { get; set; } + public bool HasChildren { get; set; } + public List? Items { get; set; } + public string Name { get; set; } = string.Empty; + public string Notes { get; set; } = string.Empty; + public decimal? Salary { get; set; } + public DateTime? HireDate { get; set; } + public bool IsDriver { get; set; } + + public override bool Equals(object? obj) + { + return obj is Employee && ((Employee)obj).Id == Id; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + + #region Data Service + + public class EmployeeService + { + private List Items { get; set; } = new(); + + private readonly int TreeLevelCount; + private readonly int RootItemCount; + private readonly int ChildItemCount; + + private int LastId { get; set; } + private readonly Random Rnd = Random.Shared; + + public async Task> Read() + { + await SimulateAsyncOperation(); + + return Items; + } + + private async Task SimulateAsyncOperation() + { + await Task.Delay(100); + } + + private void PopulateItems(List items, int level) + { + for (int i = 1; i <= (level == 1 ? RootItemCount : ChildItemCount); i++) + { + var itemId = ++LastId; + + Employee newItem = new Employee() + { + Id = itemId, + HasChildren = level < TreeLevelCount, + Name = $"Employee Name {itemId} ({level}-{i})", + Notes = $"Multi-line\nnotes {itemId}", + Salary = Rnd.Next(1_000, 10_000) * 1.23m, + HireDate = DateTime.Today.AddDays(-Rnd.Next(365, 3650)), + IsDriver = itemId % 2 == 0 + }; + + items.Add(newItem); + } + + if (level < TreeLevelCount) + { + PopulateChildren(items, level + 1); + } + } + + private void PopulateChildren(List items, int level) + { + foreach (var item in items) + { + item.Items = new List(); + + PopulateItems(item.Items, level); + } + } + + public EmployeeService(int treeLevelCount = 3, int rootItemCount = 5, int childItemCount = 3) + { + TreeLevelCount = treeLevelCount; + RootItemCount = rootItemCount; + ChildItemCount = childItemCount; + + List items = new(); + PopulateItems(items, 1); + + Items = items; + } + } + + #endregion Data Service +} +```` + +## Programmatic Export + +You can programmatically invoke the export feature of the TreeList, by using the following methods exposed on the `@ref` of the TreeList: + +| Method | Type | Description | +| --- | --- | --- | +| `SaveAsExcelFileAsync` | `ValueTask` | Sends the exported Excel file to the browser for download. You can pass [`TreeListExcelExportOptions`](slug:Telerik.Blazor.Components.TreeList.TreeListExcelExportOptions) to customize the export. | +| `ExportToExcelAsync` | `Task` | Returns the exported data as a `MemoryStream`. The stream itself is finalized, so that the resource does not leak. To read and work with the stream, clone its available binary data to a new `MemoryStream` instance. You can pass [`TreeListExcelExportOptions`](slug:Telerik.Blazor.Components.TreeList.TreeListExcelExportOptions) to customize the export. | + +When exporting programmatically with a `TreeListExcelExportOptions` argument, the `Columns` and `Data` properties of `TreeListExcelExportOptions` are required. + +>caption Invoke the export function from code + +````RAZOR +Download Excel +Get Excel Stream +Download Excel with Options +Get Excel Stream with Options + + + + + + + Export to Excel + + + + + + + + + +@code { + private TelerikTreeList? TreeListRef; + private IEnumerable? TreeListData { get; set; } + + private EmployeeService TreeListEmployeeService { get; set; } = new(); + + private async Task GetTheDataAsAStream() + { + MemoryStream finalizedStream = await TreeListRef!.ExportToExcelAsync(); + MemoryStream exportedExcelStream = new MemoryStream(finalizedStream.ToArray()); + } + + private async Task SaveAsExcelWithOptions() + { + TreeListExcelExportOptions excelOptions = new TreeListExcelExportOptions() + { + FileName = "custom-export", + Data = TreeListData?.Take(2).ToList(), + Columns = new List() + { + new TreeListExcelExportColumn() { Field = nameof(Employee.Name), Width = "300px" }, + new TreeListExcelExportColumn() { Field = nameof(Employee.Salary), Width = "120px" } + } + }; + + await TreeListRef!.SaveAsExcelFileAsync(excelOptions); + } + + private async Task ExportToExcelWithOptions() + { + TreeListExcelExportOptions excelOptions = new TreeListExcelExportOptions() + { + FileName = "custom-export", + Data = TreeListData?.Take(2).ToList(), + Columns = new List() + { + new TreeListExcelExportColumn() { Field = nameof(Employee.Name), Width = "300px" }, + new TreeListExcelExportColumn() { Field = nameof(Employee.Salary), Width = "120px" } + } + }; + + MemoryStream exportStream = await TreeListRef!.ExportToExcelAsync(excelOptions); + + MemoryStream exportedExcelStream = new MemoryStream(exportStream.ToArray()); + } + + protected override async Task OnInitializedAsync() + { + TreeListData = await TreeListEmployeeService.Read(); + } + + public class Employee + { + public int Id { get; set; } + public bool HasChildren { get; set; } + public List? Items { get; set; } + public string Name { get; set; } = string.Empty; + public string Notes { get; set; } = string.Empty; + public decimal? Salary { get; set; } + public DateTime? HireDate { get; set; } + public bool IsDriver { get; set; } + + public override bool Equals(object? obj) + { + return obj is Employee && ((Employee)obj).Id == Id; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + } + + #region Data Service + + public class EmployeeService + { + private List Items { get; set; } = new(); + + private readonly int TreeLevelCount; + private readonly int RootItemCount; + private readonly int ChildItemCount; + + private int LastId { get; set; } + private readonly Random Rnd = Random.Shared; + + public async Task> Read() + { + await SimulateAsyncOperation(); + + return Items; + } + + private async Task SimulateAsyncOperation() + { + await Task.Delay(100); + } + + private void PopulateItems(List items, int level) + { + for (int i = 1; i <= (level == 1 ? RootItemCount : ChildItemCount); i++) + { + var itemId = ++LastId; + + Employee newItem = new Employee() + { + Id = itemId, + HasChildren = level < TreeLevelCount, + Name = $"Employee Name {itemId} ({level}-{i})", + Notes = $"Multi-line\nnotes {itemId}", + Salary = Rnd.Next(1_000, 10_000) * 1.23m, + HireDate = DateTime.Today.AddDays(-Rnd.Next(365, 3650)), + IsDriver = itemId % 2 == 0 + }; + + items.Add(newItem); + } + + if (level < TreeLevelCount) + { + PopulateChildren(items, level + 1); + } + } + + private void PopulateChildren(List items, int level) + { + foreach (var item in items) + { + item.Items = new List(); + + PopulateItems(item.Items, level); + } + } + + public EmployeeService(int treeLevelCount = 3, int rootItemCount = 5, int childItemCount = 3) + { + TreeLevelCount = treeLevelCount; + RootItemCount = rootItemCount; + ChildItemCount = childItemCount; + + List items = new(); + PopulateItems(items, 1); + + Items = items; + } + } + + #endregion Data Service +} +```` + +## Customization + +To customize the exported file, handle the `OnBeforeExport` or `OnAfterExport` events the TreeList exposes. + +The component allows you to control the data set that will be exported. It also provides built-in customization options for the columns such as `Width`, `Title` and more. + +For more advanced customization (such as coloring the headers or bolding the titles) the TreeList lets you get the `MemoryStream` of the file. Thus, you can customize it using the [`SpreadProcessing`](https://docs.telerik.com/devtools/document-processing/libraries/radspreadprocessing/overview) or the [`SpreadStreamProcessing`](https://docs.telerik.com/devtools/document-processing/libraries/radspreadstreamprocessing/overview) libraries that are available with your license. Find examples on how to [format the cells of the exported Excel file with RadSpreadProcessing](slug:grid-kb-custom-cell-formatting-with-radspreadprocessing) and how to [format the cells of the exported Excel file with RadSpreadStreamProcessing](slug:grid-kb-custom-cell-formatting-with-radspreadstreamprocessing). + +Read more about how to [customize the exported file](slug:treelist-export-events). + +## See Also + +* [Live Demo: TreeList Export](https://demos.telerik.com/blazor-ui/treelist/excel-export) +* [Custom Cell Formatting of the Exported File with RadSpreadProcessing](slug:grid-kb-custom-cell-formatting-with-radspreadprocessing) +* [Custom Cell Formatting of the Exported File with RadSpreadStreamProcessing](slug:grid-kb-custom-cell-formatting-with-radspreadstreamprocessing) +* [Showing a Loader While Exporting](slug:grid-kb-show-loader-while-exporting) diff --git a/components/treelist/export/overview.md b/components/treelist/export/overview.md new file mode 100644 index 000000000..ca2ce7be0 --- /dev/null +++ b/components/treelist/export/overview.md @@ -0,0 +1,43 @@ +--- +title: Overview +page_title: TreeList - Export Overview +description: Export basics for the TreeList for Blazor. +slug: treelist-export-overview +tags: telerik, blazor, treelist, export +published: True +position: 1 +components: ["treelist"] +--- + +# Blazor TreeList Export + +The TreeList for Blazor provides a built-in functionality to export the data to Excel. + +## How the Export Works + +The TreeList Excel Export uses [Telerik SpreadStreamProcessing](slug:dpl-in-blazor) to generate an `.xlsx` file. The exporting time depends on the number of records and whether the exporting occurs on a server or in a WebAssembly. + +While the file is being generated, the UI will be unresponsive, so you may want to [show a loading sign to the user during the export process](slug:grid-kb-show-loader-while-exporting). + +## Requirements + +In server-side Blazor apps, the file may become larger than the default SignalR message size limit. This can disconnect the client and result in an error. You may need to [increase the maximum SignalR message size](slug:common-kb-increase-signalr-max-message-size). + +## Limitations + +The TreeList export feature has the following limitations: + +* Templates are not exported, because there is no provision in the framework for getting `RenderFragment` content at runtime. Thus, column, header or group header/footer templates are ignored. The headers in the exported file match the `Title` of the column. The exported values match the data from the column `Field`. If you need additional information, see if you can add it to a property in the model, or create your own file. Find a [project example on how to generate your own exported file](https://feedback.telerik.com/blazor/1485764-customize-the-Pdf-file-before-it-gets-to-the-client). +* `bool` fields are exported as `TRUE` or `FALSE` strings, because there is no native boolean data type in the exported formats and these string values are the most common ones used in data and macros. +* Dates are exported in the following format: `mm/dd/yyyy hh:mm:ss` plus the current app culture AM/PM specifier. The Excel date formats are different than .NET date formats and Excel may not always recognize the column as dates, for example, if the entire date format from the .NET culture is used. To customize the date formats, use the [Export Events](slug:treelist-export-events). +* Numbers are exported in the following format which uses the current thread culture: `Convert.ToDouble(value)`. To customize the number formats use the [Export Events](slug:treelist-export-events). +* The TreeList exports only `` instances. Other types of columns are not exported (for example: command, checkbox, row-drag columns). + +## Customization + +The TreeList allows customization of the exported files. You can determine the desired data to be exported, change the number and date formats, and more. For such customizations, [handle the export events](slug:treelist-export-events). + +## See Also + +* [TreeList Export to Excel](slug:treelist-export-excel) +* [Live Demo: TreeList Excel Export](https://demos.telerik.com/blazor-ui/treelist/excel-export) diff --git a/components/treelist/overview.md b/components/treelist/overview.md index 39cc13f3a..8fe577fe3 100644 --- a/components/treelist/overview.md +++ b/components/treelist/overview.md @@ -139,6 +139,7 @@ The various [TreeList templates](slug:treelist-templates-overview) provide bette * [Selection - single and multiple](slug:treelist-selection-overview). * [State - get or set the TreeList configuration programmatically](slug:treelist-state) * [Toolbar - define custom TreeList actions](slug:treelist-toolbar) +* [Export](slug:treelist-export-overview) ## TreeList Parameters