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:
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?
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
and resized
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.
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.
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>
精彩评论