Grid State
The Grid lets you read, save, load, and change its state through code. The state includes the Grid features that are controlled by the user, such as the current sorting, page number, applied grouping, column widths, and many others.
This article describes:
-
The properties of the
GridState
object. -
How to set initial Grid configuration programmatically in
OnStateInit
. -
How to detect user changes in the Grid state with
OnStateChanged
. - How to use Grid methods to get and set the Grid state.
-
Why you may need to override the
Equals
method of the Grid model class.
Information in the Grid State
The Grid state is a generic class GridState<TItem>
. The type depends on the type of the Grid model. The GridState<TItem>
object exposes the following properties:
Property | Type | Description |
---|---|---|
CollapsedGroups |
ICollection<int> |
The indexes of all collapsed groups when LoadGroupsOnDemand="false" , i.e. when not loading groups on demand. |
ColumnStates |
ICollection<GridColumnState> |
Information about each column's reorder index, width, visibility, locked state, Id parameter value and Field . The column order in the collection matches the column order in the Grid declaration. On the other hand, the Index property matches the current column's position in the UI. Id and Field are always null after deserialization, because these properties have no public setters.
|
EditField |
string |
The currently edited data item property in Incell edit mode. |
EditItem |
TItem * |
The currently edited data item in any edit mode. |
ExpandedItems |
ICollection<TItem> |
The expanded data items, when using <DetailTemplate> (hierarchy). |
FilterDescriptors |
ICollection<IFilterDescriptor> |
All filtering criteria, except the ones that relate to theGridSearchBox . |
GroupDescriptors |
ICollection<GroupDescriptor> |
Information about currently applied grouping. |
InsertedItem |
TItem * |
The data item that is being added in Inline or Popup edit mode. Not applicable for Incell editing. |
OriginalEditItem |
TItem * |
The original copy of the data item that is currently in edit mode. This GridState property holds the unmodified data item values. |
Page |
int? |
The current page index. Some user actions reset the page index to 1, such as filtering or changing the page size. |
SearchFilter |
IFilterDescriptor |
The CompositeFilterDescriptor that holds the filter descriptors for the GridSearchBox . |
SelectedItems |
ICollection<TItem> |
The currently selected data item(s). |
Skip |
int? |
The number of scrolled data items when using virtual row scrolling. In other words, this is the number of rows above the currently visible ones. |
SortDescriptors |
ICollection<SortDescriptor> |
The currently applied sorts. |
TableWidth |
string |
The sum of all visible column widths. This property changes together with ColumnStates . The OnStateChanged event does not fire separately for it. |
* TItem
is the Grid model type.
Events
The Grid features two events, which are related to its state.
OnStateInit
The OnStateInit
event fires when the Grid is initializing. Use this event to:
- Define initial state, for example default initial sorting;
- Load and apply state that was previously saved in a database or in
localStorage
.
The generic event argument is of type GridStateEventArgs<TItem>
and has a GridState
property. See Information in the Grid State for details.
If you change the column order or number of columns in the Grid declaration, this can break state restore. In such cases, either ignore the stored column state, or implement custom logic to restore only the columns that still exist in the Grid.
To set the initial visibility of columns, better use the
Visible
parameter, rather than conditional markup for the whole column. TheVisible
parameter values will be present in the Grid state and the columns collection count will remain the same. This makes it easier to reconcile changes.
The example below shows how to apply initial sorting, filtering and grouping.
@using Telerik.DataSource
<TelerikGrid Data="@GridData"
Pageable="true"
PageSize="5"
Sortable="true"
FilterMode="@GridFilterMode.FilterMenu"
Groupable="true"
OnStateInit="@( (GridStateEventArgs<Product> args) => OnGridStateInit(args) )">
<GridColumns>
<GridColumn Field="@nameof(Product.Name)" />
<GridColumn Field="@nameof(Product.Category)" />
<GridColumn Field="@nameof(Product.Stock)" />
<GridColumn Field="@nameof(Product.Discontinued)" />
</GridColumns>
</TelerikGrid>
@code {
private List<Product> GridData { get; set; }
private async Task OnGridStateInit(GridStateEventArgs<Product> args)
{
// Sort by Stock
args.GridState.SortDescriptors.Add(new SortDescriptor()
{
Member = nameof(Product.Stock),
SortDirection = ListSortDirection.Descending
});
// Filter Discontinued products
var discontinuedColumnFilter = new CompositeFilterDescriptor()
{
FilterDescriptors = new FilterDescriptorCollection() {
new FilterDescriptor()
{
Member = nameof(Product.Discontinued),
MemberType = typeof(bool),
Operator = FilterOperator.IsEqualTo,
Value = false
}
}
};
args.GridState.FilterDescriptors.Add(discontinuedColumnFilter);
// Group by Category
args.GridState.GroupDescriptors.Add(new GroupDescriptor()
{
Member = nameof(Product.Category),
MemberType = typeof(string)
});
}
protected override void OnInitialized()
{
GridData = new List<Product>();
var rnd = new Random();
for (int i = 1; i <= 12; i++)
{
GridData.Add(new Product()
{
Id = i,
Name = $"Product {i}",
Category = $"Category {i % 2 + 1}",
Stock = rnd.Next(0, 100),
Discontinued = i % 3 == 0
});
}
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Category { get; set; }
public int Stock { get; set; }
public bool Discontinued { get; set; }
}
}
OnStateChanged
OnStateChanged
fires when the user performs an action that changes the value of a property in the Grid state. The event argument is of type GridStateEventArgs<TItem>
and exposes these properties:
Property | Type | Description |
---|---|---|
PropertyName |
string |
Information about what changed in the Grid state. The possible values match the property names of the GridState object. The possible values for the PropertyName are SortDescriptors , FilterDescriptors , SearchFilter , GroupDescriptors , Page , Skip , CollapsedGroups , ColumnStates , ExpandedItems , InsertedItem , OriginalEditItem , EditItem . |
GridState |
GridState<TItem> |
The current (up-to-date) Grid state object. |
Here is some additional information about certain PropertyName
values:
-
EditItem
is used when the user starts editing an existing item. -
InsertedItem
signifies the user adding a new item in inline or popup edit mode. It's not applicable forIncell
editing. -
OriginalEditItem
is used when the user exits edit or insert mode via save or cancel. -
ColumnStates
is used for several column actions such as hiding, showing, locking, reordering and resizing.
Some user actions will trigger two
OnStateChanged
events with a differentPropertyName
each time. These include filtering, searching and grouping. For example, filtering resets the current page to 1. First, the event will fire withPropertyName
equal to"FilterDescriptors"
, and thenPropertyName
will be"Page"
. However, theGridState
property of the event argument will provide correct information about the overall Grid state in both event handler executions.
We recommend using an
async Task
handler for theOnStateChanged
event, in order to reduce re-rendering and avoid blocking UI updates if the handler will wait for a service to save the Grid state somewhere.
To observe the changes in the Grid state more easily, copy and run the following example in a local app and at full screen.
@using System.Text.Json
<div id="demo-container">
<TelerikGrid Data="@GridData"
EditMode="@GridEditMode.Inline"
FilterMode="@GridFilterMode.FilterMenu"
Groupable="true"
Pageable="true"
@bind-PageSize="@GridPageSize"
Reorderable="true"
Resizable="true"
@bind-SelectedItems="@GridSelectedItems"
SelectionMode="@GridSelectionMode.Multiple"
ShowColumnMenu="true"
Sortable="true"
OnCreate="@OnGridCreate"
OnUpdate="@OnGridUpdate"
OnStateChanged="@( (GridStateEventArgs<Product> args) => OnGridStateChanged(args) )">
<GridSettings>
<GridPagerSettings PageSizes="@( new List<int?>() { null, 5, 10 } )" />
</GridSettings>
<GridToolBarTemplate>
<GridCommandButton Command="Add">Add</GridCommandButton>
<GridSearchBox />
</GridToolBarTemplate>
<GridColumns>
<GridCheckboxColumn SelectAll="true" />
<GridColumn Field="@nameof(Product.Name)" />
<GridColumn Field="@nameof(Product.Category)" />
<GridColumn Field="@nameof(Product.Stock)" />
<GridColumn Field="@nameof(Product.Discontinued)" />
<GridCommandColumn>
<GridCommandButton Command="Edit">Edit</GridCommandButton>
<GridCommandButton Command="Save" ShowInEdit="true">Save</GridCommandButton>
<GridCommandButton Command="Cancel" ShowInEdit="true">Cancel</GridCommandButton>
</GridCommandColumn>
</GridColumns>
<DetailTemplate>
Detail Template for product <strong>@context.Name</strong>.
</DetailTemplate>
</TelerikGrid>
<div id="console">
<code class="@GridStateChangedPropertyClass">OnStateChanged</code> count:
@OnStateChangedCount
<TelerikButton OnClick="@( () => OnStateChangedCount = 0 )">Reset</TelerikButton>
<br /><br />
Last <code>OnStateChanged</code> event:
<br />
<strong class="@GridStateChangedPropertyClass">PropertyName</strong>:
<code>"@GridStateChangedProperty"</code>
<br />
<strong>GridState</strong>:
<pre>
@( new MarkupString(GridStateString) )
</pre>
</div>
</div>
<style>
.first-of-two {
color: #f00;
}
.latest-changed-property {
color: #00f;
}
@@media (min-width: 800px) {
#demo-container {
display: flex;
align-items: flex-start;
gap: 1em;
}
#demo-container > .k-grid {
flex: 2 2 800px;
}
#console {
height: 90vh;
overflow: auto;
flex: 1 0 300px;
border: 1px solid rgba(128, 128, 128, .3);
padding: 1em;
}
}
</style>
@code {
private List<Product> GridData { get; set; } = new List<Product>();
private int GridPageSize { get; set; } = 5;
private IEnumerable<Product> GridSelectedItems { get; set; } = new List<Product>();
private int OnStateChangedCount { get; set; }
private string GridStateChangedProperty { get; set; } = string.Empty;
private string GridStateChangedPropertyClass { get; set; } = string.Empty;
private string GridStateString { get; set; } = string.Empty;
private bool _doubleStateChanged { get; set; }
private List<string> _operationsWithMultipleStateChanged = new List<string>() {
"FilterDescriptors",
"GroupDescriptors",
"SearchFilter"
};
private async Task OnGridStateChanged(GridStateEventArgs<Product> args)
{
if (_doubleStateChanged)
{
_doubleStateChanged = false;
await Task.Delay(1500);
GridStateChangedPropertyClass = string.Empty;
}
++OnStateChangedCount;
GridStateChangedProperty = args.PropertyName;
// serialize the GridState and highlight the changed property
GridStateString = JsonSerializer.Serialize(args.GridState, new JsonSerializerOptions() { WriteIndented = true })
.Replace($"\"{GridStateChangedProperty}\"", $"\"<strong class='latest-changed-property'>{GridStateChangedProperty}</strong>\"");
// highlight first GridStateChangedProperty during filtering, grouping and search
if (_operationsWithMultipleStateChanged.Contains(GridStateChangedProperty))
{
_doubleStateChanged = true;
GridStateChangedPropertyClass = "first-of-two";
}
}
private void OnGridUpdate(GridCommandEventArgs args)
{
var updatedItem = (Product)args.Item;
var originalItemIndex = GridData.FindIndex(x => x.Id == updatedItem.Id);
GridData[originalItemIndex] = updatedItem;
}
private void OnGridCreate(GridCommandEventArgs args)
{
var createdItem = (Product)args.Item;
GridData.Insert(0, createdItem);
}
protected override void OnInitialized()
{
var rnd = new Random();
for (int i = 1; i <= 12; i++)
{
GridData.Add(new Product()
{
Id = i,
Name = $"Product {i}",
Category = $"Category {i % 4 + 1}",
Stock = rnd.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; }
}
}
Methods
The GetState
and SetStateAsync
methods of the Grid instance let you get and set the current Grid state on demand at any time after OnStateInit
.
GetState
returns the current Grid state, so you can save it or retrieve specific information. For example, you can useGetState
to get the current filters, sorts, and page number. Or, you can get the current Grid column properties like order index, width, and others).SetStateAsync
receives an instance of aGridState<TItem>
object and applies it to the Grid. For example, you can have a button that puts the Grid in a certain configuration programmatically, for example sort or filter the data, enter or exit edit mode, expand or collapse groups or detail Grids, etc.
If you want to make changes to the current Grid state:
- First, get the current state with the
GetState
method. - Apply the desired modifications to the obtained
GridState
object. - Set the modified state object via the
SetStateAsync
method.
Do not use
GetState()
in theOnStateInit
orOnStateChanged
events. Do not useSetStateAsync()
inOnStateInit
. Instead, get or set theGridState
property of the event argument.
If you want to put the Grid in a certain configuration without preserving the old one, create a new GridState<T>()
and apply the settings there. Then pass the state object to SetStateAsync()
.
To reset the Grid state to its initial markup configuration, call
SetStateAsync(null)
.
Avoid calling SetStateAsync
in the Grid CRUD methods (such as OnRead, OnUpdate
, OnEdit
, OnCreate
, OnCancel
). Doing so may lead to unexpected results because the Grid has more logic to execute after these events. Setting the Grid state fires OnRead
, so calling SetStateAsync()
in this handler can lead to an endless loop.
SetStateAsync Examples
The tabs below show how to set the Grid state and control filtering, sorting and other Grid features.
If you want to set an initial state to the Grid, use a similar snippet, but in the
OnStateInit event
@* This snippet shows how to set sorting state to the grid from your code *@
@using Telerik.DataSource;
<TelerikButton ThemeColor="primary" OnClick="@SetGridSort">set sort from code</TelerikButton>
<TelerikGrid Data="@MyData" Height="400px" @ref="@GridRef"
Pageable="true" Sortable="true">
<GridColumns>
<GridColumn Field="@(nameof(SampleData.Id))" Width="120px" />
<GridColumn Field="@(nameof(SampleData.Name))" Title="Employee Name" />
<GridColumn Field="@(nameof(SampleData.Team))" Title="Team" />
<GridColumn Field="@(nameof(SampleData.HireDate))" Title="Hire Date" />
</GridColumns>
</TelerikGrid>
@code {
private TelerikGrid<SampleData> GridRef { get; set; }
private async Task SetGridSort()
{
GridState<SampleData> desiredState = new GridState<SampleData>()
{
SortDescriptors = new List<SortDescriptor>()
{
new SortDescriptor { Member = "Id", SortDirection = ListSortDirection.Descending }
}
};
await GridRef.SetStateAsync(desiredState);
}
private IEnumerable<SampleData> MyData = Enumerable.Range(1, 30).Select(x => new SampleData
{
Id = x,
Name = "name " + x,
Team = "team " + x % 5,
HireDate = DateTime.Now.AddDays(-x).Date
});
public class SampleData
{
public int Id { get; set; }
public string Name { get; set; }
public string Team { get; set; }
public DateTime HireDate { get; set; }
}
}
@* This snippet shows how to set filtering state to the grid from your code
Applies to the FilterRow mode *@
@using Telerik.DataSource;
<TelerikButton ThemeColor="primary" OnClick="@SetGridFilter">Filter From Code</TelerikButton>
<TelerikGrid @ref="@GridRef"
Data="@GridData"
Height="400px"
Pageable="true"
FilterMode="@GridFilterMode.FilterRow">
<GridColumns>
<GridColumn Field="@(nameof(SampleData.Id))" Width="150px" />
<GridColumn Field="@(nameof(SampleData.Name))" Title="Employee Name" />
<GridColumn Field="@(nameof(SampleData.Team))" Title="Team" />
<GridColumn Field="@(nameof(SampleData.HireDate))" Title="Hire Date" />
</GridColumns>
</TelerikGrid>
@code {
private TelerikGrid<SampleData> GridRef { get; set; }
private async Task SetGridFilter()
{
GridState<SampleData> desiredState = new GridState<SampleData>()
{
FilterDescriptors = new List<IFilterDescriptor>()
{
new CompositeFilterDescriptor(){
FilterDescriptors = new FilterDescriptorCollection()
{
new FilterDescriptor() { Member = "Id", Operator = FilterOperator.IsGreaterThan, Value = 10, MemberType = typeof(int)}
}
},
new CompositeFilterDescriptor()
{
FilterDescriptors = new FilterDescriptorCollection()
{
new FilterDescriptor() { Member = "Team", Operator = FilterOperator.Contains, Value = "3", MemberType = typeof(string) },
}
}
}
};
await GridRef.SetStateAsync(desiredState);
}
private IEnumerable<SampleData> GridData = Enumerable.Range(1, 30).Select(x => new SampleData
{
Id = x,
Name = "name " + x,
Team = "team " + x % 5,
HireDate = DateTime.Now.AddDays(-x).Date
});
public class SampleData
{
public int Id { get; set; }
public string Name { get; set; }
public string Team { get; set; }
public DateTime HireDate { get; set; }
}
}
@* This snippet shows how to set filtering state to the grid from your code
Applies to the FilterMenu mode *@
@using Telerik.DataSource;
<TelerikButton ThemeColor="primary" OnClick="@SetGridFilter">set filtering from code</TelerikButton>
<TelerikGrid Data="@MyData" Height="400px" @ref="@GridRef"
Pageable="true" FilterMode="@GridFilterMode.FilterMenu">
<GridColumns>
<GridColumn Field="@(nameof(SampleData.Id))" Width="120px" />
<GridColumn Field="@(nameof(SampleData.Name))" Title="Employee Name" />
<GridColumn Field="@(nameof(SampleData.Team))" Title="Team" />
<GridColumn Field="@(nameof(SampleData.HireDate))" Title="Hire Date" />
</GridColumns>
</TelerikGrid>
@code {
private TelerikGrid<SampleData> GridRef { get; set; }
private async Task SetGridFilter()
{
GridState<SampleData> desiredState = new GridState<SampleData>()
{
FilterDescriptors = new List<IFilterDescriptor>()
{
new CompositeFilterDescriptor()
{
FilterDescriptors = new FilterDescriptorCollection()
{
new FilterDescriptor() { Member = "Id", Operator = FilterOperator.IsGreaterThan, Value = 5, MemberType = typeof(int) },
new FilterDescriptor() { Member = "Id", Operator = FilterOperator.IsLessThan, Value = 20, MemberType = typeof(int) },
},
LogicalOperator = FilterCompositionLogicalOperator.And
},
new CompositeFilterDescriptor()
{
FilterDescriptors = new FilterDescriptorCollection()
{
new FilterDescriptor() { Member = "Team", Operator = FilterOperator.Contains, Value = "3", MemberType = typeof(string) },
}
}
}
};
await GridRef.SetStateAsync(desiredState);
}
private IEnumerable<SampleData> MyData = Enumerable.Range(1, 30).Select(x => new SampleData
{
Id = x,
Name = "name " + x,
Team = "team " + x % 5,
HireDate = DateTime.Now.AddDays(-x).Date
});
public class SampleData
{
public int Id { get; set; }
public string Name { get; set; }
public string Team { get; set; }
public DateTime HireDate { get; set; }
}
}
@using Telerik.DataSource;
<TelerikButton ThemeColor="primary" OnClick="@SetGridGroup">set grouping from code</TelerikButton>
<TelerikGrid Data="@MyData" Height="400px" @ref="@GridRef" Groupable="true"
Pageable="true" FilterMode="@GridFilterMode.FilterMenu">
<GridColumns>
<GridColumn Field="@(nameof(SampleData.Id))" Width="120px" />
<GridColumn Field="@(nameof(SampleData.Name))" Title="Employee Name" />
<GridColumn Field="@(nameof(SampleData.Team))" Title="Team" />
<GridColumn Field="@nameof(SampleData.IsOnLeave)" Title="On Vacation" />
<GridColumn Field="@(nameof(SampleData.HireDate))" Title="Hire Date" />
</GridColumns>
</TelerikGrid>
@code {
private TelerikGrid<SampleData> GridRef { get; set; }
private async Task SetGridGroup()
{
GridState<SampleData> desiredState = new GridState<SampleData>()
{
GroupDescriptors = new List<GroupDescriptor>()
{
new GroupDescriptor()
{
Member = "Team",
MemberType = typeof(string)
},
new GroupDescriptor()
{
Member = "IsOnLeave",
MemberType = typeof(bool),
SortDirection = ListSortDirection.Descending // not required, but a feature not yet available through the UI
}
},
// choose indexes of groups to be collapsed (they are all expanded by default)
CollapsedGroups = new List<int>() { 0 },
};
await GridRef.SetStateAsync(desiredState);
}
private IEnumerable<SampleData> MyData = Enumerable.Range(1, 30).Select(x => new SampleData
{
Id = x,
Name = "name " + x,
Team = "team " + x % 5,
IsOnLeave = x % 2 == 0,
HireDate = DateTime.Now.AddDays(-x).Date
});
public class SampleData
{
public int Id { get; set; }
public string Name { get; set; }
public string Team { get; set; }
public bool IsOnLeave { get; set; }
public DateTime HireDate { get; set; }
}
}
@using Telerik.DataSource;
<TelerikButton ThemeColor="primary" OnClick="@ExpandHierarchy">Expand hierarchy from code</TelerikButton>
<TelerikGrid Data="salesTeamMembers" @ref="@GridRef">
<DetailTemplate>
@{
var employee = context as MainModel;
<TelerikGrid Data="employee.Orders" Pageable="true" PageSize="5">
<GridColumns>
<GridColumn Field="OrderId"></GridColumn>
<GridColumn Field="DealSize"></GridColumn>
</GridColumns>
</TelerikGrid>
}
</DetailTemplate>
<GridColumns>
<GridColumn Field="Id"></GridColumn>
<GridColumn Field="Name"></GridColumn>
</GridColumns>
</TelerikGrid>
@code {
private TelerikGrid<MainModel> GridRef { get; set; }
private async Task ExpandHierarchy()
{
var gridState = GridRef.GetState();
//expand the first two rows
gridState.ExpandedItems = new List<MainModel> {
salesTeamMembers[0],
salesTeamMembers[1]
};
await GridRef.SetStateAsync(gridState);
}
private List<MainModel> salesTeamMembers { get; set; }
protected override void OnInitialized()
{
salesTeamMembers = GenerateData();
}
private List<MainModel> GenerateData()
{
List<MainModel> data = new List<MainModel>();
for (int i = 1; i <= 5; i++)
{
MainModel mdl = new MainModel { Id = i, Name = $"Name {i}" };
mdl.Orders = Enumerable.Range(1, 3).Select(x => new DetailsModel {
OrderId = i * 100 + x, DealSize = (x ^ i) + 1 }
).ToList();
data.Add(mdl);
}
return data;
}
public class MainModel
{
public int Id { get; set; }
public string Name { get; set; }
public List<DetailsModel> Orders { get; set; }
}
public class DetailsModel
{
public int OrderId { get; set; }
public double DealSize { get; set; }
}
}
<TelerikButton OnClick="@OnButtonClick">Reoder Name and Price Columns</TelerikButton>
<TelerikGrid @ref="@GridRef"
Data="@GridData"
Reorderable="true">
<GridColumns>
<GridColumn Field="@nameof(Product.Name)" />
<GridColumn Field="@nameof(Product.Price)" DisplayFormat="{0:c2}" />
<GridColumn Field="@nameof(Product.ReleaseDate)" DisplayFormat="{0:d}" />
<GridColumn Field="@nameof(Product.Active)" />
</GridColumns>
</TelerikGrid>
@code {
private TelerikGrid<Product> GridRef { get; set; }
private List<Product> GridData { get; set; }
private async Task OnButtonClick()
{
var gridState = GridRef.GetState();
var nameColState = gridState.ColumnStates.ElementAt(0);
var nameColIndex = nameColState.Index;
var priceColState = gridState.ColumnStates.ElementAt(1);
var priceColIndex = priceColState.Index;
nameColState.Index = priceColIndex;
priceColState.Index = nameColIndex;
await GridRef.SetStateAsync(gridState);
}
protected override void OnInitialized()
{
GridData = new List<Product>();
var rnd = new Random();
for (int i = 1; i <= 5; i++)
{
GridData.Add(new Product()
{
Id = i,
Name = $"Product {i}",
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; } = string.Empty;
public decimal Price { get; set; }
public DateTime ReleaseDate { get; set; }
public bool Active { get; set; }
}
}
If you want to alter the filters for a specific column, do not use more than one
FilterDescriptor
inFilterRow
mode, and more than twoFilterDescriptors
inFilterMenu
mode. Otherwise additional descriptors will not show up in the UI. This means that you may need to replace or modify an existing descriptor, rather than add a new one.Inactive filter descriptors in
FilterMenu
mode are distinguished by theirnull
Value
.
Equals Comparison
State properties that pertain to data items (for example, edited item or selected items) are typed according to the Grid model. If you restore such data, make sure to implement appropriate comparison checks - by default the .Equals()
check for a class (object) is a reference check and the reference from the restored state is very unlikely to match the current reference in the Grid data. Thus, you may want to override the .Equals()
method of the Grid model class, so that it compares by ID, or otherwise re-populate the models in the state object with the new model references from the Grid data.
Examples
You can find multiple examples for using the Grid state in the following Knowledge Base articles:
- Save and load the Grid state from
localStorage
- Save the Grid state in a WebAssembly app
- Override a user action that changes the Grid state, for example, sort descending first
- Initiate programmatic editing or inserting of a Grid row
- Get current Grid column state (order index, width, and others)