The FileGrid component is a specialised grid for managing Dataverse records whose main content is a single file column. It wraps MainGrid and layers on drag-and-drop upload, per-row preview and download icons, and a bulk zip-download of every loaded file. The grid also honours Dataverse's environment-wide blocked-extensions list and per-column size limits, and participates in MainContext / RecordContext save pipelines when one is in scope.
Basic Usage
Specify the table and the logical name of the file column that the grid should manage. Upload, Replace File, Download All, and Delete buttons appear automatically; files can also be dropped onto the grid. Pass a <Buttons> render fragment to add your own toolbar buttons alongside the built-ins — the same pattern MainGrid and SubGrid support.
If the active view's FetchXML and column list don't already include the FileColumn, the grid grafts it onto a cloned copy so the filename, type icon, and per-row preview/download icons always render — regardless of which view the user picks. The cache-shared view instance is left untouched, so other grids aren't affected.
<!-- Even if the saved view doesn't include ppp_data in its column list, the
FileGrid adds it automatically so the filename and action icons are always visible. -->
<FileGrid TableName="ppp_file"
FileColumn="ppp_data"
DefaultViewId="@(new Guid("..."))"
ViewIds="_viewIds" />
Maximum File Size
Use MaxFileSizeBytes to cap the size of uploaded files. The grid clamps your value down to the smaller of the column'sMaxSizeInKB metadata and the environment'smaxuploadfilesize setting, so a file rejected server-side can't get queued in the first place. Files over the effective cap produce a dialog before they're sent to Dataverse.
Pass AllowedExtensions to restrict uploads to a whitelist of extensions. Rejected files produce a dialog explaining which extensions are accepted. When left unset, any extension is allowed (subject to the environment's blocklist below).
Dataverse environments maintain a blockedattachments list that rejects certain extensions (SVG, EXE, BAT, JS, etc.) regardless of caller. The FileGrid reads this list at init via the IPowerPortalsProService.GetEnvironmentFileSettingsAsync() call, caches it for an hour, and rejects matching files client-side with a friendly message instead of a server-side fault. The environment blocklist always wins over AllowedExtensions — a column's allow-list can't override an environment-level block.
Can't upload SVGs?
Dataverse environments block svg by default because SVGs can carry embedded scripts. If you need SVG uploads, remove it from the environment's Set blocked file extensions for attachments setting in the Power Platform admin center — this grid will pick the change up within an hour.
Permission Gating
The Upload button and the drop-zone activation both honour whether the current portal user has Create permission on the target table. When the user lacks create, the Upload button is hidden and dragging files over the grid is a no-op — server-side enforcement still runs as a backstop. Permission is resolved via ITablePermissionCache so custom ITableRecordPermissionHandler implementations feed into the same gating logic. The Upload button also auto-hides whenever one or more rows are selected, to keep the toolbar tidy.
Per-Row Preview & Download
The file column renders as [type-icon] filename [eye] [download]. The Preview icon opens the FilePreviewDialog — see Preview Rendering below for what each file type looks like. The Download icon streams the file through the FileDownload JSInterop component. Either icon can be toggled off per-grid via AllowPreviewForFileColumns / AllowDownloadForFileColumns.
<!-- Both icons default to true. Turn either off per-grid. -->
<FileGrid TableName="ppp_file"
FileColumn="ppp_data"
AllowPreviewForFileColumns="true"
AllowDownloadForFileColumns="true" />
The Preview icon only appears for file types the dialog can render — unsupported extensions skip the icon rather than surface a broken affordance. Both action icons are hidden on pending-create rows (records that haven't been saved to Dataverse yet).
Preview Rendering
The FilePreviewDialog picks a renderer based on the file's extension:
Images (png, jpg, jpeg, gif, bmp, webp, svg) — inline <img> with a base64 data URL. Constrained to 70 vh so very tall images stay scrollable within the dialog.
PDFs — loaded as a blob URL into an <iframe> so the browser's built-in viewer handles multi-page scrolling, zoom, search, and text selection. The blob URL is revoked on dialog close.
Markdown (md, markdown) — converted to HTML server-side via Markdig with its advanced extensions (tables, task lists, fenced code, autolinks, footnotes) and shown in a Preview tab. A second Text tab shows the raw source with syntax highlighting. Raw HTML passthrough in the source is stripped before rendering, so a .md can't smuggle in <script> or an <iframe>.
Source & structured text (json, xml, yaml, yml, html, htm, js, ts, cs, css) — rendered in a <pre> with highlight.js colorization. The language is chosen from the extension via FileTypeIcons.GetSyntaxLanguage; only the advertised languages are bundled to keep the asset size small. Highlighting runs through a colocated FilePreviewDialog.razor.ts module over JS interop — if the interop call fails the dialog silently falls back to uncolored text.
Plain text (txt, log, csv) — rendered in a <pre> with no highlighting. highlightAuto is intentionally not used because its guesses on short snippets (a tiny log or a single CSV row) produce more confusing color than no color at all.
Zip archives (zip) — parsed in-memory via System.IO.Compression.ZipArchive and rendered as a split pane: a collapsible tree on the left (folders first, alphabetical, with per-entry size and last-modified surfaced on hover), and the selected entry's preview on the right. The right pane is a nested FilePreviewContent, so inner files route through the same renderer lookup — images, PDFs, highlighted source, markdown, and even zip-within-zip all preview the same way they would standalone. The archive is never extracted to disk.
Download All (Zip)
The toolbar's Download All button streams every file currently rendered in the grid as a single zip, built in-memory server-side via ZipArchive and piped through the Blazor circuit — no extra HTTP endpoint required. Duplicate filenames get a (2), (3) suffix. Set AllowDownloadAll="false" to hide the button without disabling the per-row download icons.
By default each uploaded file creates a minimal record with only the file column populated. For tables with additional required columns — or to seed other data — pass OnUploadRecordAsync and return a fully-populated TableRecord. The delegate is async so you can consult services, resolve lookups, or generate names before returning.
<FileGrid TableName="ppp_file"
FileColumn="ppp_data"
OnUploadRecordAsync="BuildFileRecord" />
@code {
private async Task<TableRecord> BuildFileRecord(string fileName, byte[] data)
{
var record = new TableRecord { TableName = "ppp_file" };
record["ppp_filename"] = new StringValue { Value = fileName };
record["ppp_data"] = new FileValue
{
FileName = fileName,
Id = Guid.Empty,
Value = data,
};
return record;
}
}
MainContext / RecordContext Integration
When the grid is nested inside a MainContext or RecordContext, uploads are staged into the grid's pending-create list instead of being created immediately. The parent context's IsDirty flips to true, and on SaveAsync the staged records (with their FileValue bytes) are batched through ExecuteMultipleAsync — which handles the post-create chunked file upload for each record. This keeps the file uploads transactional with the rest of the form.
Pending-create rows appear in the grid immediately with the file type icon and filename, but without the Preview or Download icons (there's no server file yet). They're removed from the pending list when the context saves, or reset via RefreshContextButton / MainContext.ResetState().
Example
Manages file uploads on the ppp_file demo table (the file itself lives on the ppp_data column). The grid is wrapped in a MainContext with Save and Refresh buttons — dropped files queue in memory rather than hitting Dataverse, and the demo's OnBeforeSave cancels the real save and mock-resets state. Try dragging a PDF or image onto the grid, selecting multiple rows and clicking Download All (notice the zip), and clicking the Preview icon on a single row. You must be logged in to see the Upload button — files you don't own are visible but read-only.
Example
You must be logged in to upload, edit, or delete files. Records you don't own are visible but read-only.
SaveRefresh
Are you sure you'd like to delete the 0 record/s?
Active Files
Download All
Active Files
Download All
Page size
102050100
Filename
Created On
Data
chart.png
4/22/2026 1:02 AM
chart.png
PowerPortalsProDemo_1_0_0_1 (1)
4/22/2026 1:14 AM
PowerPortalsProDemo_1_0_0_1 (1).zip
Sample — API Schema
4/22/2026 2:27 PM
api-schema.xml
Sample — App Configuration
4/22/2026 2:27 PM
app-config.json
Sample — Brand Blue
4/22/2026 2:27 PM
brand-blue.png
Sample — Brand Green
4/22/2026 2:27 PM
brand-green.png
Sample — Brand Red
4/22/2026 2:27 PM
brand-red.png
Sample — Client Script
4/22/2026 2:27 PM
app.js
Sample — Client Types
4/22/2026 2:27 PM
app.ts
Sample — Deploy Log
4/22/2026 2:27 PM
deploy.log
Sample — Environment Settings
4/22/2026 2:27 PM
settings.yaml
Sample — Landing Page
4/22/2026 2:27 PM
index.html
Sample — Meeting Notes
4/22/2026 2:27 PM
meeting-notes.md
Sample — Program Entry
4/22/2026 2:27 PM
Program.cs
Sample — Project Archive
4/22/2026 2:27 PM
archive.zip
Sample — Project Brief
4/22/2026 2:27 PM
project-brief.txt
Sample — Quarterly Revenue
4/22/2026 2:27 PM
revenue-q1.csv
Sample — Release Checklist
4/22/2026 2:27 PM
release-checklist.txt
Sample — Stylesheet
4/22/2026 2:27 PM
styles.css
supo2.png
4/22/2026 1:02 AM
supo2.png
tooltip.jpg
4/22/2026 1:02 AM
tooltip.jpg
FileGrid Class
Parameters
Name
Type
Default
Description
AllowChangingItemsPerPage
bool
True
Whether the user can change the page size via the paging dropdown. Forwarded to FluentGridBase.AllowChangingItemsPerPage.
AllowDownloadAll
bool
True
Whether the toolbar's 'Download All' button is rendered. Defaults to true. Set to false on read-only or display-oriented grids where bulk export isn't appropriate — the per-row download icon still works when FileGrid.AllowDownloadForFileColumns is enabled.
AllowDownloadForFileColumns
bool
True
Whether file and image column cells render a per-row download icon at the trailing edge. Defaults to true. Forwarded to FluentGridBase.AllowDownloadForFileColumns.
AllowedExtensions
IReadOnlyCollection<string>?
Optional allow-list of extensions (with or without the leading dot, case-insensitive). When set, files whose extension isn't in the list are rejected before upload. When null, any extension is accepted.
AllowPreviewForFileColumns
bool
True
Whether file and image column cells render a per-row preview icon at the trailing edge. Defaults to true. Forwarded to FluentGridBase.AllowPreviewForFileColumns.
AllowSearch
bool
True
Whether to render the grid search box. Forwarded to GridBase.AllowSearch.
AutoAddFileColumnIfMissingFromView
bool
True
Whether the grid auto-adds FileGrid.FileColumn to the active view when the view doesn't already include it. Defaults to true so file data is visible no matter which view the user picks. Set to false to let the view be the sole source of truth for rendered columns — useful when a view deliberately shows only metadata (name, uploaded-on, etc.) without the file cell.
BorderVisible
bool
True
Whether the grid draws its themed border. Forwarded to FluentGridBase.BorderVisible.
Buttons
RenderFragment?
Optional caller-supplied grid buttons (rendered alongside the built-in file buttons).
CustomViewDefinitions
List<GridViewDefinition>?
Custom view definitions (inline FetchXML), forwarded to GridBase.CustomViewDefinitions.
DefaultItemsPerPage
int
50
Default page size. Forwarded to GridBase.DefaultItemsPerPage.
DefaultViewId
Guid?
Optional default view ID, forwarded to GridBase.DefaultViewId.
FileColumn
string
Logical name of the file column on FileGrid.TableName this grid manages. Required because a record may expose several file columns; the grid needs to know which one upload/download/preview actions target.
FullSize
bool
False
Whether the grid takes up its container's full height. Forwarded to FluentGridBase.FullSize.
HidePaging
bool
False
Hide the paging controls entirely. Forwarded to GridBase.HidePaging.
MaxFileSizeBytes
long
134217728
Upper bound on a single upload, in bytes. Files larger than this are rejected client-side with a dialog. Defaults to 128MB — Dataverse's documented ceiling for file columns.
MaxHeight
string?
Max height the grid body should expand to. Forwarded to GridBase.MaxHeight.
MinHeight
string?
Minimum height the grid body should occupy. Forwarded to GridBase.MinHeight.
OnBeforeQuery
Func<FetchXMLBuilder, Task<FetchXMLBuilder>>?
Optional hook to mutate the FetchXML query before it runs. Forwarded to GridBase.OnBeforeQuery.
OnUploadRecordAsync
Func<string, byte[], Task<TableRecord>>?
Customises the record created for each uploaded file. When set, the grid awaits the delegate with the file name and bytes and uses the returned Models.TableRecord as the record to create (which must have the file column populated). When null, the grid creates a minimal record with only the file column populated. Async so callers can fetch defaults, resolve lookups, or hit other services before returning the record.
PageSizes
IEnumerable<int>
Page size options shown in the grid's page-size dropdown. Forwarded to GridBase.PageSizes.
PagingMode
GridPagingMode
Paged
Paging mode. Forwarded to FluentGridBase.PagingMode.
SelectedRecords
IEnumerable<TableRecord>
Records currently selected in the grid. Two-way bindable with FileGrid.SelectedRecordsChanged.
SelectedViewQueryParameter
string?
URL query parameter name that persists the selected view ID. Forwarded to GridBase.SelectedViewQueryParameter.
SelectFromEntireRow
bool
True
Whether clicking anywhere in a row selects it. Forwarded to FluentGridBase.SelectFromEntireRow.
SelectMode
DataGridSelectMode
Multiple
Row selection mode. Defaults to multi-select so bulk download works out of the box. Forwarded to FluentGridBase.SelectMode.
TableName
string
Table logical name shown in the grid. Forwarded to the wrapped MainGrid.
Optional hook to transform the active view. Applied after the built-in file-column injection, so callers can layer additional view tweaks on top without losing the file column guarantee. Async so callers can consult services while deciding how to shape the view.
ViewIds
IEnumerable<Guid>?
Optional list of view IDs, forwarded to GridBase.ViewIds.
ViewSort
ViewSort
NameAscending
Sort order for the view dropdown. Forwarded to GridBase.ViewSort.
Name:AllowChangingItemsPerPage
Type:bool
Default:True
Description:Whether the user can change the page size via the paging dropdown. Forwarded to FluentGridBase.AllowChangingItemsPerPage.
Name:AllowDownloadAll
Type:bool
Default:True
Description:Whether the toolbar's 'Download All' button is rendered. Defaults to true. Set to false on read-only or display-oriented grids where bulk export isn't appropriate — the per-row download icon still works when FileGrid.AllowDownloadForFileColumns is enabled.
Name:AllowDownloadForFileColumns
Type:bool
Default:True
Description:Whether file and image column cells render a per-row download icon at the trailing edge. Defaults to true. Forwarded to FluentGridBase.AllowDownloadForFileColumns.
Name:AllowedExtensions
Type:IReadOnlyCollection<string>?
Description:Optional allow-list of extensions (with or without the leading dot, case-insensitive). When set, files whose extension isn't in the list are rejected before upload. When null, any extension is accepted.
Name:AllowPreviewForFileColumns
Type:bool
Default:True
Description:Whether file and image column cells render a per-row preview icon at the trailing edge. Defaults to true. Forwarded to FluentGridBase.AllowPreviewForFileColumns.
Name:AllowSearch
Type:bool
Default:True
Description:Whether to render the grid search box. Forwarded to GridBase.AllowSearch.
Name:AutoAddFileColumnIfMissingFromView
Type:bool
Default:True
Description:Whether the grid auto-adds FileGrid.FileColumn to the active view when the view doesn't already include it. Defaults to true so file data is visible no matter which view the user picks. Set to false to let the view be the sole source of truth for rendered columns — useful when a view deliberately shows only metadata (name, uploaded-on, etc.) without the file cell.
Name:BorderVisible
Type:bool
Default:True
Description:Whether the grid draws its themed border. Forwarded to FluentGridBase.BorderVisible.
Name:Buttons
Type:RenderFragment?
Description:Optional caller-supplied grid buttons (rendered alongside the built-in file buttons).
Name:CustomViewDefinitions
Type:List<GridViewDefinition>?
Description:Custom view definitions (inline FetchXML), forwarded to GridBase.CustomViewDefinitions.
Name:DefaultItemsPerPage
Type:int
Default:50
Description:Default page size. Forwarded to GridBase.DefaultItemsPerPage.
Name:DefaultViewId
Type:Guid?
Description:Optional default view ID, forwarded to GridBase.DefaultViewId.
Name:FileColumn
Type:string
Description:Logical name of the file column on FileGrid.TableName this grid manages. Required because a record may expose several file columns; the grid needs to know which one upload/download/preview actions target.
Name:FullSize
Type:bool
Default:False
Description:Whether the grid takes up its container's full height. Forwarded to FluentGridBase.FullSize.
Name:HidePaging
Type:bool
Default:False
Description:Hide the paging controls entirely. Forwarded to GridBase.HidePaging.
Name:MaxFileSizeBytes
Type:long
Default:134217728
Description:Upper bound on a single upload, in bytes. Files larger than this are rejected client-side with a dialog. Defaults to 128MB — Dataverse's documented ceiling for file columns.
Name:MaxHeight
Type:string?
Description:Max height the grid body should expand to. Forwarded to GridBase.MaxHeight.
Name:MinHeight
Type:string?
Description:Minimum height the grid body should occupy. Forwarded to GridBase.MinHeight.
Description:Optional hook to mutate the FetchXML query before it runs. Forwarded to GridBase.OnBeforeQuery.
Name:OnUploadRecordAsync
Type:Func<string, byte[], Task<TableRecord>>?
Description:Customises the record created for each uploaded file. When set, the grid awaits the delegate with the file name and bytes and uses the returned Models.TableRecord as the record to create (which must have the file column populated). When null, the grid creates a minimal record with only the file column populated. Async so callers can fetch defaults, resolve lookups, or hit other services before returning the record.
Name:PageSizes
Type:IEnumerable<int>
Description:Page size options shown in the grid's page-size dropdown. Forwarded to GridBase.PageSizes.
Name:PagingMode
Type:GridPagingMode
Default:Paged
Description:Paging mode. Forwarded to FluentGridBase.PagingMode.
Name:SelectedRecords
Type:IEnumerable<TableRecord>
Description:Records currently selected in the grid. Two-way bindable with FileGrid.SelectedRecordsChanged.
Name:SelectedViewQueryParameter
Type:string?
Description:URL query parameter name that persists the selected view ID. Forwarded to GridBase.SelectedViewQueryParameter.
Name:SelectFromEntireRow
Type:bool
Default:True
Description:Whether clicking anywhere in a row selects it. Forwarded to FluentGridBase.SelectFromEntireRow.
Name:SelectMode
Type:DataGridSelectMode
Default:Multiple
Description:Row selection mode. Defaults to multi-select so bulk download works out of the box. Forwarded to FluentGridBase.SelectMode.
Name:TableName
Type:string
Description:Table logical name shown in the grid. Forwarded to the wrapped MainGrid.
Name:Title
Type:string?
Description:Optional header text forwarded to the grid.
Description:Optional hook to transform the active view. Applied after the built-in file-column injection, so callers can layer additional view tweaks on top without losing the file column guarantee. Async so callers can consult services while deciding how to shape the view.
Name:ViewIds
Type:IEnumerable<Guid>?
Description:Optional list of view IDs, forwarded to GridBase.ViewIds.
Name:ViewSort
Type:ViewSort
Default:NameAscending
Description:Sort order for the view dropdown. Forwarded to GridBase.ViewSort.
Events
Name
Type
Description
SelectedRecordsChanged
EventCallback<IEnumerable<TableRecord>>
Fires when the selected record set changes.
Name:SelectedRecordsChanged
Type:EventCallback<IEnumerable<TableRecord>>
Description:Fires when the selected record set changes.
Paged Virtualize
Single SingleSticky Multiple
Default NameAscending NameDescending
Apply begins with filter on the following columns:
Filename
Apply begins with filter on the following columns: