simo
simo

Reputation: 24610

Why does ItemContainerGenerator return null?

I have a ListBox, and I need to set its ControlTemplate to a Virtualizing WrapPanel which is a class that extends VirtualizingPanel, using a style that looks like this:

<Style TargetType="{x:Type ListBox}" x:Key="PhotoListBoxStyle">
                <Setter Property="Foreground" Value="White" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate  TargetType="{x:Type ListBox}" >
                            <s:VirtualizingVerticalWrapPanel>
                            </s:VirtualizingVerticalWrapPanel>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>

Now, in the private method of Virtualizing WrapPanel below I try to access this.ItemContainerGenerator, but I get null value, any idea what's the problem ??

private void RealizeFirstItem()
{
    IItemContainerGenerator generator = this.ItemContainerGenerator;
    GeneratorPosition pos = generator.GeneratorPositionFromIndex(0);

    using (generator.StartAt(pos, GeneratorDirection.Forward))
    {
        UIElement element = generator.GenerateNext() as UIElement;

         generator.PrepareItemContainer(element);

                    this.AddInternalChild(element);
    }
 }

Upvotes: 1

Views: 4487

Answers (5)

fvojvodic
fvojvodic

Reputation: 156

Most probably this is a virtualization-related issue so ListBoxItem containers get generated only for currently visible items (e.g. https://msdn.microsoft.com/en-us/library/system.windows.controls.virtualizingstackpanel(v=vs.110).aspx#Anchor_9)

I'd suggest switching to ListView instead of ListBox - it inherits from ListBoxand it supports ScrollIntoView() method which you can utilize to control virtualization;

targetListView.ScrollIntoView(itemVM);
DoEvents();
ListViewItem itemContainer = targetListView.ItemContainerGenerator.ContainerFromItem(itemVM) as ListViewItem;

(the example above also utilizes the DoEvents() static method explained in more detail here; WPF how to wait for binding update to occur before processing more code?)

There are a few other minor differences between the ListBox and ListView controls (What is The difference between ListBox and ListView) - which should not essentially affect your use case.

Upvotes: 0

FUR10N
FUR10N

Reputation: 760

For Windows 8.1 Metro apps, the ItemContainerGenerator was depricated and will return null. New Apis:

ItemsControl.ItemContainerGenerator.ItemFromContainer = ItemsControl.ItemFromContainer

ItemsControl.ItemContainerGenerator.ContainerFromItem = ItemsControl.ContainerFromItem

ItemsControl.ItemContainerGenerator.IndexFromContainer = ItemsControl.IndexFromContainer

ItemsControl.ItemContainerGenerator.ContainerFromIndex = ItemsControl.ContainerFromIndex

http://msdn.microsoft.com/en-us/library/windows/apps/dn376326.aspx

Upvotes: 2

Quark Soup
Quark Soup

Reputation: 4760

Falck is mostly correct. Actually, you need to reference the 'InternalChildren' of the virtualized stack panel. The decompiled code for this property is:

    protected internal UIElementCollection InternalChildren
    {
        get
        {
            this.VerifyBoundState();
            if (this.IsItemsHost)
            {
                this.EnsureGenerator();
            }
            else if (this._uiElementCollection == null)
            {
                this.EnsureEmptyChildren(this);
            }
            return this._uiElementCollection;
        }
    }

The 'EnsureGenerator' does the work of making sure that a generator is available. Very poor 'just in time' design, IMO.

Upvotes: 0

Elad Katz
Elad Katz

Reputation: 7601

This is because you changed the Template of the Listbox, while u should have just changed the ItemsPanel:

    <ListBox>
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <s:VirtualizingVerticalWrapPanel />
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
    </ListBox>

Upvotes: -1

falck
falck

Reputation: 111

I think I had a similar problem and this helped:

var necessaryChidrenTouch = this.Children;
IItemContainerGenerator generator = this.ItemContainerGenerator;

... for some reason you have to "touch" the children collection in order for the ItemContainerGenerator to initialize properly.

Upvotes: 10

Related Questions