¿Cómo se establece programáticamente el foco en SelectedItem en un WPF ListBox que ya tiene foco?
(5)
En tu XAML, ¿has probado esto y no has funcionado?
<ListBox IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Path=YourCollectionView}" SelectedItem="{Binding SelectedItem}"></ListBox>
Y la propiedad SelectedItem
:
private YourObject _SelectedItem;
public YourObject SelectedItem
{
get
{
return _SelectedItem;
}
set
{
if (_SelectedItem == value)
return;
_SelectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
Ahora en tu código puedes hacer:
SelectedItem = theItemYouWant;
Para mí, este enfoque funciona siempre.
Queremos establecer el elemento SelectedItem
de un ListBox
programáticamente y queremos que ese elemento tenga un foco para que las teclas de flecha funcionen en relación con ese elemento seleccionado. Parece lo suficientemente simple.
Sin embargo, el problema es que si el ListBox
ya tiene el foco del teclado al establecer SelectedItem
programación, mientras actualiza correctamente la propiedad IsSelected
en el ListBoxItem
, no establece el foco del teclado en él y, por lo tanto, las teclas de flecha se mueven con respecto al anterior. elemento enfocado en la lista y no el elemento recién seleccionado como uno esperaría.
Esto es muy confuso para el usuario, ya que hace que la selección parezca saltar cuando se utiliza el teclado, ya que vuelve a estar donde estaba antes de que se llevara a cabo la selección programática.
Nota: Como dije, esto solo ocurre si establece programáticamente la propiedad SelectedItem
en un ListBox
que ya tiene el foco del teclado. Si no lo hace (o si lo hace pero te vas, luego regresa), cuando el foco del teclado regrese al ListBox
, el elemento correcto ahora tendrá el foco del teclado como se esperaba.
Aquí hay un código de muestra que muestra este problema. Para hacer una demostración de esto, ejecute el código, use el mouse para seleccionar ''Siete'' en la lista (poniendo así el foco en el ListBox
), luego haga clic en el botón ''Prueba''. Finalmente, toque la tecla ''Alt'' en su teclado para revelar el foco rect. Verás que todavía está en ''Siete'' y si utilizas las flechas hacia arriba y hacia abajo, están relacionadas con esa fila, no con ''Cuatro'' como esperaría un usuario.
Tenga en cuenta que Focusable
configurado en false
en el botón como para no robar el cuadro de lista de enfoque al presionarlo. Si no tuviera esto, el ListBox
perdería el foco cuando haga clic en el botón, y así, cuando el foco volviera al ListBox, estaría en el elemento correcto.
Archivo 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>
Código detrás:
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];
}
}
}
Nota: Algunos han sugerido utilizar IsSynchronizedWithCurrentItem
, pero esa propiedad sincroniza el SelectedItem
del ListBox
con la propiedad Current
de la vista asociada. No está relacionado con el enfoque ya que este problema aún existe.
Nuestra solución es establecer temporalmente el foco en otro lugar, luego establecer el elemento seleccionado, luego establecer el foco nuevamente en el ListBox
pero esto tiene el efecto indeseable de tener que hacer que nuestro ViewModel
consciente del ListBox
sí mismo, luego realizar la lógica dependiendo sobre si tiene o no el foco, etc. (es decir, no querría decir simplemente ''Enfóquese en otro lado y vuelva aquí, si'' aquí ''no tuviera el enfoque ya que lo robaría de otro lado). ) Además, no puedes simplemente manejar esto a través de enlaces declarativos. No hace falta decir que esto es feo.
Por otra parte, naves ''feos'', entonces eso es todo.
Solo necesita usar ListBox.SelectedItem y luego usar ListBox.ScrollIntoView (listBox.SelectedItem)
Código de ejemplo:
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);
}
}
Son un par de líneas de código. Si no lo quería en código subyacente, estoy seguro de que podría estar empaquetado en un comportamiento adjunto.
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();
}
Tal vez con un comportamiento adjunto? Algo como
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();
}
y en xaml
<Style TargetType="ListBoxItem">
<Setter Property="local:FocusWhenSelectedBehavior.FocusWhenSelected" Value="True"/>
</Style>
Primero) Debe encontrar los elementos seleccionados en el cuadro de lista con ListBox.Items.IndexOf ().
Segundo) Ahora agregue elementos con ListBox.SelectedItems.Add ().
Este es mi código:
DataRow[] drWidgetItem = dtItemPrice.Select(widgetItemsFilter);
lbxWidgetItem.SelectedItems.Clear(); foreach(DataRow drvItem in
drWidgetItem)
lbxWidgetItem.SelectedItems.Add(lbxWidgetItem.Items[dtItemPrice.Rows.IndexOf(drvItem)]);
Si desea seleccionar un elemento en ListBox, puede usarlo de esta manera:
ListBox.SelectedItem = (Your ListBoxItem);
Si desea seleccionar algunos elementos en ListBox, debe usar esta opción:
ListBox.SelectedItems.Add (Your ListBoxItem);