Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 397
Dave Clemmer Avatar asked Aug 01 '11 22:08

Dave Clemmer


2 Answers

There's a simple trick to get it working. Use a Canvas and then bind the width of the textbox to the actualwidth of the canvas and the height of the canvas to the actualheight of the textbox.

<Canvas 
    x:Name="Canvas" 
    Height="{Binding ElementName=TextBlock, Path=ActualHeight}" 
    VerticalAlignment="Stretch" HorizontalAlignment="Stretch">

        <TextBlock
            x:Name="TextBlock"
            Width="{Binding ElementName=Canvas, Path=ActualWidth}"
            TextWrapping="WrapWithOverflow"
            Text="blah blah blah blah" />


</Canvas>

Screen shots of our production app using it

enter image description here

and resized

enter image description here

The trick is that the Canvas inherits the width from the parent container and the height from it's child. I'm considering wrapping the pattern in a custom control.

like image 167
bradgonesurfing Avatar answered Oct 11 '22 11:10

bradgonesurfing


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.

like image 27
sellmeadog Avatar answered Oct 11 '22 12:10

sellmeadog