Nutzer-Feedback: die Tagesliste-Buttons waren schon in Ordnung, das Problem
lag im Wochenraster darüber — ein Klick auf eine Stunden-Kachel führt dort
entweder in den Planungsviewer oder zur Einheitenplanung, ohne dass von
außen erkennbar wäre welches Ziel man bekommt. Tagesliste auf den
ursprünglichen Stand zurückgesetzt.
Neu: ein kleiner "⋮"-Button pro Kachel öffnet ein Popup-Menü mit vier
ausdrücklich benannten Zielen (Unterrichtsansicht/Planungsviewer/Sitzplan/
Planung). MenuFlyout statt ComboBox — dabei verstanden, dass ein
$parent[ItemsControl]-Vorfahrenpfad im Flyout nicht funktioniert, eine
normale {Binding} über die DataContext-Vererbung aber sehr wohl. Der
Direktklick springt jetzt außerdem "einheitlicher": bei einer Lesson
während der eigentlichen Unterrichtszeit direkt in den Unterrichtsmodus
statt in den Viewer.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
144 lines
6.6 KiB
C#
144 lines
6.6 KiB
C#
using Avalonia.Controls;
|
|
using Avalonia.Interactivity;
|
|
using LehrerApp.Core.Interfaces;
|
|
using LehrerApp.Core.Models;
|
|
using LehrerApp.Core.Services;
|
|
using LehrerApp.Desktop.ViewModels;
|
|
using LehrerApp.Desktop.ViewModels.Planning;
|
|
using LehrerApp.Desktop.ViewModels.Groups;
|
|
using LehrerApp.Desktop.Views.Groups;
|
|
using LehrerApp.Desktop.Services;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
namespace LehrerApp.Desktop.Views.Planning;
|
|
|
|
public partial class TimetableView : UserControl
|
|
{
|
|
public TimetableView() => InitializeComponent();
|
|
|
|
protected override void OnDataContextChanged(EventArgs e)
|
|
{
|
|
base.OnDataContextChanged(e);
|
|
if (DataContext is TimetableViewModel vm)
|
|
{
|
|
vm.OnEditSlot = ShowSlotDialog;
|
|
vm.OnAddSubstitution = ShowSubstitutionDialog;
|
|
vm.OnImportWebUntisTimetable = ShowWebUntisTimetableDialog;
|
|
vm.OnOpenLessonViewer = ShowLessonViewerDialog;
|
|
vm.OnOpenTeachingMode = ShowTeachingMode;
|
|
}
|
|
}
|
|
|
|
/// Nutzer-Feedback (zweite Runde — die erste Fassung hatte das Problem an der falschen
|
|
/// Stelle gelöst, in der "Heute"-Tagesliste statt im Wochenraster): im Wochenraster oben
|
|
/// führt ein Klick auf eine Stunden-Kachel bislang entweder in den Planungsviewer oder zur
|
|
/// Einheitenplanung — je nachdem, ob schon eine Lesson existiert, ohne dass das von außen
|
|
/// erkennbar wäre. Popup-Menü (MenuFlyout, kein ComboBox mehr) mit allen vier Zielen als
|
|
/// Alternative zum Direktklick, siehe <see cref="TimetableViewModel.OpenWeekCellCommand"/>
|
|
/// für den "einheitlicheren" Standard-Klick (Unterrichtsansicht bei laufender Stunde, sonst
|
|
/// Planungsviewer, sonst Einheitenplanung). MenuItem.Click statt Command-Binding: ein
|
|
/// $parent[ItemsControl]-Vorfahrenpfad (wie beim Zeilen-Button) funktioniert innerhalb eines
|
|
/// Flyouts nicht zuverlässig, weil dessen Popup nicht im normalen visuellen Baum hängt (siehe
|
|
/// TODO.md-Nachtrag zum Klassenlehrer-Bereich) — DataContext-Vererbung (kein Pfad-Suchen,
|
|
/// nur der normale Eltern-Wert) funktioniert dort aber sehr wohl, deshalb liest der Handler
|
|
/// die Zelle über <c>((MenuItem)sender).DataContext</c> statt über eine Binding.
|
|
private async void OnWeekCellMenuItemClick(object? sender, RoutedEventArgs e)
|
|
{
|
|
if (sender is not MenuItem { Tag: TimetableLessonDestination destination } menuItem) return;
|
|
if (menuItem.DataContext is not WeekCellItem cell) return;
|
|
if (DataContext is not TimetableViewModel vm) return;
|
|
|
|
switch (destination)
|
|
{
|
|
case TimetableLessonDestination.TeachingMode:
|
|
if (cell.Lesson is { } teachLesson && vm.OnOpenTeachingMode is not null)
|
|
await vm.OnOpenTeachingMode(teachLesson);
|
|
break;
|
|
case TimetableLessonDestination.Viewer:
|
|
if (cell.Lesson is { } viewLesson && vm.OnOpenLessonViewer is not null)
|
|
await vm.OnOpenLessonViewer(viewLesson);
|
|
break;
|
|
case TimetableLessonDestination.Planning:
|
|
if (cell.GroupId != Guid.Empty)
|
|
App.Services.GetRequiredService<MainWindowViewModel>().NavigateToGroupDetail(cell.GroupId, 6);
|
|
break;
|
|
case TimetableLessonDestination.SeatingPlan:
|
|
if (cell.GroupId != Guid.Empty)
|
|
App.Services.GetRequiredService<MainWindowViewModel>().NavigateToGroupDetail(cell.GroupId, 2);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private async Task ShowWebUntisTimetableDialog()
|
|
{
|
|
var owner = TopLevel.GetTopLevel(this) as Window;
|
|
if (owner is null) return;
|
|
var vm = new WebUntisTimetableImportViewModel(
|
|
App.Services.GetRequiredService<WebUntisIntegrationService>(),
|
|
App.Services.GetRequiredService<WebUntisSettingsService>(),
|
|
App.Services.GetRequiredService<ITimetableSlotRepository>(),
|
|
App.Services.GetRequiredService<IGroupRepository>());
|
|
var dialog = new WebUntisTimetableImportDialog { DataContext = vm };
|
|
vm.OnCreateGroup = dialog.CreateGroupAsync;
|
|
await vm.InitializeAsync();
|
|
await dialog.ShowDialog<bool>(owner);
|
|
}
|
|
|
|
private async Task ShowTeachingMode(Lesson lesson)
|
|
{
|
|
var owner = TopLevel.GetTopLevel(this) as Window;
|
|
var group = App.Services.GetRequiredService<IGroupRepository>().GetById(lesson.GroupId);
|
|
if (owner is null || group is null) return;
|
|
|
|
var teachingModeVm = new TeachingModeViewModel(lesson, group,
|
|
App.Services.GetRequiredService<IAlternativeLessonPathRepository>(),
|
|
App.Services.GetRequiredService<ILessonRepository>(),
|
|
App.Services.GetRequiredService<SeatingPlanTabViewModel>(),
|
|
App.Services.GetRequiredService<ParticipationTabViewModel>());
|
|
var window = new TeachingModeWindow { DataContext = teachingModeVm };
|
|
await window.ShowDialog(owner);
|
|
}
|
|
|
|
private async Task ShowLessonViewerDialog(Lesson lesson)
|
|
{
|
|
var owner = TopLevel.GetTopLevel(this) as Window;
|
|
if (owner is null) return;
|
|
|
|
var viewerVm = new LessonViewerViewModel(lesson,
|
|
App.Services.GetRequiredService<IAlternativeLessonPathRepository>());
|
|
var dialog = new LessonViewerDialog { DataContext = viewerVm };
|
|
await dialog.ShowDialog(owner);
|
|
}
|
|
|
|
private async Task ShowSlotDialog(TimetableCellItem cell)
|
|
{
|
|
var owner = TopLevel.GetTopLevel(this) as Window;
|
|
if (owner is null || cell.Weekday is null) return;
|
|
|
|
var vm = new TimetableSlotDialogViewModel(
|
|
App.Services.GetRequiredService<ITimetableSlotRepository>(),
|
|
App.Services.GetRequiredService<IGroupRepository>(),
|
|
App.Services.GetRequiredService<ISubjectRepository>(),
|
|
App.Services.GetRequiredService<SchoolYearService>(),
|
|
cell.Weekday.Value, cell.PeriodNumber, cell.Slot);
|
|
|
|
var dialog = new TimetableSlotDialog { DataContext = vm };
|
|
await dialog.ShowDialog(owner);
|
|
}
|
|
|
|
private async Task ShowSubstitutionDialog()
|
|
{
|
|
var owner = TopLevel.GetTopLevel(this) as Window;
|
|
if (owner is null) return;
|
|
|
|
var vm = new SubstitutionEntryDialogViewModel(
|
|
App.Services.GetRequiredService<ISubstitutionEntryRepository>(),
|
|
App.Services.GetRequiredService<IGroupRepository>(),
|
|
App.Services.GetRequiredService<IUnitRepository>(),
|
|
App.Services.GetRequiredService<ILessonRepository>());
|
|
|
|
var dialog = new SubstitutionEntryDialog { DataContext = vm };
|
|
await dialog.ShowDialog(owner);
|
|
}
|
|
}
|