Refactor InfoPanel: non-generic, nested grid support
- Replace generic InfoPanel with non-generic version using IInfoPanelBase - Add ParentGrid, GetRootGrid, and InfoPanelInstance to IMgGridBase for nested grid hierarchy - Only root grid displays InfoPanel; nested grids inherit context - InfoPanel now handles any data type via reflection and object - All grid-to-InfoPanel communication routed through root grid - Add option to show/hide readonly fields in edit mode - Improve InfoPanel CSS for up to 4 responsive columns - Remove redundant code and add debug output for InfoPanel data flow
This commit is contained in:
parent
109a4b82b4
commit
90f12a160e
|
|
@ -27,6 +27,16 @@ public interface IMgGridBase : IGrid
|
|||
/// </summary>
|
||||
MgGridEditState GridEditState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Parent grid in nested grid hierarchy (null if this is a root grid)
|
||||
/// </summary>
|
||||
IMgGridBase? ParentGrid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the root grid in the hierarchy
|
||||
/// </summary>
|
||||
IMgGridBase GetRootGrid();
|
||||
|
||||
/// <summary>
|
||||
/// Navigates to the previous row in the grid
|
||||
/// </summary>
|
||||
|
|
@ -36,6 +46,16 @@ public interface IMgGridBase : IGrid
|
|||
/// Navigates to the next row in the grid
|
||||
/// </summary>
|
||||
void StepNextRow();
|
||||
|
||||
/// <summary>
|
||||
/// Whether this grid shows an InfoPanel
|
||||
/// </summary>
|
||||
bool ShowInfoPanel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// InfoPanel instance for displaying row details
|
||||
/// </summary>
|
||||
IInfoPanelBase? InfoPanelInstance { get; set; }
|
||||
}
|
||||
|
||||
public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClient> : DxGrid, IMgGridBase, IAsyncDisposable
|
||||
|
|
@ -61,11 +81,30 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
|
|||
/// <inheritdoc />
|
||||
public MgGridEditState GridEditState { get; private set; } = MgGridEditState.None;
|
||||
|
||||
[Parameter] public bool ShowInfoPanel { get; set; } = true;
|
||||
/// <inheritdoc />
|
||||
[CascadingParameter]
|
||||
public IMgGridBase? ParentGrid { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IMgGridBase GetRootGrid()
|
||||
{
|
||||
var current = (IMgGridBase)this;
|
||||
while (current.ParentGrid != null)
|
||||
{
|
||||
current = current.ParentGrid;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
[Parameter] public bool ShowInfoPanel { get; set; } = false;
|
||||
|
||||
private object _focusedDataItem;
|
||||
private MgGridInfoPanel<TDataItem>? _infoPanelInstance;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// InfoPanel instance for displaying row details
|
||||
/// </summary>
|
||||
public IInfoPanelBase? InfoPanelInstance { get; set; }
|
||||
|
||||
public MgGridBase() : base()
|
||||
{
|
||||
}
|
||||
|
|
@ -137,54 +176,65 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
|
|||
|
||||
protected override void BuildRenderTree(RenderTreeBuilder builder)
|
||||
{
|
||||
if (!ShowInfoPanel)
|
||||
{
|
||||
base.BuildRenderTree(builder);
|
||||
return;
|
||||
}
|
||||
|
||||
var seq = 0;
|
||||
|
||||
builder.OpenComponent<DxSplitter>(seq++);
|
||||
builder.AddAttribute(seq++, "Width", "100%");
|
||||
builder.AddAttribute(seq++, "CssClass", "mg-grid-splitter");
|
||||
builder.AddAttribute(seq++, "Orientation", Orientation.Horizontal);
|
||||
|
||||
builder.AddAttribute(seq++, "Panes", (RenderFragment)(panesBuilder =>
|
||||
// Wrap everything in a CascadingValue to provide this grid as ParentGrid to nested grids
|
||||
builder.OpenComponent<CascadingValue<IMgGridBase>>(seq++);
|
||||
builder.AddAttribute(seq++, "Value", (IMgGridBase)this);
|
||||
builder.AddAttribute(seq++, "ChildContent", (RenderFragment)(contentBuilder =>
|
||||
{
|
||||
var paneSeq = 0;
|
||||
|
||||
// Left pane - Grid
|
||||
panesBuilder.OpenComponent<DxSplitterPane>(paneSeq++);
|
||||
panesBuilder.AddAttribute(paneSeq++, "ChildContent", (RenderFragment)(gridBuilder =>
|
||||
// Nested grids or root without InfoPanel: render base grid only
|
||||
if (ParentGrid != null || !ShowInfoPanel)
|
||||
{
|
||||
base.BuildRenderTree(gridBuilder);
|
||||
}));
|
||||
panesBuilder.CloseComponent();
|
||||
base.BuildRenderTree(contentBuilder);
|
||||
return;
|
||||
}
|
||||
|
||||
// Right pane - InfoPanel (sticky to viewport)
|
||||
panesBuilder.OpenComponent<DxSplitterPane>(paneSeq++);
|
||||
panesBuilder.AddAttribute(paneSeq++, "Size", "400px");
|
||||
panesBuilder.AddAttribute(paneSeq++, "MinSize", "0px");
|
||||
panesBuilder.AddAttribute(paneSeq++, "MaxSize", "100%");
|
||||
panesBuilder.AddAttribute(paneSeq++, "AllowCollapse", true);
|
||||
panesBuilder.AddAttribute(paneSeq++, "CssClass", "mg-info-panel-pane");
|
||||
// Root grid with InfoPanel enabled: render splitter with grid + InfoPanel
|
||||
var innerSeq = 0;
|
||||
|
||||
panesBuilder.AddAttribute(paneSeq++, "ChildContent", (RenderFragment)(infoPanelBuilder =>
|
||||
contentBuilder.OpenComponent<DxSplitter>(innerSeq++);
|
||||
contentBuilder.AddAttribute(innerSeq++, "Width", "100%");
|
||||
contentBuilder.AddAttribute(innerSeq++, "CssClass", "mg-grid-splitter");
|
||||
contentBuilder.AddAttribute(innerSeq++, "Orientation", Orientation.Horizontal);
|
||||
|
||||
contentBuilder.AddAttribute(innerSeq++, "Panes", (RenderFragment)(panesBuilder =>
|
||||
{
|
||||
var infoPanelSeq = 0;
|
||||
infoPanelBuilder.OpenComponent<MgGridInfoPanel<TDataItem>>(infoPanelSeq++);
|
||||
infoPanelBuilder.AddComponentReferenceCapture(infoPanelSeq++, instance =>
|
||||
var paneSeq = 0;
|
||||
|
||||
// Left pane - Grid
|
||||
panesBuilder.OpenComponent<DxSplitterPane>(paneSeq++);
|
||||
panesBuilder.AddAttribute(paneSeq++, "ChildContent", (RenderFragment)(gridBuilder =>
|
||||
{
|
||||
_infoPanelInstance = (MgGridInfoPanel<TDataItem>)instance;
|
||||
});
|
||||
infoPanelBuilder.CloseComponent();
|
||||
base.BuildRenderTree(gridBuilder);
|
||||
}));
|
||||
panesBuilder.CloseComponent();
|
||||
|
||||
// Right pane - InfoPanel
|
||||
panesBuilder.OpenComponent<DxSplitterPane>(paneSeq++);
|
||||
panesBuilder.AddAttribute(paneSeq++, "Size", "400px");
|
||||
panesBuilder.AddAttribute(paneSeq++, "MinSize", "0px");
|
||||
panesBuilder.AddAttribute(paneSeq++, "MaxSize", "100%");
|
||||
panesBuilder.AddAttribute(paneSeq++, "AllowCollapse", true);
|
||||
panesBuilder.AddAttribute(paneSeq++, "CssClass", "mg-info-panel-pane");
|
||||
|
||||
panesBuilder.AddAttribute(paneSeq++, "ChildContent", (RenderFragment)(infoPanelBuilder =>
|
||||
{
|
||||
var infoPanelSeq = 0;
|
||||
infoPanelBuilder.OpenComponent<MgGridInfoPanel>(infoPanelSeq++);
|
||||
infoPanelBuilder.AddComponentReferenceCapture(infoPanelSeq++, instance =>
|
||||
{
|
||||
InfoPanelInstance = (MgGridInfoPanel)instance;
|
||||
});
|
||||
infoPanelBuilder.CloseComponent();
|
||||
}));
|
||||
|
||||
panesBuilder.CloseComponent();
|
||||
}));
|
||||
|
||||
panesBuilder.CloseComponent();
|
||||
contentBuilder.CloseComponent();
|
||||
}));
|
||||
|
||||
builder.CloseComponent();
|
||||
builder.CloseComponent(); // Close CascadingValue
|
||||
}
|
||||
|
||||
//protected override Task RaiseFocusedRowChangedAsync(GridFocusedRowChangedEventArgsBase args)
|
||||
|
|
@ -311,6 +361,7 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
|
|||
throw new NullReferenceException($"[{GetType().Name}] SignalRClient == null");
|
||||
}
|
||||
|
||||
ShowInfoPanel = IsMasterGrid;
|
||||
var crudTags = new SignalRCrudTags(GetAllMessageTag, GetItemMessageTag, AddMessageTag, UpdateMessageTag, RemoveMessageTag);
|
||||
|
||||
_dataSource = (TSignalRDataSource)Activator.CreateInstance(typeof(TSignalRDataSource), SignalRClient, crudTags, ContextIds)!;
|
||||
|
|
@ -450,40 +501,44 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
|
|||
|
||||
await OnGridCustomizeEditModel.InvokeAsync(e);
|
||||
|
||||
// Frissítjük az InfoPanel-t edit módba - itt az EditModel már elérhető
|
||||
if (ShowInfoPanel && _infoPanelInstance != null)
|
||||
// Update InfoPanel to edit mode
|
||||
var rootGrid = GetRootGrid();
|
||||
if (rootGrid.ShowInfoPanel && rootGrid.InfoPanelInstance != null)
|
||||
{
|
||||
_infoPanelInstance.SetEditMode(editModel);
|
||||
rootGrid.InfoPanelInstance.SetEditMode(editModel);
|
||||
}
|
||||
|
||||
// Force grid refresh to apply edit mode styling
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private async Task OnEditStart(GridEditStartEventArgs e)
|
||||
{
|
||||
await OnGridEditStart.InvokeAsync(e);
|
||||
// Az InfoPanel-t és az EditMode-ot a CustomizeEditModel-ben frissítjük, mert ott az EditModel már elérhető
|
||||
}
|
||||
|
||||
protected virtual async Task OnFocusedRowChanged(GridFocusedRowChangedEventArgs e)
|
||||
{
|
||||
_focusedDataItem = e.DataItem;
|
||||
|
||||
var rootGrid = GetRootGrid();
|
||||
|
||||
if (ShowInfoPanel && _infoPanelInstance != null)
|
||||
if (!rootGrid.ShowInfoPanel) return;
|
||||
|
||||
// Get the root grid's InfoPanel (the visible one)
|
||||
var infoPanelInstance = rootGrid.InfoPanelInstance;
|
||||
|
||||
if (infoPanelInstance != null && e.DataItem != null)
|
||||
{
|
||||
// Ha edit módban vagyunk, de a felhasználó egy másik sorra kattintott,
|
||||
// akkor kilépünk az edit módból
|
||||
if (GridEditState != MgGridEditState.None)
|
||||
{
|
||||
_infoPanelInstance.ClearEditMode();
|
||||
infoPanelInstance.ClearEditMode();
|
||||
}
|
||||
|
||||
// Frissítjük az InfoPanel-t az új sor adataival
|
||||
if (e.DataItem is TDataItem dataItem)
|
||||
{
|
||||
_infoPanelInstance.RefreshData(this, dataItem, e.VisibleIndex);
|
||||
}
|
||||
// The InfoPanel is now non-generic and works with any data type!
|
||||
infoPanelInstance.RefreshData(this, e.DataItem, e.VisibleIndex);
|
||||
}
|
||||
|
||||
await OnGridFocusedRowChanged.InvokeAsync(e);
|
||||
|
|
@ -516,29 +571,27 @@ public abstract class MgGridBase<TSignalRDataSource, TDataItem, TId, TLoggerClie
|
|||
}
|
||||
else await UpdateDataItemAsync(dataItem);
|
||||
|
||||
// Kilépés edit módból
|
||||
GridEditState = MgGridEditState.None;
|
||||
|
||||
if (ShowInfoPanel && _infoPanelInstance != null)
|
||||
var rootGrid = GetRootGrid();
|
||||
if (rootGrid.ShowInfoPanel && rootGrid.InfoPanelInstance != null)
|
||||
{
|
||||
_infoPanelInstance.ClearEditMode();
|
||||
rootGrid.InfoPanelInstance.ClearEditMode();
|
||||
}
|
||||
|
||||
// Force grid refresh to remove edit mode styling
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private async Task OnEditCanceling(GridEditCancelingEventArgs e)
|
||||
{
|
||||
// Kilépés edit módból
|
||||
GridEditState = MgGridEditState.None;
|
||||
|
||||
if (ShowInfoPanel && _infoPanelInstance != null)
|
||||
var rootGrid = GetRootGrid();
|
||||
if (rootGrid.ShowInfoPanel && rootGrid.InfoPanelInstance != null)
|
||||
{
|
||||
_infoPanelInstance.ClearEditMode();
|
||||
rootGrid.InfoPanelInstance.ClearEditMode();
|
||||
}
|
||||
|
||||
// Force grid refresh to remove edit mode styling
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
@using DevExpress.Blazor
|
||||
@using Microsoft.AspNetCore.Components.Rendering
|
||||
@using System.Reflection
|
||||
@typeparam TDataItem where TDataItem : class
|
||||
|
||||
<div @ref="_panelElement" class="mg-grid-info-panel @(_isEditMode ? "edit-mode" : "view-mode")">
|
||||
@* Header - matches grid toolbar height *@
|
||||
@* Header *@
|
||||
<div class="dxbl-grid-header-panel px-3 py-2 border-bottom">
|
||||
<span class="fw-semibold">Sor részletei</span>
|
||||
</div>
|
||||
|
||||
@* Content - scrollable area *@
|
||||
@* Content *@
|
||||
<div class="mg-info-panel-content">
|
||||
@if (GetActiveDataItem() != null && _currentGrid != null)
|
||||
{
|
||||
var dataItem = GetActiveDataItem()!;
|
||||
var dataItemType = dataItem.GetType();
|
||||
|
||||
<div class="mg-info-panel-grid">
|
||||
@foreach (var column in _allDataColumns)
|
||||
@foreach (var column in GetVisibleColumns())
|
||||
{
|
||||
var displayText = GetDisplayTextFromGrid(column);
|
||||
var value = GetCellValue(column);
|
||||
|
|
@ -31,7 +31,7 @@
|
|||
<div class="dxbl-fl-ec">
|
||||
@if (_isEditMode && !column.ReadOnly)
|
||||
{
|
||||
@RenderEditableCell(column, dataItem, value, displayText, settingsType)
|
||||
@RenderEditableCell(column, dataItem, dataItemType, value, displayText, settingsType)
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -53,30 +53,40 @@
|
|||
</div>
|
||||
|
||||
@code {
|
||||
private string GetColumnCaption(DxGridDataColumn column)
|
||||
/// <summary>
|
||||
/// Gets the columns to display based on edit mode and ShowReadOnlyFieldsInEditMode setting
|
||||
/// </summary>
|
||||
private IEnumerable<DxGridDataColumn> GetVisibleColumns()
|
||||
{
|
||||
if (!_isEditMode || ShowReadOnlyFieldsInEditMode)
|
||||
{
|
||||
return _allDataColumns;
|
||||
}
|
||||
|
||||
// In edit mode with ShowReadOnlyFieldsInEditMode=false, hide readonly columns
|
||||
return _allDataColumns.Where(c => !c.ReadOnly);
|
||||
}
|
||||
|
||||
private static string GetColumnCaption(DxGridDataColumn column)
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(column.Caption) ? column.Caption : column.FieldName;
|
||||
}
|
||||
|
||||
private string GetCaptionCssClass(bool isReadOnly)
|
||||
private static string GetCaptionCssClass(bool isReadOnly)
|
||||
{
|
||||
return isReadOnly ? "fw-semibold" : "fw-semibold text-primary";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders an editable cell with two-way binding to the EditModel
|
||||
/// </summary>
|
||||
private RenderFragment RenderEditableCell(DxGridDataColumn column, TDataItem dataItem, object? value, string displayText, EditSettingsType settingsType)
|
||||
private RenderFragment RenderEditableCell(DxGridDataColumn column, object dataItem, Type dataItemType, object? value, string displayText, EditSettingsType settingsType)
|
||||
{
|
||||
return builder =>
|
||||
{
|
||||
var seq = 0;
|
||||
var fieldName = column.FieldName;
|
||||
var propertyInfo = typeof(TDataItem).GetProperty(fieldName);
|
||||
|
||||
var propertyInfo = dataItemType.GetProperty(fieldName);
|
||||
|
||||
if (propertyInfo == null)
|
||||
{
|
||||
// Fallback to readonly if property not found
|
||||
RenderCellContent(column, value, displayText, settingsType)(builder);
|
||||
return;
|
||||
}
|
||||
|
|
@ -84,10 +94,10 @@
|
|||
var propertyType = propertyInfo.PropertyType;
|
||||
var underlyingType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;
|
||||
|
||||
// ComboBox columns
|
||||
// ComboBox
|
||||
if (settingsType == EditSettingsType.ComboBox)
|
||||
{
|
||||
var comboSettings = GetEditSettings(fieldName) as DxComboBoxSettings;
|
||||
var comboSettings = GetEditSettings(column.FieldName) as DxComboBoxSettings;
|
||||
if (comboSettings != null)
|
||||
{
|
||||
RenderComboBoxEditor(builder, ref seq, dataItem, propertyInfo, comboSettings);
|
||||
|
|
@ -95,54 +105,29 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Render based on property type
|
||||
// Render based on type
|
||||
if (underlyingType == typeof(bool))
|
||||
{
|
||||
RenderCheckBoxEditor(builder, ref seq, dataItem, propertyInfo);
|
||||
}
|
||||
else if (underlyingType == typeof(DateTime))
|
||||
{
|
||||
RenderDateTimeEditor(builder, ref seq, dataItem, propertyInfo, column.DisplayFormat);
|
||||
}
|
||||
else if (underlyingType == typeof(DateOnly))
|
||||
{
|
||||
RenderDateOnlyEditor(builder, ref seq, dataItem, propertyInfo, column.DisplayFormat);
|
||||
}
|
||||
else if (underlyingType == typeof(TimeOnly))
|
||||
{
|
||||
RenderTimeOnlyEditor(builder, ref seq, dataItem, propertyInfo);
|
||||
}
|
||||
else if (underlyingType == typeof(TimeSpan))
|
||||
{
|
||||
RenderTimeSpanEditor(builder, ref seq, dataItem, propertyInfo);
|
||||
}
|
||||
else if (underlyingType == typeof(int))
|
||||
{
|
||||
RenderSpinIntEditor(builder, ref seq, dataItem, propertyInfo);
|
||||
}
|
||||
else if (underlyingType == typeof(decimal))
|
||||
{
|
||||
RenderSpinDecimalEditor(builder, ref seq, dataItem, propertyInfo);
|
||||
}
|
||||
else if (underlyingType == typeof(double))
|
||||
{
|
||||
RenderSpinDoubleEditor(builder, ref seq, dataItem, propertyInfo);
|
||||
}
|
||||
else if (settingsType == EditSettingsType.Memo || (propertyType == typeof(string) && fieldName.Contains("Comment", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
else if (settingsType == EditSettingsType.Memo)
|
||||
RenderMemoEditor(builder, ref seq, dataItem, propertyInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
RenderTextBoxEditor(builder, ref seq, dataItem, propertyInfo);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void RenderCheckBoxEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo)
|
||||
private void RenderCheckBoxEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo)
|
||||
{
|
||||
var currentValue = (bool)(propertyInfo.GetValue(dataItem) ?? false);
|
||||
|
||||
builder.OpenComponent<DxCheckBox<bool>>(seq++);
|
||||
builder.AddAttribute(seq++, "Checked", currentValue);
|
||||
builder.AddAttribute(seq++, "CheckedChanged", EventCallback.Factory.Create<bool>(this, newValue =>
|
||||
|
|
@ -153,7 +138,7 @@
|
|||
builder.CloseComponent();
|
||||
}
|
||||
|
||||
private void RenderDateTimeEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo, string? displayFormat)
|
||||
private void RenderDateTimeEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo, string? displayFormat)
|
||||
{
|
||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
||||
var currentValue = propertyInfo.GetValue(dataItem);
|
||||
|
|
@ -182,7 +167,7 @@
|
|||
builder.CloseComponent();
|
||||
}
|
||||
|
||||
private void RenderDateOnlyEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo, string? displayFormat)
|
||||
private void RenderDateOnlyEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo, string? displayFormat)
|
||||
{
|
||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
||||
var currentValue = propertyInfo.GetValue(dataItem);
|
||||
|
|
@ -211,63 +196,7 @@
|
|||
builder.CloseComponent();
|
||||
}
|
||||
|
||||
private void RenderTimeOnlyEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo)
|
||||
{
|
||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
||||
var currentValue = propertyInfo.GetValue(dataItem);
|
||||
|
||||
if (isNullable)
|
||||
{
|
||||
builder.OpenComponent<DxTimeEdit<TimeOnly?>>(seq++);
|
||||
builder.AddAttribute(seq++, "Time", (TimeOnly?)currentValue);
|
||||
builder.AddAttribute(seq++, "TimeChanged", EventCallback.Factory.Create<TimeOnly?>(this, newValue =>
|
||||
{
|
||||
propertyInfo.SetValue(dataItem, newValue);
|
||||
InvokeAsync(StateHasChanged);
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.OpenComponent<DxTimeEdit<TimeOnly>>(seq++);
|
||||
builder.AddAttribute(seq++, "Time", (TimeOnly)(currentValue ?? TimeOnly.MinValue));
|
||||
builder.AddAttribute(seq++, "TimeChanged", EventCallback.Factory.Create<TimeOnly>(this, newValue =>
|
||||
{
|
||||
propertyInfo.SetValue(dataItem, newValue);
|
||||
InvokeAsync(StateHasChanged);
|
||||
}));
|
||||
}
|
||||
builder.CloseComponent();
|
||||
}
|
||||
|
||||
private void RenderTimeSpanEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo)
|
||||
{
|
||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
||||
var currentValue = propertyInfo.GetValue(dataItem);
|
||||
|
||||
if (isNullable)
|
||||
{
|
||||
builder.OpenComponent<DxTimeEdit<TimeSpan?>>(seq++);
|
||||
builder.AddAttribute(seq++, "Time", (TimeSpan?)currentValue);
|
||||
builder.AddAttribute(seq++, "TimeChanged", EventCallback.Factory.Create<TimeSpan?>(this, newValue =>
|
||||
{
|
||||
propertyInfo.SetValue(dataItem, newValue);
|
||||
InvokeAsync(StateHasChanged);
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.OpenComponent<DxTimeEdit<TimeSpan>>(seq++);
|
||||
builder.AddAttribute(seq++, "Time", (TimeSpan)(currentValue ?? TimeSpan.Zero));
|
||||
builder.AddAttribute(seq++, "TimeChanged", EventCallback.Factory.Create<TimeSpan>(this, newValue =>
|
||||
{
|
||||
propertyInfo.SetValue(dataItem, newValue);
|
||||
InvokeAsync(StateHasChanged);
|
||||
}));
|
||||
}
|
||||
builder.CloseComponent();
|
||||
}
|
||||
|
||||
private void RenderSpinIntEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo)
|
||||
private void RenderSpinIntEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo)
|
||||
{
|
||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
||||
var currentValue = propertyInfo.GetValue(dataItem);
|
||||
|
|
@ -295,7 +224,7 @@
|
|||
builder.CloseComponent();
|
||||
}
|
||||
|
||||
private void RenderSpinDecimalEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo)
|
||||
private void RenderSpinDecimalEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo)
|
||||
{
|
||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
||||
var currentValue = propertyInfo.GetValue(dataItem);
|
||||
|
|
@ -323,7 +252,7 @@
|
|||
builder.CloseComponent();
|
||||
}
|
||||
|
||||
private void RenderSpinDoubleEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo)
|
||||
private void RenderSpinDoubleEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo)
|
||||
{
|
||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
||||
var currentValue = propertyInfo.GetValue(dataItem);
|
||||
|
|
@ -351,10 +280,9 @@
|
|||
builder.CloseComponent();
|
||||
}
|
||||
|
||||
private void RenderTextBoxEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo)
|
||||
private void RenderTextBoxEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo)
|
||||
{
|
||||
var currentValue = propertyInfo.GetValue(dataItem)?.ToString() ?? string.Empty;
|
||||
|
||||
builder.OpenComponent<DxTextBox>(seq++);
|
||||
builder.AddAttribute(seq++, "Text", currentValue);
|
||||
builder.AddAttribute(seq++, "TextChanged", EventCallback.Factory.Create<string>(this, newValue =>
|
||||
|
|
@ -365,10 +293,9 @@
|
|||
builder.CloseComponent();
|
||||
}
|
||||
|
||||
private void RenderMemoEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo)
|
||||
private void RenderMemoEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo)
|
||||
{
|
||||
var currentValue = propertyInfo.GetValue(dataItem)?.ToString() ?? string.Empty;
|
||||
|
||||
builder.OpenComponent<DxMemo>(seq++);
|
||||
builder.AddAttribute(seq++, "Text", currentValue);
|
||||
builder.AddAttribute(seq++, "TextChanged", EventCallback.Factory.Create<string>(this, newValue =>
|
||||
|
|
@ -380,40 +307,29 @@
|
|||
builder.CloseComponent();
|
||||
}
|
||||
|
||||
private void RenderComboBoxEditor(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo, DxComboBoxSettings settings)
|
||||
private void RenderComboBoxEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo, DxComboBoxSettings settings)
|
||||
{
|
||||
var currentValue = propertyInfo.GetValue(dataItem);
|
||||
var propertyType = propertyInfo.PropertyType;
|
||||
var underlyingType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;
|
||||
|
||||
// Determine TData type from settings.Data
|
||||
var dataType = settings.Data?.GetType();
|
||||
var itemType = typeof(object);
|
||||
if (dataType != null && dataType.IsGenericType)
|
||||
if (dataType?.IsGenericType == true)
|
||||
{
|
||||
var genericArgs = dataType.GetGenericArguments();
|
||||
if (genericArgs.Length > 0)
|
||||
{
|
||||
itemType = genericArgs[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Handle common value types for FK fields
|
||||
if (underlyingType == typeof(int))
|
||||
{
|
||||
RenderComboBoxInt(builder, ref seq, dataItem, propertyInfo, settings, itemType, currentValue);
|
||||
}
|
||||
else if (underlyingType == typeof(long))
|
||||
{
|
||||
RenderComboBoxLong(builder, ref seq, dataItem, propertyInfo, settings, itemType, currentValue);
|
||||
}
|
||||
else if (underlyingType == typeof(Guid))
|
||||
{
|
||||
RenderComboBoxGuid(builder, ref seq, dataItem, propertyInfo, settings, itemType, currentValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback: render as TextBox with display text
|
||||
var displayText = ResolveComboBoxDisplayText(settings, currentValue ?? new object()) ?? currentValue?.ToString() ?? string.Empty;
|
||||
builder.OpenComponent<DxTextBox>(seq++);
|
||||
builder.AddAttribute(seq++, "Text", displayText);
|
||||
|
|
@ -422,12 +338,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
private void RenderComboBoxInt(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo, DxComboBoxSettings settings, Type itemType, object? currentValue)
|
||||
private void RenderComboBoxInt(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo, DxComboBoxSettings settings, Type itemType, object? currentValue)
|
||||
{
|
||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
||||
|
||||
// Create the generic ComboBox type: DxComboBox<TItem, int> or DxComboBox<TItem, int?>
|
||||
var comboType = isNullable
|
||||
var comboType = isNullable
|
||||
? typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(int?))
|
||||
: typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(int));
|
||||
|
||||
|
|
@ -435,7 +349,7 @@
|
|||
builder.AddAttribute(seq++, "Data", settings.Data);
|
||||
builder.AddAttribute(seq++, "ValueFieldName", settings.ValueFieldName);
|
||||
builder.AddAttribute(seq++, "TextFieldName", settings.TextFieldName);
|
||||
|
||||
|
||||
if (isNullable)
|
||||
{
|
||||
builder.AddAttribute(seq++, "Value", currentValue as int?);
|
||||
|
|
@ -454,16 +368,14 @@
|
|||
StateHasChanged();
|
||||
}));
|
||||
}
|
||||
|
||||
builder.AddAttribute(seq++, "ClearButtonDisplayMode", DataEditorClearButtonDisplayMode.Auto);
|
||||
builder.CloseComponent();
|
||||
}
|
||||
|
||||
private void RenderComboBoxLong(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo, DxComboBoxSettings settings, Type itemType, object? currentValue)
|
||||
private void RenderComboBoxLong(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo, DxComboBoxSettings settings, Type itemType, object? currentValue)
|
||||
{
|
||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
||||
|
||||
var comboType = isNullable
|
||||
var comboType = isNullable
|
||||
? typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(long?))
|
||||
: typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(long));
|
||||
|
||||
|
|
@ -471,7 +383,7 @@
|
|||
builder.AddAttribute(seq++, "Data", settings.Data);
|
||||
builder.AddAttribute(seq++, "ValueFieldName", settings.ValueFieldName);
|
||||
builder.AddAttribute(seq++, "TextFieldName", settings.TextFieldName);
|
||||
|
||||
|
||||
if (isNullable)
|
||||
{
|
||||
builder.AddAttribute(seq++, "Value", currentValue as long?);
|
||||
|
|
@ -490,16 +402,14 @@
|
|||
StateHasChanged();
|
||||
}));
|
||||
}
|
||||
|
||||
builder.AddAttribute(seq++, "ClearButtonDisplayMode", DataEditorClearButtonDisplayMode.Auto);
|
||||
builder.CloseComponent();
|
||||
}
|
||||
|
||||
private void RenderComboBoxGuid(RenderTreeBuilder builder, ref int seq, TDataItem dataItem, PropertyInfo propertyInfo, DxComboBoxSettings settings, Type itemType, object? currentValue)
|
||||
private void RenderComboBoxGuid(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo, DxComboBoxSettings settings, Type itemType, object? currentValue)
|
||||
{
|
||||
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
|
||||
|
||||
var comboType = isNullable
|
||||
var comboType = isNullable
|
||||
? typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(Guid?))
|
||||
: typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(Guid));
|
||||
|
||||
|
|
@ -507,7 +417,7 @@
|
|||
builder.AddAttribute(seq++, "Data", settings.Data);
|
||||
builder.AddAttribute(seq++, "ValueFieldName", settings.ValueFieldName);
|
||||
builder.AddAttribute(seq++, "TextFieldName", settings.TextFieldName);
|
||||
|
||||
|
||||
if (isNullable)
|
||||
{
|
||||
builder.AddAttribute(seq++, "Value", currentValue as Guid?);
|
||||
|
|
@ -526,7 +436,6 @@
|
|||
StateHasChanged();
|
||||
}));
|
||||
}
|
||||
|
||||
builder.AddAttribute(seq++, "ClearButtonDisplayMode", DataEditorClearButtonDisplayMode.Auto);
|
||||
builder.CloseComponent();
|
||||
}
|
||||
|
|
@ -537,11 +446,9 @@
|
|||
{
|
||||
var seq = 0;
|
||||
|
||||
// If column has EditSettings, render based on that
|
||||
switch (settingsType)
|
||||
{
|
||||
case EditSettingsType.ComboBox:
|
||||
// ComboBox columns show resolved display text
|
||||
builder.OpenElement(seq++, "div");
|
||||
builder.AddAttribute(seq++, "title", displayText);
|
||||
builder.AddAttribute(seq++, "class", "info-panel-text-wrapper");
|
||||
|
|
@ -559,26 +466,6 @@
|
|||
builder.CloseComponent();
|
||||
return;
|
||||
|
||||
case EditSettingsType.DateEdit:
|
||||
RenderDateEditor(builder, ref seq, value, column.DisplayFormat);
|
||||
return;
|
||||
|
||||
case EditSettingsType.TimeEdit:
|
||||
RenderTimeEditor(builder, ref seq, value, column.DisplayFormat);
|
||||
return;
|
||||
|
||||
case EditSettingsType.SpinEdit:
|
||||
builder.OpenElement(seq++, "div");
|
||||
builder.AddAttribute(seq++, "title", displayText);
|
||||
builder.AddAttribute(seq++, "class", "info-panel-text-wrapper");
|
||||
builder.OpenComponent<DxTextBox>(seq++);
|
||||
builder.AddAttribute(seq++, "Text", displayText);
|
||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
||||
builder.AddAttribute(seq++, "CssClass", "text-end");
|
||||
builder.CloseComponent();
|
||||
builder.CloseElement();
|
||||
return;
|
||||
|
||||
case EditSettingsType.Memo:
|
||||
builder.OpenElement(seq++, "div");
|
||||
builder.AddAttribute(seq++, "title", displayText);
|
||||
|
|
@ -591,7 +478,7 @@
|
|||
return;
|
||||
}
|
||||
|
||||
// Default: render based on value type
|
||||
// Default by value type
|
||||
switch (value)
|
||||
{
|
||||
case bool boolValue:
|
||||
|
|
@ -617,20 +504,6 @@
|
|||
builder.CloseComponent();
|
||||
break;
|
||||
|
||||
case TimeOnly timeOnlyValue:
|
||||
builder.OpenComponent<DxTimeEdit<TimeOnly>>(seq++);
|
||||
builder.AddAttribute(seq++, "Time", timeOnlyValue);
|
||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
||||
builder.CloseComponent();
|
||||
break;
|
||||
|
||||
case TimeSpan timeSpanValue:
|
||||
builder.OpenComponent<DxTimeEdit<TimeSpan>>(seq++);
|
||||
builder.AddAttribute(seq++, "Time", timeSpanValue);
|
||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
||||
builder.CloseComponent();
|
||||
break;
|
||||
|
||||
case decimal or double or float or int or long or short:
|
||||
builder.OpenElement(seq++, "div");
|
||||
builder.AddAttribute(seq++, "title", displayText);
|
||||
|
|
@ -656,69 +529,4 @@
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void RenderDateEditor(RenderTreeBuilder builder, ref int seq, object? value, string? displayFormat)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case DateTime dateTime:
|
||||
builder.OpenComponent<DxDateEdit<DateTime>>(seq++);
|
||||
builder.AddAttribute(seq++, "Date", dateTime);
|
||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
||||
builder.AddAttribute(seq++, "DisplayFormat", displayFormat ?? "yyyy-MM-dd HH:mm");
|
||||
builder.CloseComponent();
|
||||
break;
|
||||
case DateOnly dateOnly:
|
||||
builder.OpenComponent<DxDateEdit<DateOnly>>(seq++);
|
||||
builder.AddAttribute(seq++, "Date", dateOnly);
|
||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
||||
builder.AddAttribute(seq++, "DisplayFormat", displayFormat ?? "yyyy-MM-dd");
|
||||
builder.CloseComponent();
|
||||
break;
|
||||
case DateTimeOffset dateTimeOffset:
|
||||
builder.OpenComponent<DxDateEdit<DateTimeOffset>>(seq++);
|
||||
builder.AddAttribute(seq++, "Date", dateTimeOffset);
|
||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
||||
builder.AddAttribute(seq++, "DisplayFormat", displayFormat ?? "yyyy-MM-dd HH:mm");
|
||||
builder.CloseComponent();
|
||||
break;
|
||||
default:
|
||||
builder.OpenComponent<DxTextBox>(seq++);
|
||||
builder.AddAttribute(seq++, "Text", value?.ToString() ?? string.Empty);
|
||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
||||
builder.CloseComponent();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderTimeEditor(RenderTreeBuilder builder, ref int seq, object? value, string? displayFormat)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case TimeOnly timeOnly:
|
||||
builder.OpenComponent<DxTimeEdit<TimeOnly>>(seq++);
|
||||
builder.AddAttribute(seq++, "Time", timeOnly);
|
||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
||||
builder.CloseComponent();
|
||||
break;
|
||||
case TimeSpan timeSpan:
|
||||
builder.OpenComponent<DxTimeEdit<TimeSpan>>(seq++);
|
||||
builder.AddAttribute(seq++, "Time", timeSpan);
|
||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
||||
builder.CloseComponent();
|
||||
break;
|
||||
case DateTime dateTime:
|
||||
builder.OpenComponent<DxTimeEdit<DateTime>>(seq++);
|
||||
builder.AddAttribute(seq++, "Time", dateTime);
|
||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
||||
builder.CloseComponent();
|
||||
break;
|
||||
default:
|
||||
builder.OpenComponent<DxTextBox>(seq++);
|
||||
builder.AddAttribute(seq++, "Text", value?.ToString() ?? string.Empty);
|
||||
builder.AddAttribute(seq++, "ReadOnly", true);
|
||||
builder.CloseComponent();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,23 +4,40 @@ using Microsoft.JSInterop;
|
|||
|
||||
namespace AyCode.Blazor.Components.Components.Grids;
|
||||
|
||||
public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposable where TDataItem : class
|
||||
/// <summary>
|
||||
/// Interface for InfoPanel to support grid access
|
||||
/// </summary>
|
||||
public interface IInfoPanelBase
|
||||
{
|
||||
void ClearEditMode();
|
||||
void SetEditMode(object editModel);
|
||||
void RefreshData(DxGrid grid, object? dataItem, int visibleIndex = -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Non-generic version of the InfoPanel component
|
||||
/// </summary>
|
||||
public partial class MgGridInfoPanel : ComponentBase, IAsyncDisposable, IInfoPanelBase
|
||||
{
|
||||
[Inject] private IJSRuntime JSRuntime { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to show readonly fields when in edit mode. Default is false.
|
||||
/// </summary>
|
||||
[Parameter] public bool ShowReadOnlyFieldsInEditMode { get; set; } = false;
|
||||
|
||||
private ElementReference _panelElement;
|
||||
private IJSObjectReference? _jsModule;
|
||||
private bool _isJsInitialized;
|
||||
private const int DefaultTopOffset = 300; // Increased from 180 to account for header + tabs + toolbar
|
||||
|
||||
private DxGrid? _currentGrid;
|
||||
private TDataItem? _currentDataItem;
|
||||
private object? _currentDataItem;
|
||||
private int _focusedRowVisibleIndex = -1;
|
||||
private List<DxGridDataColumn> _allDataColumns = [];
|
||||
|
||||
// Edit mode state
|
||||
private bool _isEditMode;
|
||||
private TDataItem? _editModel;
|
||||
private object? _editModel;
|
||||
|
||||
// Cache for edit settings to avoid repeated lookups
|
||||
private readonly Dictionary<string, IEditSettings?> _editSettingsCache = [];
|
||||
|
|
@ -52,39 +69,46 @@ public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposabl
|
|||
/// <summary>
|
||||
/// Refreshes the InfoPanel with data from the specified grid row (view mode)
|
||||
/// </summary>
|
||||
public void RefreshData(DxGrid grid, TDataItem? dataItem, int visibleIndex = -1)
|
||||
public void RefreshData(DxGrid grid, object? dataItem, int visibleIndex = -1)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(grid);
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"[InfoPanel] RefreshData called - dataItem type: {dataItem?.GetType().Name ?? "null"}, visibleIndex: {visibleIndex}");
|
||||
|
||||
_currentGrid = grid;
|
||||
_currentDataItem = dataItem;
|
||||
_focusedRowVisibleIndex = visibleIndex;
|
||||
_editSettingsCache.Clear();
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"[InfoPanel] RefreshData - _currentDataItem is null: {_currentDataItem == null}, cast success: {dataItem != null && _currentDataItem != null}");
|
||||
|
||||
// Ha nem vagyunk edit módban, frissítjük az oszlopokat
|
||||
if (!_isEditMode)
|
||||
{
|
||||
if (_currentGrid != null && _currentDataItem != null)
|
||||
{
|
||||
_allDataColumns = GetAllDataColumns(_currentGrid);
|
||||
System.Diagnostics.Debug.WriteLine($"[InfoPanel] RefreshData - loaded {_allDataColumns.Count} columns");
|
||||
}
|
||||
else
|
||||
{
|
||||
_allDataColumns = [];
|
||||
System.Diagnostics.Debug.WriteLine($"[InfoPanel] RefreshData - cleared columns (grid or dataItem is null)");
|
||||
}
|
||||
}
|
||||
|
||||
StateHasChanged();
|
||||
System.Diagnostics.Debug.WriteLine($"[InfoPanel] RefreshData - StateHasChanged called");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the InfoPanel to edit mode with the given edit model
|
||||
/// </summary>
|
||||
public void SetEditMode(TDataItem editModel)
|
||||
public void SetEditMode(object editModel)
|
||||
{
|
||||
_editModel = editModel;
|
||||
_isEditMode = true;
|
||||
_currentDataItem = editModel;
|
||||
_currentDataItem = _editModel;
|
||||
|
||||
if (_currentGrid != null)
|
||||
{
|
||||
|
|
@ -101,6 +125,7 @@ public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposabl
|
|||
{
|
||||
_isEditMode = false;
|
||||
_editModel = null;
|
||||
_editSettingsCache.Clear();
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
|
|
@ -132,17 +157,12 @@ public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposabl
|
|||
// Ignore disposal errors
|
||||
}
|
||||
}
|
||||
|
||||
if (_jsModule != null)
|
||||
{
|
||||
await _jsModule.DisposeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data item to display/edit (EditModel in edit mode, otherwise CurrentDataItem)
|
||||
/// </summary>
|
||||
private TDataItem? GetActiveDataItem() => _isEditMode && _editModel != null ? _editModel : _currentDataItem;
|
||||
private object? GetActiveDataItem() => _isEditMode && _editModel != null ? _editModel : _currentDataItem;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the display text for a field using the grid's internal formatting.
|
||||
|
|
@ -163,9 +183,9 @@ public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposabl
|
|||
|
||||
// Try to resolve display text from EditSettings
|
||||
var editSettings = GetEditSettings(column.FieldName);
|
||||
if (editSettings != null)
|
||||
if (editSettings is DxComboBoxSettings comboSettings)
|
||||
{
|
||||
var displayText = ResolveEditSettingsDisplayText(editSettings, value);
|
||||
var displayText = ResolveComboBoxDisplayText(comboSettings, value);
|
||||
if (!string.IsNullOrEmpty(displayText))
|
||||
return displayText;
|
||||
}
|
||||
|
|
@ -206,15 +226,13 @@ public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposabl
|
|||
try
|
||||
{
|
||||
// Try each EditSettings type
|
||||
settings = TryGetEditSettings<DxComboBoxSettings>(fieldName)
|
||||
?? TryGetEditSettings<DxDateEditSettings>(fieldName)
|
||||
?? TryGetEditSettings<DxTimeEditSettings>(fieldName)
|
||||
?? TryGetEditSettings<DxSpinEditSettings>(fieldName)
|
||||
?? TryGetEditSettings<DxCheckBoxSettings>(fieldName)
|
||||
?? TryGetEditSettings<DxMemoSettings>(fieldName)
|
||||
?? TryGetEditSettings<DxMaskedInputSettings>(fieldName)
|
||||
?? TryGetEditSettings<DxTextBoxSettings>(fieldName)
|
||||
?? (IEditSettings?)TryGetEditSettings<DxDropDownEditSettings>(fieldName);
|
||||
settings = _currentGrid.GetColumnEditSettings<DxComboBoxSettings>(fieldName)
|
||||
?? _currentGrid.GetColumnEditSettings<DxDateEditSettings>(fieldName)
|
||||
?? _currentGrid.GetColumnEditSettings<DxTimeEditSettings>(fieldName)
|
||||
?? _currentGrid.GetColumnEditSettings<DxSpinEditSettings>(fieldName)
|
||||
?? _currentGrid.GetColumnEditSettings<DxCheckBoxSettings>(fieldName)
|
||||
?? _currentGrid.GetColumnEditSettings<DxMemoSettings>(fieldName)
|
||||
?? (IEditSettings?)_currentGrid.GetColumnEditSettings<DxTextBoxSettings>(fieldName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
@ -225,33 +243,6 @@ public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposabl
|
|||
return settings;
|
||||
}
|
||||
|
||||
private T? TryGetEditSettings<T>(string fieldName) where T : class, IEditSettings
|
||||
{
|
||||
try
|
||||
{
|
||||
return _currentGrid?.GetColumnEditSettings<T>(fieldName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves display text based on EditSettings type
|
||||
/// </summary>
|
||||
private string? ResolveEditSettingsDisplayText(IEditSettings settings, object value)
|
||||
{
|
||||
return settings switch
|
||||
{
|
||||
DxComboBoxSettings comboSettings => ResolveComboBoxDisplayText(comboSettings, value),
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the display text from a ComboBox data source
|
||||
/// </summary>
|
||||
private string? ResolveComboBoxDisplayText(DxComboBoxSettings settings, object value)
|
||||
{
|
||||
if (settings.Data == null || string.IsNullOrEmpty(settings.ValueFieldName) || string.IsNullOrEmpty(settings.TextFieldName))
|
||||
|
|
@ -284,7 +275,7 @@ public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposabl
|
|||
return null;
|
||||
}
|
||||
|
||||
private string FormatValue(object? value)
|
||||
private static string FormatValue(object? value)
|
||||
{
|
||||
if (value == null)
|
||||
return string.Empty;
|
||||
|
|
@ -304,13 +295,16 @@ public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposabl
|
|||
};
|
||||
}
|
||||
|
||||
private List<DxGridDataColumn> GetAllDataColumns(DxGrid grid)
|
||||
private static List<DxGridDataColumn> GetAllDataColumns(DxGrid grid)
|
||||
{
|
||||
var columns = new List<DxGridDataColumn>();
|
||||
|
||||
try
|
||||
{
|
||||
var allColumns = grid.GetDataColumns();
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"[InfoPanel] GetAllDataColumns - grid type: {grid.GetType().Name}, columns count: {allColumns?.Count() ?? 0}");
|
||||
|
||||
if (allColumns != null)
|
||||
{
|
||||
foreach (var column in allColumns)
|
||||
|
|
@ -319,15 +313,17 @@ public partial class MgGridInfoPanel<TDataItem> : ComponentBase, IAsyncDisposabl
|
|||
!string.IsNullOrWhiteSpace(dataColumn.FieldName))
|
||||
{
|
||||
columns.Add(dataColumn);
|
||||
System.Diagnostics.Debug.WriteLine($"[InfoPanel] - Column: {dataColumn.FieldName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Fallback: empty list if GetDataColumns fails
|
||||
System.Diagnostics.Debug.WriteLine($"[InfoPanel] GetAllDataColumns error: {ex.Message}");
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"[InfoPanel] GetAllDataColumns result: {columns.Count} columns");
|
||||
return columns;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
/* Breakpoint configuration - CHANGE ONLY THESE VALUES */
|
||||
/* 2 column breakpoint: 500px */
|
||||
/* 3 column breakpoint: 800px */
|
||||
/* 4 column breakpoint: 1200px (for 1920px+ screens) */
|
||||
|
||||
/* Main panel - contained within splitter pane */
|
||||
.mg-grid-info-panel {
|
||||
|
|
@ -61,13 +62,20 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* 3 columns for wider panels (>= 800px) */
|
||||
@container infopanel (min-width: 800px) {
|
||||
/* 3 columns for wider panels (800px - 1199px) */
|
||||
@container infopanel (min-width: 800px) and (max-width: 1199px) {
|
||||
.mg-info-panel-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
/* 4 columns for very wide panels (>= 1200px, typically on 1920px+ screens) */
|
||||
@container infopanel (min-width: 1200px) {
|
||||
.mg-info-panel-grid {
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.mg-info-panel-item {
|
||||
min-width: 0; /* Prevent grid blowout */
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue