Search code examples
wpfscrollheightautosizestretching

Setting a WPF control to expand to fill available space, and no more


How can I set up a WPF control to fill the available space in its parent's container, but not expand the parent?

The following snippet describes the layout I am attempting. I would like the Grid to stretch to accommodate the Expander, and I would like the ListBox only to fill the Grid. I want the ListBox's scroll bar to appear when the Grid is too small to show all the ListBoxItems.

<ScrollViewer>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
        <ListBox Grid.Row="0" Grid.Column="0" />
        <Expander Grid.Row="0" Grid.Column="1" Header="Expander" />
    </Grid>
</ScrollViewer>

Currently what happens is that the Grid stretches to fit the entire ListBox, and the outer ScrollViewer's vertical scroll bar appears. I only want the outer scroll bar to appear when the Expander gets too big to fit on screen.


Solution

  • To solve the same problem I wrote special container class:

    class FrugalContainer : Decorator
    {
        protected override Size MeasureOverride(Size availableSize)
        {
            return new Size(0, 0);
        }
    
        protected override Size ArrangeOverride(Size arrangeSize)
        {
            // get it all
            Child.Measure(arrangeSize);
            Child.Arrange(new Rect(arrangeSize));
            return Child.RenderSize;
        }
    }
    

    Surround your ListBox by the container and the height of ListBox will be the same as of Expander.

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
        </Grid.RowDefinitions>
        <FrugalContainer Grid.Row="0" Grid.Column="0" >
            <ListBox />
        </FrugalContainer>
        <Expander Grid.Row="0" Grid.Column="1" Header="Expander" />
    </Grid>
    

    Note that I remove Width="Auto" from column's definition because the FrugalContainer will be as small as it can. So you can't set the width or height of the parent grid's cell to Auto.

    If you need autosizing, rewrite the container:

    class FrugalHeightContainer : Decorator
    {
        protected override Size MeasureOverride(Size availableSize)
        {
            Child.Measure(availableSize);
            return new Size(Child.DesiredSize.Width, 0);
        }
    
        protected override Size ArrangeOverride(Size arrangeSize)
        {
            Child.Measure(arrangeSize);
            Child.Arrange(new Rect(arrangeSize));
            return Child.RenderSize;
        }
    }