KMVUnion.Grid 1.3.0

dotnet add package KMVUnion.Grid --version 1.3.0
                    
NuGet\Install-Package KMVUnion.Grid -Version 1.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="KMVUnion.Grid" Version="1.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="KMVUnion.Grid" Version="1.3.0" />
                    
Directory.Packages.props
<PackageReference Include="KMVUnion.Grid" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add KMVUnion.Grid --version 1.3.0
                    
#r "nuget: KMVUnion.Grid, 1.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package KMVUnion.Grid@1.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=KMVUnion.Grid&version=1.3.0
                    
Install as a Cake Addin
#tool nuget:?package=KMVUnion.Grid&version=1.3.0
                    
Install as a Cake Tool

KMVUnion.Grid

NuGet License: MIT

A high-performance, feature-rich DataGrid control for .NET MAUI (.NET 10+). Supports in-memory collections and async/server-side data sources with pagination, multi-column sorting, global search, per-column filters, inline editing, multi-row selection, grouping, CSV export, clipboard copy, conditional styling, summary aggregates, and fully custom DataTemplate cells.

Platforms: Android - iOS - macCatalyst - Windows


Table of Contents

  1. Requirements
  2. Installation & Setup
  3. Quick Start
  4. Column Types
  5. Column Base Properties
  6. Data Sources
  7. Sorting
  8. Global Search
  9. Column Filters
  10. Pagination
  11. Row Selection
  12. Inline Row Editing
  13. Row Grouping
  14. CSV Export & Share
  15. Clipboard Copy
  16. Conditional Row Styling
  17. Summary Row
  18. Appearance & Theming
  19. Commands & Events
  20. Page Load Hooks
  21. License

Requirements

  • .NET MAUI targeting .NET 10 or later
  • Platforms: Android 5.0+, iOS 15+, macOS 15+ (Mac Catalyst), Windows 10 1809+

Installation & Setup

dotnet add package KMVUnion.Grid

Register the library in MauiProgram.cs:

using KMVUnion.Grid;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .UseDataGridMaui();   // registers Material Icons font + default theme
        return builder.Build();
    }
}

Add XML namespaces to any XAML page:

xmlns:dg="clr-namespace:KMVUnion.Grid.Controls;assembly=KMVUnion.Grid"
xmlns:col="clr-namespace:KMVUnion.Grid.Columns;assembly=KMVUnion.Grid"

Quick Start

<dg:DataGridControl
    ItemsSource="{Binding Employees}"
    ShowSearchBar="True"
    ShowPagination="True"
    PageSize="25">
    <dg:DataGridControl.Columns>
        <col:RowNumberColumn />
        <col:TextColumn    Header="Name"       Binding="{Binding Name}"       SortPropertyName="Name" />
        <col:TextColumn    Header="Department" Binding="{Binding Department}"  SortPropertyName="Department" />
        <col:NumberColumn  Header="Salary"     Binding="{Binding Salary}"      SortPropertyName="Salary" Format="N0" />
        <col:BooleanColumn Header="Active"     Binding="{Binding IsActive}" />
    </dg:DataGridControl.Columns>
</dg:DataGridControl>

ObservableCollection<T> is fully supported - adding or removing items triggers an automatic grid refresh.


Column Types

Type Description
TextColumn String values; participates in global search
NumberColumn Numeric values with optional .NET format strings; right-aligned by default
BooleanColumn True/false with customisable TrueLabel / FalseLabel (defaults: ✓ / ✗)
DateTimeColumn DateTime / DateTimeOffset with configurable Format; date picker in edit mode
UrlColumn Clickable hyperlink; opens in the system browser; tracks visited state per session
ProgressBarColumn Progress bar visualization (0–1) with individual and relative (row-relative-to-total) modes; customizable colors and height
RowNumberColumn Auto-generated row numbers, pagination-aware; excluded from CSV export
TemplateColumn Fully custom DataTemplate cell - buttons, images, checkboxes, badges, etc.

DateTimeColumn

<col:DateTimeColumn Header="Hired" Binding="{Binding HireDate}" Format="dd.MM.yyyy" SortPropertyName="HireDate" />

UrlColumn

<col:UrlColumn Header="Website" Binding="{Binding WebsiteUrl}" DisplayTextBinding="{Binding WebsiteLabel}" />

ProgressBarColumn

Displays a customizable progress bar (0–1). Supports two modes:

  • Individual - each row shows its own progress value.
  • Relative - each row's value is shown relative to the sum of all values in the column.

<col:ProgressBarColumn
    Header="Progress"
    Binding="{Binding ProgressValue}"
    Mode="Individual"
    BarColor="Green"
    TrackColor="LightGray"
    BarHeight="8"
    ShowLabel="True" />


<col:ProgressBarColumn
    Header="Relative Share"
    Binding="{Binding SalesAmount}"
    Mode="Relative"
    AllRows="{Binding AllEmployees}"
    BarColor="Blue"
    TrackColor="LightGray"
    BarHeight="12"
    ShowLabel="True"
    LabelColor="Black" />
Property Type Default Description
Binding BindingBase? null Source property for the progress value (0–1 or raw value in relative mode)
Mode ProgressMode Individual Individual or Relative
AllRows BindingBase? null In relative mode, a binding to the full row collection (for computing the total)
BarColor Color Green Progress bar fill color
TrackColor Color LightGray Progress bar background/track color
BarHeight double 8 Progress bar height in DIPs
ShowLabel bool false Display percentage label inside the progress bar
LabelColor Color Black Percentage label text color

TemplateColumn

<col:TemplateColumn Header="Actions" Width="120" IsSortable="False">
    <col:TemplateColumn.CellTemplate>
        <DataTemplate>
            <HorizontalStackLayout Spacing="4" HorizontalOptions="Center">
                <Button Text="Edit"   Command="{Binding ...}" CommandParameter="{Binding .}" />
                <Button Text="Delete" Command="{Binding ...}" CommandParameter="{Binding .}" />
            </HorizontalStackLayout>
        </DataTemplate>
    </col:TemplateColumn.CellTemplate>
</col:TemplateColumn>

Column Base Properties

All column types inherit these properties from ColumnBase:

Property Type Default Description
Header string "" Column header text
HeaderTemplate DataTemplate? null Custom header cell template
CellTemplate DataTemplate? null Overrides the default cell renderer
Width GridLength * Fixed, auto, or proportional (2*)
MinWidth double 40 Minimum width in DIPs
MaxWidth double inf Maximum width in DIPs
AutoWidth bool true Measures widest content and sizes column automatically
IsVisible bool true Show / hide the entire column
IsSortable bool true Allow user sorting
SortPropertyName string? null Model property name used for sorting
IsFilterable bool false Show filter icon in the header
FilterPropertyName string? null Property for filter values (falls back to SortPropertyName)
IsEditable bool false Allow inline editing for this column
HorizontalContentAlignment LayoutOptions Start Horizontal cell content alignment
VerticalContentAlignment LayoutOptions Center Vertical cell content alignment
HeaderHorizontalAlignment HeaderTextAlignment Center Header text alignment (Inherit, Start, Center, End)
SummaryHorizontalAlignment LayoutOptions? null Override alignment in the summary row cell
Summary SummaryAggregate? null Count, Sum, Min, Max, or Avg

Data Sources

In-Memory (ItemsSource)

Bind any IEnumerable. The grid wraps it in InMemoryDataGridSource<T> which handles sorting, filtering, and pagination via LINQ.

<dg:DataGridControl ItemsSource="{Binding Employees}" />

Async / Server-Side (DataSource)

Implement IDataGridSource<T> for server-side loading, virtualization, or complex data pipelines.

<dg:DataGridControl DataSource="{Binding MyServerSource}" />

ItemsSource and DataSource are mutually exclusive - setting one clears the other.


Sorting

Tapping a sortable column header cycles Ascending > Descending > No Sort. Hold Shift (desktop) to add additional sort levels (multi-column sort with priority indicators).

<col:TextColumn Header="Name" Binding="{Binding Name}" SortPropertyName="Name" IsSortable="True" />
myGrid.SortChanged += (sender, descriptor) =>
    Console.WriteLine($"Sorted by {descriptor.PropertyName} {descriptor.Direction}");

Filters rows across all searchable TextColumn columns (debounced 300 ms).

<dg:DataGridControl
    ShowSearchBar="True"
    SearchPlaceholder="Search employees..."
    SearchText="{Binding SearchQuery, Mode=TwoWay}"
    SearchColumns="Name,Department"
    ... />

Exclude a column from search: <col:TextColumn IsSearchable="False" ... />


Column Filters

Per-column dropdown with two tabs:

  • Values - Excel-style checkbox list of distinct values.
  • Range - Numeric greater/less-than fields or DateTime From/To pickers.

Multiple column filters combine with AND logic; values within InSet use OR logic.

<col:TextColumn
    Header="Department"
    Binding="{Binding Department}"
    SortPropertyName="Department"
    IsFilterable="True" />

Available filter operations: Equals, Contains, StartsWith, EndsWith, GreaterThan, LessThan, GreaterThanOrEqual, LessThanOrEqual, IsNull, IsNotNull, InSet.


Pagination

<dg:DataGridControl
    ShowPagination="True"
    PageSize="25"
    PageSizeOptions="10,25,50,100"
    ShowPageSizeSelector="True"
    ShowRangeLabel="True"
    RangeLabelFormat="Showing {0}-{1} of {2} records"
    CurrentPage="{Binding CurrentPage, Mode=TwoWay}"
    TotalPages="{Binding TotalPages}"
    TotalCount="{Binding TotalCount}"
    ... />
Property Type Default Description
PageSize int 25 Rows per page
CurrentPage int 1 Current page (two-way bindable)
TotalPages int - Total page count (read-only)
TotalCount int - Total filtered record count (read-only)
ShowPageSizeSelector bool true Show page size buttons
PageSizeOptions string "10,25,100" Comma-separated page sizes (max 5)

Row Selection


<dg:DataGridControl
    SelectionMode="Single"
    SelectedItem="{Binding SelectedEmployee, Mode=TwoWay}"
    SelectionColor="#BFDBFE" />


<dg:DataGridControl
    SelectionMode="Multiple"
    SelectedItems="{Binding SelectedEmployees, Mode=TwoWay}" />

Inline Row Editing

Double-tap a row to enter edit mode. Confirm with checkmark / Enter, cancel with X / Escape.

<dg:DataGridControl
    IsEditable="True"
    RowCommittedCommand="{Binding SaveCommand}"
    RowCancelledCommand="{Binding RevertCommand}" />

Control per-column editing with IsEditable on individual columns:

<col:TextColumn   Header="Name" Binding="{Binding Name}" IsEditable="True" />
<col:NumberColumn Header="ID"   Binding="{Binding Id}"   IsEditable="False" />

Row Grouping

<dg:DataGridControl GroupByProperty="Department" ... >
    <dg:DataGridControl.GroupHeaderTemplate>
        <DataTemplate>
            
            <Label Text="{Binding KeyDisplay}" FontAttributes="Bold" Padding="8,4" />
        </DataTemplate>
    </dg:DataGridControl.GroupHeaderTemplate>
</dg:DataGridControl>

CSV Export & Share

<dg:DataGridControl
    ShowExportButton="True"
    ShowShareButton="True"
    ExportCommand="{Binding CustomExportCommand}" />
  • Export - saves filtered/sorted data as RFC 4180 CSV to the platform Downloads/Documents folder.
  • Share - opens the native OS share sheet (email, cloud drive, etc.).
  • Respects NumberColumn.Format and BooleanColumn.TrueLabel/FalseLabel; excludes RowNumberColumn.
  • Programmatic API: await myGrid.ExportAsync(stream, cancellationToken).

Clipboard Copy

<dg:DataGridControl
    ShowCopyButton="True"
    IsCopyEnabled="True"
    IsCellCopyEnabled="True"
    IsTableCopyEnabled="True" />
  • Cell copy - long-press any data cell to copy its value.
  • Table copy - copies all filtered rows with headers as TSV (pasteable directly into Excel/Google Sheets).

Conditional Row Styling

public class MyRowStyleSelector : DataGridRowStyleSelector
{
    public override DataGridRowStyle SelectStyle(object item, BindableObject container)
    {
        if (item is EmployeeModel { IsActive: false })
            return new DataGridRowStyle { TextColor = Colors.Gray };
        return null; // fall through to default
    }
}
<dg:DataGridControl RowStyleSelector="{StaticResource MySelector}" ... />

Evaluation order: editing > selected > hovered > conditional > default.


Summary Row

Displays aggregate values beneath all rows. Computes over the full filtered dataset, independent of the current page.

<dg:DataGridControl ShowSummaryRow="True" ... >
    <col:NumberColumn Header="Salary" Binding="{Binding Salary}" Summary="Avg" />
    <col:TextColumn   Header="Name"   Binding="{Binding Name}"   Summary="Count" />
</dg:DataGridControl>
Aggregate Description
Count Count of non-null values
Sum Sum of numeric values
Min Minimum value
Max Maximum value
Avg Arithmetic mean

Appearance & Theming

Property Type Default Description
IsStriped bool true Alternating row background colours
RowHeight double -1 Fixed row height in DIPs; -1 = auto (size to content)
HeaderHeight double 52 Header row height in DIPs
HoverColor Color? theme Row background on mouse hover (desktop/tablet)
SelectionColor Color? theme Row background when selected
NavButtonBackgroundColor Color #6B7280 Pagination button background
NavButtonTextColor Color White Pagination button text / icon colour
NavButtonCornerRadius int 3 Corner radius for all pagination buttons
ActiveButtonBackgroundColor Color #374151 Active page button background

Commands & Events

Event Command Args
RowTapped RowTappedCommand Row data object
SortChanged SortCommand SortDescriptor
PageChanged - int page number
RowCommitted RowCommittedCommand Modified item
RowCancelled RowCancelledCommand Restored item
ColumnReordered ColumnReorderedCommand ColumnReorderRequestedArgs (FromIndex, ToIndex)
ExportRequested ExportCommand Fires before export action
SelectionChanged SelectionChangedCommand IReadOnlyList<object> snapshot
PageLoaded PageLoadedCommand IReadOnlyList<object?> — all rows on the current page
RowPageLoaded RowPageLoadedCommand object? — individual row ViewModel

Page Load Hooks

Execute logic whenever a page of data is rendered — on initial load, page navigation, page size change, sort, filter, or search change. Four approaches are available; they can be combined freely.

Approach 1 — PageLoadedCommand (XAML binding, once per page)

Executed once after each page load. The command parameter is IReadOnlyList<object?> containing all rows on the current page.

<dg:DataGridControl PageLoadedCommand="{Binding OnPageLoadedCommand}" />
// CommunityToolkit.Mvvm
[RelayCommand]
void OnPageLoaded(IReadOnlyList<object?> rows)
{
    var total = rows.OfType<EmployeeModel>().Sum(e => e.Salary);
    StatusLabel = $"Page salary total: {total:N0}";
}

// Plain ICommand
public ICommand OnPageLoadedCommand { get; } = new Command<IReadOnlyList<object?>>(rows =>
{
    foreach (var item in rows.OfType<EmployeeModel>())
        item.LoadExtraData();
});

Approach 2 — RowPageLoadedCommand (XAML binding, once per row)

Executed once per row after each page load. The command parameter is the individual row ViewModel (object?).

<dg:DataGridControl RowPageLoadedCommand="{Binding OnRowLoadedCommand}" />
[RelayCommand]
void OnRowLoaded(object? row)
{
    if (row is EmployeeModel emp)
        emp.IsMarkedAsSeen = true;
}

Approach 3 — PageLoaded event (code-behind)

Subscribe in OnAppearing to guarantee the handler is registered before the first render completes. If RefreshAsync finishes before the subscription (race condition on fast in-memory sources), use CurrentPageRows as a fallback.

protected override void OnAppearing()
{
    base.OnAppearing();
    TheGrid.PageLoaded += OnGridPageLoaded;

    // Fallback: grid already loaded before subscription
    if (TheGrid.CurrentPageRows.Count > 0)
        OnGridPageLoaded(TheGrid, TheGrid.CurrentPageRows);
}

private void OnGridPageLoaded(object? sender, IReadOnlyList<object?> rows)
{
    var active = rows.OfType<EmployeeModel>().Count(e => e.IsActive);
    Debug.WriteLine($"Page loaded: {rows.Count} rows, {active} active");
}

protected override void OnDisappearing()
{
    base.OnDisappearing();
    TheGrid.PageLoaded -= OnGridPageLoaded;
}

Approach 4 — NotifyPageLoaded (manual, code-behind)

Manually re-fires all page-load notifications (PageLoaded event + PageLoadedCommand + RowPageLoaded + RowPageLoadedCommand) for the supplied row list. Use CurrentPageRows to pass exactly the rows currently displayed.

// Re-trigger for current page without a full reload
TheGrid.NotifyPageLoaded(TheGrid.CurrentPageRows);

// Or supply a custom list
TheGrid.NotifyPageLoaded(myFilteredRows);

CurrentPageRows

Read-only property that always contains the rows currently displayed on the page. Updated at the end of every RefreshAsync and every NotifyPageLoaded call.

var rows = myGrid.CurrentPageRows; // IReadOnlyList<object?>

License

This package is licensed under the MIT License.
© 2025 KMVUnion

Product Compatible and additional computed target framework versions.
.NET net10.0-android36.0 is compatible.  net10.0-ios26.0 is compatible.  net10.0-maccatalyst26.0 is compatible.  net10.0-windows10.0.19041 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.0 146 5/8/2026
1.2.0 115 5/2/2026
1.1.2 117 4/27/2026
1.1.1 118 4/19/2026
1.0.5 114 4/11/2026

v1.3.0 — Added PageLoaded / RowPageLoaded events and PageLoadedCommand / RowPageLoadedCommand bindable properties; exposed CurrentPageRows for code-behind access to the current page data; all four notification approaches (command binding, event subscription, manual NotifyPageLoaded call) fire on every page load including the first render.; Fixed some issues related to programmatic change ItemSource.