Currently, when a user exports the Grid to Excel/CSV/PDF, the export file is generated as a base64 string.
Please provide the file as a byte[] instead of a base64 string for better performance.
I need to know which cell the user right clicked on so I can adjust my context menu. I need the cell value, the row model and the field of the column.
---
ADMIN EDIT
The following KB article shows a solution you can use immediately: https://docs.telerik.com/blazor-ui/knowledge-base/grid-which-cell-context-menu.
---
Please add support for automatic Grid table layout, which will allow column widths to be set by the browser, based on the cell content.
===
TELERIK EDIT:
This is a huge task with many implications. We require large-enough customer demand before we start exploring or implementing it.
Everyone who is interested, please drop a few lines here and share:
Here are some thoughts on the matter:
HTML tables have two layout modes: auto and fixed. In auto layout, the table column widths depend on the content. In fixed layout, all column widths are equal or obey the set styles, no matter the content.
The Blazor Grid renders separate tables for its header cells and data cells. This allows the data area to scroll, while the header area remains visible at all times. The only reliable way to ensure column alignment between the two tables is to apply a fixed table-layout style. This results in the behavior, which we have described in the documentation.
The only way to have auto table layout in the Blazor Grid is to render the component as a single <table>, which is currently not available.
Possible workarounds include:
Here is are a few links on the matter:
If after adding a new item to a grid with inline edit the save button is double-clicked (e.g. by accident) two new items are added instead of one. Right now, the only way to prevent this seems to be to check if an item is already contained in GridData at the top of the OnCreate-Handler and cancel if necessary. If a unique ID is not available yet (because it is created by the database when saving at the end of the handler) this means every Property has to be compared to check for equality. This is very annoying.
Please add a parameter "DisableWhileBeingHandled" to TelerikButtons and make this the default for the CommandButtons in a Grid. The Buttons should only accept clicks if the previous handling is finished.
Kind regards,
René
I AutoFit all columns in the Grid but would like to be able to reset their width to the initial value at a later point.
===
TELERIK EDIT: Here are two workarounds for two possible scenarios:
<TelerikButton ThemeColor="@ThemeConstants.Button.ThemeColor.Primary"
OnClick="@AutoFitColumns">AutoFit Columns</TelerikButton>
<TelerikButton ThemeColor="@ThemeConstants.Button.ThemeColor.Success"
OnClick="@ResetColumns">Reset Column Widths</TelerikButton>
<TelerikGrid @ref="@GridRef"
Data="@GridData"
Reorderable="true"
Resizable="true">
<GridColumns>
<GridColumn Field="@nameof(Product.Id)" />
<GridColumn Field="@nameof(Product.Name)" />
<GridColumn Field="@nameof(Product.Category)" />
<GridColumn Field="@nameof(Product.Stock)" />
<GridColumn Field="@nameof(Product.Discontinued)" />
</GridColumns>
</TelerikGrid>
@code {
private TelerikGrid<Product>? GridRef { get; set; }
private List<Product> GridData { get; set; } = new();
private async Task AutoFitColumns()
{
if (GridRef == null)
{
return;
}
await GridRef.AutoFitAllColumnsAsync();
}
private async Task ResetColumns()
{
if (GridRef == null)
{
return;
}
var gridState = GridRef.GetState();
foreach (var columnState in gridState.ColumnStates)
{
columnState.Width = "auto;";
}
gridState.TableWidth = $"99.9{DateTime.Now.Millisecond}%"; // Set something close to 100% instead of removing the value
await GridRef.SetStateAsync(gridState);
}
protected override void OnInitialized()
{
for (int i = 1; i <= 5; i++)
{
GridData.Add(new Product()
{
Id = i,
Name = $"Product {i}",
Category = $"Category {i % 4 + 1}",
Stock = Random.Shared.Next(0, 100),
Discontinued = i % 3 == 0
});
}
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Category { get; set; } = string.Empty;
public int Stock { get; set; }
public bool Discontinued { get; set; }
}
}
<TelerikGrid @ref="@GridRef"
Data="@GridData"
Reorderable="true"
Resizable="true"
Width="@GridWidth"
ShowColumnMenu="true"
OnStateChanged="@( (GridStateEventArgs<Product> args) => OnGridStateChanged(args) )">
<GridColumns>
<GridCheckboxColumn SelectAll="true" />
<GridColumn Field="@nameof(Product.Id)" />
<GridColumn Field="@nameof(Product.Name)" />
<GridColumn Field="@nameof(Product.Category)" />
<GridColumn Field="@nameof(Product.Stock)" />
<GridColumn Field="@nameof(Product.Discontinued)" />
</GridColumns>
</TelerikGrid>
@code {
private TelerikGrid<Product>? GridRef { get; set; }
private List<Product> GridData { get; set; } = new();
private string GridWidth => $"{GridIntWidth}px";
private int GridIntWidth = 1000; // A pixel Grid width is required for this scenario
private int GridMaxTableIntWidth => GridIntWidth - 17; // Assuming no horizontal scrolling
private async Task OnGridStateChanged(GridStateEventArgs<Product> args)
{
if (args.PropertyName == "ColumnStates" && Double.Parse(args.GridState.TableWidth.Replace("px", "")) < GridMaxTableIntWidth)
{
args.GridState.TableWidth = $"99.9{DateTime.Now.Millisecond}%"; // Set something close to 100% instead of removing the value
int lastVisibleColumnIndex = GetLastVisibleIndex(args.GridState.ColumnStates);
args.GridState.ColumnStates.First(x => x.Index == lastVisibleColumnIndex).Width = "auto";
await GridRef!.SetStateAsync(args.GridState);
}
}
private int GetLastVisibleIndex(ICollection<GridColumnState> columnStates)
{
int index = 0;
var visibleColumnStates = columnStates.Where(x => !x.Visible.HasValue || x.Visible.Value);
foreach (var columnState in visibleColumnStates)
{
index = Math.Max(index, columnState.Index);
}
return index;
}
protected override void OnInitialized()
{
for (int i = 1; i <= 5; i++)
{
GridData.Add(new Product()
{
Id = i,
Name = $"Product {i}",
Category = $"Category {i % 4 + 1}",
Stock = Random.Shared.Next(0, 100),
Discontinued = i % 3 == 0
});
}
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Category { get; set; } = string.Empty;
public int Stock { get; set; }
public bool Discontinued { get; set; }
}
}
I have a master-detail draggable Grid scenario in which both Grids have RowDraggable="true". When the user starts dragging a row from the DetailTemplate Grid, a JavaScript error occurs:
TypeError: Argument 1 ('node') to Node.replaceChild must be an instance of Node
After filtering a nullable DateTime column, the SelectAll checkbox in the GridCheckboxColumn becomes selected, and its functionality stops working correctly.
When the Grid is databound via OnRead event, the initially displayed aggregates are wrong and take into account the first page only.
A possible workaround is to bind the Grid in OnAfterRenderAsync -
@using Telerik.DataSource
@using Telerik.DataSource.Extensions
<TelerikGrid @ref="@GridRef"
OnRead="@OnGridRead"
TItem="@Product"
Pageable="true">
<GridAggregates>
<GridAggregate Field="@nameof(Product.Name)" Aggregate="@GridAggregateType.Count" FieldType="@(typeof(System.String))" />
</GridAggregates>
<GridColumns>
<GridColumn Field="@nameof(Product.Name)" Title="Product Name">
<FooterTemplate>
@context.Count
</FooterTemplate>
</GridColumn>
<GridColumn Field="@nameof(Product.Price)" />
<GridColumn Field="@nameof(Product.ReleaseDate)" Title="Release Date" />
<GridColumn Field="@nameof(Product.Active)" />
</GridColumns>
</TelerikGrid>
@code {
TelerikGrid<Product> GridRef { get; set; }
List<Product> GridData { get; set; }
bool ShouldBindGrid { get; set; }
async Task OnGridRead(GridReadEventArgs args)
{
if (!ShouldBindGrid)
{
return;
}
await Task.Delay(200); // simulate network delay
DataSourceResult result = GridData.ToDataSourceResult(args.Request);
args.Data = result.Data;
args.Total = result.Total;
args.AggregateResults = result.AggregateResults;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
// workaround for initial Grid aggregates
ShouldBindGrid = true;
GridRef.Rebind();
StateHasChanged();
}
}
protected override void OnInitialized()
{
GridData = new List<Product>();
var rnd = new Random();
for (int i = 1; i <= 50; i++)
{
GridData.Add(new Product()
{
Id = i,
Name = "Product " + i.ToString(),
Price = (decimal)rnd.Next(1, 100),
ReleaseDate = DateTime.Now.AddDays(-rnd.Next(60, 1000)),
Active = i % 3 == 0
});
}
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public DateTime ReleaseDate { get; set; }
public bool Active { get; set; }
}
}
Please add a feature to export the grid to a PDF file.
---
ADMIN EDIT
We have made two examples you can use for the time being to get a PDF document from the grid:
---
I've customized the GridColumnMenu to only show the Column Chooser and assign Locked Columns (disabled all others, including Sortable):
<GridColumnMenuSettings
FilterMode="@ColumnMenuFilterMode.None"
Lockable="true"
ShowColumnChooser="true"
Groupable="false"
Sortable="false"
Reorderable="false">
</GridColumnMenuSettings>
I'm handling the sorting by clicking directly on the column header. In that configuration, the menu icon is highlighted for the actively sorted column:
This is misleading because the user cannot affect the sorting via the column menu. If Sortable = "false", I'd expect no indicator difference in the column headers.
I am resetting the Grid State by calling Grid.SetState(null). This doesn't reset ColumnState<T>.Locked boolean to false and the columns remain locked.
---
ADMIN EDIT
---
A possible workaround for the time being is to additionally loop through the ColumnStates collection of the State and set the Locked property to false for each column.
I am using this sample to implement server-side grouping:
https://github.com/telerik/blazor-ui/tree/master/grid/datasourcerequest-on-server/WasmApp
Now I need Aggregate sum for the price in the GroupFooterTemplate. When I add aggregates, exceptions are thrown if a group footer uses the aggregate value, if not - the grid simply does not look grouped.
---
ADMIN EDIT
The issue stems from the inability of the System.Text.Json to deserialize interfaces - there are a couple of interfaces in the datasource request and data source result related to aggregates. Preliminary review indicates that perhaps some or all of them might be changed to, for example, Dictionary<string, object> from the current IDictionary<string, object>, or perhaps the framework might "learn" to deserialize interfaces.
----
Consider the following scenario:
In this case, the Grid should focus the last focused cell in step 1. Instead the Grid focuses the first header cell.
I have a Grid with row selection and column virtualization. After selecting all rows and zooming out my browser to 80% or less, the column virtualization stops working - data is not loaded in the cells on vertical scroll.
Cuando agrego GridAggregates el exportador de excel no me permite dejar limpia la fila de Totales (FooterTemplates) para unicamente dejar el Dato me coloca la siguiente estructura: "aggregate_sum: X" y al remover el aggregate_sum de los archivos de recursos me deja el texto ": X" por lo que la cadena sigue sin quedar limpia.
De igual manera en mi grid view tengo la columna de Totales y no la agrega al archivo que se exporta los totales como se ve en la imagen:
In the TelerikGrid-control there seems to be a miss-alignment of the gridcell compared to their respective columnheaders.
This behavior is present on touch devices and Mac.
example of correct alignment in non-mobile browser:
example of incorrekt alignment in mobile browser (simulated via F12 tools, but same on real android device):
It looks like it maybe caused by the column where the vertical scrollbar is placed (far right). If i change the padding-right from 16px to 0px it aligns correctly again.