GridView

Learn how to create a GridView

GridView

The GridView component in BlazorForKids allows you to display and manage data in a table format — complete with built-in support for CRUD operations (Create, Read, Update, Delete), filtering, sorting, and pagination. The fastest way to create a fully functional GridView is by using a single line of code in the ComponentsDesigner method of your layout or page.

Here's a simple example:


public void ComponentsDesigner(IBkComponentsBuilder builder)
{
    builder.CreateGridView<EmployeeModel, Employee>("EmployeeGridView");
}

In this line, you're telling the framework to generate a GridView component named EmployeeGridView. The data displayed in the table will come from the Employee entity, but instead of showing the raw entity directly, the framework uses a projection class called EmployeeModel. This class defines exactly which fields (properties) will appear as columns in the table.

What the Framework Generates for You

Once you define this component, BlazorForKids will automatically generate a complete GridView with the following features:

  • Auto-generated Columns: Every public property in EmployeeModel becomes a column in the table.
  • Command Column: An additional column is added with built-in Edit, Delete, and New buttons for managing records.
  • Inline Edit Support: Clicking Edit opens an edit form for the selected record, allowing users to update data immediately.
  • Filtering: Each column includes a filter box, so users can easily search or narrow down results by that field.
  • Sorting: Columns can be sorted ascending or descending, simply by clicking on the column header.
  • Pagination: The grid is paginated by default, showing 10 rows per page. The total number of records is displayed, and users can navigate between pages.
  • Page Size Selector: Users can also change how many records are displayed per page (e.g., 10, 25, 50).
  • Consistent Styling and Behavior: The look, structure, and interaction patterns are consistent across all GridViews in your app, improving usability.

All of this is done automatically with just one line of code. You don’t need to write the data query, the pagination logic, or the UI layout — the framework handles everything behind the scenes. This approach helps you save time, avoid repetitive code, and keep your application’s UI clean and consistent.

In the next section, we’ll explore how to customize the GridView — including how to control which columns are shown, how the data is loaded, and how to extend the command column with custom actions.

GridView – Standard Configuration Options

Once you've defined a basic GridView in your ComponentsDesigner method, you can customize its behavior, appearance, and security using a wide range of configuration options provided by the framework. Below is a full example, followed by a breakdown of each option and what it does:

publicpartialclassSampleLayout:IBkApplicationLayout

{

    publicvoidLayoutDesigner(IBkLayoutBuilderbuilder)

    {

    }

    publicvoidPagesDesigner(IBkPageBuilderbuilder)

    {

    }

    publicvoidComponentsDesigner(IBkComponentsBuilderbuilder)

    {

        builder.CreateGridView<EmployeeModel,Employee>("EmployeeGridView",grid=>

        {

// add grid view caption            grid.Caption("List of Employees");

// add a title to edit form            grid.EditFormTitle("Edit Employee");

// add custom css class to the grid view            grid.GridViewCssClass("employees-table");

// add custom css class to the edit form            grid.EditFormCssClass("employee-edit-form");

// override default page size            grid.PageSize(15);

// override default sort            grid.AddDefaultSort(a=>a.,BkSortDirection.Ascending);

// add a default filter to the EmployeeQuery            grid.AddDefaultFilter(a=>a.MiddleName!=null);

// hides the command column            grid.HideCommandColumn();

// authorize the command column            grid.AllowCreateToRoles("Admin","Manager");

            grid.AllowDeleteToRoles("Admin","Manager");

            grid.AllowEditToRoles("Admin","Manager");

//allow to delete only if current user has the same email as the employee (basically the employee can delete himself)            grid.CanDelete(a=>a.==CurrentUser.);

//allow edit only if current user has the same email as the employee (basically the employee can edit himself)            grid.CanEdit(a=>a.==CurrentUser.);

// set custom rules for a column            grid.ColumnOptions(a=>a.FirstName,options=>

            {

                options.HideColumn();

// other options                options.HeaderText("First Name");

                options.Tooltip("This is the employee first name");

                options.HeaderCellTemplate<CustomHeaderCellWithCustomFilterCapability>();

                options.BodyCellTemplate<CustomBodyCellForDisplayingFirstName>();

                options.EditorTemplate<CustomEditorTemplate>();

                options.HideEditor();

                options.EditorConstantValue("John");

                options.EditorDefaultValue("John");

                options.CssClass("first-name");

            });

            grid.ColumnOptions(a=>a.LastName,options=>

            {

                options.HideColumn();

            });

// add computed columns             grid.AddComputedColumn(a=>$"{a.FirstName} {a.LastName}",options=>

            {

                options.CssClass("full-name");

                options.HeaderText("Full Name");

                options.BodyCellTemplate<CustomCellForDisplayingFullName>();

                options.HeaderCellTemplate<CustomHeaderCellWithCustomFilterCapability>();

                options.Tooltip("This is the employee full name");

            });

// add a link column by specifying the page to navigate to            grid.AddLinkColumn<SamplePage>("Details",options=>

            {

                options.CssClass("details");

// override default destination page info                options.Description("This is the employee details");

                options.Text("Details");

                options.Icon("info-circle");

                options.Url("/details");

// add query parameters                options.AddQueryParameter(a=>a.Id,"EmployeeId");

                options.AddQueryParameter(a=>a.FirstName,"EmployeeFirstName");

                options.AddQueryParameter(a=>a.LastName,"EmployeeLastName");

// limit access to this link                options.AllowAccessToRoles("Admin");

            });

        });

    }

}

Why This Is Powerful

This configuration gives you full control over the appearance, behavior, and access rules of your GridView — all without manually writing boilerplate code for data access, form creation, or table setup. The result is a consistent, user-friendly data grid that behaves the same across the application and can be customized for various use cases.

In the next section, you'll learn how to customize your GridView by adding your own template cells. This allows you to personalize the appearance and behavior of each cell to better fit your needs.

Custom Cell Templates in GridView

One of the most powerful customization features in BlazorForKids GridView is the ability to define custom cell templates. This allows developers to control exactly how data is displayed in individual cells, going far beyond the default text rendering.

By defining a custom component and inheriting from the built-in base class BkGridViewBodyCellTemplate<TModel, TValue>, you can insert any HTML, styling, icons, or logic inside the table cell. This gives you the freedom to design highly tailored views with conditional formatting, badges, icons, links, colored indicators, and more — all per row and per field.

How to Create a Custom Cell Template

To create your own custom cell, follow these steps:

  1. Create a new Blazor component and inherit from the base class BkGridViewBodyCellTemplate<TModel, TValue>.
  2. Use the Value and RowModel properties to define how the cell should look and behave.
  3. Place your content inside a <td> tag — this is required by the framework.

Here is a full example for a custom cell that displays a placeholder if the value is missing:


            // CustomCellForDisplayingFirstName.razor

            @using BlazorForKids.Designer.Web.Widgets.GridViewComponents.Templates
            @using BlazorForKidsProjectTemplate.Domain.Entities

            @inherits BkGridViewBodyCellTemplate<EmployeeModel, string>

            <td>
                @if (string.IsNullOrEmpty(Value))
                {
                    <span class ="text-muted">No Name </span >
                }
                else
                {
                    <span>@Value</span>
                }
            </td>
        

What You Can Access in the Template

When you inherit from BkGridViewBodyCellTemplate<TModel, TValue>, the framework automatically provides all required parameters:

  • Value – the value of the cell for the current column (e.g., a string, number, or enum)
  • RowModel – the full model instance of the row (e.g., EmployeeModel)
  • Items – the entire paginated result set
  • Settings – column metadata and settings (e.g., name, format)
  • RefreshComponent – an event callback that refreshes the data (useful after inline actions)

You can use these properties to display the data however you want, add logic based on other values in the row, or even show different elements depending on the user’s role or the application state.

Using the Custom Cell Template

Once your custom component is defined, you can apply it to a column in your GridView configuration using the BodyCellTemplate option:


            grid.ColumnOptions(a => a.FirstName, options =>
            {
                options.BodyCellTemplate<CustomFirstNameCell>();
            });
        

Why Use Custom Cell Templates?

This feature allows you to completely personalize how data is presented inside the table — not just style it, but also change the logic behind it. For example:

  • Show tooltips or badges based on values
  • Display custom icons or status indicators
  • Add links, progress bars, or charts inside cells
  • Style certain rows or values differently based on business rules
  • Create inline dropdowns, toggles, or quick actions

With custom cell templates, you can turn a plain table into a rich and interactive user interface — all while keeping your logic encapsulated and maintainable.

In summary, this feature opens the door to building beautiful, dynamic, and user-friendly GridViews that truly match your application’s needs and style.

Custom Header Cell Templates in GridView

BlazorForKids GridView includes built-in support for filtering and sorting columns that are based on standard property types like strings, numbers, dates, enums, and navigation keys. However, when you define a computed column or need more advanced filtering and sorting logic, the default behavior is not enough. In such cases, you can provide a custom header cell template.

A custom header cell lets you control the layout and behavior of the <th> element for a column. You can implement your own filter input, a custom sort button, or even both — with full control over how they behave.

How to Create a Custom Header Cell

To define a custom header cell, create a new Blazor component and inherit from the base class BkGridViewHeaderCellTemplate<TEntity, TValue>. Your component must render its content inside a <th> tag, as this is required by the framework.

Here is a full example:


            // CustomHeaderCellForDisplayingFullName.razor
            @using BlazorForKids.Designer.Domain.Enumerations
            @using BlazorForKids.Designer.Domain.Services.Extensions
            @using BlazorForKids.Designer.Web.Widgets.GridViewComponents.Templates
            @using BlazorForKidsProjectTemplate.Domain.Entities

            @inherits BkGridViewHeaderCellTemplate<EmployeeModel, string>

            <th>
                <button @onclick="ChangeSortDirection">Sort</button>
                <span>Full Name</span>
                <div class="filter-column">
                    <input type="search" @bind="searchText" @bind:event="oninput" @bind:after="FilterGridViewByFullName" />
                </div>
            </th>

            @code {
                string searchText = string.Empty;

                private Task ChangeSortDirection()
                {
                    var sortDirection = Filters.SortDirection == BkSortDirection.Ascending
                        ? BkSortDirection.Descending
                        : BkSortDirection.Ascending;

                    Filters.AddOrderBy(a = >a.FirstName, sortDirection);
                    return RefreshComponent.InvokeAsync();
                }

                private Task FilterGridViewByFullName()
                {
                    var text = searchText.Trim().ToLower();

                    // You can decide when filtering should apply (e.g., text length > 2)
                    if (text.Length>2)
                    {
                        Filters.AddTemporaryFilter(a = >a.FirstName.Contains(text) || a.LastName.Contains(text),Property.GetPropertyName());
                    }

                    return RefreshComponent.InvokeAsync();
                }

            }
        

What the Framework Provides

When you inherit from BkGridViewHeaderCellTemplate<TEntity, TValue>, the framework automatically injects the following properties into your component:

  • Filters – the filter collection for the current grid; used to apply or clear filters and sort orders
  • RefreshComponent – a callback to trigger a requery and refresh the grid content after filter or sort changes
  • Property – the column’s bound property (as an expression), used to link your logic to the column definition
  • Settings – metadata about the column (e.g., header text, formatting, visibility)

When to Use Custom Header Cells

Use a custom header cell template when:

  • You define a computed column (e.g., full name from first + last name)
  • You need advanced filtering logic (e.g., combining multiple fields)
  • You want to display a custom sort button or toggle
  • You want to redesign the look of the filter input area

How to Apply a Custom Header Cell

After creating your custom header cell component, register it for a specific column using the HeaderCellTemplate<TComponent>() method inside your grid.ColumnOptions(...) configuration:


        grid.ColumnOptions(a => a.FirstName, options =>
        {
            options.HeaderCellTemplate<CustomFirstNameHeader>();
        });
        

Conclusion

Custom header cells give you full control over how filtering and sorting work at the column level. Whether you're displaying calculated data or applying special logic, this feature allows you to make your GridViews smarter, more flexible, and more user-friendly.

Working with Catalog-Based Select Inputs in GridView

In BlazorForKids, many entity properties — like DepartmentId or JobTitleId — are tied to navigation keys (foreign keys). The framework automatically generates select dropdowns for these fields, allowing users to choose from a list of available catalog items (e.g., a list of departments or job titles).

By default, these dropdowns include all items from the related table, but in real-world scenarios, you often need to apply additional logic to filter these options. For example:

  • You want to filter the list of departments based on a condition
  • You want to show only job titles that belong to the selected department

To support this, BlazorForKids provides two powerful features: cascading dropdown filters and option filters. Both are configured using the overloaded ColumnOptions<TEntity, TKey> method, which is specifically designed for navigation properties.

1. Cascading Dropdowns (Cascading Filters)

A cascading dropdown is a select input whose available options depend on the value of another field. This is common in scenarios like:

  • Filtering Job Titles based on the selected Department
  • Filtering Cities based on the selected Country

Here's how you can set this up:


            grid.ColumnOptions<JobTitle, JobTitleId>(a => a.JobTitleId, options =>
            {
                // This ensures that only job titles from the selected department are available
                options.AddCascadingOptionsFilter(model => jobTitle => jobTitle.DepartmentId == model.DepartmentId);

                // Whenever DepartmentId changes, refresh the JobTitleId options
                options.RefreshOptionsOnPropertyChanged(model => model.DepartmentId);
            });
    
How it works:
  • AddCascadingOptionsFilter defines a dynamic filter applied to the catalog query, based on another property in the model
  • RefreshOptionsOnPropertyChanged tells the framework to re-evaluate the dropdown options when the dependent property changes

💡 This is essential in multi-step forms where dropdowns depend on each other. Without this, users might accidentally select mismatched options (like assigning a job title from the wrong department).

2. Catalog Option Filtering

Sometimes, you want to apply a static or user-specific filter to a select dropdown. This is different from cascading filters — here, you're just filtering the available catalog items before the user even sees them.


                grid.ColumnOptions<Department, DepartmentId>(a => a.DepartmentId, options =>
                {
                    // Only show departments where the name length is greater than 2
                    options.AddOptionsFilter(d => d.Name.Length > 2);
                });
    

In practice, this could be replaced with a more meaningful rule, such as:

  • Only showing departments from a specific region
  • Filtering based on the CurrentUser’s assigned branch or permission level
  • Using query parameters passed into the page (covered in another topic)

Using the Correct Overload

When configuring select inputs that deal with navigation keys, you must use the two-parameter version of ColumnOptions:


            grid.ColumnOptions<TargetEntity, TargetKey>(propertySelector, options => { ... });
    

This gives the framework full context about the relationship, allowing it to correctly generate dropdowns and apply filters.

When to Use Cascading Selects

Cascading selects are recommended when:

  • One catalog depends logically on another (e.g., job titles depend on departments)
  • You want to prevent users from selecting invalid combinations
  • You want to improve UX by showing only valid choices instead of all possibilities

Without cascading filters, you'd need to manually validate input or risk users selecting options that don’t logically belong together — which leads to data integrity issues.

Summary

BlazorForKids gives you the tools to filter catalog options in a smart, dynamic way. Whether you're filtering dropdowns based on user roles, query parameters, or other selected values, these configuration options help you build cleaner, safer, and more intuitive forms — all without extra boilerplate code.

In future sections, we’ll explore how to combine this with CurrentUser, QueryParameters, and how to dynamically load catalog values from external sources.

Final Thoughts on GridView

As you've seen throughout this section, the GridView in BlazorForKids is a powerful and feature-rich component. It handles many complex tasks for you — such as data loading, filtering, sorting, paging, editing, and form generation — all with a consistent and customizable structure.

That said, GridView can feel a bit overwhelming at first, especially if you're just getting started with Blazor or building your first serious web application. Don’t worry — you don’t need to understand or use every feature right away. You can start small, with just a single line of code that gives you a fully working table, and then gradually add customization as your application grows.

The key idea is this: focus on getting your data displayed and editable first. Once that’s working, you can come back and fine-tune the details — like column visibility, custom cells, access control, filtering, and layout.

Remember, GridView was designed to save you time and effort. Use it as a foundation, and let the framework do the heavy lifting. With practice, you’ll be able to create clean, responsive, and user-friendly data views with minimal code and maximum impact.

🧩 Start simple. Evolve as you go. The framework has your back.

An unhandled error has occurred. Reload 🗙