Firoso
Firoso

Reputation: 6685

Irregular layout ItemsControl

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?

Upvotes: 2

Views: 556

Answers (2)

Ray Burns
Ray Burns

Reputation: 62919

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.

Upvotes: 2

Josh
Josh

Reputation: 69272

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.

Upvotes: 0

Related Questions