Search code examples
c#wpfxamllayoutwpf-grid

WPF Grid Layout query


In a WPF Project I have a Grid defined with two rows each containing a ListBox. The RowDefinitions have their Height = '*' so equal space is allocated to each ListBox. What I would like to happen however, is that when either ListBox is not fully needing all its own space (through items being removed at runtime) then that 'spare' space is given over to the other ListBox.

So, using the code I list below, both ListBox's start off with equal space which is correct. Pressing the button deletes some entries from the top ListBox (topList), resulting in white space below the remaining 2 entries. It's this space I would like to be granted to the bottom ListBox. And vice versa.

XAML:

<Window x:Class="FiftyPercentSplit.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:FiftyPercentSplit"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Grid Height="200">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
   </Grid.RowDefinitions>

    <Button x:Name="btnDeleteFromTopList" Click="btnDeleteFromTopList_Click" Content="Delete from Top List"/>

    <ListBox  x:Name="topList" Grid.Row="1">
        <ListBoxItem>1</ListBoxItem>
        <ListBoxItem>2</ListBoxItem>
        <ListBoxItem>3</ListBoxItem>
        <ListBoxItem>4</ListBoxItem>
        <ListBoxItem>5</ListBoxItem>
        <ListBoxItem>6</ListBoxItem>
        <ListBoxItem>7</ListBoxItem>
        <ListBoxItem>8</ListBoxItem>
        <ListBoxItem>9</ListBoxItem>
    </ListBox>

    <ListBox  x:Name="bottomList" Grid.Row="2">
        <ListBoxItem>1</ListBoxItem>
        <ListBoxItem>2</ListBoxItem>
        <ListBoxItem>3</ListBoxItem>
        <ListBoxItem>4</ListBoxItem>
        <ListBoxItem>5</ListBoxItem>
        <ListBoxItem>6</ListBoxItem>
        <ListBoxItem>7</ListBoxItem>
        <ListBoxItem>8</ListBoxItem>
        <ListBoxItem>9</ListBoxItem>
    </ListBox>

</Grid>
</Window>

Code behind:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void btnDeleteFromTopList_Click(object sender, RoutedEventArgs e)
    {
        for (int i = 0; i < 7; i++)
        {
            topList.Items.RemoveAt(0);
        }
    }
}

Solution

  • effectively the 2nd row should behave as Auto-height row. But scrollable controls like ListBox don't work well in Auto-height row - they grow infinitely and don't show scroll. To overcome it I usually have to set MaxHeight.

    In this case I left 2 RowDefinitions with Height="*", used element with name Measurer to get actual height of the rows and set the MaxHeight for topList from ActualHeight of Measurer. Note also that I used a nested Grid with <RowDefinition Height="Auto"/>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
    
        <Button x:Name="btnDeleteFromTopList" Click="btnDeleteFromTopList_Click" Content="Del"/>
    
        <Border Grid.Row="2" Name="Measurer"/>
    
        <Grid Grid.Row="1" Grid.RowSpan="2">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
    
            <ListBox  Grid.Row="0" x:Name="topList" VerticalAlignment="Top"
                      MaxHeight="{Binding ActualHeight, ElementName=Measurer}">
    
            </ListBox>
    
            <ListBox  x:Name="bottomList" Grid.Row="1">
    
            </ListBox>
        </Grid>       
    </Grid>
    
    • ListBoxItems removed from the sample to avoid code clutter in the answer