Jake
Jake

Reputation: 1781

Horizontally aligning child elements in a wrap panel

I have the following XAML in my window:

<WrapPanel>
    <WrapPanel.Resources>
        <Style TargetType="Rectangle">
            <Setter Property="Margin" Value="10,10,10,10"/>
            <Setter Property="VerticalAlignment" Value="Top"/>
        </Style>
    </WrapPanel.Resources>
    <Rectangle Height="100" Width="100" Fill="Red"/>
    <Rectangle Height="200" Width="200" Fill="Yellow"/>
    <Rectangle Height="150" Width="150" Fill="Green"/>
    <Rectangle Height="50" Width="50" Fill="Blue"/>
    <Rectangle Height="250" Width="250" Fill="Purple"/>
</WrapPanel>

Which produces the following UI in the designer: Current layout

What I'm looking for is a way to 'horizontally align' the child elements in the wrap panel, so that the window would look something like this: Desired layout

Is there any way to achieve this in a wrap panel or do I need to consider using other controls or writing a custom control?

This image additionally illustrates what I'm trying to achieve: enter image description here

Upvotes: 4

Views: 1984

Answers (2)

Martin Noreke
Martin Noreke

Reputation: 4136

Using a few different techniques, you may be able to get to where you want to be. This SO answer shows how to define responsive column widths:

<Grid.ColumnDefinitions>
    <ColumnDefinition Width="3*"/>
    <ColumnDefinition Width="2*"/>
    <ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
    <RowDefinition Height="3*"/>
    <RowDefinition Height="2*"/>
    <RowDefinition Height="3*"/>
</Grid.RowDefinitions>

If that isn't enough, you could go down the path of having a custom control that uses a grid layout and repositions which column/row each child control is currently in, and on a control or window resize event, redo the layout positioning of what is where based upon columns / pixel calculations.

UPDATE:

For a custom control, you could try something similar to this:

public class CustomLayout : Control { private List children;

  public CustomLayout()
  {
      children = new List<Control>();      
  }

  public void AddChild(Control childToAdd)
  {
      children.Add(childToAdd);
      // TODO: Determine if new layout is needed.
  }

  public void RemoveChild(Control childToRemove)
  {
      children.Remove(childToRemove);
      // TODO: Determine if new layout is needed.
  }

  // Call to update the layout.
  private void PerformLayout(int columns, int rows)
  {
     Grid layoutGrid = new Grid();
     // TODO: Programmatically build grid layout.
     RowDefinition row = new RowDefinition();
     row.Height = 100;
     layoutGrid.Rows.Add(row);

     // Methods for layout:
     // Grid.SetRow(rowID, childControl);
     // Grid.SetColumn(columnID, childControl);


     // Note: This may produce some flashing on resize, so there may be better ways to remove/add the grid such as redoing rows and columns instead.
     Controls.Clear();
     Controls.Add(layoutGrid);
  }

  // Should be called when the window or parent resizes. I can't remember the events in question.
  public void OnResize()
  {
     int columns;
     int rows;
     // TODO: Calculate columns/rows
     PerformLayout(columns, rows);
  }

}

Hopefully this gets you going in the right direction. Please keep in mind that this is pseudo code and may not fully compile.

Upvotes: 1

Glen Thomas
Glen Thomas

Reputation: 10744

It looks like what you want is a Grid with rows and columns.

<Grid>
    <Grid.Resources>
        <Style TargetType="Rectangle">
            <Setter Property="Margin" Value="10,10,10,10"/>
            <Setter Property="VerticalAlignment" Value="Top"/>
            <Setter Property="HorizontalAlignment" Value="Left"/>
        </Style>
    </Grid.Resources>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
    <Rectangle Height="100" Width="100" Fill="Red" Grid.Column="0"/>
    <Rectangle Height="200" Width="200" Fill="Yellow" Grid.Column="1"/>
    <Rectangle Height="150" Width="150" Fill="Green"  Grid.Column="2"/>
    <Rectangle Height="50" Width="50" Fill="Blue"  Grid.Column="3"/>
    <Rectangle Height="250" Width="250" Fill="Purple" Grid.Row="1"/>
</Grid>

This achieves the exact layout of your second picture, but doesn't wrap items as window size changes. To do that you would probably need a custom Grid control that dynamically arranges children as container is resized.

Upvotes: 1

Related Questions