AyCode.Blazor/AyCode.Blazor.Components/Components/Grids/MgGridInfoPanel.razor

575 lines
23 KiB
Plaintext

@using DevExpress.Blazor
@using Microsoft.AspNetCore.Components.Rendering
@using System.Reflection
<div @ref="_panelElement" class="mg-grid-info-panel @(_isEditMode ? "edit-mode" : "view-mode") @GetColumnCountClass()"
style="@GetBreakpointStyles()">
@* Header *@
@if (HeaderTemplate != null)
{
@HeaderTemplate(GetActiveDataItem())
}
else
{
<div class="dxbl-grid-header-panel px-3 py-2 border-bottom">
<span class="fw-semibold">@(_currentGrid?.Caption ?? "")</span>
</div>
}
@* Toolbar *@
@if (_currentGrid != null)
{
<div class="mg-info-panel-toolbar border-bottom">
<MgGridToolbarTemplate Grid="_currentGrid" OnlyGridEditTools="true" ShowOnlyIcon="true" />
</div>
}
@* Content *@
<div class="mg-info-panel-content">
@if (GetActiveDataItem() != null && _currentGrid != null)
{
@* Before Columns *@
@if (BeforeColumnsTemplate != null)
{
@BeforeColumnsTemplate(GetActiveDataItem())
}
@* Columns *@
@if (ColumnsTemplate != null)
{
@ColumnsTemplate(GetActiveDataItem())
}
else
{
@RenderDefaultColumns()
}
@* After Columns *@
@if (AfterColumnsTemplate != null)
{
@AfterColumnsTemplate(GetActiveDataItem())
}
}
else
{
<div class="text-center text-muted py-5">
<p>Válasszon ki egy sort az adatok megtekintéséhez</p>
</div>
}
</div>
@* Footer *@
@if (FooterTemplate != null)
{
@FooterTemplate(GetActiveDataItem())
}
</div>
@code {
/// <summary>
/// Custom header template. Receives the current data item.
/// </summary>
[Parameter] public RenderFragment<object?>? HeaderTemplate { get; set; }
/// <summary>
/// Content to render before the columns.
/// </summary>
[Parameter] public RenderFragment<object?>? BeforeColumnsTemplate { get; set; }
/// <summary>
/// Custom columns template. If not set, columns are auto-generated.
/// </summary>
[Parameter] public RenderFragment<object?>? ColumnsTemplate { get; set; }
/// <summary>
/// Content to render after the columns.
/// </summary>
[Parameter] public RenderFragment<object?>? AfterColumnsTemplate { get; set; }
/// <summary>
/// Custom footer template.
/// </summary>
[Parameter] public RenderFragment<object?>? FooterTemplate { get; set; }
private string GetColumnCountClass()
{
if (FixedColumnCount.HasValue)
{
return FixedColumnCount.Value switch
{
1 => "mg-columns-1",
2 => "mg-columns-2",
3 => "mg-columns-3",
4 => "mg-columns-4",
_ => ""
};
}
return "";
}
private string GetBreakpointStyles()
{
return $"--mg-bp-2col: {TwoColumnBreakpoint}px; --mg-bp-3col: {ThreeColumnBreakpoint}px; --mg-bp-4col: {FourColumnBreakpoint}px;";
}
private RenderFragment RenderDefaultColumns() => builder =>
{
var dataItem = GetActiveDataItem()!;
var dataItemType = dataItem.GetType();
var seq = 0;
builder.OpenElement(seq++, "div");
builder.AddAttribute(seq++, "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;
builder.OpenElement(seq++, "div");
builder.AddAttribute(seq++, "class", "mg-info-panel-item");
builder.OpenElement(seq++, "div");
builder.AddAttribute(seq++, "class", "dxbl-form-layout-item");
builder.OpenElement(seq++, "label");
builder.AddAttribute(seq++, "class", $"dxbl-fl-lc {GetCaptionCssClass(isReadOnly)} d-block mb-1 small");
builder.AddContent(seq++, GetColumnCaption(column));
builder.CloseElement();
builder.OpenElement(seq++, "div");
builder.AddAttribute(seq++, "class", "dxbl-fl-ec");
if (_isEditMode && !column.ReadOnly)
{
RenderEditableCell(column, dataItem, dataItemType, value, displayText, settingsType)(builder);
}
else
{
RenderCellContent(column, value, displayText, settingsType)(builder);
}
builder.CloseElement();
builder.CloseElement();
builder.CloseElement();
}
builder.CloseElement();
};
private static string GetColumnCaption(DxGridDataColumn column)
{
return !string.IsNullOrWhiteSpace(column.Caption) ? column.Caption : column.FieldName;
}
private static string GetCaptionCssClass(bool isReadOnly)
{
return isReadOnly ? "fw-semibold" : "fw-semibold text-primary";
}
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 = dataItemType.GetProperty(fieldName);
if (propertyInfo == null)
{
RenderCellContent(column, value, displayText, settingsType)(builder);
return;
}
var propertyType = propertyInfo.PropertyType;
var underlyingType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;
// ComboBox
if (settingsType == EditSettingsType.ComboBox)
{
var comboSettings = GetEditSettings(column.FieldName) as DxComboBoxSettings;
if (comboSettings != null)
{
RenderComboBoxEditor(builder, ref seq, dataItem, propertyInfo, comboSettings);
return;
}
}
// 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(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)
RenderMemoEditor(builder, ref seq, dataItem, propertyInfo);
else
RenderTextBoxEditor(builder, ref seq, dataItem, 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 =>
{
propertyInfo.SetValue(dataItem, newValue);
InvokeAsync(StateHasChanged);
}));
builder.CloseComponent();
}
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);
if (isNullable)
{
builder.OpenComponent<DxDateEdit<DateTime?>>(seq++);
builder.AddAttribute(seq++, "Date", (DateTime?)currentValue);
builder.AddAttribute(seq++, "DateChanged", EventCallback.Factory.Create<DateTime?>(this, newValue =>
{
propertyInfo.SetValue(dataItem, newValue);
InvokeAsync(StateHasChanged);
}));
}
else
{
builder.OpenComponent<DxDateEdit<DateTime>>(seq++);
builder.AddAttribute(seq++, "Date", (DateTime)(currentValue ?? DateTime.MinValue));
builder.AddAttribute(seq++, "DateChanged", EventCallback.Factory.Create<DateTime>(this, newValue =>
{
propertyInfo.SetValue(dataItem, newValue);
InvokeAsync(StateHasChanged);
}));
}
builder.AddAttribute(seq++, "DisplayFormat", displayFormat ?? "yyyy-MM-dd HH:mm");
builder.CloseComponent();
}
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);
if (isNullable)
{
builder.OpenComponent<DxDateEdit<DateOnly?>>(seq++);
builder.AddAttribute(seq++, "Date", (DateOnly?)currentValue);
builder.AddAttribute(seq++, "DateChanged", EventCallback.Factory.Create<DateOnly?>(this, newValue =>
{
propertyInfo.SetValue(dataItem, newValue);
InvokeAsync(StateHasChanged);
}));
}
else
{
builder.OpenComponent<DxDateEdit<DateOnly>>(seq++);
builder.AddAttribute(seq++, "Date", (DateOnly)(currentValue ?? DateOnly.MinValue));
builder.AddAttribute(seq++, "DateChanged", EventCallback.Factory.Create<DateOnly>(this, newValue =>
{
propertyInfo.SetValue(dataItem, newValue);
InvokeAsync(StateHasChanged);
}));
}
builder.AddAttribute(seq++, "DisplayFormat", displayFormat ?? "yyyy-MM-dd");
builder.CloseComponent();
}
private void RenderSpinIntEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo)
{
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
var currentValue = propertyInfo.GetValue(dataItem);
if (isNullable)
{
builder.OpenComponent<DxSpinEdit<int?>>(seq++);
builder.AddAttribute(seq++, "Value", (int?)currentValue);
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<int?>(this, newValue =>
{
propertyInfo.SetValue(dataItem, newValue);
InvokeAsync(StateHasChanged);
}));
}
else
{
builder.OpenComponent<DxSpinEdit<int>>(seq++);
builder.AddAttribute(seq++, "Value", (int)(currentValue ?? 0));
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<int>(this, newValue =>
{
propertyInfo.SetValue(dataItem, newValue);
InvokeAsync(StateHasChanged);
}));
}
builder.CloseComponent();
}
private void RenderSpinDecimalEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo)
{
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
var currentValue = propertyInfo.GetValue(dataItem);
if (isNullable)
{
builder.OpenComponent<DxSpinEdit<decimal?>>(seq++);
builder.AddAttribute(seq++, "Value", (decimal?)currentValue);
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<decimal?>(this, newValue =>
{
propertyInfo.SetValue(dataItem, newValue);
InvokeAsync(StateHasChanged);
}));
}
else
{
builder.OpenComponent<DxSpinEdit<decimal>>(seq++);
builder.AddAttribute(seq++, "Value", (decimal)(currentValue ?? 0m));
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<decimal>(this, newValue =>
{
propertyInfo.SetValue(dataItem, newValue);
InvokeAsync(StateHasChanged);
}));
}
builder.CloseComponent();
}
private void RenderSpinDoubleEditor(RenderTreeBuilder builder, ref int seq, object dataItem, PropertyInfo propertyInfo)
{
var isNullable = Nullable.GetUnderlyingType(propertyInfo.PropertyType) != null;
var currentValue = propertyInfo.GetValue(dataItem);
if (isNullable)
{
builder.OpenComponent<DxSpinEdit<double?>>(seq++);
builder.AddAttribute(seq++, "Value", (double?)currentValue);
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<double?>(this, newValue =>
{
propertyInfo.SetValue(dataItem, newValue);
InvokeAsync(StateHasChanged);
}));
}
else
{
builder.OpenComponent<DxSpinEdit<double>>(seq++);
builder.AddAttribute(seq++, "Value", (double)(currentValue ?? 0d));
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<double>(this, newValue =>
{
propertyInfo.SetValue(dataItem, newValue);
InvokeAsync(StateHasChanged);
}));
}
builder.CloseComponent();
}
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 =>
{
propertyInfo.SetValue(dataItem, newValue);
InvokeAsync(StateHasChanged);
}));
builder.CloseComponent();
}
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 =>
{
propertyInfo.SetValue(dataItem, newValue);
InvokeAsync(StateHasChanged);
}));
builder.AddAttribute(seq++, "Rows", 3);
builder.CloseComponent();
}
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;
var dataType = settings.Data?.GetType();
var itemType = typeof(object);
if (dataType?.IsGenericType == true)
{
var genericArgs = dataType.GetGenericArguments();
if (genericArgs.Length > 0)
itemType = genericArgs[0];
}
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
{
var displayText = ResolveComboBoxDisplayText(settings, currentValue ?? new object()) ?? currentValue?.ToString() ?? string.Empty;
builder.OpenComponent<DxTextBox>(seq++);
builder.AddAttribute(seq++, "Text", displayText);
builder.AddAttribute(seq++, "ReadOnly", true);
builder.CloseComponent();
}
}
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;
var comboType = isNullable
? typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(int?))
: typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(int));
builder.OpenComponent(seq++, comboType);
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?);
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<int?>(this, newValue =>
{
propertyInfo.SetValue(dataItem, newValue);
StateHasChanged();
}));
}
else
{
builder.AddAttribute(seq++, "Value", currentValue is int intVal ? intVal : 0);
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<int>(this, newValue =>
{
propertyInfo.SetValue(dataItem, newValue);
StateHasChanged();
}));
}
builder.AddAttribute(seq++, "ClearButtonDisplayMode", DataEditorClearButtonDisplayMode.Auto);
builder.CloseComponent();
}
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
? typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(long?))
: typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(long));
builder.OpenComponent(seq++, comboType);
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?);
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<long?>(this, newValue =>
{
propertyInfo.SetValue(dataItem, newValue);
StateHasChanged();
}));
}
else
{
builder.AddAttribute(seq++, "Value", currentValue is long longVal ? longVal : 0L);
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<long>(this, newValue =>
{
propertyInfo.SetValue(dataItem, newValue);
StateHasChanged();
}));
}
builder.AddAttribute(seq++, "ClearButtonDisplayMode", DataEditorClearButtonDisplayMode.Auto);
builder.CloseComponent();
}
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
? typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(Guid?))
: typeof(DxComboBox<,>).MakeGenericType(itemType, typeof(Guid));
builder.OpenComponent(seq++, comboType);
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?);
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<Guid?>(this, newValue =>
{
propertyInfo.SetValue(dataItem, newValue);
StateHasChanged();
}));
}
else
{
builder.AddAttribute(seq++, "Value", currentValue is Guid guidVal ? guidVal : Guid.Empty);
builder.AddAttribute(seq++, "ValueChanged", EventCallback.Factory.Create<Guid>(this, newValue =>
{
propertyInfo.SetValue(dataItem, newValue);
StateHasChanged();
}));
}
builder.AddAttribute(seq++, "ClearButtonDisplayMode", DataEditorClearButtonDisplayMode.Auto);
builder.CloseComponent();
}
private RenderFragment RenderCellContent(DxGridDataColumn column, object? value, string displayText, EditSettingsType settingsType)
{
return builder =>
{
var seq = 0;
// View mode: simple span display with DevExpress theme styling
builder.OpenElement(seq++, "span");
builder.AddAttribute(seq++, "class", GetViewModeCssClass(value, settingsType));
builder.AddAttribute(seq++, "title", displayText);
// Special handling for boolean - show checkbox icon
if (value is bool boolValue)
{
builder.OpenElement(seq++, "span");
builder.AddAttribute(seq++, "class", boolValue ? "dx-icon dx-icon-check text-success" : "dx-icon dx-icon-close text-muted");
builder.CloseElement();
}
else
{
builder.AddContent(seq++, displayText);
}
builder.CloseElement();
};
}
private static string GetViewModeCssClass(object? value, EditSettingsType settingsType)
{
var baseCss = "mg-info-panel-value text-body";
return value switch
{
bool => $"{baseCss} mg-info-panel-value-bool",
decimal or double or float or int or long or short => $"{baseCss} mg-info-panel-value-numeric",
DateTime or DateOnly or TimeOnly => $"{baseCss} mg-info-panel-value-date",
_ => baseCss
};
}
}