ItemContainerGenerator.ContainerFromItem如何与分组列表一起使用?

时间:2020-03-06 15:03:32  来源:igfitidea点击:

我有一个ListBox,直到最近才显示项目的平面列表。我能够使用myList.ItemContainerGenerator.ConainerFromItem(thing)检索在列表中托管"事物"的ListBoxItem。

本周,我对ListBox进行了一些修改,因为它为其项目绑定到的CollectionViewSource已启用分组。现在,ListBox中的项目被分组在漂亮的标题下面。

但是,由于这样做,ItemContainerGenerator.ContainerFromItem已停止工作,即使对于我知道在ListBox中的项目,它也返回null。即使ListBox填充了很多项目,Heck ContainerFromIndex(0)仍返回null!

如何从显示分组项目的列表框中检索ListBoxItem?

编辑:这是XAML,后面是精简示例的代码。这会引发NullReferenceException,因为即使列表中有四个项,ContainerFromIndex(1)也会返回null。

XAML:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
    Title="Window1">

    <Window.Resources>
        <XmlDataProvider x:Key="myTasks" XPath="Tasks/Task">
            <x:XData>
                <Tasks xmlns="">
                    <Task Name="Groceries" Type="Home"/>
                    <Task Name="Cleaning" Type="Home"/>
                    <Task Name="Coding" Type="Work"/>
                    <Task Name="Meetings" Type="Work"/>
                </Tasks>
            </x:XData>
        </XmlDataProvider>

        <CollectionViewSource x:Key="mySortedTasks" Source="{StaticResource myTasks}">
            <CollectionViewSource.SortDescriptions>
                <scm:SortDescription PropertyName="@Type" />
                <scm:SortDescription PropertyName="@Name" />
            </CollectionViewSource.SortDescriptions>

            <CollectionViewSource.GroupDescriptions>
                <PropertyGroupDescription PropertyName="@Type" />
            </CollectionViewSource.GroupDescriptions>
        </CollectionViewSource>
    </Window.Resources>

    <ListBox 
        x:Name="listBox1" 
        ItemsSource="{Binding Source={StaticResource mySortedTasks}}" 
        DisplayMemberPath="@Name"
        >
        <ListBox.GroupStyle>
            <GroupStyle>
                <GroupStyle.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Name}"/>
                    </DataTemplate>
                </GroupStyle.HeaderTemplate>
            </GroupStyle>
        </ListBox.GroupStyle>
    </ListBox>
</Window>

CS:

public Window1()
{
    InitializeComponent();
    listBox1.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
}

void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
{
    if (listBox1.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
    {
        listBox1.ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged;

        var i = listBox1.ItemContainerGenerator.ContainerFromIndex(1) as ListBoxItem;

        // select and keyboard-focus the second item
        i.IsSelected = true;
        i.Focus();
    }
}

解决方案

尝试从"事物"中解析VisualTree,直到达到ListBoxItem类型

我们必须侦听并响应ItemsGenerator.StatusChanged事件,并等待直到生成ItemContainers才能使用ContainerFromElement访问它们。

进一步搜索,我在MSDN论坛中发现了一个有相同问题的人的话题。当设置了GroupStyle时,这似乎是WPF中的一个错误。解决方案是在渲染过程之后平移对ItemGenerator的访问。下面是问题的代码。我试过了,它有效:

void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
    {
        if (listBox1.ItemContainerGenerator.Status
            == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
        {
            listBox1.ItemContainerGenerator.StatusChanged
                -= ItemContainerGenerator_StatusChanged;
            Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Input,
                new Action(DelayedAction));
        }
    }

    void DelayedAction()
    {
        var i = listBox1.ItemContainerGenerator.ContainerFromIndex(1) as ListBoxItem;

        // select and keyboard-focus the second item
        i.IsSelected = true;
        i.Focus();
    }