fix: Anwesenheitskalender/Fehltagesliste nutzen die real deklarierte Boxgröße
CI / build-and-test (push) Waiting to run
CI / build-and-test (push) Waiting to run
Die DrawingValue-Builder gingen bisher immer von einer festen Breite (170) aus, unabhängig davon, wie breit die DRAWBOX/FLOWDRAWBOX in der jeweiligen Vorlage tatsächlich deklariert ist. Bei einer breiteren Box (oder einer in "pt" statt "mm" deklarierten Seite, wie im echten Fehlzeitenbrief-Template) blieb dadurch ein Großteil der eigentlich verfügbaren Fläche ungenutzt leer - sichtbar als "Luft" rechts und unterhalb des Kalenders im generierten PDF. CreateLetterDialogViewModel/LetterTemplateTools ermitteln jetzt die real deklarierte Boxbreite samt Maßeinheit aus dem geladenen Template (LetterPlaceholderBuilder.FindDeclaredDrawingBox) und reichen sie an die Builder durch, die ihr mm-entworfenes Raster entsprechend skalieren. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -62,7 +62,7 @@ public sealed class CreateLetterDialogViewModelTests : IDisposable
|
|||||||
StudentAttendanceCalendarDrawingBuilder.PlaceholderName, PlaceholderType.Drawing, true));
|
StudentAttendanceCalendarDrawingBuilder.PlaceholderName, PlaceholderType.Drawing, true));
|
||||||
var drawing = StudentAttendanceCalendarDrawingBuilder.Build("Lena Beispiel", new DateOnly(2026, 9, 1), [], []);
|
var drawing = StudentAttendanceCalendarDrawingBuilder.Build("Lena Beispiel", new DateOnly(2026, 9, 1), [], []);
|
||||||
var vm = new CreateLetterDialogViewModel(StudentWithContact("Sehr geehrte Frau Muster,"), store,
|
var vm = new CreateLetterDialogViewModel(StudentWithContact("Sehr geehrte Frau Muster,"), store,
|
||||||
new QuestTemplateRenderer(), new FakeMemberships([]), new FakeGroups([]), _ => drawing);
|
new QuestTemplateRenderer(), new FakeMemberships([]), new FakeGroups([]), (_, _, _) => drawing);
|
||||||
var output = Path.Combine(_directory, "Kalender.pdf");
|
var output = Path.Combine(_directory, "Kalender.pdf");
|
||||||
|
|
||||||
Assert.True(vm.UsesAttendanceCalendar);
|
Assert.True(vm.UsesAttendanceCalendar);
|
||||||
@@ -76,6 +76,67 @@ public sealed class CreateLetterDialogViewModelTests : IDisposable
|
|||||||
Assert.Equal("%PDF", System.Text.Encoding.ASCII.GetString(File.ReadAllBytes(output), 0, 4));
|
Assert.Equal("%PDF", System.Text.Encoding.ASCII.GetString(File.ReadAllBytes(output), 0, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Regression: Der DrawingValue-Builder hat früher immer eine feste Breite (170) angenommen,
|
||||||
|
// unabhängig davon, wie breit die DRAWBOX in der jeweiligen Vorlage tatsächlich deklariert war -
|
||||||
|
// bei breiteren (oder in "pt" statt "mm" deklarierten) Boxen blieb dadurch ein Großteil der
|
||||||
|
// eigentlich verfügbaren Fläche ungenutzt leer (sichtbar in echten Elternbriefen als "Luft"
|
||||||
|
// rechts/unterhalb des Kalenders). Der Dialog muss die real deklarierte Breite ermitteln und an
|
||||||
|
// den Builder weiterreichen.
|
||||||
|
[Fact]
|
||||||
|
public void AdvancedContent_Anwesenheitskalender_NutztDieImLayoutDeklarierteBoxbreite()
|
||||||
|
{
|
||||||
|
var manifest = new TemplateManifest
|
||||||
|
{
|
||||||
|
Id = $"brief-{Guid.NewGuid():N}", Name = "Elternbrief",
|
||||||
|
Placeholders = [new(StudentAttendanceCalendarDrawingBuilder.PlaceholderName, PlaceholderType.Drawing, true)],
|
||||||
|
};
|
||||||
|
var source = Path.Combine(_directory, $"{Guid.NewGuid():N}.lavorlage");
|
||||||
|
TemplatePackage.Create(source, manifest,
|
||||||
|
$"PAGE 210 297 mm\nDRAWBOX 20 20 250 80 ${StudentAttendanceCalendarDrawingBuilder.PlaceholderName}",
|
||||||
|
new Dictionary<string, byte[]>());
|
||||||
|
var store = new TemplateStore(Path.Combine(_directory, $"store-{Guid.NewGuid():N}"));
|
||||||
|
store.Import(source);
|
||||||
|
float? receivedWidth = null; float? receivedMillimeterScale = null;
|
||||||
|
|
||||||
|
var vm = new CreateLetterDialogViewModel(StudentWithContact("Sehr geehrte Frau Muster,"), store,
|
||||||
|
new QuestTemplateRenderer(), new FakeMemberships([]), new FakeGroups([]),
|
||||||
|
(_, width, millimeterScale) => { receivedWidth = width; receivedMillimeterScale = millimeterScale; return new DrawingValue([], 0); });
|
||||||
|
|
||||||
|
Assert.True(vm.UsesAttendanceCalendar);
|
||||||
|
Assert.Equal(250, receivedWidth);
|
||||||
|
Assert.Equal(1, receivedMillimeterScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regression: Eine reale, aus einem Word-Dokument übernommene Vorlage deklariert die Seite in
|
||||||
|
// "pt" statt "mm" (PAGE ... pt). Ohne Umrechnung würde das mm-entworfene Kalenderraster dort nur
|
||||||
|
// rund ein Drittel der vorgesehenen physischen Größe erreichen (1 "mm-Einheit" würde als 1pt statt
|
||||||
|
// als ~2.83pt gerendert).
|
||||||
|
[Fact]
|
||||||
|
public void AdvancedContent_Anwesenheitskalender_RechnetBoxbreiteBeiPunktBasierterVorlageUm()
|
||||||
|
{
|
||||||
|
var manifest = new TemplateManifest
|
||||||
|
{
|
||||||
|
Id = $"brief-{Guid.NewGuid():N}", Name = "Elternbrief",
|
||||||
|
Placeholders = [new(StudentAttendanceCalendarDrawingBuilder.PlaceholderName, PlaceholderType.Drawing, true)],
|
||||||
|
};
|
||||||
|
var source = Path.Combine(_directory, $"{Guid.NewGuid():N}.lavorlage");
|
||||||
|
TemplatePackage.Create(source, manifest,
|
||||||
|
$"PAGE 595.32 841.92 pt\nDRAWBOX 71 350 487 177 ${StudentAttendanceCalendarDrawingBuilder.PlaceholderName}",
|
||||||
|
new Dictionary<string, byte[]>());
|
||||||
|
var store = new TemplateStore(Path.Combine(_directory, $"store-{Guid.NewGuid():N}"));
|
||||||
|
store.Import(source);
|
||||||
|
float? receivedWidth = null; float? receivedMillimeterScale = null;
|
||||||
|
|
||||||
|
var vm = new CreateLetterDialogViewModel(StudentWithContact("Sehr geehrte Frau Muster,"), store,
|
||||||
|
new QuestTemplateRenderer(), new FakeMemberships([]), new FakeGroups([]),
|
||||||
|
(_, width, millimeterScale) => { receivedWidth = width; receivedMillimeterScale = millimeterScale; return new DrawingValue([], 0); });
|
||||||
|
|
||||||
|
Assert.True(vm.UsesAttendanceCalendar);
|
||||||
|
Assert.Equal(487, receivedWidth);
|
||||||
|
Assert.NotNull(receivedMillimeterScale);
|
||||||
|
Assert.Equal(2.8346f, receivedMillimeterScale!.Value, 0.001f);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task AttendanceKalenderKonfigurieren_LoestGezieltenDatenAbrufFuerDenZeitraumAus()
|
public async Task AttendanceKalenderKonfigurieren_LoestGezieltenDatenAbrufFuerDenZeitraumAus()
|
||||||
{
|
{
|
||||||
@@ -84,7 +145,7 @@ public sealed class CreateLetterDialogViewModelTests : IDisposable
|
|||||||
AttendanceCalendarOptions? requested = null;
|
AttendanceCalendarOptions? requested = null;
|
||||||
var vm = new CreateLetterDialogViewModel(StudentWithContact("Sehr geehrte Frau Muster,"), store,
|
var vm = new CreateLetterDialogViewModel(StudentWithContact("Sehr geehrte Frau Muster,"), store,
|
||||||
new QuestTemplateRenderer(), new FakeMemberships([]), new FakeGroups([]),
|
new QuestTemplateRenderer(), new FakeMemberships([]), new FakeGroups([]),
|
||||||
_ => new DrawingValue([], 0), attendanceDataRefresher: (options, _) =>
|
(_, _, _) => new DrawingValue([], 0), attendanceDataRefresher: (options, _) =>
|
||||||
{ requested = options; return Task.CompletedTask; });
|
{ requested = options; return Task.CompletedTask; });
|
||||||
|
|
||||||
await vm.SetAttendanceCalendarOptionsAsync(new AttendanceCalendarOptions(
|
await vm.SetAttendanceCalendarOptionsAsync(new AttendanceCalendarOptions(
|
||||||
@@ -104,7 +165,7 @@ public sealed class CreateLetterDialogViewModelTests : IDisposable
|
|||||||
StudentAttendanceCalendarDrawingBuilder.PlaceholderName, PlaceholderType.Drawing, true));
|
StudentAttendanceCalendarDrawingBuilder.PlaceholderName, PlaceholderType.Drawing, true));
|
||||||
var vm = new CreateLetterDialogViewModel(StudentWithContact("Sehr geehrte Frau Muster,"), store,
|
var vm = new CreateLetterDialogViewModel(StudentWithContact("Sehr geehrte Frau Muster,"), store,
|
||||||
new QuestTemplateRenderer(), new FakeMemberships([]), new FakeGroups([]),
|
new QuestTemplateRenderer(), new FakeMemberships([]), new FakeGroups([]),
|
||||||
_ => new DrawingValue([], 0),
|
(_, _, _) => new DrawingValue([], 0),
|
||||||
attendanceDataRefresher: (_, _) => throw new WebUntisIntegrationException("Keine Verbindung."));
|
attendanceDataRefresher: (_, _) => throw new WebUntisIntegrationException("Keine Verbindung."));
|
||||||
|
|
||||||
await vm.SetAttendanceCalendarOptionsAsync(new AttendanceCalendarOptions(
|
await vm.SetAttendanceCalendarOptionsAsync(new AttendanceCalendarOptions(
|
||||||
@@ -124,7 +185,7 @@ public sealed class CreateLetterDialogViewModelTests : IDisposable
|
|||||||
new AttendanceCalendarOptions(new DateOnly(2026, 9, 1), 1), []);
|
new AttendanceCalendarOptions(new DateOnly(2026, 9, 1), 1), []);
|
||||||
var vm = new CreateLetterDialogViewModel(StudentWithContact("Sehr geehrte Frau Muster,"), store,
|
var vm = new CreateLetterDialogViewModel(StudentWithContact("Sehr geehrte Frau Muster,"), store,
|
||||||
new QuestTemplateRenderer(), new FakeMemberships([]), new FakeGroups([]),
|
new QuestTemplateRenderer(), new FakeMemberships([]), new FakeGroups([]),
|
||||||
attendanceCalendarFactory: null, absenceDayListFactory: _ => drawing);
|
attendanceCalendarFactory: null, absenceDayListFactory: (_, _, _) => drawing);
|
||||||
|
|
||||||
Assert.False(vm.UsesAttendanceCalendar);
|
Assert.False(vm.UsesAttendanceCalendar);
|
||||||
Assert.True(vm.UsesAbsenceDayList);
|
Assert.True(vm.UsesAbsenceDayList);
|
||||||
|
|||||||
@@ -20,9 +20,10 @@ public static class LetterDialogs
|
|||||||
App.Services.GetRequiredService<ITemplateRenderer>(),
|
App.Services.GetRequiredService<ITemplateRenderer>(),
|
||||||
App.Services.GetRequiredService<IGroupMembershipRepository>(),
|
App.Services.GetRequiredService<IGroupMembershipRepository>(),
|
||||||
App.Services.GetRequiredService<IGroupRepository>(),
|
App.Services.GetRequiredService<IGroupRepository>(),
|
||||||
options => App.Services.GetRequiredService<StudentAttendanceCalendarService>().Build(student, options),
|
(options, contentWidth, millimeterScale) => App.Services.GetRequiredService<StudentAttendanceCalendarService>()
|
||||||
options => App.Services.GetRequiredService<StudentAttendanceCalendarService>()
|
.Build(student, options, contentWidth, millimeterScale),
|
||||||
.BuildAbsenceDayList(student, options),
|
(options, contentWidth, millimeterScale) => App.Services.GetRequiredService<StudentAttendanceCalendarService>()
|
||||||
|
.BuildAbsenceDayList(student, options, contentWidth, millimeterScale),
|
||||||
RefreshAttendanceDataAsync);
|
RefreshAttendanceDataAsync);
|
||||||
var dialog = new CreateLetterDialog { DataContext = vm };
|
var dialog = new CreateLetterDialog { DataContext = vm };
|
||||||
var path = await dialog.ShowDialog<string?>(owner);
|
var path = await dialog.ShowDialog<string?>(owner);
|
||||||
|
|||||||
@@ -50,4 +50,31 @@ public static class LetterPlaceholderBuilder
|
|||||||
DrawingValue x => x.ContentHeight <= 0 || x.Commands.Count == 0,
|
DrawingValue x => x.ContentHeight <= 0 || x.Commands.Count == 0,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>Tatsächliche DRAWBOX/FLOWDRAWBOX-Breite eines Platzhalters (in der Maßeinheit der
|
||||||
|
/// Vorlage) plus dem Umrechnungsfaktor "1mm in dieser Maßeinheit". DrawingValue-Builder wie
|
||||||
|
/// <see cref="StudentAttendanceCalendarDrawingBuilder"/> entwerfen ihr Raster in Millimetern;
|
||||||
|
/// ohne MillimeterScale bliebe das bei einer in "pt" (statt "mm") deklarierten Vorlage viel zu
|
||||||
|
/// klein (1 "mm-Einheit" würde als 1pt ≈ 0.35mm gerendert).</summary>
|
||||||
|
public readonly record struct DeclaredDrawingBox(float Width, float MillimeterScale);
|
||||||
|
|
||||||
|
public static DeclaredDrawingBox? FindDeclaredDrawingBox(LoadedTemplate template, string placeholderName) =>
|
||||||
|
FindDeclaredDrawingBox(template.Layout, placeholderName) ??
|
||||||
|
(template.ContinuationLayout is not null ? FindDeclaredDrawingBox(template.ContinuationLayout, placeholderName) : null);
|
||||||
|
|
||||||
|
private static DeclaredDrawingBox? FindDeclaredDrawingBox(TemplateLayout layout, string placeholderName)
|
||||||
|
{
|
||||||
|
var width = layout.Elements.Concat(layout.PageTemplates.SelectMany(p => p.Elements))
|
||||||
|
.Concat(layout.ContentFlows.SelectMany(f => f.Elements))
|
||||||
|
.Select(e => e switch
|
||||||
|
{
|
||||||
|
DrawBoxElement draw when draw.Placeholder == placeholderName => (float?)draw.Width,
|
||||||
|
FlowDrawBoxElement flow when flow.Placeholder == placeholderName => (float?)flow.Width,
|
||||||
|
_ => null,
|
||||||
|
})
|
||||||
|
.FirstOrDefault(w => w is not null);
|
||||||
|
if (width is null) return null;
|
||||||
|
var millimeterScale = UnitConverter.Points(1, "mm") / UnitConverter.Points(1, layout.Unit);
|
||||||
|
return new DeclaredDrawingBox(width.Value, millimeterScale);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,9 +74,15 @@ public class LetterTemplateTools(
|
|||||||
var group = groupId is { } gid ? groups.GetById(gid) : null;
|
var group = groupId is { } gid ? groups.GetById(gid) : null;
|
||||||
var date = letterDate ?? DateOnly.FromDateTime(DateTime.Today);
|
var date = letterDate ?? DateOnly.FromDateTime(DateTime.Today);
|
||||||
|
|
||||||
|
var calendarBox = LetterPlaceholderBuilder.FindDeclaredDrawingBox(loaded,
|
||||||
|
StudentAttendanceCalendarDrawingBuilder.PlaceholderName) ?? new(170, 1);
|
||||||
|
var absenceDayListBox = LetterPlaceholderBuilder.FindDeclaredDrawingBox(loaded,
|
||||||
|
StudentAbsenceDayListDrawingBuilder.PlaceholderName) ?? new(170, 1);
|
||||||
var values = LetterPlaceholderBuilder.BuildStandardValues(student, contact, group, date, letterText,
|
var values = LetterPlaceholderBuilder.BuildStandardValues(student, contact, group, date, letterText,
|
||||||
teacherName, attendanceCalendars?.Build(student, date),
|
teacherName, attendanceCalendars?.Build(student, new AttendanceCalendarOptions(date, 1),
|
||||||
attendanceCalendars?.BuildAbsenceDayList(student, new AttendanceCalendarOptions(date, 1)));
|
calendarBox.Width, calendarBox.MillimeterScale),
|
||||||
|
attendanceCalendars?.BuildAbsenceDayList(student, new AttendanceCalendarOptions(date, 1),
|
||||||
|
absenceDayListBox.Width, absenceDayListBox.MillimeterScale));
|
||||||
foreach (var (name, raw) in extraValues ?? [])
|
foreach (var (name, raw) in extraValues ?? [])
|
||||||
{
|
{
|
||||||
var definition = loaded.Manifest.Placeholders.FirstOrDefault(p => p.Name == name);
|
var definition = loaded.Manifest.Placeholders.FirstOrDefault(p => p.Name == name);
|
||||||
|
|||||||
@@ -28,42 +28,48 @@ public static class StudentAttendanceCalendarDrawingBuilder
|
|||||||
|
|
||||||
public static DrawingValue Build(string studentName, AttendanceCalendarOptions options,
|
public static DrawingValue Build(string studentName, AttendanceCalendarOptions options,
|
||||||
IReadOnlyList<ClassAbsenceDaySummaryRow> absences,
|
IReadOnlyList<ClassAbsenceDaySummaryRow> absences,
|
||||||
IReadOnlyList<UntisForeignClassRegisterEventDto> registerEntries)
|
IReadOnlyList<UntisForeignClassRegisterEventDto> registerEntries, float contentWidth = 170,
|
||||||
|
float millimeterScale = 1)
|
||||||
{
|
{
|
||||||
const float width = 170;
|
|
||||||
var scale = options.Size switch
|
var scale = options.Size switch
|
||||||
{
|
{
|
||||||
AttendanceCalendarSize.Small => .7f,
|
AttendanceCalendarSize.Small => .7f,
|
||||||
AttendanceCalendarSize.Large => 1f,
|
AttendanceCalendarSize.Large => 1f,
|
||||||
_ => .85f,
|
_ => .85f,
|
||||||
};
|
};
|
||||||
// Die Breite richtet sich nach der DRAWBOX-Deklaration im Layout-Skript (fest, unabhängig
|
// contentWidth/millimeterScale kommen aus der tatsächlichen DRAWBOX/FLOWDRAWBOX-Deklaration
|
||||||
// von der Größenwahl) - nur Schrift/Zellenhöhe skalieren mit "Größe". Würde die Breite mit
|
// der jeweiligen Vorlage (siehe LetterPlaceholderBuilder.FindDeclaredDrawingBox). Das Raster
|
||||||
// skalieren, würde "Groß" (scale=1) exakt die Skript-Box ausfüllen, "Klein"/"Normal" aber
|
// ist in Millimetern entworfen; millimeterScale rechnet das in die Koordinaten-Einheit der
|
||||||
// nur einen Teil davon - und eine größere Skalierung als 1 liefe über die Box hinaus und
|
// Vorlage um (1, wenn die Vorlage schon "mm" nutzt; ≈2.83 bei "pt") - ohne das würde das
|
||||||
// würde am rechten Rand abgeschnitten (SVG overflow="hidden").
|
// Raster bei einer in "pt" deklarierten Vorlage nur rund ein Drittel der vorgesehenen Größe
|
||||||
var contentWidth = width;
|
// erreichen. Schriftgrößen (FontSize) sind davon unabhängig immer echte Punktgrößen.
|
||||||
var cellHeight = 10 * scale;
|
// contentWidth ist unabhängig von der Größenwahl fest - nur Schrift/Zellenhöhe skalieren mit
|
||||||
// War 10 - bei 3 Monaten nebeneinander größer als eine einzelne Tageskachel und zog damit
|
// "Größe". Würde die Breite mit skalieren, würde "Groß" (scale=1) exakt die Skript-Box
|
||||||
|
// ausfüllen, "Klein"/"Normal" aber nur einen Teil davon - und eine größere Skalierung als 1
|
||||||
|
// liefe über die Box hinaus und würde am rechten Rand abgeschnitten (SVG overflow="hidden").
|
||||||
|
// Die Defaults (170/1) greifen nur, wenn die Vorlage nicht ermittelt werden kann (z.B.
|
||||||
|
// Vorschau ohne Kontext).
|
||||||
|
var cellHeight = 10 * scale * millimeterScale;
|
||||||
|
// War 10mm - bei 3 Monaten nebeneinander größer als eine einzelne Tageskachel und zog damit
|
||||||
// spürbar Platz von den Kacheln ab, ohne selbst als Inhalt wahrgenommen zu werden.
|
// spürbar Platz von den Kacheln ab, ohne selbst als Inhalt wahrgenommen zu werden.
|
||||||
var monthGapX = 6f;
|
var monthGapX = 6f * millimeterScale;
|
||||||
var first = options.NormalizedStartMonth;
|
var first = options.NormalizedStartMonth;
|
||||||
var monthCount = options.NormalizedMonthCount;
|
var monthCount = options.NormalizedMonthCount;
|
||||||
var monthWidth = (contentWidth - monthGapX * (monthCount - 1)) / monthCount;
|
var monthWidth = (contentWidth - monthGapX * (monthCount - 1)) / monthCount;
|
||||||
var cellWidth = monthWidth / 7;
|
var cellWidth = monthWidth / 7;
|
||||||
// Kachel-Zwischenraum (Trennung zu Nachbarzellen) deutlich knapper als vorher (war "scale",
|
// Kachel-Zwischenraum (Trennung zu Nachbarzellen) deutlich knapper als vorher (war "scale"
|
||||||
// also bis zu 1mm auf jeder Seite - bei einer ~7mm breiten Zelle ein gutes Viertel reiner
|
// mm, also bis zu 1mm auf jeder Seite - bei einer ~7mm breiten Zelle ein gutes Viertel
|
||||||
// Leerraum). Die Kachel selbst füllt dadurch ihren Rasterplatz sichtbar besser aus.
|
// reiner Leerraum). Die Kachel selbst füllt dadurch ihren Rasterplatz sichtbar besser aus.
|
||||||
var cellInset = scale * .4f;
|
var cellInset = scale * .4f * millimeterScale;
|
||||||
var cellCornerRadius = Math.Min(cellWidth, cellHeight) * .2f;
|
var cellCornerRadius = Math.Min(cellWidth, cellHeight) * .2f;
|
||||||
var commands = new List<DrawingCommand>();
|
var commands = new List<DrawingCommand>();
|
||||||
var y = 0f;
|
var y = 0f;
|
||||||
commands.Add(new DrawStringEx(0, y, 14 * scale, contentWidth, "Anwesenheit",
|
commands.Add(new DrawStringEx(0, y, 14 * scale * millimeterScale, contentWidth, "Anwesenheit",
|
||||||
DrawingTextAlignment.AlignLeft, 11 * scale, Color: "#1F2937", Bold: true));
|
DrawingTextAlignment.AlignLeft, 11 * scale, Color: "#1F2937", Bold: true));
|
||||||
y += 14 * scale;
|
y += 14 * scale * millimeterScale;
|
||||||
commands.Add(new DrawStringEx(0, y, 10 * scale, contentWidth, studentName,
|
commands.Add(new DrawStringEx(0, y, 10 * scale * millimeterScale, contentWidth, studentName,
|
||||||
DrawingTextAlignment.AlignLeft, 8 * scale, Color: "#6B7280"));
|
DrawingTextAlignment.AlignLeft, 8 * scale, Color: "#6B7280"));
|
||||||
y += 10 * scale + 2 * scale;
|
y += 10 * scale * millimeterScale + 2 * scale * millimeterScale;
|
||||||
var weekdays = new[] { "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So" };
|
var weekdays = new[] { "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So" };
|
||||||
var gridStartY = y;
|
var gridStartY = y;
|
||||||
var maxWeeks = 0;
|
var maxWeeks = 0;
|
||||||
@@ -77,14 +83,15 @@ public static class StudentAttendanceCalendarDrawingBuilder
|
|||||||
maxWeeks = Math.Max(maxWeeks, weeks);
|
maxWeeks = Math.Max(maxWeeks, weeks);
|
||||||
var xOffset = monthIndex * (monthWidth + monthGapX);
|
var xOffset = monthIndex * (monthWidth + monthGapX);
|
||||||
var monthY = gridStartY;
|
var monthY = gridStartY;
|
||||||
commands.Add(new DrawStringEx(xOffset, monthY, 11 * scale, monthWidth,
|
commands.Add(new DrawStringEx(xOffset, monthY, 11 * scale * millimeterScale, monthWidth,
|
||||||
current.ToString("MMMM yyyy", CultureInfo.GetCultureInfo("de-DE")),
|
current.ToString("MMMM yyyy", CultureInfo.GetCultureInfo("de-DE")),
|
||||||
DrawingTextAlignment.AlignLeft, 9 * scale, Color: "#374151", Bold: true));
|
DrawingTextAlignment.AlignLeft, 9 * scale, Color: "#374151", Bold: true));
|
||||||
monthY += 11 * scale;
|
monthY += 11 * scale * millimeterScale;
|
||||||
for (var column = 0; column < 7; column++)
|
for (var column = 0; column < 7; column++)
|
||||||
commands.Add(new DrawStringEx(xOffset + column * cellWidth, monthY, 9 * scale, cellWidth,
|
commands.Add(new DrawStringEx(xOffset + column * cellWidth, monthY, 9 * scale * millimeterScale,
|
||||||
weekdays[column], DrawingTextAlignment.AlignCenter, 7 * scale, Color: "#6B7280", Bold: true));
|
cellWidth, weekdays[column], DrawingTextAlignment.AlignCenter, 7 * scale, Color: "#6B7280",
|
||||||
monthY += 9 * scale;
|
Bold: true));
|
||||||
|
monthY += 9 * scale * millimeterScale;
|
||||||
|
|
||||||
foreach (var day in days)
|
foreach (var day in days)
|
||||||
{
|
{
|
||||||
@@ -96,17 +103,18 @@ public static class StudentAttendanceCalendarDrawingBuilder
|
|||||||
commands.Add(new DrawRoundedRectangle(x + cellInset, cellY, cellWidth - 2 * cellInset,
|
commands.Add(new DrawRoundedRectangle(x + cellInset, cellY, cellWidth - 2 * cellInset,
|
||||||
cellHeight - cellInset, cellCornerRadius, "#D1D5DB", .35f,
|
cellHeight - cellInset, cellCornerRadius, "#D1D5DB", .35f,
|
||||||
day.HasSignal ? day.SignalColorHex : "#FFFFFF"));
|
day.HasSignal ? day.SignalColorHex : "#FFFFFF"));
|
||||||
commands.Add(new DrawStringEx(x, cellY + scale, cellHeight - 2 * scale, cellWidth,
|
commands.Add(new DrawStringEx(x, cellY + scale * millimeterScale,
|
||||||
|
cellHeight - 2 * scale * millimeterScale, cellWidth,
|
||||||
day.HasSignal ? day.SignalCode : day.DayNumber, DrawingTextAlignment.AlignCenter, 7 * scale,
|
day.HasSignal ? day.SignalCode : day.DayNumber, DrawingTextAlignment.AlignCenter, 7 * scale,
|
||||||
Color: day.HasSignal ? "#FFFFFF" : "#374151", Bold: day.HasSignal));
|
Color: day.HasSignal ? "#FFFFFF" : "#374151", Bold: day.HasSignal));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
y = gridStartY + 11 * scale + 9 * scale + maxWeeks * cellHeight + monthGapX;
|
y = gridStartY + 11 * scale * millimeterScale + 9 * scale * millimeterScale + maxWeeks * cellHeight + monthGapX;
|
||||||
|
|
||||||
commands.Add(new DrawStringEx(0, y, 8 * scale, contentWidth,
|
commands.Add(new DrawStringEx(0, y, 8 * scale * millimeterScale, contentWidth,
|
||||||
"U unentschuldigt · A abwesend · V verspätet · E entschuldigt · ! Klassenbuch",
|
"U unentschuldigt · A abwesend · V verspätet · E entschuldigt · ! Klassenbuch",
|
||||||
DrawingTextAlignment.AlignLeft, 6.5f * scale, Color: "#6B7280"));
|
DrawingTextAlignment.AlignLeft, 6.5f * scale, Color: "#6B7280"));
|
||||||
return new DrawingValue(commands, y + 9 * scale);
|
return new DrawingValue(commands, y + 9 * scale * millimeterScale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,19 +124,19 @@ public static class StudentAbsenceDayListDrawingBuilder
|
|||||||
public const string PlaceholderName = "Student.AbsenceDays";
|
public const string PlaceholderName = "Student.AbsenceDays";
|
||||||
|
|
||||||
public static DrawingValue Build(string studentName, AttendanceCalendarOptions options,
|
public static DrawingValue Build(string studentName, AttendanceCalendarOptions options,
|
||||||
IReadOnlyList<ClassAbsenceDaySummaryRow> absences)
|
IReadOnlyList<ClassAbsenceDaySummaryRow> absences, float contentWidth = 170,
|
||||||
|
float millimeterScale = 1)
|
||||||
{
|
{
|
||||||
const float width = 170;
|
|
||||||
var scale = options.Size switch
|
var scale = options.Size switch
|
||||||
{
|
{
|
||||||
AttendanceCalendarSize.Small => .75f,
|
AttendanceCalendarSize.Small => .75f,
|
||||||
AttendanceCalendarSize.Large => 1f,
|
AttendanceCalendarSize.Large => 1f,
|
||||||
_ => .88f,
|
_ => .88f,
|
||||||
};
|
};
|
||||||
// Breite bleibt an die DRAWBOX-Deklaration im Layout-Skript gebunden (siehe
|
// contentWidth/millimeterScale: siehe StudentAttendanceCalendarDrawingBuilder - contentWidth
|
||||||
// StudentAttendanceCalendarDrawingBuilder) - nur Zeilenhöhe/Schrift skalieren mit "Größe".
|
// bleibt an die tatsächliche DRAWBOX/FLOWDRAWBOX-Deklaration im Layout-Skript gebunden,
|
||||||
var contentWidth = width;
|
// millimeterScale rechnet das mm-entworfene Raster in die Koordinaten-Einheit der Vorlage um.
|
||||||
var rowHeight = 12 * scale;
|
var rowHeight = 12 * scale * millimeterScale;
|
||||||
var start = options.NormalizedStartMonth;
|
var start = options.NormalizedStartMonth;
|
||||||
var end = start.AddMonths(options.NormalizedMonthCount).AddDays(-1);
|
var end = start.AddMonths(options.NormalizedMonthCount).AddDays(-1);
|
||||||
var rows = absences
|
var rows = absences
|
||||||
@@ -138,33 +146,33 @@ public static class StudentAbsenceDayListDrawingBuilder
|
|||||||
.ToList();
|
.ToList();
|
||||||
var commands = new List<DrawingCommand>();
|
var commands = new List<DrawingCommand>();
|
||||||
var y = 0f;
|
var y = 0f;
|
||||||
commands.Add(new DrawStringEx(0, y, 14 * scale, contentWidth, "Fehltage",
|
commands.Add(new DrawStringEx(0, y, 14 * scale * millimeterScale, contentWidth, "Fehltage",
|
||||||
DrawingTextAlignment.AlignLeft, 11 * scale, Color: "#1F2937", Bold: true));
|
DrawingTextAlignment.AlignLeft, 11 * scale, Color: "#1F2937", Bold: true));
|
||||||
y += 14 * scale;
|
y += 14 * scale * millimeterScale;
|
||||||
commands.Add(new DrawStringEx(0, y, 10 * scale, contentWidth, studentName,
|
commands.Add(new DrawStringEx(0, y, 10 * scale * millimeterScale, contentWidth, studentName,
|
||||||
DrawingTextAlignment.AlignLeft, 8 * scale, Color: "#6B7280"));
|
DrawingTextAlignment.AlignLeft, 8 * scale, Color: "#6B7280"));
|
||||||
y += 10 * scale + 2 * scale;
|
y += 10 * scale * millimeterScale + 2 * scale * millimeterScale;
|
||||||
// Spaltenbreiten so gewählt, dass auch die Datenzeilen (nicht nur die kurzen Kopfzeilen-
|
// Spaltenbreiten so gewählt, dass auch die Datenzeilen (nicht nur die kurzen Kopfzeilen-
|
||||||
// Labels) hineinpassen - "Datum" als Kopfzeile ist kürzer als "dd.MM.yyyy" und wurde bei
|
// Labels) hineinpassen - "Datum" als Kopfzeile ist kürzer als "dd.MM.yyyy" und wurde bei
|
||||||
// 31 zu schmal bemessen, wodurch das Datum am rechten Rand abgeschnitten wurde.
|
// 31 zu schmal bemessen, wodurch das Datum am rechten Rand abgeschnitten wurde.
|
||||||
const float dateColumnX = 2, dateColumnWidth = 38;
|
var dateColumnX = 2 * millimeterScale; var dateColumnWidth = 38 * millimeterScale;
|
||||||
const float extentColumnX = 44, extentColumnWidth = 66;
|
var extentColumnX = 44 * millimeterScale; var extentColumnWidth = 66 * millimeterScale;
|
||||||
const float statusColumnX = 114, statusColumnWidth = 54;
|
var statusColumnX = 114 * millimeterScale; var statusColumnWidth = 54 * millimeterScale;
|
||||||
commands.Add(new DrawRectangle(0, y, contentWidth, rowHeight, "#CBD5E1", .4f, "#F3F4F6"));
|
commands.Add(new DrawRectangle(0, y, contentWidth, rowHeight, "#CBD5E1", .4f, "#F3F4F6"));
|
||||||
commands.Add(new DrawStringEx(dateColumnX, y + scale, rowHeight - scale, dateColumnWidth, "Datum",
|
commands.Add(new DrawStringEx(dateColumnX, y + scale * millimeterScale, rowHeight - scale * millimeterScale,
|
||||||
DrawingTextAlignment.AlignLeft, 7.5f * scale, Color: "#374151", Bold: true));
|
dateColumnWidth, "Datum", DrawingTextAlignment.AlignLeft, 7.5f * scale, Color: "#374151", Bold: true));
|
||||||
commands.Add(new DrawStringEx(extentColumnX, y + scale, rowHeight - scale, extentColumnWidth, "Umfang",
|
commands.Add(new DrawStringEx(extentColumnX, y + scale * millimeterScale, rowHeight - scale * millimeterScale,
|
||||||
DrawingTextAlignment.AlignLeft, 7.5f * scale, Color: "#374151", Bold: true));
|
extentColumnWidth, "Umfang", DrawingTextAlignment.AlignLeft, 7.5f * scale, Color: "#374151", Bold: true));
|
||||||
commands.Add(new DrawStringEx(statusColumnX, y + scale, rowHeight - scale, statusColumnWidth, "Status",
|
commands.Add(new DrawStringEx(statusColumnX, y + scale * millimeterScale, rowHeight - scale * millimeterScale,
|
||||||
DrawingTextAlignment.AlignLeft, 7.5f * scale, Color: "#374151", Bold: true));
|
statusColumnWidth, "Status", DrawingTextAlignment.AlignLeft, 7.5f * scale, Color: "#374151", Bold: true));
|
||||||
y += rowHeight;
|
y += rowHeight;
|
||||||
|
|
||||||
if (rows.Count == 0)
|
if (rows.Count == 0)
|
||||||
{
|
{
|
||||||
commands.Add(new DrawStringEx(2, y + 2 * scale, rowHeight, contentWidth - 4,
|
commands.Add(new DrawStringEx(2 * millimeterScale, y + 2 * scale * millimeterScale, rowHeight,
|
||||||
"Keine Fehltage im gewählten Zeitraum", DrawingTextAlignment.AlignLeft, 8 * scale,
|
contentWidth - 4 * millimeterScale, "Keine Fehltage im gewählten Zeitraum",
|
||||||
Color: "#6B7280", Italic: true));
|
DrawingTextAlignment.AlignLeft, 8 * scale, Color: "#6B7280", Italic: true));
|
||||||
y += rowHeight + 3 * scale;
|
y += rowHeight + 3 * scale * millimeterScale;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -184,12 +192,15 @@ public static class StudentAbsenceDayListDrawingBuilder
|
|||||||
: row.FriendlyStatusLabel;
|
: row.FriendlyStatusLabel;
|
||||||
var statusColor = row.IsUnexcused ? "#C62828" : "#2E7D32";
|
var statusColor = row.IsUnexcused ? "#C62828" : "#2E7D32";
|
||||||
commands.Add(new DrawRectangle(0, y, contentWidth, rowHeight, "#E5E7EB", .3f, fill));
|
commands.Add(new DrawRectangle(0, y, contentWidth, rowHeight, "#E5E7EB", .3f, fill));
|
||||||
commands.Add(new DrawStringEx(dateColumnX, y + scale, rowHeight - scale, dateColumnWidth,
|
commands.Add(new DrawStringEx(dateColumnX, y + scale * millimeterScale,
|
||||||
|
rowHeight - scale * millimeterScale, dateColumnWidth,
|
||||||
row.Date.ToString("dd.MM.yyyy"), DrawingTextAlignment.AlignLeft, 7.5f * scale,
|
row.Date.ToString("dd.MM.yyyy"), DrawingTextAlignment.AlignLeft, 7.5f * scale,
|
||||||
Color: "#374151"));
|
Color: "#374151"));
|
||||||
commands.Add(new DrawStringEx(extentColumnX, y + scale, rowHeight - scale, extentColumnWidth,
|
commands.Add(new DrawStringEx(extentColumnX, y + scale * millimeterScale,
|
||||||
|
rowHeight - scale * millimeterScale, extentColumnWidth,
|
||||||
extent, DrawingTextAlignment.AlignLeft, 7.5f * scale, Color: "#374151"));
|
extent, DrawingTextAlignment.AlignLeft, 7.5f * scale, Color: "#374151"));
|
||||||
commands.Add(new DrawStringEx(statusColumnX, y + scale, rowHeight - scale, statusColumnWidth,
|
commands.Add(new DrawStringEx(statusColumnX, y + scale * millimeterScale,
|
||||||
|
rowHeight - scale * millimeterScale, statusColumnWidth,
|
||||||
status, DrawingTextAlignment.AlignLeft, 7.5f * scale, Color: statusColor,
|
status, DrawingTextAlignment.AlignLeft, 7.5f * scale, Color: statusColor,
|
||||||
Bold: row.IsUnexcused));
|
Bold: row.IsUnexcused));
|
||||||
y += rowHeight;
|
y += rowHeight;
|
||||||
@@ -210,7 +221,8 @@ public sealed class StudentAttendanceCalendarService(
|
|||||||
public DrawingValue Build(Student student, DateOnly month) =>
|
public DrawingValue Build(Student student, DateOnly month) =>
|
||||||
Build(student, new AttendanceCalendarOptions(month, 1));
|
Build(student, new AttendanceCalendarOptions(month, 1));
|
||||||
|
|
||||||
public DrawingValue Build(Student student, AttendanceCalendarOptions options)
|
public DrawingValue Build(Student student, AttendanceCalendarOptions options, float contentWidth = 170,
|
||||||
|
float millimeterScale = 1)
|
||||||
{
|
{
|
||||||
var className = settings.HomeroomClassName;
|
var className = settings.HomeroomClassName;
|
||||||
if (string.IsNullOrWhiteSpace(className)) return new DrawingValue([], 0);
|
if (string.IsNullOrWhiteSpace(className)) return new DrawingValue([], 0);
|
||||||
@@ -233,10 +245,11 @@ public sealed class StudentAttendanceCalendarService(
|
|||||||
e.TeacherUsername, e.CategoryName, e.CategoryGroup, e.Text))
|
e.TeacherUsername, e.CategoryName, e.CategoryGroup, e.Text))
|
||||||
.ToList();
|
.ToList();
|
||||||
return StudentAttendanceCalendarDrawingBuilder.Build(rosterName, options,
|
return StudentAttendanceCalendarDrawingBuilder.Build(rosterName, options,
|
||||||
ClassAbsenceDaySummaryRow.GroupByStudentAndDay(absences), register);
|
ClassAbsenceDaySummaryRow.GroupByStudentAndDay(absences), register, contentWidth, millimeterScale);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DrawingValue BuildAbsenceDayList(Student student, AttendanceCalendarOptions options)
|
public DrawingValue BuildAbsenceDayList(Student student, AttendanceCalendarOptions options, float contentWidth = 170,
|
||||||
|
float millimeterScale = 1)
|
||||||
{
|
{
|
||||||
var className = settings.HomeroomClassName;
|
var className = settings.HomeroomClassName;
|
||||||
if (string.IsNullOrWhiteSpace(className)) return new DrawingValue([], 0);
|
if (string.IsNullOrWhiteSpace(className)) return new DrawingValue([], 0);
|
||||||
@@ -254,6 +267,6 @@ public sealed class StudentAttendanceCalendarService(
|
|||||||
e.AbsentPeriods, e.AbsentMinutes, e.TeacherUsernames, e.Subject, e.AbsenceReason, e.Note,
|
e.AbsentPeriods, e.AbsentMinutes, e.TeacherUsernames, e.Subject, e.AbsenceReason, e.Note,
|
||||||
e.EntryId, e.HandledOn, e.Counts, e.ExcuseNote, e.PeriodNumber, e.Status, e.CountsAsFullDay));
|
e.EntryId, e.HandledOn, e.Counts, e.ExcuseNote, e.PeriodNumber, e.Status, e.CountsAsFullDay));
|
||||||
return StudentAbsenceDayListDrawingBuilder.Build(rosterName, options,
|
return StudentAbsenceDayListDrawingBuilder.Build(rosterName, options,
|
||||||
ClassAbsenceDaySummaryRow.GroupByStudentAndDay(absences));
|
ClassAbsenceDaySummaryRow.GroupByStudentAndDay(absences), contentWidth, millimeterScale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ public partial class CreateLetterDialogViewModel : ObservableObject
|
|||||||
private readonly Student _student;
|
private readonly Student _student;
|
||||||
private readonly TemplateStore _templates;
|
private readonly TemplateStore _templates;
|
||||||
private readonly ITemplateRenderer _renderer;
|
private readonly ITemplateRenderer _renderer;
|
||||||
private readonly Func<AttendanceCalendarOptions, DrawingValue>? _attendanceCalendarFactory;
|
private readonly Func<AttendanceCalendarOptions, float, float, DrawingValue>? _attendanceCalendarFactory;
|
||||||
private readonly Func<AttendanceCalendarOptions, DrawingValue>? _absenceDayListFactory;
|
private readonly Func<AttendanceCalendarOptions, float, float, DrawingValue>? _absenceDayListFactory;
|
||||||
private readonly Func<AttendanceCalendarOptions, CancellationToken, Task>? _attendanceDataRefresher;
|
private readonly Func<AttendanceCalendarOptions, CancellationToken, Task>? _attendanceDataRefresher;
|
||||||
private AttendanceCalendarOptions _attendanceCalendarOptions = new(
|
private AttendanceCalendarOptions _attendanceCalendarOptions = new(
|
||||||
new DateOnly(DateTime.Today.Year, DateTime.Today.Month, 1), 1);
|
new DateOnly(DateTime.Today.Year, DateTime.Today.Month, 1), 1);
|
||||||
@@ -52,8 +52,8 @@ public partial class CreateLetterDialogViewModel : ObservableObject
|
|||||||
|
|
||||||
public CreateLetterDialogViewModel(Student student, TemplateStore templates, ITemplateRenderer renderer,
|
public CreateLetterDialogViewModel(Student student, TemplateStore templates, ITemplateRenderer renderer,
|
||||||
IGroupMembershipRepository memberships, IGroupRepository groups,
|
IGroupMembershipRepository memberships, IGroupRepository groups,
|
||||||
Func<AttendanceCalendarOptions, DrawingValue>? attendanceCalendarFactory = null,
|
Func<AttendanceCalendarOptions, float, float, DrawingValue>? attendanceCalendarFactory = null,
|
||||||
Func<AttendanceCalendarOptions, DrawingValue>? absenceDayListFactory = null,
|
Func<AttendanceCalendarOptions, float, float, DrawingValue>? absenceDayListFactory = null,
|
||||||
Func<AttendanceCalendarOptions, CancellationToken, Task>? attendanceDataRefresher = null)
|
Func<AttendanceCalendarOptions, CancellationToken, Task>? attendanceDataRefresher = null)
|
||||||
{
|
{
|
||||||
_student = student; _templates = templates; _renderer = renderer;
|
_student = student; _templates = templates; _renderer = renderer;
|
||||||
@@ -137,16 +137,32 @@ public partial class CreateLetterDialogViewModel : ObservableObject
|
|||||||
|
|
||||||
private IReadOnlyDictionary<string, PlaceholderValue> BuildValues()
|
private IReadOnlyDictionary<string, PlaceholderValue> BuildValues()
|
||||||
{
|
{
|
||||||
|
var calendarBox = DeclaredDrawingBox(StudentAttendanceCalendarDrawingBuilder.PlaceholderName);
|
||||||
|
var absenceDayListBox = DeclaredDrawingBox(StudentAbsenceDayListDrawingBuilder.PlaceholderName);
|
||||||
var values = LetterPlaceholderBuilder.BuildStandardValues(
|
var values = LetterPlaceholderBuilder.BuildStandardValues(
|
||||||
_student, SelectedContact?.Model, SelectedGroup?.Model,
|
_student, SelectedContact?.Model, SelectedGroup?.Model,
|
||||||
DateOnly.FromDateTime((LetterDate ?? DateTimeOffset.Now).LocalDateTime), LetterText, TeacherName,
|
DateOnly.FromDateTime((LetterDate ?? DateTimeOffset.Now).LocalDateTime), LetterText, TeacherName,
|
||||||
UsesAttendanceCalendar ? _attendanceCalendarFactory?.Invoke(_attendanceCalendarOptions) : null,
|
UsesAttendanceCalendar ? _attendanceCalendarFactory?.Invoke(_attendanceCalendarOptions,
|
||||||
UsesAbsenceDayList ? _absenceDayListFactory?.Invoke(_attendanceCalendarOptions) : null);
|
calendarBox.Width, calendarBox.MillimeterScale) : null,
|
||||||
|
UsesAbsenceDayList ? _absenceDayListFactory?.Invoke(_attendanceCalendarOptions,
|
||||||
|
absenceDayListBox.Width, absenceDayListBox.MillimeterScale) : null);
|
||||||
values["Anrede"] = new TextValue(Anrede); values["Letter.Salutation"] = new TextValue(Anrede);
|
values["Anrede"] = new TextValue(Anrede); values["Letter.Salutation"] = new TextValue(Anrede);
|
||||||
foreach (var custom in CustomPlaceholders) values[custom.Name] = custom.ToPlaceholderValue();
|
foreach (var custom in CustomPlaceholders) values[custom.Name] = custom.ToPlaceholderValue();
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Tatsächliche DRAWBOX/FLOWDRAWBOX-Größe der aktuell gewählten Vorlage für diesen
|
||||||
|
/// Platzhalter, damit der Kalender/die Fehltagesliste die real verfügbare Fläche ausfüllen statt
|
||||||
|
/// eine feste Breite zu raten (siehe LetterPlaceholderBuilder.FindDeclaredDrawingBox). Fällt auf
|
||||||
|
/// 170mm zurück, wenn keine Vorlage gewählt ist oder die Box nicht gefunden wird.</summary>
|
||||||
|
private LetterPlaceholderBuilder.DeclaredDrawingBox DeclaredDrawingBox(string placeholderName)
|
||||||
|
{
|
||||||
|
var fallback = new LetterPlaceholderBuilder.DeclaredDrawingBox(170, 1);
|
||||||
|
if (SelectedTemplate is null) return fallback;
|
||||||
|
try { return LetterPlaceholderBuilder.FindDeclaredDrawingBox(_templates.Load(SelectedTemplate.Model), placeholderName) ?? fallback; }
|
||||||
|
catch (Exception ex) when (ex is IOException or InvalidDataException or TemplateValidationException) { return fallback; }
|
||||||
|
}
|
||||||
|
|
||||||
private void RebuildCustomPlaceholders(LetterTemplateChoice? choice)
|
private void RebuildCustomPlaceholders(LetterTemplateChoice? choice)
|
||||||
{
|
{
|
||||||
foreach (var existing in CustomPlaceholders) existing.PropertyChanged -= OnCustomPlaceholderChanged;
|
foreach (var existing in CustomPlaceholders) existing.PropertyChanged -= OnCustomPlaceholderChanged;
|
||||||
|
|||||||
Reference in New Issue
Block a user