Add extensible InfoPanel system with custom templates
Introduce a flexible InfoPanel architecture for grid components, allowing per-grid customization via a new InfoPanelType parameter. Add MgInfoPanelTemplateBase for code-based InfoPanel templates with overridable header, content, and footer sections. Support custom templates in MgGridInfoPanel via new RenderFragment parameters. Add MgGridDataColumn for InfoPanel-specific column settings. Demonstrate usage with a custom InfoPanel for ShippingDocument grid. Maintains backward compatibility with default InfoPanel behavior.
This commit is contained in:
parent
fe1a59a0bd
commit
112d633590
|
|
@ -99,6 +99,12 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
|
||||||
|
|
||||||
[Parameter] public bool ShowInfoPanel { get; set; } = false;
|
[Parameter] public bool ShowInfoPanel { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom InfoPanel component type. Must inherit from MgInfoPanelTemplateBase.
|
||||||
|
/// If not set, the default MgGridInfoPanel is used.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter] public Type? InfoPanelType { get; set; }
|
||||||
|
|
||||||
private object _focusedDataItem;
|
private object _focusedDataItem;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -222,10 +228,13 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
|
||||||
panesBuilder.AddAttribute(paneSeq++, "ChildContent", (RenderFragment)(infoPanelBuilder =>
|
panesBuilder.AddAttribute(paneSeq++, "ChildContent", (RenderFragment)(infoPanelBuilder =>
|
||||||
{
|
{
|
||||||
var infoPanelSeq = 0;
|
var infoPanelSeq = 0;
|
||||||
infoPanelBuilder.OpenComponent<MgGridInfoPanel>(infoPanelSeq++);
|
|
||||||
|
// Use custom InfoPanel type if provided, otherwise use default MgGridInfoPanel
|
||||||
|
var panelType = InfoPanelType ?? typeof(MgGridInfoPanel);
|
||||||
|
infoPanelBuilder.OpenComponent(infoPanelSeq++, panelType);
|
||||||
infoPanelBuilder.AddComponentReferenceCapture(infoPanelSeq++, instance =>
|
infoPanelBuilder.AddComponentReferenceCapture(infoPanelSeq++, instance =>
|
||||||
{
|
{
|
||||||
InfoPanelInstance = (MgGridInfoPanel)instance;
|
InfoPanelInstance = instance as IInfoPanelBase;
|
||||||
});
|
});
|
||||||
infoPanelBuilder.CloseComponent();
|
infoPanelBuilder.CloseComponent();
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
using DevExpress.Blazor;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace AyCode.Blazor.Components.Components.Grids;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extended DxGridDataColumn with additional parameters for InfoPanel support.
|
||||||
|
/// </summary>
|
||||||
|
public class MgGridDataColumn : DxGridDataColumn
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this column should be visible in the InfoPanel. Default is true.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool ShowInInfoPanel { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom display format for InfoPanel (overrides DisplayFormat if set).
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string? InfoPanelDisplayFormat { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Column order in InfoPanel (lower = earlier). Default is int.MaxValue.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public int InfoPanelOrder { get; set; } = int.MaxValue;
|
||||||
|
}
|
||||||
|
|
@ -4,16 +4,23 @@
|
||||||
|
|
||||||
<div @ref="_panelElement" class="mg-grid-info-panel @(_isEditMode ? "edit-mode" : "view-mode")">
|
<div @ref="_panelElement" class="mg-grid-info-panel @(_isEditMode ? "edit-mode" : "view-mode")">
|
||||||
@* Header *@
|
@* Header *@
|
||||||
<div class="dxbl-grid-header-panel px-3 py-2 border-bottom">
|
@if (HeaderTemplate != null)
|
||||||
<span class="fw-semibold">@((_currentGrid as IMgGridBase)?.Caption ?? "")</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@* Toolbar *@
|
|
||||||
@if (_currentGrid != null)
|
|
||||||
{
|
{
|
||||||
<div class="mg-info-panel-toolbar border-bottom">
|
@HeaderTemplate
|
||||||
<MgGridToolbarTemplate Grid="_currentGrid as IMgGridBase" OnlyGridEditTools="true" ShowOnlyIcon="true" />
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="dxbl-grid-header-panel px-3 py-2 border-bottom">
|
||||||
|
<span class="fw-semibold">@((_currentGrid as IMgGridBase)?.Caption ?? "")</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@* Toolbar *@
|
||||||
|
@if (_currentGrid != null)
|
||||||
|
{
|
||||||
|
<div class="mg-info-panel-toolbar border-bottom">
|
||||||
|
<MgGridToolbarTemplate Grid="_currentGrid as IMgGridBase" OnlyGridEditTools="true" ShowOnlyIcon="true" />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@* Content *@
|
@* Content *@
|
||||||
|
|
@ -23,33 +30,40 @@
|
||||||
var dataItem = GetActiveDataItem()!;
|
var dataItem = GetActiveDataItem()!;
|
||||||
var dataItemType = dataItem.GetType();
|
var dataItemType = dataItem.GetType();
|
||||||
|
|
||||||
<div class="mg-info-panel-grid">
|
@if (ColumnsTemplate != null)
|
||||||
@foreach (var column in GetVisibleColumns())
|
{
|
||||||
{
|
@ColumnsTemplate
|
||||||
var displayText = GetDisplayTextFromGrid(column);
|
}
|
||||||
var value = GetCellValue(column);
|
else
|
||||||
var settingsType = GetEditSettingsType(column);
|
{
|
||||||
var isReadOnly = !_isEditMode || column.ReadOnly;
|
<div class="mg-info-panel-grid">
|
||||||
|
@foreach (var column in GetVisibleColumns())
|
||||||
|
{
|
||||||
|
var displayText = GetDisplayTextFromGrid(column);
|
||||||
|
var value = GetCellValue(column);
|
||||||
|
var settingsType = GetEditSettingsType(column);
|
||||||
|
var isReadOnly = !_isEditMode || column.ReadOnly;
|
||||||
|
|
||||||
<div class="mg-info-panel-item">
|
<div class="mg-info-panel-item">
|
||||||
<div class="dxbl-form-layout-item">
|
<div class="dxbl-form-layout-item">
|
||||||
<label class="dxbl-fl-lc @GetCaptionCssClass(isReadOnly) d-block mb-1 small">
|
<label class="dxbl-fl-lc @GetCaptionCssClass(isReadOnly) d-block mb-1 small">
|
||||||
@GetColumnCaption(column)
|
@GetColumnCaption(column)
|
||||||
</label>
|
</label>
|
||||||
<div class="dxbl-fl-ec">
|
<div class="dxbl-fl-ec">
|
||||||
@if (_isEditMode && !column.ReadOnly)
|
@if (_isEditMode && !column.ReadOnly)
|
||||||
{
|
{
|
||||||
@RenderEditableCell(column, dataItem, dataItemType, value, displayText, settingsType)
|
@RenderEditableCell(column, dataItem, dataItemType, value, displayText, settingsType)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@RenderCellContent(column, value, displayText, settingsType)
|
@RenderCellContent(column, value, displayText, settingsType)
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
}
|
</div>
|
||||||
</div>
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -58,6 +72,12 @@
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@* Footer *@
|
||||||
|
@if (FooterTemplate != null)
|
||||||
|
{
|
||||||
|
@FooterTemplate
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,21 @@ public partial class MgGridInfoPanel : ComponentBase, IAsyncDisposable, IInfoPan
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter] public bool ShowReadOnlyFieldsInEditMode { get; set; } = false;
|
[Parameter] public bool ShowReadOnlyFieldsInEditMode { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom header template. If not set, default header is used.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter] public RenderFragment? HeaderTemplate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom columns template. If not set, columns are auto-generated.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter] public RenderFragment? ColumnsTemplate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Custom footer template. If not set, no footer is rendered.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter] public RenderFragment? FooterTemplate { get; set; }
|
||||||
|
|
||||||
private ElementReference _panelElement;
|
private ElementReference _panelElement;
|
||||||
private bool _isJsInitialized;
|
private bool _isJsInitialized;
|
||||||
private const int DefaultTopOffset = 300; // Increased from 180 to account for header + tabs + toolbar
|
private const int DefaultTopOffset = 300; // Increased from 180 to account for header + tabs + toolbar
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,32 @@
|
||||||
@using AyCode.Blazor.Components.Components.Grids
|
@using AyCode.Blazor.Components.Components.Grids
|
||||||
|
|
||||||
<MgGridToolbarBase @ref="GridToolbar" Grid="Grid" ItemRenderStyleMode="ToolbarRenderStyleMode.Plain" ShowOnlyIcon="ShowOnlyIcon">
|
<MgGridToolbarBase @ref="GridToolbar" Grid="Grid" ItemRenderStyleMode="ToolbarRenderStyleMode.Plain" ShowOnlyIcon="ShowOnlyIcon">
|
||||||
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "New")" Click="NewItem_Click" IconCssClass="grid-new-row" Visible="@(!IsEditing)" Enabled="@(!IsSyncing)" />
|
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "New")" Click="NewItem_Click" IconCssClass="grid-new-row" Visible="@(!IsEditing)" Enabled="@(!IsSyncing)" />
|
||||||
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Edit")" Click="EditItem_Click" IconCssClass="grid-edit-row" Visible="@(!IsEditing)" Enabled="@(HasFocusedRow && !IsSyncing)" />
|
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Edit")" Click="EditItem_Click" IconCssClass="grid-edit-row" Visible="@(!IsEditing)" Enabled="@(HasFocusedRow && !IsSyncing)" />
|
||||||
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Delete")" Click="DeleteItem_Click" IconCssClass="grid-delete-row" Visible="@(!IsEditing)" Enabled="@(false && HasFocusedRow && !IsSyncing)" />
|
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Delete")" Click="DeleteItem_Click" IconCssClass="grid-delete-row" Visible="@(!IsEditing)" Enabled="@(false && HasFocusedRow && !IsSyncing)" />
|
||||||
|
|
||||||
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Save")" Click="SaveItem_Click" IconCssClass="grid-save" Visible="@IsEditing" RenderStyle="ButtonRenderStyle.Primary" />
|
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Save")" Click="SaveItem_Click" IconCssClass="grid-save" Visible="@IsEditing" RenderStyle="ButtonRenderStyle.Primary" />
|
||||||
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Cancel")" Click="CancelEdit_Click" IconCssClass="grid-cancel" Visible="@IsEditing" RenderStyle="ButtonRenderStyle.Secondary" />
|
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Cancel")" Click="CancelEdit_Click" IconCssClass="grid-cancel" Visible="@IsEditing" RenderStyle="ButtonRenderStyle.Secondary" />
|
||||||
|
|
||||||
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Prev Row")" BeginGroup="true" Click="PrevRow_Click" IconCssClass="grid-chevron-up" Enabled="@(HasFocusedRow && !IsSyncing && !IsEditing)" />
|
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Prev Row")" BeginGroup="true" Click="PrevRow_Click" IconCssClass="grid-chevron-up" Enabled="@(HasFocusedRow && !IsSyncing && !IsEditing)" />
|
||||||
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Next Row")" Click="NextRow_Click" IconCssClass="grid-chevron-down" Enabled="@(HasFocusedRow && !IsSyncing && !IsEditing)" />
|
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Next Row")" Click="NextRow_Click" IconCssClass="grid-chevron-down" Enabled="@(HasFocusedRow && !IsSyncing && !IsEditing)" />
|
||||||
|
|
||||||
@if (!OnlyGridEditTools)
|
@if (!OnlyGridEditTools)
|
||||||
{
|
{
|
||||||
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Column Chooser")" BeginGroup="true" Click="ColumnChooserItem_Click" IconCssClass="grid-column-chooser" />
|
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Column Chooser")" BeginGroup="true" Click="ColumnChooserItem_Click" IconCssClass="grid-column-chooser" />
|
||||||
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Export")" IconCssClass="grid-export" Enabled="@(false && HasFocusedRow && !IsEditing)">
|
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Export")" IconCssClass="grid-export" Visible="false" Enabled="@(HasFocusedRow && !IsEditing)">
|
||||||
<Items>
|
<Items>
|
||||||
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "To CSV")" Click="ExportCsvItem_Click" IconCssClass="grid-export-xlsx" />
|
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "To CSV")" Click="ExportCsvItem_Click" IconCssClass="grid-export-xlsx" />
|
||||||
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "To XLSX")" Click="ExportXlsxItem_Click" IconCssClass="grid-export-xlsx" />
|
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "To XLSX")" Click="ExportXlsxItem_Click" IconCssClass="grid-export-xlsx" />
|
||||||
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "To XLS")" Click="ExportXlsItem_Click" IconCssClass="grid-export-xlsx" />
|
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "To XLS")" Click="ExportXlsItem_Click" IconCssClass="grid-export-xlsx" />
|
||||||
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "To PDF")" Click="ExportPdfItem_Click" IconCssClass="grid-export-pdf" />
|
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "To PDF")" Click="ExportPdfItem_Click" IconCssClass="grid-export-pdf" />
|
||||||
</Items>
|
</Items>
|
||||||
</DxToolbarItem>
|
</DxToolbarItem>
|
||||||
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Reload data")" BeginGroup="true" Click="ReloadData_Click" IconCssClass="grid-refresh" Enabled="@(!IsSyncing && !_isReloadInProgress && !IsEditing)" />
|
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "Reload data")" BeginGroup="true" Click="ReloadData_Click" IconCssClass="grid-refresh" Enabled="@(!IsSyncing && !_isReloadInProgress && !IsEditing)" />
|
||||||
<DxToolbarItem BeginGroup="true"></DxToolbarItem>
|
<DxToolbarItem BeginGroup="true"></DxToolbarItem>
|
||||||
@ToolbarItemsExtended
|
@ToolbarItemsExtended
|
||||||
}
|
}
|
||||||
</MgGridToolbarBase>
|
</MgGridToolbarBase>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter] public bool OnlyGridEditTools { get; set; } = false;
|
[Parameter] public bool OnlyGridEditTools { get; set; } = false;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace AyCode.Blazor.Components.Components.Grids;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for custom InfoPanel templates.
|
||||||
|
/// Inherit from this class to create a custom InfoPanel for a specific grid.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class MgInfoPanelTemplateBase : MgGridInfoPanel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Override this to provide custom header content.
|
||||||
|
/// Return null to use default header.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual RenderFragment? GetHeaderTemplate() => null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override this to provide custom columns/content.
|
||||||
|
/// Return null to use auto-generated columns.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual RenderFragment? GetColumnsTemplate() => null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Override this to provide custom footer content.
|
||||||
|
/// Return null to hide footer.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual RenderFragment? GetFooterTemplate() => null;
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
SetTemplates();
|
||||||
|
base.OnInitialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
SetTemplates();
|
||||||
|
base.OnParametersSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetTemplates()
|
||||||
|
{
|
||||||
|
HeaderTemplate = GetHeaderTemplate();
|
||||||
|
ColumnsTemplate = GetColumnsTemplate();
|
||||||
|
FooterTemplate = GetFooterTemplate();
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue