Search code examples
wpfxamlresizewpf-grid

How can I achieve this particular resizing behavior?


I am trying to achieve a certain resizing behavior in the footer of an application, which has the following general xaml structure:

<Window>
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        ... 

        <Grid Grid.Row="3" Grid.Column="0">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>

            <CheckBox Grid.Column="0" VerticalContentAlignment="Center" VerticalAlignment="Center" Padding="2,0" Content="Checkbox" />

            <TextBlock Grid.Column="1" Margin="10,0,0,0" VerticalAlignment="Center" Text="Log Folder: " />

            <TextBox Grid.Column="2" Height="24" VerticalAlignment="Center" VerticalContentAlignment="Center" Padding="2" Focusable="False" IsReadOnly="True" Cursor="Hand" Text="{Binding LogFolder}" />

            <Button Grid.Column="3" Height="24" Width="24" Margin="4,0" VerticalAlignment="Center" Command="{Binding SelectFolderCommand}">
                <Button.Content>
                    <Image Width="16" Height="16" Source="/Images/Refresh.png" />
                </Button.Content>
            </Button>

            <Button Grid.Column="4" VerticalAlignment="Center" HorizontalAlignment="Right" Content=" Button " />
        </Grid>
    </Grid>
</Window>

In the footer, the text box is truncated if there isn't enough horizontal space to display the whole path:

Application footer

When there is more than enough space, the text box expands horizontally to fill the available space:

Application footer resized

While the former behavior is desired, I would like the text box to not expand to fill the entire available space in the latter case. Rather, I would like the text box to expand just enough to display the entire path, and then have the left button remain adjacent to it, with any excess space appearing between the two buttons, like in this mock-up:

Desired behavior mock-up

I've tried wrapping the text box and left button in a nested grid like this:

<Grid Grid.Row="3" Grid.Column="0">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>

    <CheckBox Grid.Column="0" VerticalContentAlignment="Center" VerticalAlignment="Center" Padding="2,0" Content="Checkbox" />

    <TextBlock Grid.Column="1" Margin="10,0,0,0" VerticalAlignment="Center" Text="Log Folder: " />

    <Grid Grid.Column="2">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        
        <TextBox Grid.Column="0" Height="24" VerticalAlignment="Center" VerticalContentAlignment="Center" Padding="2" Focusable="False" IsReadOnly="True" Cursor="Hand" Text="{Binding LogFolder}" />

        <Button Grid.Column="1" Height="24" Width="24" Margin="4,0" HorizontalAlignment="Left" VerticalAlignment="Center" Command="{Binding SelectFolderCommand}">
            <Button.Content>
                <Image Width="16" Height="16" Source="/Images/Refresh.png" />
            </Button.Content>
        </Button>
    </Grid>

    <Button Grid.Column="4" VerticalAlignment="Center" HorizontalAlignment="Right" Content=" Button " />
</Grid>

While this does achieve the desired behavior when there is more than enough space, when there isn't enough space the left button is overrun:

Undesired behavior with left button overrun

Beyond this, I've tried numerous other permutations of swapping the Auto and * widths for the Grids' columns, wrapping things in StackPanels, docking things in DockPanels, and adding further Grids, but I can only ever achieve one of the two previously described sets of behavior. I.e. either the left button doesn't get overrun when the window is narrow but the text box expands without bound when the window is wide, or the text box's expansion is bounded when the window is wide but the left button gets overrun when the window is narrow.

Is the behavior I'm looking for achievable? Pure-xaml answers preferred, but if I can only achieve this via code in the codebehind, the viewmodel, or a converter, so be it.


Solution

  • Nested Grids as you already have will work as expected.

    You just have to make sure to set HorizontalAlignment="Left" on the inner Grid, and use column widths * and Auto in that order.

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
    
        <CheckBox Grid.Column="0" Content="Checkbox" Padding="2,0"
            VerticalAlignment="Center" VerticalContentAlignment="Center" />
    
        <TextBlock Grid.Column="1" Margin="10,0,0,0"
            VerticalAlignment="Center" Text="Log Folder: " />
    
        <Grid Grid.Column="2" HorizontalAlignment="Left" VerticalAlignment="Center">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
    
            <TextBox Grid.Column="0" Height="24" Padding="2"
                VerticalContentAlignment="Center" Focusable="False"
                IsReadOnly="True" Cursor="Hand" Text="{Binding LogFolder}" />
    
            <Button Grid.Column="1" Height="24" Width="24" Margin="4,0"
                Command="{Binding SelectFolderCommand}">
                <Image Width="16" Height="16" Source="/Images/Refresh.png" />
            </Button>
    
        </Grid>
    
        <Button Grid.Column="3" VerticalAlignment="Center" Content=" Button " />
    </Grid>