Add grid font size selector and fix German character display

Introduced font size adjustment for grids via dropdown toolbar and CSS variable. Added JavaScript for dynamic font size changes. Replaced German umlauts with HTML entities for proper rendering. Refactored grid code and improved error/info message display. Enhances accessibility and user experience.
This commit is contained in:
OlgunR
2026-02-19 17:01:33 +01:00
parent e7aa41aa4d
commit c5ca9f0048
5 changed files with 111 additions and 28 deletions

View File

@@ -25,6 +25,7 @@
<link rel="stylesheet" href="app.css" /> <link rel="stylesheet" href="app.css" />
<link rel="stylesheet" href="DbFirst.BlazorWebApp.styles.css" /> <link rel="stylesheet" href="DbFirst.BlazorWebApp.styles.css" />
<link rel="icon" type="image/png" href="favicon.png" /> <link rel="icon" type="image/png" href="favicon.png" />
<script src="js/size-manager.js"></script>
<HeadOutlet /> <HeadOutlet />
</head> </head>

View File

@@ -75,15 +75,15 @@ else if (!string.IsNullOrWhiteSpace(infoMessage))
} }
else if (items.Count == 0) else if (items.Count == 0)
{ {
<p>Keine Einträge vorhanden.</p> <p>Keine Eintr&#228;ge vorhanden.</p>
} }
else else
{ {
<div class="band-editor"> <div class="band-editor">
<div class="band-controls"> <div class="band-controls">
<DxButton Text="Band hinzufügen" Click="AddBand" /> <DxButton Text="Band hinzuf&#252;gen" Click="AddBand" />
<DxButton Text="Layout speichern" Click="SaveLayoutAsync" Enabled="@CanSaveBandLayout" /> <DxButton Text="Layout speichern" Click="SaveLayoutAsync" Enabled="@CanSaveBandLayout" />
<DxButton Text="Band-Layout zurücksetzen" Click="ResetBandLayoutAsync" /> <DxButton Text="Band-Layout zur&#252;cksetzen" Click="ResetBandLayoutAsync" />
</div> </div>
@foreach (var band in bandLayout.Bands) @foreach (var band in bandLayout.Bands)
{ {
@@ -113,6 +113,7 @@ else
<DxGrid Data="@items" <DxGrid Data="@items"
TItem="CatalogReadDto" TItem="CatalogReadDto"
KeyFieldName="@nameof(CatalogReadDto.Guid)" KeyFieldName="@nameof(CatalogReadDto.Guid)"
SizeMode="@_sizeMode"
ShowGroupPanel="true" ShowGroupPanel="true"
ShowGroupedColumns="true" ShowGroupedColumns="true"
AllowGroup="true" AllowGroup="true"
@@ -131,6 +132,25 @@ else
FocusedRowEnabled="true" FocusedRowEnabled="true"
@bind-FocusedRowKey="focusedRowKey" @bind-FocusedRowKey="focusedRowKey"
@ref="gridRef"> @ref="gridRef">
<ToolbarTemplate>
<DxToolbar>
<DxToolbarItem Alignment="ToolbarItemAlignment.Right">
<Template Context="_">
<DxDropDownButton Text="@FormatSizeText(_sizeMode)"
RenderStyle="ButtonRenderStyle.Secondary"
RenderStyleMode="ButtonRenderStyleMode.Text"
ItemClick="OnSizeChange">
<Items>
@foreach (var size in _sizeModes)
{
<DxDropDownButtonItem Text="@FormatSizeText(size)" Id="@size.ToString()" />
}
</Items>
</DxDropDownButton>
</Template>
</DxToolbarItem>
</DxToolbar>
</ToolbarTemplate>
<Columns> <Columns>
@RenderColumns() @RenderColumns()
</Columns> </Columns>
@@ -191,8 +211,8 @@ else
new() { FieldName = nameof(CatalogReadDto.CatString), Caption = "String", FilterType = ColumnFilterType.Text }, new() { FieldName = nameof(CatalogReadDto.CatString), Caption = "String", FilterType = ColumnFilterType.Text },
new() { FieldName = nameof(CatalogReadDto.AddedWho), Caption = "Angelegt von", ReadOnly = true, FilterType = ColumnFilterType.Text }, new() { FieldName = nameof(CatalogReadDto.AddedWho), Caption = "Angelegt von", ReadOnly = true, FilterType = ColumnFilterType.Text },
new() { FieldName = nameof(CatalogReadDto.AddedWhen), Caption = "Angelegt am", ReadOnly = true, FilterType = ColumnFilterType.Date }, new() { FieldName = nameof(CatalogReadDto.AddedWhen), Caption = "Angelegt am", ReadOnly = true, FilterType = ColumnFilterType.Date },
new() { FieldName = nameof(CatalogReadDto.ChangedWho), Caption = "Geändert von", ReadOnly = true, FilterType = ColumnFilterType.Text }, new() { FieldName = nameof(CatalogReadDto.ChangedWho), Caption = "Ge&#228;ndert von", ReadOnly = true, FilterType = ColumnFilterType.Text },
new() { FieldName = nameof(CatalogReadDto.ChangedWhen), Caption = "Geändert am", ReadOnly = true, FilterType = ColumnFilterType.Date } new() { FieldName = nameof(CatalogReadDto.ChangedWhen), Caption = "Ge&#228;ndert am", ReadOnly = true, FilterType = ColumnFilterType.Date }
}; };
private readonly List<ProcedureOption> procedureOptions = new() private readonly List<ProcedureOption> procedureOptions = new()
@@ -204,6 +224,23 @@ else
private bool CanSaveBandLayout => !string.IsNullOrWhiteSpace(layoutUser); private bool CanSaveBandLayout => !string.IsNullOrWhiteSpace(layoutUser);
private bool gridLayoutApplied; private bool gridLayoutApplied;
private DevExpress.Blazor.SizeMode _sizeMode = DevExpress.Blazor.SizeMode.Medium;
private static readonly List<DevExpress.Blazor.SizeMode> _sizeModes =
Enum.GetValues<DevExpress.Blazor.SizeMode>().ToList();
private string FormatSizeText(DevExpress.Blazor.SizeMode size) => size switch
{
DevExpress.Blazor.SizeMode.Small => "Klein",
DevExpress.Blazor.SizeMode.Medium => "Mittel",
DevExpress.Blazor.SizeMode.Large => "Groß",
_ => size.ToString()
};
private void OnSizeChange(DropDownButtonItemClickEventArgs args)
{
_sizeMode = Enum.Parse<DevExpress.Blazor.SizeMode>(args.ItemInfo.Id);
}
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase); columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase);
@@ -397,7 +434,7 @@ else
if (editModel.UpdateProcedure == 0 && if (editModel.UpdateProcedure == 0 &&
!string.Equals(editModel.CatTitle, editModel.OriginalCatTitle, StringComparison.OrdinalIgnoreCase)) !string.Equals(editModel.CatTitle, editModel.OriginalCatTitle, StringComparison.OrdinalIgnoreCase))
{ {
AddValidationError(editModel, nameof(CatalogEditModel.CatTitle), "Titel kann nicht geändert werden."); AddValidationError(editModel, nameof(CatalogEditModel.CatTitle), "Titel kann nicht ge&#228;ndert werden.");
return false; return false;
} }
@@ -416,17 +453,17 @@ else
var deleted = await Api.DeleteAsync(item.Guid); var deleted = await Api.DeleteAsync(item.Guid);
if (!deleted.Success) if (!deleted.Success)
{ {
errorMessage = deleted.Error ?? "Löschen fehlgeschlagen."; errorMessage = deleted.Error ?? "L&#246;schen fehlgeschlagen.";
e.Cancel = true; e.Cancel = true;
return; return;
} }
infoMessage = "Katalog gelöscht."; infoMessage = "Katalog gel&#246;scht.";
await LoadCatalogs(); await LoadCatalogs();
} }
catch (Exception ex) catch (Exception ex)
{ {
errorMessage = $"Fehler beim Löschen: {ex.Message}"; errorMessage = $"Fehler beim L&#246;schen: {ex.Message}";
e.Cancel = true; e.Cancel = true;
} }
} }
@@ -529,7 +566,7 @@ else
bandLayout = new BandLayout(); bandLayout = new BandLayout();
columnBandAssignments.Clear(); columnBandAssignments.Clear();
UpdateBandOptions(); UpdateBandOptions();
infoMessage = "Band-Layout zurückgesetzt."; infoMessage = "Band-Layout zur&#252;ckgesetzt.";
} }
private void ApplyColumnLayoutFromStorage() private void ApplyColumnLayoutFromStorage()

View File

@@ -83,12 +83,12 @@ else if (!string.IsNullOrWhiteSpace(infoMessage))
} }
else if (items.Count == 0) else if (items.Count == 0)
{ {
<p>Keine Einträge vorhanden.</p> <p>Keine Eintr&#228;ge vorhanden.</p>
} }
else else
{ {
<div class="mb-3 page-size-selector"> <div class="mb-3 page-size-selector">
<span class="page-size-label">Datensätze je Seite:</span> <span class="page-size-label">Datens&#228;tze je Seite:</span>
<DxComboBox Data="@pageSizeOptions" <DxComboBox Data="@pageSizeOptions"
TData="PageSizeOption" TData="PageSizeOption"
TValue="int?" TValue="int?"
@@ -101,9 +101,9 @@ else
<div class="band-editor"> <div class="band-editor">
<div class="band-controls"> <div class="band-controls">
<DxButton Text="Band hinzufügen" Click="AddBand" /> <DxButton Text="Band hinzuf&#252;gen" Click="AddBand" />
<DxButton Text="Layout speichern" Click="SaveLayoutAsync" Enabled="@CanSaveBandLayout" /> <DxButton Text="Layout speichern" Click="SaveLayoutAsync" Enabled="@CanSaveBandLayout" />
<DxButton Text="Band-Layout zurücksetzen" Click="ResetBandLayoutAsync" /> <DxButton Text="Band-Layout zur&#252;cksetzen" Click="ResetBandLayoutAsync" />
</div> </div>
@foreach (var band in bandLayout.Bands) @foreach (var band in bandLayout.Bands)
{ {
@@ -133,6 +133,7 @@ else
<DxGrid Data="@items" <DxGrid Data="@items"
TItem="MassDataReadDto" TItem="MassDataReadDto"
KeyFieldName="@nameof(MassDataReadDto.Id)" KeyFieldName="@nameof(MassDataReadDto.Id)"
SizeMode="@_sizeMode"
ShowGroupPanel="true" ShowGroupPanel="true"
ShowGroupedColumns="true" ShowGroupedColumns="true"
AllowGroup="true" AllowGroup="true"
@@ -151,6 +152,25 @@ else
FocusedRowEnabled="true" FocusedRowEnabled="true"
@bind-FocusedRowKey="focusedRowKey" @bind-FocusedRowKey="focusedRowKey"
@ref="gridRef"> @ref="gridRef">
<ToolbarTemplate>
<DxToolbar>
<DxToolbarItem Alignment="ToolbarItemAlignment.Right">
<Template Context="_">
<DxDropDownButton Text="@FormatSizeText(_sizeMode)"
RenderStyle="ButtonRenderStyle.Secondary"
RenderStyleMode="ButtonRenderStyleMode.Text"
ItemClick="OnSizeChange">
<Items>
@foreach (var size in _sizeModes)
{
<DxDropDownButtonItem Text="@FormatSizeText(size)" Id="@size.ToString()" />
}
</Items>
</DxDropDownButton>
</Template>
</DxToolbarItem>
</DxToolbar>
</ToolbarTemplate>
<Columns> <Columns>
@RenderColumns() @RenderColumns()
</Columns> </Columns>
@@ -249,6 +269,23 @@ else
private bool gridLayoutApplied; private bool gridLayoutApplied;
private DevExpress.Blazor.SizeMode _sizeMode = DevExpress.Blazor.SizeMode.Medium;
private static readonly List<DevExpress.Blazor.SizeMode> _sizeModes =
Enum.GetValues<DevExpress.Blazor.SizeMode>().ToList();
private string FormatSizeText(DevExpress.Blazor.SizeMode size) => size switch
{
DevExpress.Blazor.SizeMode.Small => "Klein",
DevExpress.Blazor.SizeMode.Medium => "Mittel",
DevExpress.Blazor.SizeMode.Large => "Groß",
_ => size.ToString()
};
private void OnSizeChange(DropDownButtonItemClickEventArgs args)
{
_sizeMode = Enum.Parse<DevExpress.Blazor.SizeMode>(args.ItemInfo.Id);
}
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase); columnLookup = columnDefinitions.ToDictionary(column => column.FieldName, StringComparer.OrdinalIgnoreCase);
@@ -392,7 +429,7 @@ else
bandLayout = new BandLayout(); bandLayout = new BandLayout();
columnBandAssignments.Clear(); columnBandAssignments.Clear();
UpdateBandOptions(); UpdateBandOptions();
infoMessage = "Band-Layout zurückgesetzt."; infoMessage = "Band-Layout zur&#252;ckgesetzt.";
} }
private void ApplyColumnLayoutFromStorage() private void ApplyColumnLayoutFromStorage()
@@ -653,7 +690,7 @@ else
var editModel = (MassDataEditModel)e.EditModel; var editModel = (MassDataEditModel)e.EditModel;
if (!decimal.TryParse(editModel.AmountText, out var amount)) if (!decimal.TryParse(editModel.AmountText, out var amount))
{ {
AddValidationError(editModel, nameof(MassDataEditModel.AmountText), "Amount ist ungültig."); AddValidationError(editModel, nameof(MassDataEditModel.AmountText), "Amount ist ung&#252;ltig.");
e.Cancel = true; e.Cancel = true;
return; return;
} }
@@ -706,11 +743,21 @@ else
private Task OnDataItemDeleting(GridDataItemDeletingEventArgs e) private Task OnDataItemDeleting(GridDataItemDeletingEventArgs e)
{ {
errorMessage = null; errorMessage = null;
infoMessage = "Löschen ist aktuell noch nicht verfügbar."; infoMessage = "L&#246;schen ist aktuell noch nicht verf&#252;gbar.";
e.Cancel = true; e.Cancel = true;
return Task.CompletedTask; return Task.CompletedTask;
} }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (!gridLayoutApplied && gridRef != null && bandLayout.GridLayout != null)
{
gridRef.LoadLayout(bandLayout.GridLayout);
gridLayoutApplied = true;
await InvokeAsync(StateHasChanged);
}
}
private sealed class BandLayout private sealed class BandLayout
{ {
public List<BandDefinition> Bands { get; set; } = new(); public List<BandDefinition> Bands { get; set; } = new();
@@ -784,14 +831,4 @@ else
public string Value { get; set; } = string.Empty; public string Value { get; set; } = string.Empty;
public string Text { get; set; } = string.Empty; public string Text { get; set; } = string.Empty;
} }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (!gridLayoutApplied && gridRef != null && bandLayout.GridLayout != null)
{
gridRef.LoadLayout(bandLayout.GridLayout);
gridLayoutApplied = true;
await InvokeAsync(StateHasChanged);
}
}
} }

View File

@@ -1,5 +1,10 @@
:root {
--global-size: 16px;
}
html, body { html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: var(--global-size);
} }
.app-dark { .app-dark {

View File

@@ -0,0 +1,3 @@
window.setSize = function (fontSize) {
document.documentElement.style.setProperty('--global-size', fontSize);
};