Skip to content
IzzyBlazor 0.1.0-alpha.20

Enter all results ↑ ↓ move Esc close

Adaptability

Since 0.1.0-alpha.20
Fit phones and narrow spaces: hide columns that don't fit, and show their values under the row.

Static server-side rendering: no circuit, no WebAssembly.

Columns that hide

With SmallScreen="DataGridSmallScreen.HideColumns", columns that don't fit the grid's width hide, lowest Priority first and the last of equals first, instead of the grid scrolling sideways. A button at the start of each row then opens its detail, with the hidden columns' values in it, labels over values. The row's actions move into its ⋮ menu then, unless one says Overflow="RowActionOverflow.Never". The toolbar folds by its own width: your ToolbarContent and Export move into its "…" menu, while search, Add and Columns stay, as icons. The first column and frozen ones never hide. It goes by the grid's own width, so it works in a side panel too, and the grid takes all of that width; it's worked out on the server, so it's right from the first paint, with no script. Hiding needs an ItemKey, as open rows are kept by it, and can't be used with Virtualize. A hidden column's header goes with it: sort or filter it from the column picker or the filter chips. With a DetailTemplate, the hidden values come first in its detail.

Preview

Orders that fit a phone
OrderCustomerDateStatusTotalActions
1001Margaret HamiltonSep 8Failed$66.25
1002Katherine JohnsonSep 15Refunded$108.00
1003Grace HopperSep 22Pending$149.75
1004Barbara LiskovSep 29Paid$191.50
1005Linus TorvaldsOct 6Failed$233.25

Code

HideColumns.razor
@using Rows = IzzyBlazor.Site.Examples.Table.Orders
@inject NavigationManager Navigation
@* Narrow, the columns with the lowest priority hide first, and a row's button shows them under it.
   The row's actions move into its ⋮ menu then, and the toolbar's own buttons and Export into its
   "…" menu; search stays. Switch to Phone to see it. *@
<DataGrid Items="Rows.All" PageSize="5" Caption="Orders that fit a phone" QueryPrefix="narrow-"
          ItemKey="o => o.Number.ToString()" RowLabel="@(o => $"Order {o.Number}")"
          SmallScreen="DataGridSmallScreen.HideColumns" Searchable
          ExportExcelHref="/examples/orders.xlsx" ExportCsvHref="/examples/orders.csv">
    <ToolbarContent>
        <ToolbarButton StartIcon="Lucide.Printer" Position="ToolbarPosition.End">Print</ToolbarButton>
        <ToolbarButton StartIcon="Lucide.Share2" Position="ToolbarPosition.End">Share</ToolbarButton>
        <ToolbarButton StartIcon="Lucide.Archive" Position="ToolbarPosition.End">Archive</ToolbarButton>
    </ToolbarContent>
    <ChildContent>
        <PropertyColumn Property="o => o.Number" Title="Order" Align="CellAlign.Start" Width="6.Rem" />
        <PropertyColumn Property="o => o.Customer" Priority="2" MinWidth="7.Rem" />
        <PropertyColumn Property="o => o.Date" Format="MMM d" Width="7.Rem" />
        <PropertyColumn Property="o => o.Status" Width="8.Rem" />
        <PropertyColumn Property="o => o.Total" Format="C" Width="8.Rem" Priority="1" />
        <ActionColumn Context="order">
            <RowAction Icon="Lucide.Eye" Label="@($"View {order.Number}")" Href="@Navigation.GetUriWithQueryParameter("order", order.Number)" />
            <RowAction Icon="Lucide.Copy" Label="@($"Duplicate {order.Number}")" Href="@Navigation.GetUriWithQueryParameter("from", order.Number)" />
        </ActionColumn>
    </ChildContent>
</DataGrid>

With groups and a detail

Grouped, a row's button sits one level in from its group's, as for any detail. With a DetailTemplate, the button column is always there, and the hidden values come first in the detail, above the template. A frozen column, here the total at the end, never hides.

Preview

Orders in groups that fit a phone
OrderCustomerDateTotal
Status: Paid (5)
1004Barbara LiskovSep 29$191.50
1008Margaret HamiltonSep 7$358.50
1012Linus TorvaldsOct 5$65.50
1016Katherine JohnsonSep 13$232.50
1020Alan TuringOct 11$399.50
Status: Pending (6)
1003Grace HopperSep 22$149.75
1007Ada LovelaceOct 20$316.75
1011Barbara LiskovSep 28$483.75
1015Margaret HamiltonSep 6$190.75
1019Linus TorvaldsOct 4$357.75
1023Katherine JohnsonSep 12$64.75

Code

HideColumnsDetail.razor
@using Rows = IzzyBlazor.Site.Examples.Table.Orders
@* Grouped, with a detail of its own and the total kept at the end: hidden values come first in the
   detail, above what the template draws. *@
<DataGrid Items="Rows.All" PageSize="2" Caption="Orders in groups that fit a phone" QueryPrefix="narrow-groups-"
          ItemKey="o => o.Number.ToString()" RowLabel="@(o => $"Order {o.Number}")"
          SmallScreen="DataGridSmallScreen.HideColumns" InitialGroup="status" GroupsOpen>
    <ChildContent>
        <PropertyColumn Property="o => o.Number" Title="Order" Align="CellAlign.Start" Width="6.Rem" />
        <PropertyColumn Property="o => o.Customer" Priority="1" />
        <PropertyColumn Property="o => o.Date" Format="MMM d" Width="7.Rem" />
        <PropertyColumn Property="o => o.Status" Width="8.Rem" />
        <PropertyColumn Property="o => o.Total" Format="C" Width="8.Rem" Frozen="ColumnFrozen.End" />
    </ChildContent>
    <DetailTemplate>
        @OrderLines.For(context.Number).Count lines, @OrderLines.For(context.Number).Sum(l => l.Quantity) items
    </DetailTemplate>
</DataGrid>

Editing a row

With DataGridEditMode.Row, editing a row opens its detail, with fields for the columns hidden to fit, and Save and Cancel stay in the row. Adding a row does the same. With JavaScript off, or if its script fails to load, the detail shows those columns' values and any message instead of fields: an edited row keeps them, and a new row gets their defaults. Edit them where the grid is wide enough to show them.

Preview

Orders edited on a phone
OrderCustomerStatusDateTotalActions
1001Margaret HamiltonFailed9/8/2026$66.25
1002Katherine JohnsonRefunded9/15/2026$108.00
1003Grace HopperPending9/22/2026$149.75
1004Barbara LiskovPaid9/29/2026$191.50
1005Linus TorvaldsFailed10/6/2026$233.25

Code

HideColumnsEdit.razor

razor
@using Rows = IzzyBlazor.Site.Examples.Table.Orders
@* Narrow, editing a row opens its detail, with the fields of the columns hidden to fit. Save and
   Cancel stay in the row. *@
<DataGrid TItem="EditableOrder" Items="_orders" PageSize="5" Caption="Orders edited on a phone" ItemKey="o => o.Number.ToString()" QueryPrefix="phone-"
          RowLabel="@(o => $"Order {o.Number}")" EditMode="DataGridEditMode.Row" OnSave="Save" NewItem="NewOrder"
          SmallScreen="DataGridSmallScreen.HideColumns">
    <PropertyColumn Property="o => o.Number" Title="Order" Align="CellAlign.Start" Width="6.Rem" Editable="false" />
    <PropertyColumn Property="o => o.Customer" MinWidth="9.Rem" Priority="1" />
    <PropertyColumn Property="o => o.Status" Width="9.Rem" />
    <PropertyColumn Property="o => o.Date" Width="10.Rem" />
    <PropertyColumn Property="o => o.Total" Format="C" Width="9.Rem" />
</DataGrid>

@code {
    [CascadingParameter] private HttpContext? Http { get; set; }

    private List<EditableOrder> _orders = [];

    // Stands in for your database, with a copy of the rows for each visitor to this site.
    protected override void OnInitialized() => _orders = ExampleRows.For(Http, "edit-on-phone", () => Rows.All
        .Select(o => new EditableOrder { Number = o.Number, Customer = o.Customer, Date = o.Date, Status = (OrderStatus)o.Status, Total = o.Total })
        .ToList());

    private void Save(DataGridSave<EditableOrder> save)
    {
        if (save.IsNew)
        {
            _orders.Insert(0, save.Item);
            return;
        }

        save.CopyTo(_orders.First(o => o.Number == save.Item.Number));
    }

    private EditableOrder NewOrder() => new() { Number = _orders.Max(o => o.Number) + 1, Date = DateOnly.FromDateTime(DateTime.Today) };
}

EditableOrder.cs

csharp
using System.ComponentModel.DataAnnotations;

namespace IzzyBlazor.Site.Examples.DataGrid;

// On a static page a row needs a parameterless constructor and settable properties, for the post to
// fill. The attributes make the form: [Required] marks and checks a field, and MultilineText makes
// it a text area.
public sealed class EditableOrder
{
    public int Number { get; set; }

    [Required, StringLength(60)]
    public string? Customer { get; set; }

    public DateOnly Date { get; set; }

    public OrderStatus Status { get; set; }

    [Range(0, 1_000_000)]
    public decimal Total { get; set; }

    [StringLength(200), DataType(DataType.MultilineText)]
    public string? Notes { get; set; }
}

public enum OrderStatus { Paid, Pending, Refunded, Failed }

Connection lost

Rejoining the server…