Punkteeingabe: Enter und Pfeiltasten unterstützen beide Korrektur-Workflows
Enter läuft jetzt zeilenweise (Arbeit für Arbeit), Pfeil-Hoch/-Runter spaltenweise mit Umbruch zur Nachbaraufgabe (Aufgabe für Aufgabe) - beides ohne Moduswechsel, da sich beide Reihenfolgen ohnehin auf dieselbe Tabelle abbilden. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -48,7 +48,7 @@ public partial class ExamGradingDialog : Window
|
||||
{
|
||||
Header = string.IsNullOrWhiteSpace(task.Title) ? $"Aufg. {task.Nr}" : $"{task.Nr}. {task.Title}",
|
||||
Width = new DataGridLength(1, DataGridLengthUnitType.Star),
|
||||
CellTemplate = BuildPointsCellTemplate(i, grid),
|
||||
CellTemplate = BuildPointsCellTemplate(i, grid, vm.Tasks.Count),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public partial class ExamGradingDialog : Window
|
||||
});
|
||||
}
|
||||
|
||||
private IDataTemplate BuildPointsCellTemplate(int taskIndex, DataGrid grid)
|
||||
private IDataTemplate BuildPointsCellTemplate(int taskIndex, DataGrid grid, int taskCount)
|
||||
{
|
||||
var cellName = $"Pts_{taskIndex}";
|
||||
return new FuncDataTemplate<ExamResultRow>((row, _) =>
|
||||
@@ -102,7 +102,7 @@ public partial class ExamGradingDialog : Window
|
||||
};
|
||||
|
||||
tb.LostFocus += (_, _) => CommitPointsCell(tb, cell);
|
||||
tb.KeyDown += (_, e) => HandleCellKeyDown(e, grid, row, cellName);
|
||||
tb.KeyDown += (_, e) => HandlePointsCellKeyDown(e, grid, row, taskIndex, taskCount);
|
||||
|
||||
return tb;
|
||||
});
|
||||
@@ -150,14 +150,53 @@ public partial class ExamGradingDialog : Window
|
||||
Padding = new Thickness(6, 4),
|
||||
};
|
||||
tb.LostFocus += (_, _) => row.Comment = tb.Text ?? "";
|
||||
tb.KeyDown += (_, e) => HandleCellKeyDown(e, grid, row, cellName);
|
||||
tb.KeyDown += (_, e) => HandleCommentCellKeyDown(e, grid, row, cellName);
|
||||
return tb;
|
||||
});
|
||||
}
|
||||
|
||||
/// Enter/Pfeil-Hoch/Pfeil-Runter springen zur gleichen Spalte in der Nachbarzeile
|
||||
/// (Tab funktioniert bereits über die normale Fokus-Reihenfolge).
|
||||
private void HandleCellKeyDown(KeyEventArgs e, DataGrid grid, object rowItem, string cellName)
|
||||
/// Zwei Korrektur-Workflows (Nutzer-Feedback): "Arbeit für Arbeit" (ein Schüler komplett,
|
||||
/// dann der nächste) und "Aufgabe für Aufgabe" (eine Aufgabe für alle Schüler, dann die
|
||||
/// nächste) — beide bewusst ohne Moduswechsel über zwei verschiedene Tasten, weil sich beide
|
||||
/// Reihenfolgen ohnehin schon auf die Tabelle abbilden (Zeile vs. Spalte). Enter läuft
|
||||
/// zeilenweise (am Zeilenende zurück zu Aufgabe 1 der nächsten Zeile), Pfeil-hoch/-runter
|
||||
/// spaltenweise (am Spaltenende weiter zur Nachbaraufgabe) — beides bricht am Tabellenende
|
||||
/// einfach ab, kein Rundlauf zurück zum Anfang.
|
||||
private void HandlePointsCellKeyDown(KeyEventArgs e, DataGrid grid, ExamResultRow row, int taskIndex, int taskCount)
|
||||
{
|
||||
if (e.Key is not (Key.Down or Key.Up or Key.Enter)) return;
|
||||
e.Handled = true;
|
||||
|
||||
if (grid.ItemsSource is not IList items) return;
|
||||
var rowIdx = items.IndexOf(row);
|
||||
if (rowIdx < 0) return;
|
||||
|
||||
int targetRowIdx;
|
||||
int targetTaskIndex;
|
||||
|
||||
if (e.Key == Key.Enter)
|
||||
{
|
||||
targetTaskIndex = taskIndex + 1;
|
||||
targetRowIdx = rowIdx;
|
||||
if (targetTaskIndex >= taskCount) { targetTaskIndex = 0; targetRowIdx = rowIdx + 1; }
|
||||
}
|
||||
else
|
||||
{
|
||||
targetTaskIndex = taskIndex;
|
||||
targetRowIdx = e.Key == Key.Up ? rowIdx - 1 : rowIdx + 1;
|
||||
if (targetRowIdx < 0) { targetRowIdx = items.Count - 1; targetTaskIndex = taskIndex - 1; }
|
||||
else if (targetRowIdx >= items.Count) { targetRowIdx = 0; targetTaskIndex = taskIndex + 1; }
|
||||
}
|
||||
|
||||
if (targetTaskIndex < 0 || targetTaskIndex >= taskCount
|
||||
|| targetRowIdx < 0 || targetRowIdx >= items.Count) return;
|
||||
|
||||
FocusPointsCell(grid, items[targetRowIdx]!, targetTaskIndex);
|
||||
}
|
||||
|
||||
/// Enter/Pfeil-Hoch/Pfeil-Runter springen im Kommentarfeld zur gleichen Spalte in der
|
||||
/// Nachbarzeile (Tab funktioniert bereits über die normale Fokus-Reihenfolge).
|
||||
private void HandleCommentCellKeyDown(KeyEventArgs e, DataGrid grid, object rowItem, string cellName)
|
||||
{
|
||||
if (e.Key is not (Key.Down or Key.Up or Key.Enter)) return;
|
||||
e.Handled = true;
|
||||
@@ -181,5 +220,25 @@ public partial class ExamGradingDialog : Window
|
||||
}, DispatcherPriority.Loaded);
|
||||
}
|
||||
|
||||
/// Spalte 0 ist "Schüler", danach folgen die Aufgaben-Spalten in Reihenfolge — daher der
|
||||
/// Offset von 1 beim Auflösen des Spaltenobjekts für ScrollIntoView (muss auch horizontal
|
||||
/// zur Nachbaraufgabe scrollen, nicht nur vertikal zur Nachbarzeile).
|
||||
private void FocusPointsCell(DataGrid grid, object targetItem, int targetTaskIndex)
|
||||
{
|
||||
var targetColumn = grid.Columns.ElementAtOrDefault(1 + targetTaskIndex);
|
||||
if (targetColumn is null) return;
|
||||
grid.ScrollIntoView(targetItem, targetColumn);
|
||||
|
||||
var cellName = $"Pts_{targetTaskIndex}";
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
var targetTb = grid.GetVisualDescendants().OfType<TextBox>()
|
||||
.FirstOrDefault(t => t.DataContext == targetItem && t.Name == cellName);
|
||||
if (targetTb is null) return;
|
||||
targetTb.Focus();
|
||||
targetTb.SelectAll();
|
||||
}, DispatcherPriority.Loaded);
|
||||
}
|
||||
|
||||
private void OnClose(object? sender, RoutedEventArgs e) => Close();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user