Refactor grid and band layout persistence logic

Unify band and grid layout saving into a single "Layout speichern" action and method. Store the full grid layout in BandLayout, and simplify column/band rendering logic. Apply saved layouts after render for consistency. Remove obsolete ordering logic and update messages for clarity. These changes improve robustness and maintainability of layout persistence in CatalogsGrid and MassDataGrid.
This commit is contained in:
OlgunR
2026-02-09 11:36:56 +01:00
parent d78fd5e3d1
commit 2a730ddfcc
4 changed files with 193 additions and 578 deletions

View File

@@ -2,6 +2,7 @@
@using Microsoft.AspNetCore.Components
@using Microsoft.AspNetCore.Components.Rendering
@using Microsoft.AspNetCore.Components.Forms
@using DevExpress.Blazor
@inject CatalogApiClient Api
@inject LayoutApiClient LayoutApi
@inject IJSRuntime JsRuntime
@@ -96,8 +97,7 @@ else
<div class="band-editor">
<div class="band-controls">
<DxButton Text="Band hinzufügen" Click="AddBand" />
<DxButton Text="Band-Layout speichern" Click="SaveBandLayoutAsync" Enabled="@CanSaveBandLayout" />
<DxButton Text="Grid-Layout speichern" Click="SaveGridLayoutAsync" Enabled="@CanSaveBandLayout" />
<DxButton Text="Layout speichern" Click="SaveLayoutAsync" Enabled="@CanSaveBandLayout" />
<DxButton Text="Band-Layout zurücksetzen" Click="ResetBandLayoutAsync" />
</div>
@foreach (var band in bandLayout.Bands)
@@ -211,6 +211,8 @@ else
private bool CanSaveBandLayout => !string.IsNullOrWhiteSpace(layoutUser);
private bool gridLayoutApplied;
protected override async Task OnInitializedAsync()
{
columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase);
@@ -219,6 +221,16 @@ else
await LoadCatalogs();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (!gridLayoutApplied && gridRef != null && bandLayout.GridLayout != null)
{
gridRef.LoadLayout(bandLayout.GridLayout);
gridLayoutApplied = true;
await InvokeAsync(StateHasChanged);
}
}
private void SetEditContext(EditContext context)
{
if (editContext == context)
@@ -457,16 +469,10 @@ else
columnBandAssignments = BuildAssignmentsFromLayout(bandLayout);
ApplyColumnLayoutFromStorage();
ApplyBandOrderingFromColumnOrder();
UpdateBandOptions();
}
private async Task SaveBandLayoutAsync()
{
await SaveGridLayoutAsync();
}
private async Task SaveGridLayoutAsync()
private async Task SaveLayoutAsync()
{
if (string.IsNullOrWhiteSpace(layoutUser))
{
@@ -485,15 +491,39 @@ else
UserName = layoutUser,
LayoutData = layoutData
});
infoMessage = "Grid-Layout gespeichert.";
infoMessage = "Layout gespeichert.";
errorMessage = null;
}
catch (Exception ex)
{
errorMessage = $"Grid-Layout konnte nicht gespeichert werden: {ex.Message}";
errorMessage = $"Layout konnte nicht gespeichert werden: {ex.Message}";
}
}
private void CaptureColumnLayoutFromGrid()
{
if (gridRef == null)
{
return;
}
var layout = gridRef.SaveLayout();
bandLayout.GridLayout = layout;
var orderedColumns = layout.Columns
.Where(column => !string.IsNullOrWhiteSpace(column.FieldName))
.OrderBy(column => column.VisibleIndex)
.ToList();
bandLayout.ColumnOrder = orderedColumns
.Select(column => column.FieldName)
.ToList();
bandLayout.ColumnWidths = orderedColumns
.Where(column => !string.IsNullOrWhiteSpace(column.Width))
.ToDictionary(column => column.FieldName, column => column.Width, StringComparer.OrdinalIgnoreCase);
}
private async Task ResetBandLayoutAsync()
{
if (string.IsNullOrWhiteSpace(layoutUser))
@@ -510,17 +540,6 @@ else
private void ApplyColumnLayoutFromStorage()
{
if (bandLayout.ColumnOrder.Count > 0)
{
var ordered = bandLayout.ColumnOrder
.Where(columnLookup.ContainsKey)
.Select(field => columnLookup[field])
.ToList();
ordered.AddRange(columnDefinitions.Where(column => !ordered.Contains(column)));
columnDefinitions = ordered;
}
foreach (var column in columnDefinitions)
{
if (bandLayout.ColumnWidths.TryGetValue(column.FieldName, out var width) && !string.IsNullOrWhiteSpace(width))
@@ -532,30 +551,6 @@ else
columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase);
}
private void CaptureColumnLayoutFromGrid()
{
if (gridRef == null)
{
return;
}
var gridColumns = gridRef.GetColumns()
.OfType<IGridDataColumn>()
.Where(column => !string.IsNullOrWhiteSpace(column.FieldName))
.ToList();
bandLayout.ColumnOrder = gridColumns
.OrderBy(column => column.VisibleIndex)
.Select(column => column.FieldName)
.ToList();
bandLayout.ColumnWidths = gridColumns
.Where(column => !string.IsNullOrWhiteSpace(column.Width))
.ToDictionary(column => column.FieldName, column => column.Width, StringComparer.OrdinalIgnoreCase);
ApplyBandOrderingFromColumnOrder();
}
private void ApplyBandOrderingFromColumnOrder()
{
if (bandLayout.ColumnOrder.Count == 0)
@@ -671,6 +666,8 @@ else
{
layout ??= new BandLayout();
layout.Bands ??= new List<BandDefinition>();
layout.ColumnOrder ??= new List<string>();
layout.ColumnWidths ??= new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
foreach (var band in layout.Bands)
{
if (string.IsNullOrWhiteSpace(band.Id))
@@ -710,88 +707,15 @@ else
builder.AddAttribute(seq++, "Width", "120px");
builder.CloseComponent();
var bandLookup = bandLayout.Bands.ToDictionary(band => band.Id, StringComparer.OrdinalIgnoreCase);
var renderedBands = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var orderedFields = bandLayout.ColumnOrder
.Where(columnLookup.ContainsKey)
.ToList();
if (orderedFields.Count == 0)
var grouped = bandLayout.Bands.SelectMany(band => band.Columns).ToHashSet(StringComparer.OrdinalIgnoreCase);
foreach (var column in columnDefinitions.Where(column => !grouped.Contains(column.FieldName)))
{
var grouped = bandLayout.Bands.SelectMany(band => band.Columns).ToHashSet(StringComparer.OrdinalIgnoreCase);
foreach (var column in columnDefinitions.Where(column => !grouped.Contains(column.FieldName)))
{
BuildDataColumn(builder, ref seq, column);
}
foreach (var band in bandLayout.Bands)
{
if (band.Columns.Count == 0)
{
continue;
}
builder.OpenComponent<DxGridBandColumn>(seq++);
builder.AddAttribute(seq++, "Caption", band.Caption);
builder.AddAttribute(seq++, "Columns", (RenderFragment)(bandBuilder =>
{
var bandSeq = 0;
foreach (var columnName in band.Columns)
{
if (columnLookup.TryGetValue(columnName, out var column))
{
BuildDataColumn(bandBuilder, ref bandSeq, column);
}
}
}));
builder.CloseComponent();
}
return;
}
foreach (var fieldName in orderedFields)
{
if (columnBandAssignments.TryGetValue(fieldName, out var bandId) && bandLookup.TryGetValue(bandId, out var band))
{
if (!renderedBands.Add(bandId) || band.Columns.Count == 0)
{
continue;
}
builder.OpenComponent<DxGridBandColumn>(seq++);
builder.AddAttribute(seq++, "Caption", band.Caption);
builder.AddAttribute(seq++, "Columns", (RenderFragment)(bandBuilder =>
{
var bandSeq = 0;
foreach (var columnName in band.Columns)
{
if (columnLookup.TryGetValue(columnName, out var column))
{
BuildDataColumn(bandBuilder, ref bandSeq, column);
}
}
}));
builder.CloseComponent();
}
else if (columnLookup.TryGetValue(fieldName, out var column))
{
BuildDataColumn(builder, ref seq, column);
}
}
foreach (var column in columnDefinitions)
{
if (!orderedFields.Contains(column.FieldName, StringComparer.OrdinalIgnoreCase) &&
(!columnBandAssignments.TryGetValue(column.FieldName, out var bandId) || !bandLookup.ContainsKey(bandId)))
{
BuildDataColumn(builder, ref seq, column);
}
BuildDataColumn(builder, ref seq, column);
}
foreach (var band in bandLayout.Bands)
{
if (renderedBands.Contains(band.Id) || band.Columns.Count == 0)
if (band.Columns.Count == 0)
{
continue;
}
@@ -856,6 +780,7 @@ else
public List<BandDefinition> Bands { get; set; } = new();
public List<string> ColumnOrder { get; set; } = new();
public Dictionary<string, string?> ColumnWidths { get; set; } = new(StringComparer.OrdinalIgnoreCase);
public GridPersistentLayout? GridLayout { get; set; }
}
private sealed class BandDefinition

View File

@@ -2,6 +2,7 @@
@using Microsoft.AspNetCore.Components
@using Microsoft.AspNetCore.Components.Rendering
@using Microsoft.AspNetCore.Components.Forms
@using DevExpress.Blazor
@inject MassDataApiClient Api
@inject LayoutApiClient LayoutApi
@inject IJSRuntime JsRuntime
@@ -131,8 +132,7 @@ else
<div class="band-editor">
<div class="band-controls">
<DxButton Text="Band hinzufügen" Click="AddBand" />
<DxButton Text="Band-Layout speichern" Click="SaveBandLayoutAsync" Enabled="@CanSaveBandLayout" />
<DxButton Text="Grid-Layout speichern" Click="SaveGridLayoutAsync" Enabled="@CanSaveBandLayout" />
<DxButton Text="Layout speichern" Click="SaveLayoutAsync" Enabled="@CanSaveBandLayout" />
<DxButton Text="Band-Layout zurücksetzen" Click="ResetBandLayoutAsync" />
</div>
@foreach (var band in bandLayout.Bands)
@@ -353,16 +353,10 @@ else
columnBandAssignments = BuildAssignmentsFromLayout(bandLayout);
ApplyColumnLayoutFromStorage();
ApplyBandOrderingFromColumnOrder();
UpdateBandOptions();
}
private async Task SaveBandLayoutAsync()
{
await SaveGridLayoutAsync();
}
private async Task SaveGridLayoutAsync()
private async Task SaveLayoutAsync()
{
if (string.IsNullOrWhiteSpace(layoutUser))
{
@@ -381,15 +375,39 @@ else
UserName = layoutUser,
LayoutData = layoutData
});
infoMessage = "Grid-Layout gespeichert.";
infoMessage = "Layout gespeichert.";
errorMessage = null;
}
catch (Exception ex)
{
errorMessage = $"Grid-Layout konnte nicht gespeichert werden: {ex.Message}";
errorMessage = $"Layout konnte nicht gespeichert werden: {ex.Message}";
}
}
private void CaptureColumnLayoutFromGrid()
{
if (gridRef == null)
{
return;
}
var layout = gridRef.SaveLayout();
bandLayout.GridLayout = layout;
var orderedColumns = layout.Columns
.Where(column => !string.IsNullOrWhiteSpace(column.FieldName))
.OrderBy(column => column.VisibleIndex)
.ToList();
bandLayout.ColumnOrder = orderedColumns
.Select(column => column.FieldName)
.ToList();
bandLayout.ColumnWidths = orderedColumns
.Where(column => !string.IsNullOrWhiteSpace(column.Width))
.ToDictionary(column => column.FieldName, column => column.Width, StringComparer.OrdinalIgnoreCase);
}
private async Task ResetBandLayoutAsync()
{
if (string.IsNullOrWhiteSpace(layoutUser))
@@ -406,17 +424,6 @@ else
private void ApplyColumnLayoutFromStorage()
{
if (bandLayout.ColumnOrder.Count > 0)
{
var ordered = bandLayout.ColumnOrder
.Where(columnLookup.ContainsKey)
.Select(field => columnLookup[field])
.ToList();
ordered.AddRange(columnDefinitions.Where(column => !ordered.Contains(column)));
columnDefinitions = ordered;
}
foreach (var column in columnDefinitions)
{
if (bandLayout.ColumnWidths.TryGetValue(column.FieldName, out var width) && !string.IsNullOrWhiteSpace(width))
@@ -428,73 +435,6 @@ else
columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase);
}
private void CaptureColumnLayoutFromGrid()
{
if (gridRef == null)
{
return;
}
var gridColumns = gridRef.GetColumns()
.OfType<IGridDataColumn>()
.Where(column => !string.IsNullOrWhiteSpace(column.FieldName))
.ToList();
bandLayout.ColumnOrder = gridColumns
.OrderBy(column => column.VisibleIndex)
.Select(column => column.FieldName)
.ToList();
bandLayout.ColumnWidths = gridColumns
.Where(column => !string.IsNullOrWhiteSpace(column.Width))
.ToDictionary(column => column.FieldName, column => column.Width, StringComparer.OrdinalIgnoreCase);
ApplyBandOrderingFromColumnOrder();
}
private void ApplyBandOrderingFromColumnOrder()
{
if (bandLayout.ColumnOrder.Count == 0)
{
return;
}
var bandById = bandLayout.Bands.ToDictionary(band => band.Id, StringComparer.OrdinalIgnoreCase);
var orderedBandIds = new List<string>();
var orderedColumnsByBand = bandLayout.Bands.ToDictionary(
band => band.Id,
_ => new List<string>(),
StringComparer.OrdinalIgnoreCase);
foreach (var field in bandLayout.ColumnOrder)
{
if (columnBandAssignments.TryGetValue(field, out var bandId) && bandById.ContainsKey(bandId))
{
if (!orderedBandIds.Contains(bandId, StringComparer.OrdinalIgnoreCase))
{
orderedBandIds.Add(bandId);
}
orderedColumnsByBand[bandId].Add(field);
}
}
foreach (var band in bandLayout.Bands)
{
var orderedColumns = orderedColumnsByBand[band.Id];
orderedColumns.AddRange(band.Columns.Where(column => !orderedColumns.Contains(column, StringComparer.OrdinalIgnoreCase)));
band.Columns = orderedColumns;
}
if (orderedBandIds.Count > 0)
{
bandLayout.Bands = orderedBandIds
.Select(id => bandById[id])
.Concat(bandLayout.Bands.Where(band => !orderedBandIds.Contains(band.Id, StringComparer.OrdinalIgnoreCase)))
.ToList();
}
}
private void AddBand()
{
bandLayout.Bands.Add(new BandDefinition
@@ -581,6 +521,8 @@ else
{
layout ??= new BandLayout();
layout.Bands ??= new List<BandDefinition>();
layout.ColumnOrder ??= new List<string>();
layout.ColumnWidths ??= new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
foreach (var band in layout.Bands)
{
if (string.IsNullOrWhiteSpace(band.Id))
@@ -606,88 +548,15 @@ else
builder.AddAttribute(seq++, "Width", "120px");
builder.CloseComponent();
var bandLookup = bandLayout.Bands.ToDictionary(band => band.Id, StringComparer.OrdinalIgnoreCase);
var renderedBands = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var orderedFields = bandLayout.ColumnOrder
.Where(columnLookup.ContainsKey)
.ToList();
if (orderedFields.Count == 0)
var grouped = bandLayout.Bands.SelectMany(band => band.Columns).ToHashSet(StringComparer.OrdinalIgnoreCase);
foreach (var column in columnDefinitions.Where(column => !grouped.Contains(column.FieldName)))
{
var grouped = bandLayout.Bands.SelectMany(band => band.Columns).ToHashSet(StringComparer.OrdinalIgnoreCase);
foreach (var column in columnDefinitions.Where(column => !grouped.Contains(column.FieldName)))
{
BuildDataColumn(builder, ref seq, column);
}
foreach (var band in bandLayout.Bands)
{
if (band.Columns.Count == 0)
{
continue;
}
builder.OpenComponent<DxGridBandColumn>(seq++);
builder.AddAttribute(seq++, "Caption", band.Caption);
builder.AddAttribute(seq++, "Columns", (RenderFragment)(bandBuilder =>
{
var bandSeq = 0;
foreach (var columnName in band.Columns)
{
if (columnLookup.TryGetValue(columnName, out var column))
{
BuildDataColumn(bandBuilder, ref bandSeq, column);
}
}
}));
builder.CloseComponent();
}
return;
}
foreach (var fieldName in orderedFields)
{
if (columnBandAssignments.TryGetValue(fieldName, out var bandId) && bandLookup.TryGetValue(bandId, out var band))
{
if (!renderedBands.Add(bandId) || band.Columns.Count == 0)
{
continue;
}
builder.OpenComponent<DxGridBandColumn>(seq++);
builder.AddAttribute(seq++, "Caption", band.Caption);
builder.AddAttribute(seq++, "Columns", (RenderFragment)(bandBuilder =>
{
var bandSeq = 0;
foreach (var columnName in band.Columns)
{
if (columnLookup.TryGetValue(columnName, out var column))
{
BuildDataColumn(bandBuilder, ref bandSeq, column);
}
}
}));
builder.CloseComponent();
}
else if (columnLookup.TryGetValue(fieldName, out var column))
{
BuildDataColumn(builder, ref seq, column);
}
}
foreach (var column in columnDefinitions)
{
if (!orderedFields.Contains(column.FieldName, StringComparer.OrdinalIgnoreCase) &&
(!columnBandAssignments.TryGetValue(column.FieldName, out var bandId) || !bandLookup.ContainsKey(bandId)))
{
BuildDataColumn(builder, ref seq, column);
}
BuildDataColumn(builder, ref seq, column);
}
foreach (var band in bandLayout.Bands)
{
if (renderedBands.Contains(band.Id) || band.Columns.Count == 0)
if (band.Columns.Count == 0)
{
continue;
}
@@ -873,6 +742,7 @@ else
public List<BandDefinition> Bands { get; set; } = new();
public List<string> ColumnOrder { get; set; } = new();
public Dictionary<string, string?> ColumnWidths { get; set; } = new(StringComparer.OrdinalIgnoreCase);
public GridPersistentLayout? GridLayout { get; set; }
}
private sealed class BandDefinition
@@ -934,4 +804,16 @@ else
public int? Value { get; set; }
public string Text { get; set; } = string.Empty;
}
private bool gridLayoutApplied;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (!gridLayoutApplied && gridRef != null && bandLayout.GridLayout != null)
{
gridRef.LoadLayout(bandLayout.GridLayout);
gridLayoutApplied = true;
await InvokeAsync(StateHasChanged);
}
}
}