MainGrid
The MainGrid component is a standalone grid for displaying Dataverse records. Unlike SubGrid, it does not require a parent RecordContext — it loads records directly from a specified table or view.
Loading by Table Name
Set the TableName parameter to automatically load all public views for that table. The first default view is selected initially.
<MainGrid TableName="contact" />
Loading by View IDs
Use ViewIds and DefaultViewId to control exactly which views are available and which one is selected on load. You can also provide CustomViewDefinitions with inline FetchXML to define views directly in code.
<MainGrid ViewIds="_viewIds"
DefaultViewId="@(new Guid("..."))">
</MainGrid>
@code {
private List<Guid> _viewIds = new List<Guid>
{
new Guid("..."),
new Guid("..."),
};
}
Custom Views with FetchXML
Use CustomViewDefinitions to define views with custom FetchXML queries. This is useful for views that include filters based on the current user, linked entities, or other dynamic criteria.
Note
The
Idfor a customGridViewDefinitionmust be a unique random GUID that does not correspond to an existing Dataverse view. If it matches an existing view ID, the Dataverse view will take precedence and the custom definition will be ignored.
private List<GridViewDefinition> _customViews = new List<GridViewDefinition>
{
new GridViewDefinition
{
Id = new Guid("..."),
TableName = "contact",
DisplayName = "All Contacts",
FetchXml = @"<fetch>
<entity name='contact'>
<attribute name='fullname' />
<attribute name='emailaddress1' />
<order attribute='fullname' />
</entity>
</fetch>",
Columns = new List<ViewColumn>
{
new ViewColumn { ColumnName = "fullname", Width = 200 },
new ViewColumn { ColumnName = "emailaddress1", Width = 250 },
}
}
};
DisplayName
Set DisplayName on a GridViewDefinition to provide a default label for the view in the dropdown selector without requiring a localization file entry. If a localization key exists at tables.{TableName}.views.{Id}.label, it takes precedence over DisplayName. This is useful for custom views where you want a readable name immediately without adding a localization entry.
Toolbar Buttons
The MainGrid supports the same toolbar buttons as SubGrid. For standalone grids, NavigateNewRecordGridButton and NavigateOpenRecordGridButton are commonly used to navigate to separate form pages. See the Grid Buttons documentation for a complete reference of all available buttons and their configuration options.
<MainGrid TableName="contact">
<Buttons>
<NavigateNewRecordGridButton Url="/contacts/new" />
<NavigateOpenRecordGridButton Url="/contacts/edit?contactId={0}" />
</Buttons>
</MainGrid>
Search Box Behavior
When the user types in the grid's search box, the configured view is automatically re-queried with additional filter conditions applied to every search-eligible column shown in the view. The conditions are joined with OR logic, so a record is included if any of its visible columns match the search term. The default match semantics differ depending on the column type.
Text and Lookup Columns
Text columns (string fields) and lookup columns (matched against the target record's primary name) use a starts with search by default. Typing joh matches values that begin with joh — for example John or Johnson. The match is delegated to FetchXML's like operator, which is case-insensitive in Dataverse.
Use * as a wildcard for more flexible matching. Typing *hn performs a contains-style match (e.g. John, Johnson); typing j*n matches values where any characters can appear between the j and the n (e.g. John, Joneson). The wildcard is converted to FetchXML's % wildcard at query time.
Choice Columns
Choice columns (option sets / picklists) and multi-select choice columns are matched against the localized display label of each option rather than the underlying integer value. Labels are compared case- and accent-insensitively in the current user's culture, so typing cafe will match an option labelled Café. When one or more option labels match, the query emits a FetchXML condition against the matching option values — an in condition for single-select choice columns, or a contain-values condition for multi-select choice columns. When no labels match, the column contributes no condition (keeping the OR filter compact).
Choice columns honor the same * wildcard as text columns: by default the match is starts with (typing act matches Active), and a leading * switches to contains (typing *act additionally matches Inactive). Matching rows have the matched substring highlighted in the grid, exactly like text-column matches.
Numeric and Money Columns
Numeric columns — int, big int, decimal, double, and money — support comparison operators in the search term. The search box parses an optional leading operator and applies the corresponding FetchXML condition operator:
= 100or just100— equal (the default when no operator is specified)> 100— greater than< 100— less than>= 100— greater than or equal<= 100— less than or equal
Note
Search conditions for every column type are added to the same
ORfilter group, so the same search term is evaluated against text columns, lookup columns, choice columns, and numeric columns simultaneously. Typing100in a grid that has both a name column and an amount column will match records whose name starts with100or whose amount equals 100. The search filter is layered on top of the view's existing filter, so view-level constraints (such as astatecode = 0filter) are always preserved.
Disabling Search
Set AllowSearch="false" on the grid to hide the search box entirely. This is useful for grids that contain only a small fixed set of records, or where filtering is handled externally (for example via a custom toolbar).
<MainGrid TableName="contact" AllowSearch="false" />
Full Size Mode
Set FullSize="true" to make the grid expand to fill the full height of its parent container. This is useful when the grid is the main content of a page.
<PageLayout>
<Body>
<MainGrid TableName="contact" FullSize="true" />
</Body>
</PageLayout>
Persisting Grid State
Use PersistedStateQueryParameter to persist the selected view, page number, page size, and sort to a single URL query parameter. The state is restored when the page is opened with that parameter present — useful for bookmarks and shared links. Pass IncludeSearchInPersistedState="true" to additionally persist the current search text (off by default since search terms can be sensitive).
<MainGrid TableName="contact"
PersistedStateQueryParameter="gridState" />
Multi-Table Grids
A MainGrid can display views from different tables by including view IDs from multiple tables in the ViewIds collection. When the user switches views, the grid automatically loads the correct table's data. Use the OnClick callback on navigation buttons to dynamically set the URL based on the selected view's table name.
<MainGrid ViewIds="_viewIds"
CustomViewDefinitions="_customViews"
DefaultViewId="@(new Guid(AllContactsViewId))">
<Buttons>
<NavigateNewRecordGridButton OnClick="OnNewClick" />
<NavigateOpenRecordGridButton OnClick="OnEditClick" />
</Buttons>
</MainGrid>
@code {
private async Task OnNewClick(NavigateGridButtonContext ctx)
{
ctx.Url = ctx.GridContext.SelectedView.TableName switch
{
"contact" => "/contacts/new",
"account" => "/accounts/new",
_ => throw new Exception("Unknown table"),
};
}
private async Task OnEditClick(NavigateGridButtonContext ctx)
{
ctx.Url = ctx.GridContext.SelectedView.TableName switch
{
"contact" => "/contacts/edit?id={0}",
"account" => "/accounts/edit?id={0}",
_ => throw new Exception("Unknown table"),
};
}
}
MainGrid Class
Parameters
Name | Type | Default | Description |
|---|---|---|---|
AllowChangingItemsPerPage | bool | True | When |
AllowDownloadForFileColumns | bool | True | When |
AllowEdit | bool | False | Should the option be available for the user to turn on inline editing for the grid. |
AllowNavigateOnPrimaryNameClick | bool | True | When GridButton with |
AllowNavigateOnRowDoubleClick | bool | True | When GridButton with GridButton.OnClick for the row's record — opening the edit dialog or navigating to the edit URL, whichever the button does. Set to |
AllowPreviewForFileColumns | bool | True | When |
AllowSearch | bool | True | Should the user be allowed to search the grid. |
BorderVisible | bool | True | Controls whether a visible border is rendered around the grid. |
Buttons | RenderFragment? | Optional render fragment used to define the button toolbar displayed above the grid. | |
Columns | RenderFragment? | Optional GridColumns fragment carrying consumer-declared GridColumn children. When supplied, the grid switches to replace mode: only the declared columns render (in declared order), the FetchXML projection is rewritten to match, and the underlying view's column list is ignored. null leaves the grid in its default behavior (auto-generate columns from the view's resolved column set). | |
CustomViewDefinitions | List<GridViewDefinition>? | Custom views to display in the dropdown. | |
DataSource | ViewDataSource? | Optional shared Data.ViewDataSource. When set, the grid reads its rows + total count from the datasource instead of issuing its own RetrieveRecordsAsync(System.String) call — the same datasource can drive a sibling GridBase.DataSource) keeps the existing internal-state machine — the grid composes FetchXML and fetches via ViewDataSource.Highlight is set (e.g. via a chart slice click in cross-filter 'highlight' mode), rows whose value in the highlighted column doesn't match are visually muted via a CSS class; the row data + selection are unaffected (the soft cross-filter is purely styling). | |
DefaultItemsPerPage | int | 50 | Default number of records to load on a page. |
DefaultViewId | Guid? | Id of the view that the grid should display upon initial load. | |
Editable | bool | False | Is inline editing turned on for the grid. |
FullSize | bool | False | When |
HidePaging | bool | False | Force the page size and paging components to be hidden. Only do this when the number of items is known and the page size is set to something greater than the item count. |
IncludeSearchInPersistedState | bool | False | When GridBase.PersistedStateQueryParameter is not set. |
IsDirty | bool | False | Indicates whether the grid has unsaved create, update, or delete operations pending. |
LoadedRecords | IEnumerable<TableRecord> | Records currently rendered in the grid (the most recent page of results). Intended for toolbar commands that need to act on 'everything shown' — e.g. a bulk download button. Does not span pages; bulk-across-pages operations should run their own unpaged fetch instead. | |
MaxHeight | string? | Max Height that the grid control should expand to. | |
MinHeight | string? | 250px | Minimum height that the grid control should occupy. |
Mode | GridMode | RecordSelection | Sets the behavioural mode of the grid, such as default interaction or record-selection mode. |
PageSizes | IEnumerable<int> | Collection of available page sizes for the grid. | |
PagingMode | GridPagingMode | Paged | Determines whether the grid uses traditional paging or infinite-scroll virtualisation. |
PersistedRowsSnapshot | PersistedGridRowsSnapshot? | Server-prerender → interactive handoff for the rendered page of rows. The framework auto-persists this property at the end of prerender and re-hydrates it before GridBase.OnInitializedAsync on the interactive side, so the data fetch can be skipped on first interactive render. Keyed by render-tree position by the framework; the PersistedGridRowsSnapshot.ViewId field is checked at consumption time so a rerender against a different view discards the stale rows. Public per the framework's requirement — | |
PersistedStateQueryParameter | string? | Name of the URL query-string parameter to persist the grid's interactive state to. When set, the grid reads this parameter on initial load and seeds the active view, page number, page size, and sort from it; subsequent user actions (view pick, page change, header sort, etc.) write the new state back via Components.NavigationManager's replace-state path. Persistence survives page refresh and bookmarks. | |
SelectedRecords | IEnumerable<TableRecord> | Records that are currently selected in the Grid. | |
SelectFromEntireRow | bool | True | When |
SelectMode | DataGridSelectMode | Multiple | Controls whether the grid allows single or multiple row selection. |
TableName | string? | The logical name of the table whose public views should be loaded. Only applicable when no values are specified for GridBase.DefaultViewId or GridBase.ViewIds. | |
Title | string? | Name to display when the view dropdown is not displayed. | |
TransformViewAsync | Func<GridViewDefinition, Task<GridViewDefinition>>? | Optional callback that runs immediately after a view is loaded and before the grid uses it to build columns or queries. Return a modified Models.GridViewDefinition to transform what the grid ultimately renders — for example, to ensure a specific column is always present regardless of the view's own configuration. Async so callers can consult metadata caches, services, or other async resources while deciding what to include. | |
ViewIds | IEnumerable<Guid>? | List of id's of the views that the grid should limit to in the view dropdown. | |
ViewSort | ViewSort | NameAscending | Sort order of the views in the view dropdown. |
AllowChangingItemsPerPageAllowDownloadForFileColumnsAllowEditAllowNavigateOnPrimaryNameClickGridButton with AllowNavigateOnRowDoubleClickGridButton with GridButton.OnClick for the row's record — opening the edit dialog or navigating to the edit URL, whichever the button does. Set to AllowPreviewForFileColumnsAllowSearchBorderVisibleButtonsColumnsGridColumns fragment carrying consumer-declared GridColumn children. When supplied, the grid switches to replace mode: only the declared columns render (in declared order), the FetchXML projection is rewritten to match, and the underlying view's column list is ignored. null leaves the grid in its default behavior (auto-generate columns from the view's resolved column set).CustomViewDefinitionsDataSourceData.ViewDataSource. When set, the grid reads its rows + total count from the datasource instead of issuing its own RetrieveRecordsAsync(System.String) call — the same datasource can drive a sibling GridBase.DataSource) keeps the existing internal-state machine — the grid composes FetchXML and fetches via ViewDataSource.Highlight is set (e.g. via a chart slice click in cross-filter 'highlight' mode), rows whose value in the highlighted column doesn't match are visually muted via a CSS class; the row data + selection are unaffected (the soft cross-filter is purely styling). DefaultItemsPerPageDefaultViewIdEditableFullSizeHidePagingIncludeSearchInPersistedStateGridBase.PersistedStateQueryParameter is not set.IsDirtyLoadedRecordsMaxHeightMinHeightModePageSizesPagingModePersistedRowsSnapshotGridBase.OnInitializedAsync on the interactive side, so the data fetch can be skipped on first interactive render. Keyed by render-tree position by the framework; the PersistedGridRowsSnapshot.ViewId field is checked at consumption time so a rerender against a different view discards the stale rows. Public per the framework's requirement — PersistedStateQueryParameterComponents.NavigationManager's replace-state path. Persistence survives page refresh and bookmarks.SelectedRecordsSelectFromEntireRowSelectModeTableNameGridBase.DefaultViewId or GridBase.ViewIds.TitleTransformViewAsyncModels.GridViewDefinition to transform what the grid ultimately renders — for example, to ensure a specific column is always present regardless of the view's own configuration. Async so callers can consult metadata caches, services, or other async resources while deciding what to include.ViewIdsViewSortEvents
Name | Type | Description |
|---|---|---|
EditableChanged | EventCallback<bool> | Callback invoked when the inline editing state changes. |
SelectedRecordsChanged | EventCallback<IEnumerable<TableRecord>> | Callback invoked when the selected records collection changes. |
EditableChangedSelectedRecordsChangedMethods
Name | Parameters | Type | Description |
|---|---|---|---|
ClearSelectionAsync | Task | Clears all currently selected rows. | |
OpenFileDownloadAsync | TableRecord record string columnName | Task | Fetches the file/image column's bytes for record and streams them to the user's browser as a download. Invoked by the per-row download icon in file and image cells. |
OpenFilePreviewAsync | TableRecord record string columnName | Task | Opens the inline preview dialog for the file/image column columnName on record. Invoked by the per-row preview icon in file/image cells and also available to composing components (for example, a toolbar button on a wrapping grid) that want a programmatic entry point. |
RefreshAsync | bool forceRefresh | Task | Instructs the grid to re-fetch and render the current data from the supplied data source. |
Validate | bool | Validates all editable rows in the grid. |
ClearSelectionAsyncOpenFileDownloadAsyncstring columnName
record and streams them to the user's browser as a download. Invoked by the per-row download icon in file and image cells.OpenFilePreviewAsyncstring columnName
columnName on record. Invoked by the per-row preview icon in file/image cells and also available to composing components (for example, a toolbar button on a wrapping grid) that want a programmatic entry point.RefreshAsyncValidateGridViewDefinition Class
Properties
Name | Type | Default | Description |
|---|---|---|---|
Columns | List<ViewColumn> | The columns displayed in the grid, including their logical names and pixel widths. | |
DisplayName | string? | Optional display name for this view. When set, this is used as the default label in the view selector dropdown. A localization entry at | |
FetchXml | string | The FetchXML query that defines which records and columns are retrieved for this view. | |
TableName | string | The logical name of the Dataverse table this view queries. |
ColumnsDisplayNameFetchXmlTableName