将CollectionViewSource与GroupDescriptions结合使用时ListBox ScrollIntoView(即IsGrouping == True)
精简版
我想在选择更改时将ListBox
项目滚动到视图中。
长版
我有一个ListBox
,其ItemsSource
绑定到具有GroupDescription
的CollectionViewSource
,如下例所示。
代码隐藏文件中有一个SelectionChanged
事件。
public List Animals { get; set; } private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { ListBox control = (ListBox)sender; control.ScrollIntoView(control.SelectedItem); }
现在。 如果我将AnimalsListBox.SelectedItem
设置为当前不可见的项目,我希望它在视图中滚动。 这是棘手的,因为ListBox
是组( IsGrouped
属性为true
),对ScrollIntoView
的调用失败。
System.Windows.Controls.ListBox
通过Reflector 。 注意base.IsGrouping
中的OnBringItemIntoView
。
public void ScrollIntoView(object item) { if (base.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) { this.OnBringItemIntoView(item); } else { base.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new DispatcherOperationCallback(this.OnBringItemIntoView), item); } } private object OnBringItemIntoView(object arg) { FrameworkElement element = base.ItemContainerGenerator.ContainerFromItem(arg) as FrameworkElement; if (element != null) { element.BringIntoView(); } else if (!base.IsGrouping && base.Items.Contains(arg)) { VirtualizingPanel itemsHost = base.ItemsHost as VirtualizingPanel; if (itemsHost != null) { itemsHost.BringIndexIntoView(base.Items.IndexOf(arg)); } } return null; }
问题
- 任何人都可以解释为什么它在使用分组时不起作用?
-
ItemContainerGenerator.ContainerFromItem
始终返回null
,即使它的状态表明已生成所有容器。
-
- 使用分组时如何实现滚动视图?
我找到了解决问题的方法。 我确信我不是第一个遇到这个问题的人所以我继续搜索StackOverflow的解决方案,我偶然发现了David 关于ItemContainerGenerator如何使用分组列表的答案。
David的解决方案是延迟访问ItemContainerGenerator
直到渲染过程完成。
我已经实现了这个解决方案,我将详细介绍一些更改。
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { ListBox control = (ListBox)sender; if (control.IsGrouping) { if (control.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(DelayedBringIntoView)); else control.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged; } else control.ScrollIntoView(control.SelectedItem); } private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e) { if (ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) return; ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged; Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(DelayedBringIntoView)); } private void DelayedBringIntoView() { var item = ItemContainerGenerator.ContainerFromItem(SelectedItem) as ListBoxItem; if (item != null) item.BringIntoView(); }
变化:
- 仅当
IsGrouping
为true
时才使用ItemContainerGenerator
方法,否则继续使用默认的ScrollIntoView
。 - 检查
ItemContainerGenerator
是否准备就绪,如果是,则调度该操作,否则侦听ItemContainerGenerator
状态以进行更改。这很重要,就好像它已准备好,然后StatusChanged
事件永远不会触发。
-
开箱即用的VirtualizingStackPanel不支持虚拟化分组集合视图。 当在ItemsControl中呈现分组集合时,每个组作为整体是项目,而不是集合中的每个项目,这导致“生涩”滚动到每个组标题而不是每个项目。
-
您可能需要滚动自己的VirtualizingStackPanel或ItemContainerGenerator,以便跟踪组中显示的容器。 这听起来很荒谬,但在WPF中使用分组的默认虚拟化至少可以说是缺乏。