I found a VirtualizingWrapPanel(VWP) project here which, I thought, could help me with optimizing my ListView scrolling performance. The listView have to has four columns and multiple rows to display the source items. So I tried to use this VWP, but scrolling(I made it as DoubleAnimation) smoothness still sucks, the framerate drops down to about 30 fps and it's very noticeable. I copied the source code from the link above to try to debug it and understand what is the reason of such a bad performance.
After several hours a found out that ArrangeOverride method of VWP and MeasureOverride method of its "inheritance parent" can execute about 20-30ms (1000ms / 30ms = ~30fps) and that's it, I found the reason why it's so slow... but why?
MeasureOverride method calls two potentially heavy methods: RealizeItems and VirtualizeItems
protected virtual void RealizeItems()
{
var startPosition = ItemContainerGenerator.GeneratorPositionFromIndex(ItemRange.StartIndex);
int childIndex = startPosition.Offset == 0 ? startPosition.Index : startPosition.Index + 1;
using (ItemContainerGenerator.StartAt(startPosition, GeneratorDirection.Forward, true))
{
for (int i = ItemRange.StartIndex; i <= ItemRange.EndIndex; i++, childIndex++)
{
UIElement child = (UIElement)ItemContainerGenerator.GenerateNext(out bool isNewlyRealized);
if (isNewlyRealized || /*recycled*/!InternalChildren.Contains(child))
{
if (childIndex >= InternalChildren.Count)
{
AddInternalChild(child);
}
else
{
InsertInternalChild(childIndex, child);
}
ItemContainerGenerator.PrepareItemContainer(child);
child.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
}
if (child is IHierarchicalVirtualizationAndScrollInfo groupItem)
{
groupItem.Constraints = new HierarchicalVirtualizationConstraints(
new VirtualizationCacheLength(0),
VirtualizationCacheLengthUnit.Item,
new Rect(0, 0, ViewportWidth, ViewportHeight));
child.Measure(new Size(ViewportWidth, ViewportHeight));
}
}
}
}
protected virtual void VirtualizeItems()
{
for (int childIndex = InternalChildren.Count - 1; childIndex >= 0; childIndex--)
{
var generatorPosition = GetGeneratorPositionFromChildIndex(childIndex);
int itemIndex = ItemContainerGenerator.IndexFromGeneratorPosition(generatorPosition);
if (!ItemRange.Contains(itemIndex))
{
if (VirtualizationMode == VirtualizationMode.Recycling)
{
ItemContainerGenerator.Recycle(generatorPosition, 1);
}
else
{
ItemContainerGenerator.Remove(generatorPosition, 1);
}
RemoveInternalChildRange(childIndex, 1);
}
}
}
So I understand that developers could make a mistake in code, I also understand that there may not be a single thing that can be optimized, but I really cannot understand why it is so slow.. The loops iterates throught about 40-50 UIElement objects, every of which has two child objects(TextBlock and ImageSource) and that's not such a huge number of objects..
I want to use parallel tasks in these loops, but UIElement and others is not thread-safe and can only be accessed in UI Thread... or can?
How can I increase performance of these methods? Can there be implemented efficient multithreading? Thanks!