Wir möchten die SelectedItem
eines ListBox
programmgesteuert festlegen und möchten, dass das Element dann den Fokus hat, damit die Pfeiltasten relativ zu diesem ausgewählten Element funktionieren. Scheint einfach genug.Wie programmierst du den Fokus programmgesteuert auf das SelectedItem in einer WPF-ListBox, die bereits im Fokus ist?
Das Problem ist jedoch, wenn die ListBox
bereits Tastaturfokus hat, wenn SelectedItem
programmatisch einstellen, während es richtig die IsSelected
Eigenschaft auf den ListBoxItem
, es es nicht Tastaturfokus nicht aktualisiert und damit die Pfeiltasten relativ zum zuvor fokussierten Element in der Liste verschieben und nicht wie erwartet das neu ausgewählte Element.
Dies ist für den Benutzer sehr verwirrend, da es die Auswahl so aussehen lässt, als würde es bei der Verwendung der Tastatur herumspringen, wenn es zurückspringt, wo es vor der programmatischen Auswahl war.
Hinweis: Wie ich bereits sagte, geschieht dies nur, wenn Sie die Eigenschaft SelectedItem
programmatisch auf eine ListBox
setzen, die bereits über den Tastaturfokus verfügt. Wenn dies nicht der Fall ist (oder wenn Sie es tun, aber Sie verlassen, dann kommen Sie gleich zurück), wenn der Tastaturfokus auf ListBox
zurückkehrt, hat das korrekte Element nun erwartungsgemäß den Tastaturfokus.
Hier ist ein Beispielcode, der dieses Problem zeigt. Um dies zu demonstrieren, führen Sie den Code aus, verwenden Sie die Maus, um 'Seven' in der Liste auszuwählen (also den Fokus auf ListBox
zu setzen), klicken Sie dann auf die Schaltfläche 'Test'. Tippen Sie abschließend auf die Taste "Alt" auf Ihrer Tastatur, um das Fokus-Rect anzuzeigen. Sie werden sehen, dass es immer noch auf "Seven" steht und wenn Sie die Pfeile nach oben und nach unten verwenden, sind sie relativ zu dieser Reihe, nicht "Vier", wie ein Benutzer erwarten würde.
Bitte beachten Sie, dass ich Focusable
auf false
auf der Schaltfläche eingestellt habe, um die Listbox des Fokus beim Drücken nicht zu rauben. Wenn ich das nicht hätte, würde der ListBox
Fokus verlieren, wenn Sie auf die Schaltfläche klicken und wenn der Fokus zu der List-Box zurückgegeben wurde, würde es sich also auf dem richtigen Element befinden.
XAML-Datei:
<Window x:Class="Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="525" Height="350" WindowStartupLocation="CenterScreen"
Title="MainWindow" x:Name="Root">
<DockPanel>
<Button Content="Test"
DockPanel.Dock="Bottom"
HorizontalAlignment="Left"
Focusable="False"
Click="Button_Click" />
<ListBox x:Name="MainListBox" />
</DockPanel>
</Window>
-Code-behind:
using System.Collections.ObjectModel;
using System.Windows;
namespace Test
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MainListBox.ItemsSource = new string[]{
"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight"
};
}
private void Button_Click(object sender, RoutedEventArgs e)
{
MainListBox.SelectedItem = MainListBox.Items[3];
}
}
}
Hinweis: Einige haben vorgeschlagen, IsSynchronizedWithCurrentItem
zu verwenden, aber das Eigentum synchronisiert die SelectedItem
der ListBox
mit der Current
Eigenschaft des zugehörigen Aussicht. Es hängt nicht mit Fokus zusammen, da dieses Problem noch existiert.
Unsere Arbeit-um woanders den Fokus vorübergehend eingestellt ist, dann stellen Sie das ausgewählte Element, und stellen Sie den Fokus wieder auf die ListBox
dies hat jedoch den unerwünschten Effekt von uns mit unserer ViewModel
sich der ListBox
selbst zu machen, dann führe die Logik aus, je nachdem, ob sie den Fokus hat oder nicht. (Du würdest nicht einfach sagen "Fokus woanders, dann komm hierher zurück, wenn 'hier' nicht schon den Fokus hat, wie du es stehst von woanders.) Außerdem kann man das nicht einfach durch deklarative Bindungen handhaben. Unnötig zu sagen, das ist hässlich.
Dann noch einmal, 'hässliche' Schiffe, also gibt es das.
ContainerFromItem gibt Null zurück, wenn noch kein Container für ihn generiert wurde, was bei einer virtualisierten Liste der Fall ist und das Objekt nicht angezeigt wird. Plus, wenn Sie versuchen, den Wert von einer Bindung zu setzen, bricht es zusammen, wie Sie keinen Zugriff auf die ListBox haben, noch sollten Sie. (Fortsetzung folgt ...) – MarqueIV
Auch ein angehängtes Verhalten wirft Probleme auf, da Sie nicht möchten, dass das Steuerelement blind den Tastaturfokus auf das ListBoxItem setzt, außer a) das Steuerelement hatte bereits den Fokus (einfach zu testen) und b) die Änderung kam aus dem code-behind (nicht einfach ohne eine Unterklasse zu testen). Andernfalls könnten Sie versehentlich den Fokus von einem anderen Steuerelement stehlen (Fall a) oder den Fokus im aktuellen Modus (Fall b) im Multi-select-Modus verwirren und die Auswahl aufheben eine Zeile, die normalerweise den Tastaturfokus beibehalten sollte, aber stattdessen auf das neue SelectedItem gesetzt wird, was zu seltsamen Verhalten führt. – MarqueIV
Eigentlich denke ich bei zweiten Gedanken, dass ich das Problem für Fall 'B' oben habe. Sie erstellen das angehängte Verhalten wie angegeben, aber Sie legen es nicht direkt in XAML fest. Stattdessen erstellen Sie eine Eigenschaft in Ihrem ViewModel und binden das Verhalten an dieses. Auf diese Weise müssen Sie nicht wissen (oder kümmern), wer zuhört. Bevor Sie dann das ausgewählte Element aus dem Code entfernen, aktivieren Sie das Verhalten, wählen Sie das Element aus und deaktivieren Sie das Verhalten erneut. Dies adressiert "B" oben. (Du brauchst natürlich immer noch 'A'.) Deine Antwort zu wählen, obwohl sie nicht vollständig ist, hat mich auf diesem Weg geführt. – MarqueIV