New to Telerik UI for Blazor? Download free 30-day trial

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:

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. The Visible 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 Grid OnStateInit

@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 for Incell 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 different PropertyName each time. These include filtering, searching and grouping. For example, filtering resets the current page to 1. First, the event will fire with PropertyName equal to "FilterDescriptors", and then PropertyName will be "Page". However, the GridState 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 the OnStateChanged 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 Grid OnStateChanged

@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>&quot;@GridStateChangedProperty&quot;</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.

If you want to make changes to the current Grid state:

  1. First, get the current state with the GetState method.
  2. Apply the desired modifications to the obtained GridState object.
  3. Set the modified state object via the SetStateAsync method.

Do not use GetState() in the OnStateInit or OnStateChanged events. Do not use SetStateAsync() in OnStateInit. Instead, get or set the GridState 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 in FilterRow mode, and more than two FilterDescriptors in FilterMenu 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 their null 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:

See Also

In this article