Files
LehrerApp/LehrerApp.Desktop/Views/Groups/ParticipationTabView.axaml.cs
T
admin 182c386cc5 Mitarbeitsnote-Aggregation (3.2)
Neuer Dialog berechnet aus den Sitzungs-Bewertungen je Schüler eine
Mitarbeitsnote (H1/H2/Gesamtjahr), mit editierbarer Aspekt-Gewichtung,
Trendanzeige und Übernahme als Grade (Category=Participation).
2026-08-12 14:10:04 +02:00

167 lines
6.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using LehrerApp.Core.Interfaces;
using LehrerApp.Core.Services;
using LehrerApp.Desktop.ViewModels.Groups;
using Microsoft.Extensions.DependencyInjection;
namespace LehrerApp.Desktop.Views.Groups;
public partial class ParticipationTabView : UserControl
{
private ParticipationTabViewModel? _vm;
public ParticipationTabView() => InitializeComponent();
protected override void OnDataContextChanged(EventArgs e)
{
base.OnDataContextChanged(e);
if (DataContext is ParticipationTabViewModel vm)
{
_vm = vm;
vm.OnAddSession = ShowAddSessionDialog;
vm.OnQuickInput = ShowQuickInputDialog;
vm.OnComputeGrade = ShowComputeGradeDialog;
vm.Aspects.CollectionChanged += (_, _) => BuildColumns();
vm.PropertyChanged += (_, pe) =>
{
if (pe.PropertyName == nameof(ParticipationTabViewModel.RebuildColumnsSignal))
BuildColumns();
};
BuildColumns();
}
}
private void BuildColumns()
{
var grid = this.FindControl<DataGrid>("RatingGrid");
if (grid is null || _vm is null) return;
grid.Columns.Clear();
grid.Columns.Add(new DataGridTextColumn
{
Header = "Schüler",
Binding = new Binding("Name"),
Width = new DataGridLength(160, DataGridLengthUnitType.Pixel),
});
// Aspekt-Spalten
foreach (var (aspect, i) in _vm.Aspects.Select((a, i) => (a, i)))
{
var idx = i;
grid.Columns.Add(new DataGridTemplateColumn
{
Header = $"{aspect.Label} [{AspectShortcut(i)}]",
Width = new DataGridLength(1, DataGridLengthUnitType.Star),
CellTemplate = BuildCellTemplate(idx, forCompetency: false),
});
}
// Kompetenz-Spalten (opt-in)
if (_vm.StudentCompetencyRatingsVisible && _vm.ActiveCompetencyCodes.Count > 0)
{
foreach (var (code, i) in _vm.ActiveCompetencyCodes.Select((c, i) => (c, i)))
{
var idx = i;
grid.Columns.Add(new DataGridTemplateColumn
{
Header = code,
Width = new DataGridLength(80, DataGridLengthUnitType.Pixel),
CellTemplate = BuildCellTemplate(idx, forCompetency: true),
});
}
}
}
private static IDataTemplate BuildCellTemplate(int cellIndex, bool forCompetency)
{
return new FuncDataTemplate<ParticipationStudentRow>((row, _) =>
{
if (row is null) return new TextBlock();
var cell = forCompetency
? row.CompetencyCells.ElementAtOrDefault(cellIndex)
: row.Cells.ElementAtOrDefault(cellIndex);
if (cell is null) return new TextBlock();
var panel = new StackPanel
{
Orientation = Avalonia.Layout.Orientation.Horizontal,
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center,
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
Spacing = 2,
Margin = new Avalonia.Thickness(0, 2),
};
foreach (var (label, val) in new[] { ("−−", -2), ("", -1), ("", 0), ("+", 1), ("++", 2) })
{
var btn = new Button
{
Content = label,
Padding = new Avalonia.Thickness(5, 1),
FontSize = 11,
Opacity = cell.Value == val ? 1.0 : 0.3,
};
var capturedVal = val;
btn.Click += (_, _) => cell.SetValue(capturedVal);
cell.PropertyChanged += (_, pe) =>
{
if (pe.PropertyName == nameof(RatingCell.Value))
btn.Opacity = cell.Value == capturedVal ? 1.0 : 0.3;
};
panel.Children.Add(btn);
}
return panel;
});
}
private static string AspectShortcut(int i) => i switch
{
0 => "Q", 1 => "W", 2 => "E", 3 => "R", 4 => "T", _ => ""
};
private async Task<LehrerApp.Core.Models.ParticipationSession?> ShowAddSessionDialog()
{
var vm = new AddSessionDialogViewModel();
var dialog = new AddSessionDialog { DataContext = vm };
var owner = TopLevel.GetTopLevel(this) as Window;
if (owner is null) return null;
var ok = await dialog.ShowDialog<bool>(owner);
return ok ? vm.Result : null;
}
private async Task ShowQuickInputDialog(ParticipationTabViewModel tabVm)
{
if (tabVm.StudentRows.Count == 0) return;
var quickVm = new QuickInputViewModel(
tabVm.StudentRows.ToList(),
tabVm.Aspects.ToList());
var dialog = new ParticipationQuickInputDialog { DataContext = quickVm };
var owner = TopLevel.GetTopLevel(this) as Window;
if (owner is not null)
await dialog.ShowDialog(owner);
}
private async Task ShowComputeGradeDialog(ParticipationTabViewModel tabVm)
{
var dialogVm = new ParticipationGradeDialogViewModel(
App.Services.GetRequiredService<IParticipationSessionRepository>(),
App.Services.GetRequiredService<IParticipationRepository>(),
App.Services.GetRequiredService<IParticipationAspectRepository>(),
App.Services.GetRequiredService<IStudentRepository>(),
App.Services.GetRequiredService<IEnrollmentRepository>(),
App.Services.GetRequiredService<IGradeRepository>(),
App.Services.GetRequiredService<GradingService>(),
tabVm.GroupId, tabVm.SchoolYear, tabVm.GradingSystem);
var dialog = new ParticipationGradeDialog { DataContext = dialogVm };
var owner = TopLevel.GetTopLevel(this) as Window;
if (owner is not null)
await dialog.ShowDialog(owner);
}
}