14

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?

double-beep
  • 5,031
  • 17
  • 33
  • 41
Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
  • making sure I understand the question: are you saying that if the textbox could support 10 lines of text, you want it to start expanding horizontally only when the 11th line is being entered? – sellmeadog Aug 01 '11 at 22:59
  • @crazyarabian, correct, I only want the horizontal expand when the nth line (beyond what is visible) is entered. Like somehow if I could trigger on when the textbox wanted to scroll vertically, then allow the expand. – Dave Clemmer Aug 01 '11 at 23:04
  • why would you want the textbox to expand horizontally? from a usability standpoint, that doesn't make much sense and seems confusing as the expected behavior would either be a) vertical expansion or b) vertical scrolling – sellmeadog Aug 01 '11 at 23:29
  • It makes sense to expand horizontally in this application, with its floating windows, etc. Almost all of the controls (except for expanders and canvases) have a fixed vertical footprint, and scroll if they go beyond that footprint. Almost all of the input controls stretch horizontally to fill the available area. – Dave Clemmer Aug 01 '11 at 23:59

3 Answers3

11

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.

bradgonesurfing
  • 30,949
  • 17
  • 114
  • 217
3

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.

sellmeadog
  • 7,437
  • 1
  • 31
  • 45
  • +1 for the idea, thanks! I'll accept if it leads to the final solution. This solution works fine if you add a MaxHeight, and the vertical scrolling kicks in when the maximum width is reached. The main problem is with setting a fixed Width, so the TextBox doesn't initially stretch to the grid, nor does it adjust if the user resizes the window. – Dave Clemmer Aug 02 '11 at 00:59
  • Didn't use this solution, but accepted this as the solution since I took your usability suggestions, leading to the solution. – Dave Clemmer Aug 02 '11 at 07:57
0

The solution I'm going with at the moment is the border trick mentioned above, and explained better here. In order for the TextBox to autofill the area in the Grid and to resize itself when the user grows or shrinks the window, the margin of the placeholder border must be greater than the margin of the TextBox.

This solution doesn't auto expand horizontally as initially desired, but it's better than the single line of text expanding to the right issue.

Example xaml of panel in image above with the border trick (margin of the textboxes are 5):

<Grid>
    <!-- Diagram Window -->
    <Expander Header="{Binding Source={StaticResource labels}, Path=DiagramToolBoxHeader}" IsExpanded="True">
        <Grid MinWidth="200" MinHeight="200">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition Height="Auto" ></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition Height="Auto"></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="10"></ColumnDefinition>
                <ColumnDefinition Width="Auto"></ColumnDefinition>
                <ColumnDefinition Width="*"></ColumnDefinition>
                <ColumnDefinition Width="10"></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Label Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" Style="{StaticResource LabelHeader}" Content="{Binding Title}" />
            <Label Grid.Row="1" Grid.Column="1" Style="{StaticResource SolutionDiagramNameInput}" Content="{Binding SolutionDiagramNameLabel}" />
            <Label Grid.Row="2" Grid.Column="1" Style="{StaticResource DescriptionInput}" Content="{Binding DescriptionLabel}" />
            <TextBox Grid.Row="1" Grid.Column="2" Text="{Binding SolutionDiagramName, Mode=TwoWay}" />
            <Border Name="PlaceHolderBorder" Grid.Column="2" Margin="7"/>
            <TextBox Grid.Row="2" Grid.Column="2" Text="{Binding Description, Mode=TwoWay}" VerticalScrollBarVisibility="Auto"
                     TextAlignment="Left" TextWrapping="Wrap" Width="{Binding ElementName=PlaceHolderBorder, Path=ActualWidth}" />
            <StackPanel Orientation="Horizontal" Grid.Row="3" Grid.Column="2" Margin="5">
                <Button Command="{Binding UpdateCommand}" Content="{Binding UpdateButtonLabel}"></Button>
                <Button Command="{Binding ResetCommand}" Content="{Binding ResetButtonLabel}"></Button>
                <Button Command="{Binding DefaultsCommand}" Content="{Binding DefaultsButtonLabel}"></Button>
                <Button Command="{Binding CloseConfirmCommand}" Content="{Binding CloseButtonLabel}"></Button>
            </StackPanel>
        </Grid>
    </Expander>
</Grid>
Community
  • 1
  • 1
Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72