c# - 将 CollectionViewSource 与 GroupDescriptions 一起使用时的 ListBox ScrollIntoView(即 IsGrouping == True)

标签 c# wpf

精简版

我想在更改选择时将 ListBox 项目滚动到 View 中。

长版

我有一个 ListBox,其 ItemsSource 绑定(bind)到一个 CollectionViewSource 和一个 GroupDescription,如示例所示以下。

<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 设置为当前不可见的项目,我希望它在 View 中滚动。这是它变得棘手的地方,因为 ListBox 正在分组(IsGrouped 属性为 true)调用 ScrollIntoView 失败。

System.Windows.Controls.ListBox 通过 Reflector。请注意 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;
}

问题

  1. 谁能解释为什么在使用分组时它起作用?
    • ItemContainerGenerator.ContainerFromItem 始终返回 null,即使它的状态表明所有容器都已生成。
  2. 使用分组时如何实现滚动到 View 中?

最佳答案

我找到了解决问题的方法。我确信我不是第一个遇到这个问题的人,所以我继续在 StackOverflow 上搜索解决方案,我偶然发现了 David 的这个答案 about how ItemContainerGenerator works with a grouped list .

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();
}

变化:

  • 仅当 IsGroupingtrue 时才使用 ItemContainerGenerator 方法,否则继续使用默认的 ScrollIntoView
  • 检查 ItemContainerGenerator 是否就绪,如果是则调度操作,否则监听 ItemContainerGenerator 状态是否改变。这很重要,因为它就绪了StatusChanged 事件永远不会触发。

关于c# - 将 CollectionViewSource 与 GroupDescriptions 一起使用时的 ListBox ScrollIntoView(即 IsGrouping == True),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7366961/

相关文章:

c# - 如何将 asp.net web 应用程序连接到 kannel 短信网关?

c# - ASP Gridview 下拉列表选择已更改,如何填充 rowDataBound() 以外的下拉列表

c# - MVVM 绑定(bind)双击到使用 telerik radtreecontrol 的方法

c# - 在屏幕顶部显示 wpf 窗口

c# - MVVM 从 View 读取数据到模型

c# - 如何使用 rowdeleting 事件删除 gridview 中的行?

c# - Azure 函数停止从 IoT 中心调用

c# - INotifyPropertyChanged 与 DependencyProperty

wpf - 使用 WPF 和数据绑定(bind)将文件拖放到应用程序窗口中

c# - ICommand.CanExecute 异步