Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/323.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将CollectionViewSource与GroupDescriptions一起使用时,列表框滚动到视图中(即IsGrouping==True)_C#_Wpf - Fatal编程技术网

C# 将CollectionViewSource与GroupDescriptions一起使用时,列表框滚动到视图中(即IsGrouping==True)

C# 将CollectionViewSource与GroupDescriptions一起使用时,列表框滚动到视图中(即IsGrouping==True),c#,wpf,C#,Wpf,短版 更改选择时,我想将列表框项目滚动到视图中 长版本 我有一个列表框,其中项资源绑定到一个集合视图源,其中组描述,如下例所示 代码隐藏文件中存在SelectionChanged事件 public List<Animal> Animals { get; set; } private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { ListBox control = (

短版

更改选择时,我想将
列表框
项目滚动到视图中

长版本

我有一个
列表框
,其中
项资源
绑定到一个
集合视图源
,其中
组描述
,如下例所示


代码隐藏文件中存在
SelectionChanged
事件

public List<Animal> Animals { get; set; }

private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    ListBox control = (ListBox)sender;
    control.ScrollIntoView(control.SelectedItem);
}
问题

  • 有人能解释为什么在使用分组时,不起作用吗?
    • ItemContainerGenerator.ContainerFromItem
      始终返回
      null
      ,即使其状态表明所有容器都已生成
  • 使用分组时如何实现滚动到视图中
  • 现成的VirtualzingStackPanel不支持虚拟化分组集合视图。在ItemsControl中呈现分组集合时,每个组作为一个整体都是一个项,而不是集合中的每个项,这会导致“突然”滚动到每个组标题,而不是每个项

  • 您可能需要滚动自己的VirtualzingStackPanel或ItemContainerGenerator,以便跟踪组中显示的容器。这听起来很可笑,但WPF中默认的分组虚拟化至少还不够


  • 我已经找到了解决我问题的办法。我确信我不是第一个碰到这个问题的人,所以我继续寻找StackOverflow的解决方案,我偶然发现了David的答案

    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
      事件将永远不会触发
    这就是我的想法,但我希望我不必这么做,因为编写虚拟化面板可能很棘手。。。因为每个组作为一个整体都是项,所以当传递
    SelectedItem
    时,
    ItemContainerGenerator
    总是返回
    null
    ?我相信是的。你应该看看Bea Stollnitz的博客。她有很多关于分组和虚拟化的好帖子:谢谢。我已经阅读了Bea Stollnitz关于WPF和CollectionViewSource和Grouping的几篇文章,我在问题中使用了她的分组示例。更新:我找到了一个解决方案,并将其作为答案发布。你应该将答案更改为正确答案,而不是上面的答案。@Valentin:我已经更改了标记的答案。然而,由于建议确实帮助我诊断了这个问题,如果您使用我的最终解决方案,最好将两个答案都投上一票。使用.NET 4.5.1和MVVM,您可以使用行为来实现这一点。该行为在这两种情况下都有效,因为它已经触发得很晚了。
    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();
    }