将CollectionViewSource与GroupDescriptions一起使用时的ListBox ScrollI
本文关键字:ListBox ScrollI CollectionViewSource GroupDescriptions 一起 | 更新日期: 2023-09-27 18:08:14
短版本
我想在更改选择时将ListBox
项目滚动到视图中。
长版本
我有一个带有ItemsSource
的ListBox
绑定到带有GroupDescription
的CollectionViewSource
,如下例所示。
<Window.Resources>
<CollectionViewSource x:Key="AnimalsView" Source="{Binding Source={StaticResource Animals}, Path=AnimalList}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Category"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<ListBox x:Name="AnimalsListBox"ItemsSource="{Binding Source={StaticResource AnimalsView}}" ItemTemplate="{StaticResource AnimalTemplate}" SelectionChanged="ListBox_SelectionChanged">
<ListBox.GroupStyle>
<GroupStyle HeaderTemplate="{StaticResource CategoryTemplate}" />
</ListBox.GroupStyle>
</ListBox>
代码隐藏文件中有一个SelectionChanged
事件。
public List<Animal> 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
通过反射器。注意OnBringItemIntoView
中的base.IsGrouping
。
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中带有分组的默认虚拟化是缺乏的。