Jonathan Tuzman
Jonathan Tuzman

Reputation: 13278

Evenly space elements in StackPanel

See this container where I have two elements. I want them to be evenly spaced, as if I were using justify-content: space-between (or space-around) in an HTML flexbox.

            <StackPanel Margin="10" Orientation="Horizontal">
                <ComboBox DockPanel.Dock="Left" SelectedIndex="0" Padding="6">
                    <ComboBoxItem Content="Info"/>
                </ComboBox>
                <Ellipse DockPanel.Dock="Right" Width="20" Height="20" Fill="Red"/>
            </StackPanel>

For this particular case, where I want them at either edge of the container, I've managed a workaround using a DockPanel.

            <DockPanel Margin="10" LastChildFill="False">
                <ComboBox DockPanel.Dock="Left" SelectedIndex="0" Padding="6">
                    <ComboBoxItem Content="Info"/>
                </ComboBox>
                <Ellipse DockPanel.Dock="Right" Width="20" Height="20" Fill="Red"/>
            </DockPanel>

However, this is definitely a workaround; if I had some dynamic number of elements that I wanted evenly spaced, it wouldn't work.

Off the top of my head I imagine I could wrap each element in a StackPanel or other container that could stretch to share the width of its container. But is there a simpler way to set equal spacing?

Using Margin or Padding on the individual elements is unacceptable. Unacceptable!!!!!

Upvotes: 1

Views: 4224

Answers (1)

aybe
aybe

Reputation: 16662

You can do the following:

  • use an ItemsControl
  • with a custom ItemsPanel that'll be UniformGrid
  • along a custom IValueConverter:

enter image description here

XAML:

<UserControl x:Class="ConsoleApp1.UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:ConsoleApp1"
             mc:Ignorable="d" 
             d:DesignHeight="400" d:DesignWidth="400">
    <UserControl.Resources>
        <local:ItemsControlToRowsConverter x:Key="ItemsControlToRowsConverter" />
    </UserControl.Resources>
    <Grid>
        <Border BorderBrush="Black" BorderThickness="1">
            <ItemsControl x:Name="ItemsControl">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <UniformGrid Rows="{Binding ElementName=ItemsControl, Converter={StaticResource ItemsControlToRowsConverter}}" />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <Border Width="50" Height="50" Background="Red" Margin="2"/>
                <Border Width="50" Height="50" Background="Green" Margin="2"/>
                <Border Width="50" Height="50" Background="Blue" Margin="2"/>
                <Border Width="50" Height="50" Background="Yellow" Margin="2"/>
            </ItemsControl>
        </Border>
    </Grid>
</UserControl>

Converter:

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace ConsoleApp1
{
    public class ItemsControlToRowsConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (!(value is ItemsControl itemsControl))
                return DependencyProperty.UnsetValue;

            return itemsControl.Items.Count;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
}

Pile things up and adjust the converter to your tastes.

Upvotes: 2

Related Questions