I know there are a lot of questions in regard to the auto sizing of text boxes in WPF, but I couldn't find a solution for the following problem.
Consider this simple window:
<Window x:Class="TestVisualBrush.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="470" Width="608">
<ScrollViewer>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox>Test</TextBox>
<TextBox MinHeight="100" Grid.Row="1" AcceptsReturn="True" >Test</TextBox>
</Grid>
</ScrollViewer>
</Window>
This implements these two constraints I need:
ScrollViewer
's contents, the ScrollViewer
shows a scrollbar.However, when you type too much text in the second text box, the ScrollViewer
shows a scrollbar, instead of the TextBox
. I'd like to stop the text box from increasing its height beyond the space given by the parent Grid
originally. I can't use MaxHeight
in this case because there is no suitable ActualHeight
to bind to (as far as I can see).
Any suggestions on how to solve this (preferably without code-behind)?
In HTML what I want would translate to this:
<table height="100%">
<tr>
<td><input type="text"></td>
</tr>
<tr height="100%">
<td>
<!-- Uses as much space as it gets, but scrolls if text inside
gets too large. Makes outer window scroll if too small
for min-height and other controls in table. -->
<textarea style="height:100%;min-height:100px"></textarea>
</td>
</tr>
</table>
Scrollable-expandable-controls problem.
Scrollable-expandable-controls : are controls that can stretch as its content grows and will display scrollbars when their size is restricted.
Problem appears when they are located inside another scrollable control. Child scrollable-expandable-controls will keep expanding and will count on the outer scrollable control's scrollbars.
if you give it a maximum width or height problem will be resolved but you will need to know the size ahead and you don't have this privilege if you want a dynamic app that works well with all different screen sizes.
in order to achieve required behavior , we need a panel in between to allow its children (scrollable-expandable-control) to grow asking them to give the minimum required size and then give them the maxiumum size the parent provides without displaying scrollbars , currently there is no panel like this.
Here is a one that I developed to provide this functionality:
class LimitChild : System.Windows.Controls.Panel
{
public LimitChild()
{
}
protected override Size MeasureOverride(System.Windows.Size availableSize)
{
System.Diagnostics.Debug.Assert(InternalChildren.Count == 1);
System.Windows.UIElement child = InternalChildren[0];
Size panelDesiredSize = new Size();
// panelDesiredSize.Width = availableSize.Width;
panelDesiredSize.Width = (double)child.GetValue(FrameworkElement.MinWidthProperty);
panelDesiredSize.Height = (double)child.GetValue(FrameworkElement.MinHeightProperty);
child.Measure(panelDesiredSize);
// IMPORTANT: do not allow PositiveInfinity to be returned, that will raise an exception in the caller!
// PositiveInfinity might be an availableSize input; this means that the parent does not care about sizing
return panelDesiredSize;
}
protected override System.Windows.Size ArrangeOverride(System.Windows.Size finalSize)
{
System.Windows.UIElement child = InternalChildren[0];
child.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
if (finalSize.Width > child.RenderSize.Width)
finalSize.Width = child.RenderSize.Width;
if (finalSize.Height > child.RenderSize.Height)
finalSize.Height = child.RenderSize.Height;
return finalSize; // Returns the final Arranged size
}
}
and then inside your xaml incapsulate your scrollable-expandable-control in it.
<l:LimitChild
Grid.Row="1">
<TextBox
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
MinHeight="200"
AcceptsReturn="True">Test</TextBox>
</l:LimitChild>
Try this:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox />
<TextBox AcceptsReturn="True" Grid.Row="1" VerticalScrollBarVisibility="Auto" />
</Grid>
</Window>
This should exactly meet your requirements.
Actually you can bind to ActualHeight
by accessing ancestor using RelativeSource
binding:
Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}},
Path=RowDefinitions[1].ActualHeight}"
If you what that scroll bar should be shown only for second TextBox
- put ScrollViewer
only for it:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox>Test</TextBox>
<ScrollViewer Grid.Row="1">
<TextBox MinHeight="100" AcceptsReturn="True" >Test</TextBox>
</ScrollViewer>
</Grid>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With