views:

43

answers:

2

I have a strange layout for an ItemsControl.

I have a 4x6 grid with the following pattern:

1  2  3  4 
13 14 15 16
5  6  7  8
17 18 19 20
9  10 11 12
21 22 23 24

Is there an easy way to do this? should I be using 6 Items Controls and take "sections" of my list? is there a good way to do this? What about notification?

It's important to note that I may, or may not, have all 24 entries present, but the layout needs to be maintained (think of it like filled slots on a bingo card or something)

Edit:

Ideally, I'd like to be able to take a list, and do some fun sorting/padding type stuff off properties on the items in the list.

for instance, if I have an ObservableCollection with a few units, and Unit has a property "Index", I'd like to have a view consumable Collection generated that automatically uses Index to make a padded list. I guess an observable dictionary could work, but that seems gross. Maybe a new custom layout panel is in order?

A: 

WPF makes this pretty trivial. Basically you just need to specify an ItemsPanelTemplate.

<ListBox>
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Columns="4" />
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>
</ListBox>

Now whatever items you add to the ListBox will be arranged according to the layout logic of the panel which in this case is a UniformGrid.

Note that you'll still need to keep the items in your collection in the order that you want them to appear. So I would sort them out first before adding them to the ListBox. If you need to create "holes" in the collection then I would use some type of placeholder object (maybe new object() will do) instead of trying to use complex layout logic to spread the items.

Josh Einstein
aren't there collection adapter classes specifically for WPF?
Firoso
I'm not sure what you mean. There's really two parts to this question. The layout aspects (making items in a ListBox show up in a grid) is simple. But I don't fully understand how you intend on getting the items into the collection in the first place.
Josh Einstein
Posted an edit for you.
Firoso
+1  A: 

There is a clever way of doing this in pure XAML using a custom template for your ItemsControl. It's easiest if all your "cards" have a fixed size, say 100x100:

<!-- Wrap each card in a decorator twice as high as the card cell -->
<DataTemplate x:Key="ItemInDoubleHighBox">
  <Decorator Width="100" Height="200">
    <Decorator Width="100" Height="100" ClipToBounds="True">
      <ContentPresenter />
    </Decorator>
  </Decorator>
</DataTemplate>

<!-- Define a template for use with WrapPanel -->
<ItemsPanelTemplate x:Key="WrapPanelTemplate">
  <WrapPanel />
</ItemsPanelTemplate>

<!-- Now the actual ItemsControl template -->
<ControlTemplate TargetType="ItemsControl">
  <Grid Width="600" Height="600" ClipToBounds="True">

    <!-- Items 1 to 12 -->
    <ItemsControl ItemsSource="{TemplateBinding ItemsSource}"
                  ItemsPanel="{StaticResource WrapPanelTemplate}"
                  ItemTemplate="{StaticResource ItemInDoubleHighBox}" />

    <!-- Items 13 to 24 -->
    <ItemsControl ItemsSource="{TemplateBinding ItemsSource}"
                  ItemsPanel="{StaticResource WrapPanelTemplate}"
                  ItemTemplate="{StaticResource ItemInDoubleHighBox}"
                  RenderTransform="1 0 0 1 0 -500" />

  </Grid>
</ControlTemplate>

How it works: The DataTemplate causes the items to be "double-spaced" with only 1-12 visible, and the RenderTransform on the second ItemsControl makes items 13-24, which are also "double-spaced" appear in the spaces between the first rows of items.

Note: You can make the height and width data-bindable, but it takes more XAML. Just add ScaleTransforms everywhere "200", "500" or "600" appears in the XAML. For example, to deal with the "200" you can set a scale transform on the inner decorator with ScaleY="0.5" and on each ItemsControl with ScaleY="2". Now the outer decorator's height will be 100, which can be data-bound. The other constants can be dealt with via similar pre- and post- scaling of the content. And because WPF combines all the transforms before rendering anyway, the extra transforms will cost basically nothing.

Ray Burns
Firoso