diff --git a/DbFirst.BlazorWebApp/Components/BandGridBase.cs b/DbFirst.BlazorWebApp/Components/BandGridBase.cs new file mode 100644 index 0000000..62517e8 --- /dev/null +++ b/DbFirst.BlazorWebApp/Components/BandGridBase.cs @@ -0,0 +1,215 @@ +using DbFirst.BlazorWebApp.Models.Grid; +using DbFirst.BlazorWebApp.Services; +using DevExpress.Blazor; +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Rendering; + +namespace DbFirst.BlazorWebApp.Components; + +public abstract class BandGridBase : ComponentBase +{ + [Inject] protected BandLayoutService BandLayoutService { get; set; } = default!; + + // --- Abstract: jedes Grid definiert diese selbst --- + protected abstract string LayoutKey { get; } + protected abstract List ColumnDefinitions { get; } + + // --- Band-Layout Felder --- + protected BandLayout bandLayout = new(); + protected Dictionary columnBandAssignments = new(); + protected List bandOptions = new(); + protected Dictionary columnLookup = new(); + protected string? layoutUser; + protected bool gridLayoutApplied; + protected bool bandEditorExpanded; + protected IGrid? gridRef; + + // --- SizeMode --- + protected SizeMode _sizeMode = SizeMode.Medium; + protected static readonly List _sizeModes = Enum.GetValues().ToList(); + + private const string LayoutType = "GRID_BANDS"; + + // --- Lifecycle --- + protected async Task InitializeBandLayoutAsync() + { + columnLookup = ColumnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase); + layoutUser = await BandLayoutService.EnsureLayoutUserAsync(); + bandLayout = await BandLayoutService.LoadBandLayoutAsync(LayoutType, LayoutKey, layoutUser, columnLookup); + columnBandAssignments = BandLayoutService.BuildAssignmentsFromLayout(bandLayout); + ApplyColumnLayoutFromStorage(); + _sizeMode = bandLayout.SizeMode; + UpdateBandOptions(); + } + + protected async Task ApplyGridLayoutAfterRenderAsync() + { + if (!gridLayoutApplied && gridRef != null && bandLayout.GridLayout != null) + { + gridRef.LoadLayout(bandLayout.GridLayout); + gridLayoutApplied = true; + await InvokeAsync(StateHasChanged); + } + } + + // --- Layout speichern / zurücksetzen --- + protected async Task SaveLayoutAsync() + { + if (string.IsNullOrWhiteSpace(layoutUser)) return; + CaptureColumnLayoutFromGrid(); + await BandLayoutService.SaveBandLayoutAsync(LayoutType, LayoutKey, layoutUser, bandLayout); + } + + protected async Task ResetLayoutAsync() + { + if (string.IsNullOrWhiteSpace(layoutUser)) return; + await BandLayoutService.ResetBandLayoutAsync(LayoutType, LayoutKey, layoutUser); + bandLayout = new BandLayout(); + columnBandAssignments.Clear(); + UpdateBandOptions(); + foreach (var column in ColumnDefinitions) + column.Width = null; + columnLookup = ColumnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase); + _sizeMode = SizeMode.Medium; + gridRef?.LoadLayout(new GridPersistentLayout()); + gridLayoutApplied = false; + } + + private void CaptureColumnLayoutFromGrid() + { + if (gridRef == null) return; + var layout = gridRef.SaveLayout(); + bandLayout.GridLayout = layout; + bandLayout.SizeMode = _sizeMode; + var orderedColumns = layout.Columns + .Where(c => !string.IsNullOrWhiteSpace(c.FieldName)) + .OrderBy(c => c.VisibleIndex) + .ToList(); + bandLayout.ColumnOrder = orderedColumns.Select(c => c.FieldName).ToList(); + bandLayout.ColumnWidths = orderedColumns + .Where(c => !string.IsNullOrWhiteSpace(c.Width)) + .ToDictionary(c => c.FieldName, c => c.Width, StringComparer.OrdinalIgnoreCase); + } + + private void ApplyColumnLayoutFromStorage() + { + foreach (var column in ColumnDefinitions) + { + if (bandLayout.ColumnWidths.TryGetValue(column.FieldName, out var width) && !string.IsNullOrWhiteSpace(width)) + column.Width = width; + } + columnLookup = ColumnDefinitions.ToDictionary(c => c.FieldName, StringComparer.OrdinalIgnoreCase); + } + + // --- Band-Methoden --- + protected bool CanSaveBandLayout => !string.IsNullOrWhiteSpace(layoutUser); + + protected void AddBand() + { + bandLayout.Bands.Add(new BandDefinition { Id = Guid.NewGuid().ToString("N"), Caption = "Band" }); + UpdateBandOptions(); + } + + protected void RemoveBand(BandDefinition band) + { + bandLayout.Bands.Remove(band); + foreach (var key in columnBandAssignments.Where(p => p.Value == band.Id).Select(p => p.Key).ToList()) + columnBandAssignments.Remove(key); + UpdateBandOptions(); + SyncBandsFromAssignments(); + } + + protected void UpdateBandCaption(BandDefinition band, string value) + { + band.Caption = value; + UpdateBandOptions(); + } + + protected void UpdateColumnBand(string fieldName, string? bandId) + { + if (string.IsNullOrWhiteSpace(bandId)) + columnBandAssignments.Remove(fieldName); + else + columnBandAssignments[fieldName] = bandId; + SyncBandsFromAssignments(); + } + + protected string GetColumnBand(string fieldName) + => columnBandAssignments.TryGetValue(fieldName, out var bandId) ? bandId : string.Empty; + + protected void SyncBandsFromAssignments() + { + foreach (var band in bandLayout.Bands) + { + band.Columns = ColumnDefinitions + .Where(c => columnBandAssignments.TryGetValue(c.FieldName, out var id) && id == band.Id) + .Select(c => c.FieldName) + .ToList(); + } + StateHasChanged(); + } + + protected void UpdateBandOptions() + { + bandOptions = new List { new() { Id = string.Empty, Caption = "Ohne Band" } }; + bandOptions.AddRange(bandLayout.Bands.Select(b => new BandOption { Id = b.Id, Caption = b.Caption })); + } + + // --- SizeMode --- + protected string FormatSizeText(SizeMode size) => size switch + { + SizeMode.Small => "Klein", + SizeMode.Medium => "Mittel", + SizeMode.Large => "Groß", + _ => size.ToString() + }; + + protected void OnSizeChange(DropDownButtonItemClickEventArgs args) + { + _sizeMode = Enum.Parse(args.ItemInfo.Id); + } + + // --- RenderColumns / BuildDataColumn --- + protected RenderFragment RenderColumns() => builder => + { + var seq = 0; + builder.OpenComponent(seq++); + builder.AddAttribute(seq++, "Width", "120px"); + builder.CloseComponent(); + + var grouped = bandLayout.Bands.SelectMany(b => b.Columns).ToHashSet(StringComparer.OrdinalIgnoreCase); + foreach (var column in ColumnDefinitions.Where(c => !grouped.Contains(c.FieldName))) + BuildDataColumn(builder, ref seq, column); + + foreach (var band in bandLayout.Bands) + { + if (band.Columns.Count == 0) continue; + builder.OpenComponent(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(); + } + }; + + protected void BuildDataColumn(RenderTreeBuilder builder, ref int seq, ColumnDefinition column) + { + builder.OpenComponent(seq++); + builder.AddAttribute(seq++, "FieldName", column.FieldName); + builder.AddAttribute(seq++, "Caption", column.Caption); + if (!string.IsNullOrWhiteSpace(column.Width)) + builder.AddAttribute(seq++, "Width", column.Width); + if (!string.IsNullOrWhiteSpace(column.DisplayFormat)) + builder.AddAttribute(seq++, "DisplayFormat", column.DisplayFormat); + if (column.ReadOnly) + builder.AddAttribute(seq++, "ReadOnly", true); + builder.CloseComponent(); + } +} \ No newline at end of file