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 NavigateEditRecordGridButton 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" />
<NavigateEditRecordGridButton 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 OnBeforeQuery or 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 the Selected View
Use SelectedViewQueryParameter to persist the currently selected view ID to a URL query parameter. This allows the selected view to be preserved when navigating back to the page.
<MainGrid TableName="contact"
SelectedViewQueryParameter="viewId" />
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" />
<NavigateEditRecordGridButton 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"),
};
}
}
Filtering with OnBeforeQuery
Use the OnBeforeQuery parameter to apply additional filter criteria to the FetchXML query before it is executed. The callback receives a FetchXMLBuilder and must return it after applying any modifications. This is called after all built-in search, sort, and paging logic has been applied.
<MainGrid TableName="contact"
OnBeforeQuery="ApplyContactFilter" />
@code {
private Task<FetchXMLBuilder> ApplyContactFilter(FetchXMLBuilder fetchXmlBuilder)
{
var filter = fetchXmlBuilder.Fetch.Entity.AddFilter();
var condition = filter.AddCondition();
condition.Column = "statecode";
condition.Operator = ConditionOperator.Equal;
condition.Value = "0";
return Task.FromResult(fetchXmlBuilder);
}
}
Tip
The
OnBeforeQuerycallback is ideal for applying context-specific filters such as restricting records to a specific status, the current user, or a parent record. Because filters are applied at the query level, filtered-out records are never retrieved from Dataverse.
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. | |
CustomViewDefinitions | List<GridViewDefinition>? | Custom views to display in the dropdown. | |
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. |
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. |
OnBeforeQuery | Func<FetchXMLBuilder, Task<FetchXMLBuilder>>? | Optional callback to apply additional filter criteria to the FetchXML query before it is executed. | |
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 | PersistedGridState? | 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 PersistedGridState.ViewId field is checked at consumption time so a rerender against a different view discards the stale rows. Public per the framework's requirement — | |
SelectedRecords | IEnumerable<TableRecord> | Records that are currently selected in the Grid. | |
SelectedViewQueryParameter | string? | Query parameter to persist the selected view to. | |
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 AllowPreviewForFileColumnsAllowSearchBorderVisibleButtonsCustomViewDefinitionsDefaultItemsPerPageDefaultViewIdEditableFullSizeHidePagingIsDirtyLoadedRecordsMaxHeightMinHeightModeOnBeforeQueryPageSizesPagingModePersistedRowsSnapshotGridBase.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 PersistedGridState.ViewId field is checked at consumption time so a rerender against a different view discards the stale rows. Public per the framework's requirement — SelectedRecordsSelectedViewQueryParameterSelectFromEntireRowSelectModeTableNameGridBase.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