.net wpf listbox

.net - wpf combobox



¿Cómo funciona ItemContainerGenerator.ContainerFromItem con una lista agrupada? (3)

Intente analizar VisualTree desde la ''cosa'' hasta que llegue a un tipo ListBoxItem

Tengo un ListBox que hasta hace poco mostraba una lista plana de elementos. Pude usar myList.ItemContainerGenerator.ConainerFromItem (thing) para recuperar el "elemento" hosting de ListBoxItem en la lista.

Esta semana modifiqué ligeramente el ListBox porque CollectViewSource al que se une para sus elementos tiene habilitada la agrupación. Ahora los elementos dentro de ListBox están agrupados debajo de buenos encabezados.

Sin embargo, desde hacer esto, ItemContainerGenerator.ContainerFromItem ha dejado de funcionar, devuelve null incluso para los elementos que sé que están en el ListBox. Heck: ContainerFromIndex (0) devuelve null incluso cuando ListBox está lleno de muchos elementos.

¿Cómo recupero un ListBoxItem de un ListBox que muestra elementos agrupados?

Editar: Aquí está el XAML y el código subyacente para un ejemplo recortado. Esto genera una NullReferenceException porque ContainerFromIndex (1) devuelve null aunque hay cuatro elementos en la lista.

XAML:

<Window x:Class="WpfApplication1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase" Title="Window1"> <Window.Resources> <XmlDataProvider x:Key="myTasks" XPath="Tasks/Task"> <x:XData> <Tasks xmlns=""> <Task Name="Groceries" Type="Home"/> <Task Name="Cleaning" Type="Home"/> <Task Name="Coding" Type="Work"/> <Task Name="Meetings" Type="Work"/> </Tasks> </x:XData> </XmlDataProvider> <CollectionViewSource x:Key="mySortedTasks" Source="{StaticResource myTasks}"> <CollectionViewSource.SortDescriptions> <scm:SortDescription PropertyName="@Type" /> <scm:SortDescription PropertyName="@Name" /> </CollectionViewSource.SortDescriptions> <CollectionViewSource.GroupDescriptions> <PropertyGroupDescription PropertyName="@Type" /> </CollectionViewSource.GroupDescriptions> </CollectionViewSource> </Window.Resources> <ListBox x:Name="listBox1" ItemsSource="{Binding Source={StaticResource mySortedTasks}}" DisplayMemberPath="@Name" > <ListBox.GroupStyle> <GroupStyle> <GroupStyle.HeaderTemplate> <DataTemplate> <TextBlock Text="{Binding Name}"/> </DataTemplate> </GroupStyle.HeaderTemplate> </GroupStyle> </ListBox.GroupStyle> </ListBox> </Window>

CS:

public Window1() { InitializeComponent(); listBox1.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged; } void ItemContainerGenerator_StatusChanged(object sender, EventArgs e) { if (listBox1.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated) { listBox1.ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged; var i = listBox1.ItemContainerGenerator.ContainerFromIndex(1) as ListBoxItem; // select and keyboard-focus the second item i.IsSelected = true; i.Focus(); } }


ItemsGenerator.StatusChanged escuchar y reaccionar al evento ItemsGenerator.StatusChanged y esperar hasta que se generen los contenedores de elementos para poder acceder a ellos con ContainerFromElement.

Buscando más, encontré un hilo en el foro de MSDN de alguien que tiene el mismo problema. Esto parece ser un error en WPF, cuando uno tiene un conjunto GroupStyle. La solución es cancelar el acceso de ItemGenerator después del proceso de renderizado. Debajo está el código para tu pregunta. Intenté esto, y funciona:

void ItemContainerGenerator_StatusChanged(object sender, EventArgs e) { if (listBox1.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated) { listBox1.ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged; Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Input, new Action(DelayedAction)); } } void DelayedAction() { var i = listBox1.ItemContainerGenerator.ContainerFromIndex(1) as ListBoxItem; // select and keyboard-focus the second item i.IsSelected = true; i.Focus(); }


Si el código anterior no funciona para ti, prueba esto

public class ListBoxExtenders : DependencyObject { public static readonly DependencyProperty AutoScrollToCurrentItemProperty = DependencyProperty.RegisterAttached("AutoScrollToCurrentItem", typeof(bool), typeof(ListBoxExtenders), new UIPropertyMetadata(default(bool), OnAutoScrollToCurrentItemChanged)); public static bool GetAutoScrollToCurrentItem(DependencyObject obj) { return (bool)obj.GetValue(AutoScrollToSelectedItemProperty); } public static void SetAutoScrollToCurrentItem(DependencyObject obj, bool value) { obj.SetValue(AutoScrollToSelectedItemProperty, value); } public static void OnAutoScrollToCurrentItemChanged(DependencyObject s, DependencyPropertyChangedEventArgs e) { var listBox = s as ListBox; if (listBox != null) { var listBoxItems = listBox.Items; if (listBoxItems != null) { var newValue = (bool)e.NewValue; var autoScrollToCurrentItemWorker = new EventHandler((s1, e2) => OnAutoScrollToCurrentItem(listBox, listBox.Items.CurrentPosition)); if (newValue) listBoxItems.CurrentChanged += autoScrollToCurrentItemWorker; else listBoxItems.CurrentChanged -= autoScrollToCurrentItemWorker; } } } public static void OnAutoScrollToCurrentItem(ListBox listBox, int index) { if (listBox != null && listBox.Items != null && listBox.Items.Count > index && index >= 0) listBox.ScrollIntoView(listBox.Items[index]); } }

Uso en XAML

<ListBox IsSynchronizedWithCurrentItem="True" extenders:ListBoxExtenders.AutoScrollToCurrentItem="True" ..../>