WPF: How to make a textbox dynamically sized yet prevent autosizing?
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}"
Update:
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>