سؤال

I have a ListBox with a horizontal WrapPanel inside to create a grid of content. It produces the correct layout but it's really slow with greater than a hundred or so items.

I see a few half-baked attempts at virtualising a WrapPanel on google but none of them seem production ready.

Am I missing a trick? How do I get performant and flexible (needs to reflow on resize) panel layouts?

(Note: cells are fixed size).

هل كانت مفيدة؟

المحلول

Yes, virtualizing is the correct step. WPF cant handle large amounts of UI items at once. Writing your own VirtualizingWrapPanel takes quite a lot of experience in WPF. WPF has no "ready" solution, so you either have to write it, or use someone else work.

Unlike VirtualizingStackPanel which offers the same functionality of StackPanel, this does not fully behave as the default WrapPanel that comes with WPF, there are some reasons for this:

1- WrapPanels can grow in two directions, while StackPanels only grow vertically or horizontally. Also, the growth in the direction orthogonal to the panel's orientation depends on the size of the items in the direction of the panel's orientation, so it is impossible to wrap into a new line/column unless the size of all items on the last line/column is known, but this is impossible if the two directions of the panel are being virtualized(since it does non-pixel based measurement for performance improvement).

2- The size of the extent is the same as the number of lines/columns(sections). On the StackPanel this is not a problem: Since only one item per section is allowed, the extent width/height is the number of items. On the WrapPanel, the number of sections depends highly on the size of items, and since the size of the items is not fixed and can change in any direction, it is impossible to calculate the number of sections on a WrapPanel.

The following solution was adopted when developing this panel:

1- The panel's extent is never known for sure. Instead, the number of sections is estimated after each MeasureOverride using the following expression : 'numberofitems/averageitemspersection'. Since the average number of items per section is updated after each scrolling, the extent of the panel is dynamic. The extent will be static if the items size in the panels orientation is fixed.

2- The items section/sectionindex can only be calculated sequentially, so if you are viewing the first items and you jump to a section that bypasses one or more unrealized sections, the panel will use the estimation to know which item is the first visible, this will be corrected if you go back to a realized section and go back sequentially. For example: if the panel knows that item 12 is in section 1 and the panel estimates 10 items per section,(section 0 is the first), if you jump to section 9 the panel will show the 100th item as the first visible item(that will only be correct if from section 1 to 9 there are exactly 10 items per section). But if you go back to section 1 and access all the sections sequentially until you reach section 9, then the panel will store all the items sections correctly so no further estimations will be made up to section 10.

3- Normally a WrapPanel inside a scrollviewer would be allowed to scroll vertically and horizontally, but since I can only virtualize one direction, this wrappanel will only scroll in the direction orthogonal to the panels orientation.(Meaning you shouldnt set the Virtualizing Panel Height/Width explicitly).

Copied from; http://virtualwrappanel.codeplex.com/

I didn't understand what you meant by fixed cells, but if you have fixed width / height per item, you could write better virtualization than the one I provided(VirtualWrapPanel). Just browse through code and try to understand what is going on.

What you call "doesnt quite work" is probably because it's not possible to make nice VirtualizingWrapPanel that would work exactly like the one WrapPanel(due to fact that the specific row of items depends on the next items and so on. ), but if you combine it with the fact that each item has the same width/height, I believe you could do better.

Also, you can get started by writing your own solution, based on these articles;

One: http://blogs.msdn.com/dancre/archive/2006/02/06/implementing-a-virtualized-panel-in-wpf-avalon.aspx

Two: http://blogs.msdn.com/dancre/archive/2006/02/13/531550.aspx

Three: http://blogs.msdn.com/dancre/archive/2006/02/14/532333.aspx

Four: http://blogs.msdn.com/dancre/archive/2006/02/16/implementing-a-virtualizingpanel-part-4-the-goods.aspx

نصائح أخرى

How about try to use Grid and add the item to Grid such as :

( But I didn't test its performance)

   public void InitializeGridAsFourColumns()
   {
        for (int i = 0; i < 4; i++)
        {
            _customGrid.ColumnDefinitions.Add(new ColumnDefinition());
        }

   }

    private static int row = -1;
    private static int column = 0;


   public void AddItem(item)
   {

        //Add new RowDefinition every 4 items
        if (column % 4 == 0)
        {
            RowDefinition rd = new RowDefinition();
            rd.Height = GridLength.Auto;
            _customGrid.RowDefinitions.Add(rd);
            row++;
        }

        item.SetValue(Grid.RowProperty, row);
        item.SetValue(Grid.ColumnProperty, column % 4);
        column++;
        _customGrid.Children.Add(item);

   }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top