Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
266 views
in Technique[技术] by (71.8m points)

.net - How does ItemContainerGenerator.ContainerFromItem work with a grouped list?

I have a ListBox which until recently was displaying a flat list of items. I was able to use myList.ItemContainerGenerator.ConainerFromItem(thing) to retrieve the ListBoxItem hosting "thing" in the list.

This week I've modified the ListBox slightly in that the CollectionViewSource that it binds to for its items has grouping enabled. Now the items within the ListBox are grouped underneath nice headers.

However, since doing this, ItemContainerGenerator.ContainerFromItem has stopped working - it returns null even for items I know are in the ListBox. Heck - ContainerFromIndex(0) is returning null even when the ListBox is populated with many items!

How do I retrieve a ListBoxItem from a ListBox that's displaying grouped items?

Edit: Here's the XAML and code-behind for a trimmed-down example. This raises a NullReferenceException because ContainerFromIndex(1) is returning null even though there are four items in the list.

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();
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You have to listen and react to the ItemsGenerator.StatusChanged Event and wait until the ItemContainers are generated before you can access them with ContainerFromElement.


Searching further, I've found a thread in the MSDN forum from someone who has the same problem. This seems to be a bug in WPF, when one has a GroupStyle set. The solution is to punt the access of the ItemGenerator after the rendering process. Below is the code for your question. I tried this, and it works:

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...