Reputation: 24610
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
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 ListBox
and 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
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
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
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
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