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:
Loretta 2025-12-18 10:03:32 +01:00
parent fe1a59a0bd
commit 112d633590
6 changed files with 177 additions and 58 deletions

View File

@ -99,6 +99,12 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
[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;
/// <summary>
@ -222,10 +228,13 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
panesBuilder.AddAttribute(paneSeq++, "ChildContent", (RenderFragment)(infoPanelBuilder =>
{
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 =>
{
InfoPanelInstance = (MgGridInfoPanel)instance;
InfoPanelInstance = instance as IInfoPanelBase;
});
infoPanelBuilder.CloseComponent();
}));

View File

@ -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;
}

View File

@ -4,6 +4,12 @@
<div @ref="_panelElement" class="mg-grid-info-panel @(_isEditMode ? "edit-mode" : "view-mode")">
@* Header *@
@if (HeaderTemplate != null)
{
@HeaderTemplate
}
else
{
<div class="dxbl-grid-header-panel px-3 py-2 border-bottom">
<span class="fw-semibold">@((_currentGrid as IMgGridBase)?.Caption ?? "")</span>
</div>
@ -15,6 +21,7 @@
<MgGridToolbarTemplate Grid="_currentGrid as IMgGridBase" OnlyGridEditTools="true" ShowOnlyIcon="true" />
</div>
}
}
@* Content *@
<div class="mg-info-panel-content">
@ -23,6 +30,12 @@
var dataItem = GetActiveDataItem()!;
var dataItemType = dataItem.GetType();
@if (ColumnsTemplate != null)
{
@ColumnsTemplate
}
else
{
<div class="mg-info-panel-grid">
@foreach (var column in GetVisibleColumns())
{
@ -51,6 +64,7 @@
}
</div>
}
}
else
{
<div class="text-center text-muted py-5">
@ -58,6 +72,12 @@
</div>
}
</div>
@* Footer *@
@if (FooterTemplate != null)
{
@FooterTemplate
}
</div>
@code {

View File

@ -26,6 +26,21 @@ public partial class MgGridInfoPanel : ComponentBase, IAsyncDisposable, IInfoPan
/// </summary>
[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 bool _isJsInitialized;
private const int DefaultTopOffset = 300; // Increased from 180 to account for header + tabs + toolbar

View File

@ -14,7 +14,7 @@
@if (!OnlyGridEditTools)
{
<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>
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "To CSV")" Click="ExportCsvItem_Click" IconCssClass="grid-export-xlsx" />
<DxToolbarItem Text="@(ShowOnlyIcon ? "" : "To XLSX")" Click="ExportXlsxItem_Click" IconCssClass="grid-export-xlsx" />

View File

@ -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();
}
}