Search code examples
c#wpftextboxautosize

How can I prioritize WPF textbox wrap over autosize?


I have a number of situations where I have panels or grids that resize automatically, but if they contain a TextBox with TextWrapping="Wrap", the TextBox continues to expand the panel/grid to the right long before it really needs to, such as the image below:

Textbox expanding the panel

What I am looking to do is to have the TextBox fill its area by wrapping text before it tries to expand to the right. A simplified example of the issue is:

<Grid>
    <Grid Background="Black" />
    <Grid VerticalAlignment="Top" HorizontalAlignment="Left" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <TextBox TextWrapping="Wrap" Height="120" MinWidth="200" />
    </Grid>
</Grid>

I found a similar question on Stack Overflow here, but the best solution posted did not allow the TextBox to expand. That solution was something like:

<Grid>
    <Grid Background="Black">
    </Grid>
    <Grid VerticalAlignment="Top" HorizontalAlignment="Left" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Border BorderThickness="0" x:Name="border" Margin="0.5" />
        <TextBox TextWrapping="Wrap" Height="120" MinWidth="200" Width="{Binding ActualWidth, ElementName=border}" />
    </Grid>
</Grid>

Any ideas other than extending TextBox with modified behaviors?


Solution

  • Although I wouldn't recommend doing this as I think it introduces unexpected behavior to the user, this seems to achieve what you're asking:

    XAML:

    <TextBox ... MinHeight="120" Width="200" SizeChanged="TextBox_SizeChanged" />

    Code behind:

    private void TextBox_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        try
        {
            if (e.PreviousSize == Size.Parse("0,0")) return;
            if (e.PreviousSize == e.NewSize) return;
    
            if (e.HeightChanged)
            {
                ((TextBox)sender).Width = e.PreviousSize.Width + 20;
            }
        }
    
        finally
        {
            e.Handled = true;
        }
    }
    

    A couple of things to note, 1) in order for this to work you must both a MinHeight and Width to allow for expansion and 2) the horizontal expansion of 20 is just an arbitrary value I used for testing purposes; you'll want to come up with a more reliable way of calculating a variable expansion value.