短版

更改选择后,我想将ListBox项滚动到 View 中。

较长版本

我有一个ListBoxItemsSource绑定(bind)到一个CollectionViewSourceGroupDescription,如下面的示例所示。

<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的调用失败。

通过Reflector的System.Windows.Controls.ListBox。注意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,即使其状态表明已生成所有容器。
  • 使用分组时如何实现滚动到 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();
    }
    

    变化:
  • 仅当ItemContainerGeneratorIsGrouping时才使用true方法,否则继续使用默认的ScrollIntoView
  • 检查ItemContainerGenerator是否已准备好,如果这样做,则分派(dispatch)操作,否则监听ItemContainerGenerator的状态更改。这很重要,因为好像它已经准备好了,那么StatusChanged事件将永远不会触发。
  • 关于c# - 将CollectionViewSource与GroupDescriptions一起使用时的ListBox ScrollIntoView(即IsGrouping == True),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/7366961/

    10-13 09:41