From a36b1d1d40fb01f925e06147ae40e052ad4d90fd Mon Sep 17 00:00:00 2001 From: Sebastian Hedtrich Date: Tue, 18 Aug 2026 09:14:14 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20Aufsicht-Zeilen=20im=20Stundenplan=20deu?= =?UTF-8?q?tlich=20schmaler=20als=20Stundenbl=C3=B6cke?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UniformGrid erzwingt für alle Zellen dieselbe, vom größten Kind bestimmte Höhe - eine Aufsicht-Zeile konnte dadurch nie schmaler werden als eine Stundenzeile, auch nicht durch kleinere Inhalte. Cells/WeekItems werden jetzt zusätzlich zeilenweise in neue GridRows/WeekRows-Collections gruppiert (TimetableRowItem/ WeekRowItem, je eine eigene, ganz normal bindbare RowHeight - Aufsicht-Zeilen 22px statt 46px/76px bei normalen Stunden-/ Kopfzeilen). Innerhalb jeder Zeile bleibt UniformGrid Columns="6" für die Spaltenaufteilung. Grid.RowDefinitions ließ sich dafür nicht per {Binding} setzen (Avalonia lehnt jede Bindungsform dafür mit AVLN3000 ab) - deshalb dieser Umweg über echte Unterzeilen statt eines einzelnen Grids mit dynamischer RowDefinitions-Bindung. $parent[ItemsControl]-Bindings in den Zell-Templates mussten auf $parent[ItemsControl;1] angepasst werden, da durch die neue Verschachtelung sonst das innere Zeilen-ItemsControl statt des äußeren (mit TimetableViewModel als DataContext) getroffen wird. Co-Authored-By: Claude Sonnet 5 --- .../TimetableViewModelTests.cs | 26 ++ .../ViewModels/Planning/TimetableViewModel.cs | 37 +++ .../Views/Planning/TimetableView.axaml | 232 ++++++++++-------- 3 files changed, 191 insertions(+), 104 deletions(-) diff --git a/LehrerApp.Desktop.Tests/TimetableViewModelTests.cs b/LehrerApp.Desktop.Tests/TimetableViewModelTests.cs index 696853f..edc4498 100644 --- a/LehrerApp.Desktop.Tests/TimetableViewModelTests.cs +++ b/LehrerApp.Desktop.Tests/TimetableViewModelTests.cs @@ -442,6 +442,19 @@ public sealed class TimetableViewModelTests Assert.DoesNotContain(vm.Cells, c => c.IsSupervisionRow); } + [Fact] + public void Load_AufsichtEingetragen_BekommtSchmalereZeilenhoeheAlsStundenzeilen() + { + var duties = new FakeSupervisionDuties(); + duties.Add(new SupervisionDuty { Weekday = DayOfWeek.Monday, AfterPeriod = 2, Location = "Pausenhof" }); + var vm = BuildViewModel(new FakeTimetableSlots(), new FakeGroups([]), supervisionDuties: duties); + + var row = vm.GridRows.Single(r => r.Cells.Any(c => c.IsSupervisionRow && c.Text.Contains("n. 2."))); + + Assert.Equal(22, row.RowHeight); + Assert.Contains(vm.GridRows, r => r.RowHeight == 46); // normale Stundenzeilen bleiben unverändert + } + // ── Aufsicht + Vertretung im Wochenraster ──────────────────────────────── [Fact] @@ -457,6 +470,19 @@ public sealed class TimetableViewModelTests Assert.False(mondayCell.IsSubstitutionSupervision); } + [Fact] + public void Load_Wochenraster_AufsichtBekommtSchmalereZeilenhoeheAlsStundenzeilen() + { + var duties = new FakeSupervisionDuties(); + duties.Add(new SupervisionDuty { Weekday = DayOfWeek.Monday, AfterPeriod = 2, Location = "Pausenhof" }); + var vm = BuildViewModel(new FakeTimetableSlots(), new FakeGroups([]), supervisionDuties: duties); + + var row = vm.WeekRows.Single(r => r.Cells.Any(c => c.IsSupervisionRow && c.Text.Contains("n. 2."))); + + Assert.Equal(22, row.RowHeight); + Assert.Contains(vm.WeekRows, r => r.RowHeight == 76); + } + [Fact] public void Load_Wochenraster_VertretungsaufsichtUeberschreibtRegulaereAnzeige() { diff --git a/LehrerApp.Desktop/ViewModels/Planning/TimetableViewModel.cs b/LehrerApp.Desktop/ViewModels/Planning/TimetableViewModel.cs index ef642ec..d4452a9 100644 --- a/LehrerApp.Desktop/ViewModels/Planning/TimetableViewModel.cs +++ b/LehrerApp.Desktop/ViewModels/Planning/TimetableViewModel.cs @@ -45,6 +45,7 @@ public partial class TimetableViewModel : ObservableObject { private const int FirstPeriod = 1; private const int LastPeriod = 10; + private const int GridColumns = 6; // Label/Kopf-Spalte + 5 Wochentage (Mo-Fr) private static readonly DayOfWeek[] Weekdays = [DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday]; private static readonly string[] WeekdayColorPalette = @@ -70,6 +71,15 @@ public partial class TimetableViewModel : ObservableObject public ObservableCollection TodaySpecialAssignments { get; } = []; public ObservableCollection UpcomingExams { get; } = []; + /// Cells/WeekItems zeilenweise gruppiert (GridColumns Zellen je Zeile), zusätzlich zur + /// flachen Liste - Grundlage für die zeilenweise Höhensteuerung in der View (Aufsicht-Zeilen + /// deutlich schmaler als Stunden-/Kopfzeilen). Grid.RowDefinitions lässt sich in Avalonia + /// nicht per {Binding} setzen (Compiler lehnt jede Binding-Form dafür ab, siehe + /// AVLN3000-Fehler „Unable to find suitable setter or adder for property RowDefinitions“) - + /// deshalb hier stattdessen echte Unterzeilen mit je eigener, ganz normal bindbarer Height. + public ObservableCollection GridRows { get; } = []; + public ObservableCollection WeekRows { get; } = []; + [ObservableProperty] private int _activeTabIndex; [ObservableProperty] private string _todayLabel = ""; [ObservableProperty] private string _weekRangeLabel = ""; @@ -325,6 +335,10 @@ public partial class TimetableViewModel : ObservableObject } AddWeekSupervisionRowIfAny(period, dutiesByPeriod, substitutionsThisWeek, dateByWeekday); } + + WeekRows.Clear(); + foreach (var rowCells in WeekItems.Chunk(GridColumns)) + WeekRows.Add(new WeekRowItem(rowCells)); } private void AddWeekSupervisionRowIfAny(int afterPeriod, ILookup dutiesByPeriod, @@ -409,6 +423,10 @@ public partial class TimetableViewModel : ObservableObject } AddGridSupervisionRowIfAny(period, dutiesByPeriod); } + + GridRows.Clear(); + foreach (var rowCells in Cells.Chunk(GridColumns)) + GridRows.Add(new TimetableRowItem(rowCells)); } /// Aufsicht wird nur in den Einstellungen gepflegt (siehe SettingsView) — im @@ -571,6 +589,18 @@ public class TimetableCellItem }; } +/// +/// Eine Zeile im Bearbeiten-Raster (GridColumns Zellen). Aufsicht-Zeilen bekommen eine deutlich +/// schmalere RowHeight als normale Kopf-/Stundenzeilen - eine echte, per Height ganz normal +/// bindbare Eigenschaft je Unterzeile statt eines vollflächigen UniformGrid-Rasters, das allen +/// Zeilen zwangsläufig dieselbe Höhe geben würde. +/// +public class TimetableRowItem(IReadOnlyList cells) +{ + public IReadOnlyList Cells { get; } = cells; + public double RowHeight { get; } = cells[0].IsSupervisionRow ? 22 : 46; +} + /// Zelle im schreibgeschützten Wochenraster der "Heute"-Ansicht. public class WeekCellItem { @@ -671,6 +701,13 @@ public class WeekCellItem }; } +/// Zeile im Wochenraster - siehe TimetableRowItem, dieselbe Rolle für WeekItems. +public class WeekRowItem(IReadOnlyList cells) +{ + public IReadOnlyList Cells { get; } = cells; + public double RowHeight { get; } = cells[0].IsSupervisionRow ? 22 : 76; +} + public class HoursWarningItem(string groupName, int assigned, int expected) { public string GroupName { get; } = groupName; diff --git a/LehrerApp.Desktop/Views/Planning/TimetableView.axaml b/LehrerApp.Desktop/Views/Planning/TimetableView.axaml index 9a15e91..b2cb774 100644 --- a/LehrerApp.Desktop/Views/Planning/TimetableView.axaml +++ b/LehrerApp.Desktop/Views/Planning/TimetableView.axaml @@ -133,63 +133,77 @@ ToolTip.Tip="Einstellungen (Ferien, Aufsichten, Stundenraster)"/> - - - - + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + @@ -237,55 +251,65 @@ - - - - + + - - - - - - - - - - - - +