Nous voulons définir la SelectedItem
d'une ListBox
par programme et voulons que cet élément ait ensuite le focus pour que les touches de direction fonctionnent par rapport à cet élément sélectionné. Cela semble assez simple.
Cependant, le problème est que si ListBox
a déjà le focus clavier lorsqu’il a programmé SelectedItem
par programme, alors qu’il met correctement à jour la propriété IsSelected
sur ListBoxItem
, il ne le définit pas le définit au focus clavier, et donc, les touches fléchées se déplacent de manière relative. à l'élément précédemment ciblé dans la liste et non au nouvel élément sélectionné comme on pourrait s'y attendre.
Ceci est très déroutant pour l’utilisateur car cela donne l’impression que la sélection saute lors de l’utilisation du clavier, car il revenait à l’endroit où il se trouvait avant la sélection par programme.
Remarque: comme je l'ai dit, cela ne se produit que si vous définissez par programme la propriété SelectedItem
sur une ListBox
qui a déjà le focus clavier. Si ce n'est pas le cas (ou si vous quittez, puis revenez immédiatement), lorsque le focus du clavier revient à la variable ListBox
, l'élément correct aura désormais le focus du clavier comme prévu.
Voici un exemple de code montrant ce problème. Pour en faire la démonstration, lancez le code, utilisez la souris pour sélectionner «Seven» dans la liste (mettant ainsi le focus sur la ListBox
), puis cliquez sur le bouton «Test». Enfin, appuyez sur la touche 'Alt' de votre clavier pour révéler le focus droit. Vous verrez qu'il est toujours en réalité sur «Sept» et si vous utilisez les flèches haut et bas, elles sont relatives à cette rangée et non pas «Quatre» comme un utilisateur pourrait s'y attendre.
Notez que Focusable
est défini sur false
sur le bouton afin de ne pas dérober la zone de liste de la liste lorsque vous appuyez dessus. Si je n'avais pas cela, la ListBox
perdrait la focalisation lorsque vous cliquez sur le bouton, et donc, lorsque la focalisation est revenue sur le contrôle ListBox, ce serait sur le bon élément.
Fichier XAML:
<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];
}
}
}
Remarque: Certains ont suggéré d'utiliser IsSynchronizedWithCurrentItem
, mais cette propriété synchronise la SelectedItem
de la ListBox
avec la propriété Current
de la vue associée. Il n'est pas lié à la focalisation car ce problème existe toujours.
Notre solution consiste à définir temporairement le focus ailleurs, puis de définir l'élément sélectionné, puis de le redéfinir sur la variable ListBox
, mais cela a pour effet indésirable que nous ayons à rendre ViewModel
conscient de la valeur ListBox
elle-même, puis d'appliquer une logique en fonction sur si oui ou non il a le focus, etc. (ie vous ne voudriez pas simplement dire "Focus ailleurs, alors revenez ici, si" ici "n'avait pas déjà le focus car vous le voleriez ailleurs. ) De plus, vous ne pouvez pas simplement gérer cela par le biais de liaisons déclaratives. Inutile de dire que c'est moche.
Encore une fois, les navires "laids", donc il y a que.
C'est quelques lignes de code. Si vous ne le vouliez pas dans code-behind, je suis sûr que cela pourrait être empaqueté dans un comportement attaché.
private void Button_Click(object sender, RoutedEventArgs e)
{
MainListBox.SelectedItem = MainListBox.Items[3];
MainListBox.UpdateLayout(); // Pre-generates item containers
var listBoxItem = (ListBoxItem) MainListBox
.ItemContainerGenerator
.ContainerFromItem(MainListBox.SelectedItem);
listBoxItem.Focus();
}
Peut-être avec un comportement attaché? Quelque chose comme
public static DependencyProperty FocusWhenSelectedProperty = DependencyProperty.RegisterAttached(
"FocusWhenSelected",
typeof(bool),
typeof(FocusWhenSelectedBehavior),
new PropertyMetadata(false, new PropertyChangedCallback(OnFocusWhenSelectedChanged)));
private static void OnFocusWhenSelectedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var i = (ListBoxItem)obj;
if ((bool)args.NewValue)
i.Selected += i_Selected;
else
i.Selected -= i_Selected;
}
static void i_Selected(object sender, RoutedEventArgs e)
{
((ListBoxItem)sender).Focus();
}
et en xaml
<Style TargetType="ListBoxItem">
<Setter Property="local:FocusWhenSelectedBehavior.FocusWhenSelected" Value="True"/>
</Style>
Dans votre XAML, vous avez essayé cela et cela n'a pas fonctionné?
<ListBox IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Path=YourCollectionView}" SelectedItem="{Binding SelectedItem}"></ListBox>
Et la propriété SelectedItem
:
private YourObject _SelectedItem;
public YourObject SelectedItem
{
get
{
return _SelectedItem;
}
set
{
if (_SelectedItem == value)
return;
_SelectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
Maintenant, dans votre code, vous pouvez faire:
SelectedItem = theItemYouWant;
Pour moi, cette approche fonctionne toujours.
First) Vous devez rechercher les éléments sélectionnés dans une liste avec ListBox.Items.IndexOf () .
Second) Maintenant, ajoutez des éléments avec ListBox.SelectedItems.Add ().
Ceci est mon code:
DataRow[] drWidgetItem = dtItemPrice.Select(widgetItemsFilter);
lbxWidgetItem.SelectedItems.Clear(); foreach(DataRow drvItem in
drWidgetItem)
lbxWidgetItem.SelectedItems.Add(lbxWidgetItem.Items[dtItemPrice.Rows.IndexOf(drvItem)]);
Si vous souhaitez sélectionner un élément dans ListBox, vous pouvez utiliser cette méthode:
ListBox.SelectedItem = (Votre ListBoxItem);
Si vous souhaitez sélectionner des éléments dans ListBox, vous devez utiliser cette méthode:
ListBox.SelectedItems.Add (Votre ListBoxItem);
Vous devez uniquement utiliser ListBox.SelectedItem, puis ListBox.ScrollIntoView (listBox.SelectedItem).
Exemple de code:
private void textBox2_TextChanged(object sender, TextChangedEventArgs e)
{
var comparision = StringComparison.InvariantCultureIgnoreCase;
string myString = textBox2.Text;
List<dynamic> index = listBox.Items.SourceCollection.OfType<dynamic>().Where(x=>x.Nombre.StartsWith(myString,comparision)).ToList();
if (index.Count > 0) {
listBox.SelectedItem= index.First();
listBox.ScrollIntoView(listBox.SelectedItem);
}
}