Rob Crocombe
Rob Crocombe

Reputation: 361

Making a horizontal list where the items fill to the control's width in Windows 8 XAML?

I'm making a Windows 8.1 C# app where there is a list of color blocks in a line. The number of colors can change, and I want the color blocks to fill up the width of the list control. I have tried using ItemsControl like this:

<ItemsControl Name="testGrid" HorizontalAlignment="Left" Margin="12,100,0,0" Width="220" Background="Black">
<ItemsControl.ItemTemplate>
    <DataTemplate>
        <Rectangle Fill="{Binding}" MinWidth="44" Height="44"/>
    </DataTemplate>
</ItemsControl.ItemTemplate>

I've tried GridView and ListView with item styles with HorizontalContentAlignment set to stretch as well. The main issue is that these are all vertical lists. I need a horizontal one.

If I try adding a StackPanel the stretched items stop working. Like this:

<ItemsControl.ItemsPanel>
   <ItemsPanelTemplate>
      <StackPanel Orientation="Horizontal" />
   </ItemsPanelTemplate>
</ItemsControl.ItemsPanel>

Here is an example image:

enter image description here

I'm using an item source bound to a List or Observable Collection. How would I go about achieving the effect in the image? Thanks

Upvotes: 2

Views: 1767

Answers (4)

Isaac Dagan
Isaac Dagan

Reputation: 11

You can use UniformGrid with Rows="1"

<UniformGrid Rows="1">
                <Border  Margin="5,0" Background="Red" Height="20" ></Border>
                <Border  Margin="5,0" Background="Green" Height="20" ></Border>
                <Border   Margin="5,0" Background="Black" Height="20" ></Border>
                <Border  Margin="5,0" Background="Gray" Height="20" ></Border>
                <Border  Margin="5,0" Background="Orange" Height="20" ></Border>
</UniformGrid>

Upvotes: 0

Depechie
Depechie

Reputation: 6142

You'll need to calculate the available width and use that to evenly divide the space between all the items. I've done what you need in a recent project, but it was done with a ListView instead of an ItemsControl. All the calculation code is inside a custom control that inherits from Panel

public class SplitPanel : Panel
{
    protected override Size MeasureOverride(Size availableSize)
    {
        // the final measure size is the available size for the width, and the maximum
        // desired size of our children for the height
        Size finalSize = new Size { Width = availableSize.Width };

        if (this.Children.Count != 0)
            availableSize.Width /= (double)this.Children.Count;

        foreach (var current in this.Children)
        {
            current.Measure(availableSize);

            Size desiredSize = current.DesiredSize;
            finalSize.Height = Math.Max(finalSize.Height, desiredSize.Height);
        }

        // make sure it will works in design time mode
        if (double.IsPositiveInfinity(finalSize.Height) || double.IsPositiveInfinity(finalSize.Width))
            return Size.Empty;

        return finalSize;
    }

    protected override Size ArrangeOverride(Size arrangeSize)
    {
        Rect finalRect = new Rect(new Point(), arrangeSize);
        double width = arrangeSize.Width / this.Children.Count;

        foreach (var child in this.Children)
        {
            finalRect.Height = Math.Max(arrangeSize.Height, child.DesiredSize.Height);
            finalRect.Width = width;

            child.Arrange(finalRect);

            // move each child by the width increment 
            finalRect.X += width;
        }

        return arrangeSize;
    }
}

When that is defined you can use it on a ListView like this

<ListView x:Name="ImageList"
            Margin="0,10,0,0"
            Style="{StaticResource HorizontalListViewStyle}"
            ItemContainerStyle="{StaticResource ImageBarListViewItemStyle}"
            SelectedIndex="{Binding SelectedIndex, ElementName=MainPivot, Mode=TwoWay}"
            Grid.Row="1">
    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <controls:SplitPanel />
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
</ListView>

It will give you this result

SplitPanel

For reference without the panel

NoSplitPanel

Upvotes: 1

Filip Skakun
Filip Skakun

Reputation: 31724

I wouldn't use a Viewbox since it's a can of worms - though it might work. I'd use an ItemsControl with a custom panel used for its ItemsPanel template. The UniformGrid in my toolkit would almost work, but you'd need to somehow bind its Columns (count) property to the number of elements in a list. It should be easy to modify though to not require a Columns property if all you need is to equally divide the space. The SquareGrid does it that way, but it expands in both directions.

Upvotes: 0

Clemens
Clemens

Reputation: 128061

If it's only about displaying filled rectangles, you might put the whole thing in a Viewbox with fixed width and height and Stretch set to Fill:

<Viewbox Stretch="Fill" Width="220" Height="44">
    <ItemsControl x:Name="testGrid">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Rectangle Fill="{Binding}" MinWidth="44" Height="44"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Viewbox>

Upvotes: 0

Related Questions