23

I have a WrapPanel, And I want to specify the Max number of its columns. So, for example, when my Collection "ObjectCollection" (binded to this WrapPanel) contains only 4 elements, the WrapPanel will have only one row. But, when "ObjectCollection" will have 5 elements, the wrapPanel will create another row to put the fifth one. (My Max_Columns_Number in this case is 4).

Cœur
  • 37,241
  • 25
  • 195
  • 267
NTinkicht
  • 972
  • 2
  • 12
  • 34
  • you dont really need to write your custom panel for this just use a grid instead of wrappanel as your itemspanel in listbox. though you will have to tell each listboxitem to which grid row or column it belongs – ninja hedgehog Sep 05 '13 at 12:46

4 Answers4

49

I'm pretty sure you can't do it with a WrapPanel, but you can use the UniformGrid instead.

That one has properties to specify the number of rows and columns you want.

If you set the Columns property to 4, it will keep 4 items in each row, and then wrap to the next one.

<UniformGrid Columns="4">
    <!-- In first row -->
    <Button Content="test"></Button>
    <Button Content="test"></Button>
    <Button Content="test"></Button>
    <Button Content="test"></Button>

    <!-- In second row -->
    <Button Content="test"></Button>
</UniformGrid>
Peter Hansen
  • 8,807
  • 1
  • 36
  • 44
  • Thank you so much! I was struggling with WrapPanel, This works so much smoother for my solution. – upizs Feb 11 '23 at 07:36
9

Basically you're going to need to create a custom Panel for yourself... now don't get despondent... it's not that difficult. To start with, please take a look at the posts that I have provided links for that explain how to create a custom Panel:

How to create a Custom Layout Panel in WPF

Creating Custom Panels In WPF

Ok, so now that you know a bit more about creating custom Panels, we can continue... here's what you're going to need:

private int columnCount;
private double leftColumnEdge, rightColumnEdge, columnWidth;

public int ColumnCount
{
    get { return columnCount; }
    set
    {
        if (value < 1) value = 1;
        columnCount = value;
    }
}

This property would be used where you declare your Panel in Resources:

<ItemsPanelTemplate x:Key="AnimatedPanel">
    <Controls:AnimatedColumnWrapPanel ColumnCount="3" ... />
</ItemsPanelTemplate>

Note that you will need to declare it inside an ItemsPanelTemplate object because that is what the ItemsPanel property expects:

 <ListBox ItemsPanel="{StaticResource AnimatedPanel}" ... />

Now back to the Panel... here is a helper method that I call from the MeasureOverride and ArrangeOverride methods:

private void UpdateColumns(int currentColumn, Size finalSize)
{
    leftColumnEdge = (finalSize.Width / ColumnCount) * currentColumn;
    rightColumnEdge = (finalSize.Width / ColumnCount) * (currentColumn + 1);
    columnWidth = rightColumnEdge - leftColumnEdge;
}

Unfortunately for you, I cannot provide you with a complete example because my custom Panels are all tied into a base AnimatedPanel class with lots of additional functionality. However, you only need to create the MeasureOverride and ArrangeOverride methods to complete this Panel. If you just think logically about it, it's really not that difficult.

Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • Well this is pro level. This is more like for you or me :) He seems not to understand layouting. He would be better of with Grid or UniformGrid. – ninja hedgehog Sep 05 '13 at 12:49
  • 1
    I totally accept that, but hopefully *some* users will find this page useful over time. – Sheridan Sep 05 '13 at 12:55
  • Sure just alot of things going on there. measuing drill down process, calculating, arranging. But its up to user to decide whether to start from scratch or not. – ninja hedgehog Sep 05 '13 at 13:07
1

You can control the number of columns by setting the width of the wrap panel. I bind the width of the wrap panel to the ActualWidth of a container like Border. That way, the number of columns is dynamic and is based on the width of the window.

<Border Name="DataBorder" Grid.Row="0" Grid.Column="1"
        BorderBrush="Navy" BorderThickness="1,2,2,2"
        Padding="4">
        <Grid>
             <Grid.RowDefinitions>
                  <RowDefinition Height="Auto"></RowDefinition>
                  <RowDefinition Height="*"></RowDefinition>
              </Grid.RowDefinitions>

              <StackPanel>
                  <TextBlock Text="{Binding NewPictureCountDisplay}"></TextBlock>
              </StackPanel>

              <ListBox Name="NewFilesListBox" Grid.Row="1"
                       ItemsSource="{Binding CreatedFiles}">
                  <ListBox.ItemsPanel>
                      <ItemsPanelTemplate>
                          <WrapPanel Orientation="Horizontal" Width="{Binding ElementName=DataBorder, Path=ActualWidth}"></WrapPanel>
                      </ItemsPanelTemplate>
                  </ListBox.ItemsPanel>
                        <ListBox.ItemTemplate>
                            <DataTemplate>
                                <Grid>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="*"></RowDefinition>
                                        <RowDefinition Height="Auto"></RowDefinition>
                                    </Grid.RowDefinitions>

                                    <Image Grid.Row="0" Source="{Binding FullPath}" Width="128" Height="128" Stretch="UniformToFill"></Image>

                                    <StackPanel Grid.Row="1" Orientation="Vertical">
                                        <Button Content="Import" Margin="2"></Button>
                                        <Button Content="Delete" Margin="2"></Button>
                                        <TextBlock HorizontalAlignment="Stretch" Text="{Binding FullPath}" Margin="2"></TextBlock>
                                        <TextBlock HorizontalAlignment="Stretch" Text="{Binding ChangeType}" Margin="2"></TextBlock>
                                    </StackPanel>

                                </Grid>
                            </DataTemplate>
                        </ListBox.ItemTemplate>
                    </ListBox>
BDH
  • 43
  • 2
1

Sometimes the UniformGrid is not enough:

  • when items are of very different sizes, or
  • when you want items vertically and don't want to use other workarounds

In this stackoverflow post can be found a WrapPanel with what you are searching for.

Xaml:

<loc:WrapPanelWithRowsOrColumnsCount
    xmlns:loc="clr-namespace:..."
    Orientation="Vertical"
    RowsOrColumnsCount="2">
    <TextBox Text="Andrew" Margin="2" Height="30" />
    <TextBox Text="Betty" Margin="2" Height="40" />
    <TextBox Text="Celine" Margin="2" Height="20" />
    <TextBox Text="Dick" Margin="2" Height="20" />
    <TextBox Text="Enron" Margin="2" Height="30" />
    <TextBox Text="Felix" Margin="2" Height="20" />
    <TextBox Text="Hanibal" Margin="2" Height="30" />
</loc:WrapPanelWithRowsOrColumnsCount>

Result:

enter image description here

Community
  • 1
  • 1
frakon
  • 1,948
  • 2
  • 22
  • 26