133 lines
11 KiB
Markdown
133 lines
11 KiB
Markdown
# MgGrid — Parameters & Events
|
|
|
|
> Part of the MgGrid system. See `README.md` for overview and component hierarchy.
|
|
|
|
## Required Parameters
|
|
|
|
| Parameter | Type | Description |
|
|
|---|---|---|
|
|
| `SignalRClient` | `AcSignalRClientBase` | SignalR client for server communication |
|
|
| `Logger` | `TLoggerClient` | Logger instance |
|
|
| `GetAllMessageTag` | `int` | SignalR tag for loading all items |
|
|
|
|
## CRUD Message Tags
|
|
|
|
| Parameter | Type | Description |
|
|
|---|---|---|
|
|
| `GetAllMessageTag` | `int` | Tag for "get all items" request |
|
|
| `GetItemMessageTag` | `int` | Tag for "get single item" request |
|
|
| `AddMessageTag` | `int` | Tag for "add item" request |
|
|
| `UpdateMessageTag` | `int` | Tag for "update item" request |
|
|
| `RemoveMessageTag` | `int` | Tag for "remove item" request |
|
|
|
|
These are bundled into a `SignalRCrudTags` during `OnInitializedAsync`. See `AyCode.Services/docs/SIGNALR_DATASOURCE/README.md` (in AyCode.Core repo) for details.
|
|
|
|
## Data & Context Parameters
|
|
|
|
| Parameter | Type | Description |
|
|
|---|---|---|
|
|
| `DataSource` | `IList<TDataItem>` | Bind with `AcObservableCollection<TDataItem>` for external data. If not set, grid creates its own. |
|
|
| `ParentDataItem` | `IId<TId>?` | Parent entity for detail grids. `null` = master grid. |
|
|
| `KeyFieldNameToParentId` | `string?` | Property name on `TDataItem` that references the parent's ID |
|
|
| `ContextIds` | `object[]?` | Additional context passed to `AcSignalRDataSource` constructor |
|
|
| `CustomFilterCriteria` | `string?` | Consumer-supplied **base** filter criteria (a DevExpress criteria string) — propagated to the data source, triggers reload; server parses + whitelists it. AND-merged with the grid's automatic `FilterCriteria` (ADR-0004 unified model). Renamed `FilterText` → `CustomFilterText` → `CustomFilterCriteria` (2026-07-16). |
|
|
|
|
## Display & Behavior Parameters
|
|
|
|
| Parameter | Type | Default | Description |
|
|
|---|---|---|---|
|
|
| `Caption` | `string` | `typeof(TDataItem).Name` | Grid title (shown in fullscreen header) |
|
|
| `GridName` | `string` | `"{TDataItem.Name}Grid"` | Name used in log messages |
|
|
| `AutoSaveLayoutName` | `string?` | `"Grid{TDataItem.Name}"` | Base name for layout storage keys |
|
|
| `EnableServerLayouts` | `bool` | `true` | Roams the UserSave layout tier via the SignalR server endpoint (see `MGGRID_LAYOUT.md` → Server Roaming). Server calls happen only on the Save/Load layout buttons. |
|
|
|
|
## Filter Panel
|
|
|
|
| Parameter | Type | Default | Description |
|
|
|---|---|---|---|
|
|
| `FilterPanel` | `RenderFragment?` | `null` | Filter controls rendered below the toolbar (composed into the grid's `ToolbarTemplate`); the slot wraps them in a styled `.mg-grid-filter-panel` container with a built-in right-aligned **Clear** button. When set, the toolbar shows a Show/Hide Filters toggle (`MGGRID_TOOLBAR.md`). |
|
|
| `ShowFilterPanel` | `bool` | `true` | Initial visibility of the filter panel; the toolbar toggle flips it at runtime (`IsFilterPanelVisible` / `ToggleFilterPanel`). |
|
|
| `FilterPanelClearButtonText` | `string` | `"Clear Filters"` | Text of the panel's built-in Clear button (`IMgGridBase.ClearFilterPanel` — clears the panel's own filters; the filter row is untouched). Enabled only while a panel-owned criteria is active (`HasActiveFilterPanelCriteria`) — enabled + empty-looking controls = an invisible panel filter, surfaced on purpose. |
|
|
|
|
Place `MgGridFilterPanelTagBox` components (from `Components/Grids/`) in the slot. Each applies a per-field `IN(FieldName, selected...)` criteria via `IMgGridBase.SetFilterPanelCriteria` → DevExpress `SetFieldFilterCriteria`; DevExpress AND-combines the per-field criteria with each other and with the user's filter row automatically (no clobber). Empty selection clears that field's criteria. Several components in one panel therefore narrow the grid cumulatively (field A **and** field B).
|
|
|
|
> **Seam split:** `MgTagBox` itself is the pure, behavior-free `DxTagBox` seam (universal — usable anywhere). All grid wiring lives in the derived `MgGridFilterPanelTagBox : MgTagBox, IMgGridFilterPanelComponent`. Future filter components (dropdown, textbox, checkbox…) follow the same pattern: own Mg* seam + `IMgGridFilterPanelComponent`; when the second one lands, the shared glue (registration + criteria adapter) is to be extracted into a helper.
|
|
|
|
**Ownership (shared per-field slot).** DevExpress keeps ONE criteria slot per field — the filter row and the panel both write it (`SetFieldFilterCriteria`). The panel therefore only manages criteria its components recognize as their **own shape** (`IMgGridFilterPanelComponent.OwnsCriteria` — for the tag box: the `IN(FieldName, ...)` it writes). A value typed into the column's filter row (e.g. `Contains("EUR")`) is never cleared by the panel — not on hide, not by the Clear button; the tag box simply shows no selection for it (it is visible in the filter row, so it is not invisible filtering).
|
|
|
|
```razor
|
|
<FilterPanel>
|
|
<MgGridFilterPanelTagBox TData="string" TValue="string" FieldName="Status"
|
|
Data="@_statusOptions" NullText="All statuses" />
|
|
</FilterPanel>
|
|
```
|
|
|
|
**Lookup (foreign-key) columns.** When the target column holds an ID (e.g. `PartnerId`) but users pick by name, bind the tag box to the lookup objects and let `ValueFieldName` supply the IDs. `FieldName` still names the *grid* column, so the emitted `IN(PartnerId, 3, 7)` matches the underlying data field — not the cell's display template:
|
|
|
|
```razor
|
|
<FilterPanel>
|
|
<MgGridFilterPanelTagBox TData="Partner" TValue="int" FieldName="PartnerId"
|
|
Data="@Partners" ValueFieldName="Id" TextFieldName="Name"
|
|
NullText="All partners" />
|
|
</FilterPanel>
|
|
```
|
|
|
|
A nullable FK column (`int?`) behaves the same — rows whose value is `null` match no selection.
|
|
|
|
**Sizing.** `MgGridFilterPanelTagBox` defaults its `SizeMode` to the grid's (`IMgGridBase.GridSizeMode`) when the consumer leaves it unset, so the panel matches the grid's density with no per-component declaration. An explicit `SizeMode` always wins.
|
|
|
|
**Layout.** The panel is a flex row: controls sit side by side sharing the width and wrap to the next line only when they no longer fit (`.mg-grid-filter-panel > *` — `flex` / `min-width` / `max-width` in `mg-grid-info-panel.css`; tune there for a different density). It is styled to read as its **own band** between the toolbar and the group panel, aligned with the native group panel via the grid's own SizeMode-adaptive tokens (`--dxbl-grid-group-panel-container-padding-*`). Since it is composed into the `ToolbarTemplate` it physically lives inside `.dxbl-grid-toolbar-container`; that container's padding is zeroed (`:has(.mg-grid-toolbar-with-filter)`) and each row supplies its own: the toolbar row (`.mg-grid-toolbar-row`) reuses the container's native padding tokens, the band reuses the group panel's. Separator above = the band's `border-top`; below = the container's native `border-bottom`. A consumer `.hide-toolbar` class (detail grids) therefore hides the panel together with the toolbar.
|
|
|
|
**Persistence & visibility.** The panel's criteria is part of the grid's persisted layout (`GridPersistentLayout.FilterCriteria` — the AutoSave tier and the server-roamed UserSave tier alike; see `MGGRID_LAYOUT.md`). Components register with the grid (`IMgGridFilterPanelComponent`, weakly held; `MgGridFilterPanelTagBox` does this automatically) and the grid **reconciles** them after layout restore (incl. on another machine), user-layout load, layout reset, and panel toggle:
|
|
|
|
- panel **visible** → each component re-reads its field's criteria and re-selects (a restored panel filter is visible again); the restore (JS interop) and an async lookup `Data` arrive in arbitrary order — the tag box re-asserts its selection idempotently on every parameter pass while an owned criteria is active, tracking both the `Data` **instance and its item count** (an in-place fill — `AcObservableCollection.Replace` on a pre-created empty collection — is detected too), so tags resolve as soon as both exist; consumers can additionally force a re-run via `IMgGridBase.RefreshFilterPanel()` after a late lookup load;
|
|
- panel **hidden** (toolbar toggle or `ShowFilterPanel="false"`) → the components' **own** criteria are **cleared** — a panel filter never filters invisibly. Hiding the panel therefore deliberately discards the current selection (filter-row values survive, per Ownership above);
|
|
- a per-field criteria the component does not own is left untouched — the component just shows no selection.
|
|
|
|
The panel content is always rendered (CSS-hidden when toggled off) so the components stay registered. Known edge — a layout with panel criteria loaded where the grid has no `FilterPanel` at all: `MGGRID_ISSUES.md` → `ACBLAZOR-GRID-I-D4T7`.
|
|
|
|
Implementation note: imperative parameter writes inside DevExpress editors (`Values`, `SizeMode`) must be wrapped in `BeginUpdate()`/`EndUpdate()` (the parameter tracker throws otherwise) — `MgGridFilterPanelTagBox` handles this internally.
|
|
|
|
Design + the server-side forward path (`FilterMode.Server`): AyCode.Core repo `docs/adr/0004-signalr-datasource-server-side-filtering.md`.
|
|
|
|
## Event Callbacks
|
|
|
|
All grid events are re-exposed with `OnGrid` prefix to avoid collisions with `DxGrid` base events:
|
|
|
|
| Event | DxGrid Equivalent | When Fired |
|
|
|---|---|---|
|
|
| `OnGridItemDeleting` | `DataItemDeleting` | Before item removal (can cancel via `e.Cancel`) |
|
|
| `OnGridEditModelSaving` | `EditModelSaving` | Before item save (can cancel via `e.Cancel`) |
|
|
| `OnGridEditStart` | `EditStart` | When edit mode begins |
|
|
| `OnGridCustomizeEditModel` | `CustomizeEditModel` | When edit model is being prepared |
|
|
| `OnGridFocusedRowChanged` | `FocusedRowChanged` | When focused row changes |
|
|
| `OnDataSourceChanged` | — | After data source is loaded/reloaded |
|
|
| `OnGridItemChanged` | — | After server confirms a CRUD operation |
|
|
| `OnGridItemChanging` | — | Before a CRUD operation is sent to server |
|
|
|
|
Internal event wiring (in `SetParametersAsyncCore`, first call):
|
|
|
|
| DxGrid Event | → Internal Handler |
|
|
|---|---|
|
|
| `DataItemDeleting` | `OnItemDeleting` |
|
|
| `EditModelSaving` | `OnItemSaving` |
|
|
| `CustomizeEditModel` | `OnCustomizeEditModel` |
|
|
| `FocusedRowChanged` | `OnFocusedRowChanged` |
|
|
| `EditStart` | `OnEditStart` |
|
|
| `EditCanceling` | `OnEditCanceling` |
|
|
|
|
## Default Grid Settings
|
|
|
|
Set in `SetParametersAsyncCore` (first call only):
|
|
|
|
| Setting | Value |
|
|
|---|---|
|
|
| `KeyFieldName` | `"Id"` |
|
|
| `TextWrapEnabled` | `false` |
|
|
| `AllowSelectRowByClick` | `true` |
|
|
| `HighlightRowOnHover` | `true` |
|
|
| `AutoCollapseDetailRow` | `true` |
|
|
| `AutoExpandAllGroupRows` | `false` |
|
|
|
|
Project adapters typically add more defaults in `OnParametersSet` (e.g., `EditMode`, `FocusedRowEnabled`, `PageSize`, `ShowFilterRow`, `SizeMode` based on `IsMasterGrid`).
|