views:

639

answers:

3

I'm trying to build a Panel or ItemsControl that will display items in the form of:

Header 1

  • Sub Item 1
  • Sub item 2
  • Sub Item 3

Header 2

  • Sub Item 1
  • Sub item 2

This is easy enough, but the catch here is that I need to be able to split the items in a Paged fashion. Based on the height of the control, whatever does not fit will be on the next Page (and so on). If a split occurs between subitems, I need to re-display the corresponding header on the next page as well.

I made some progress working with MeasureOverride and ArrangeOverride but I keep running into dead-ends. It's really frustrating because this is something that is computationally trivial but a nightmare to accomplish in WPF/Silverlight. If you have ever worked with reporting packages, the concept is very similar to this.

I keep coming back to the fact that I can't determine the height until after I have added the children to the control (using Dispatcher.BeginInvoke).

Does anyone have any suggestions for accomplishing this? Thanks in advance!


Edit

[WPF] ObservableCollection and ListBoxItem DataTemplate generation problem

This above link is very similar to what mdm20 has suggested, but I am still stuck. Everytime I try to get the ActualHeight, it returns 0. Additionally, the ItemContainerGenerator in Silverlight 3 returns null for the container unless I wrap the call in a Dispatcher.BeginInvoke operation.

A: 

If I had to implement this, I don't think I'd try to do it in a panel. I think you'd need a collection of items, a header template, paging template and an item template. You can get the height of a template using something like this:

    Size maxSize = new Size(0, 30);
    UIElement templateRoot = template.LoadContent() as UIElement;
    templateRoot.Measure(maxSize);
    Size size = templateRoot.DesiredSize;

You need to actually have a height specified in the template for this to work though. I think you only care about the height, so I left the width 0.

Once you get the template heights, you'd probably have to set up some kind of map that tracks which objects are on which page. This will have to be recreated every time the size changes or the items source changes.

After that, you just have to add items as children of a panel contained within the control, based on what page your on.

Thats how I would try to do it.. if this makes any sense at all :)

mdm20
A: 

It is indeed impossible to directly measure a UIElement until you have constructed it, but you might be able to construct the list elements in such a way that you will know in advance how much space they will take up. For example, you could use Grid to lay out each of the elements, with a row of known height (say headerHeight) for the header and rows of known height for each of the sub-items (say subitemHeight). You could then calculate the height of each element by the forumula headerHeight + numSubitems * subitemHeight. Also, if you know that the number of items will be fairly small, say less than 1000, you might just want to go ahead and create them all in advance, since the performance penalty might be tolerable at that point.

I agree that this kind of thing can be annoying, but it's the framework's very flexibility that creates the issue. This height-calculation problem is what makes it so difficult to make a smooth-scrolling--but still virtualized--list of items in a ListBox or ListView.

PeterAllenWebb
A: 

Just for future reference, I ended up finding a way to do this, it's far from ideal, but I wrote a blog entry about it here:

http://www.pagebrooks.com/archive/2009/08/24/dynamic-paging-in-silverlight.aspx

Page Brooks