WPF: How to make a text box with dynamic size, but prevent automation?

I know that there are many questions regarding the automatic selection of text fields in WPF, but I could not 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 limitations that I need:

  • This setting will cause the second text box to be dynamically allocated so that it uses the remaining window.
  • If the window gets too small for the required minimum size of the contents of the ScrollViewer , the ScrollViewer shows the scroll bar.

However, when you enter too much text in the second text box, the ScrollViewer shows the scroll bar, not the TextBox . I would like the text box not to increase its height beyond the space defined by the parent Grid initially. I cannot 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 problem (preferably without code)?

Note that the root ScrollViewer should still scroll if its contents are too large for the window.

In HTML, what I want will translate into 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> 
+7
layout wpf xaml
source share
6 answers

The problem with the ability to scroll and expand.

Scrollable-expandable-controls: controls that can stretch as content grows and display scrollbars when their size is limited.

The problem occurs when they are inside another scroll control. Child-friendly scroll controllers will continue to expand and will be counted on the external scrollable scroll controls.

if you indicate that the problem with the maximum width or height will be resolved, but you will need to know the size in front, and you do not have this privilege if you want the dynamic application to work well with all the different screen sizes.

in order to achieve the desired behavior, we need a panel between them to allow our children (with the ability to scroll-expandable-control) to grow, asking them to provide the minimum required size, and then provide them with the maximum size provided by the parent, without displaying scroll bars, currently time there is no such panel.

Here is one that I developed to provide this feature:

  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 encapsulate your scrollable-extensible control in it.

  <l:LimitChild Grid.Row="1"> <TextBox VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" MinHeight="200" AcceptsReturn="True">Test</TextBox> </l:LimitChild> 
+6
source share

Try the following:

 <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 match your requirements.

+2
source share

Try the following:

 <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <TextBox Height="50"/> <TextBox Grid.Row="1" AcceptsReturn="True" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" /> </Grid> 

And you're done :)

+1
source share

Somewhere along the line you should set the maximum height of the text box with scrolling support. Usually * will do this, but since it is wrapped in a scroll viewer, the containing grid does not have an effective maximum. You can use MultiValueConverter like this

  public class MaxHeightConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) { double result = (double)values[0]; for (int n = 1; n < values.Length; ++n) { result = result - (double)values[n]; } return result; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } 

Then your XAML will look like this

 <Window x:Class="wpf_Hacks.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:wpf_Hacks" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <local:MaxHeightConverter x:Key="maxHeightConverter"></local:MaxHeightConverter> </Window.Resources> <ScrollViewer> <Grid x:Name="rootGrid"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <TextBox x:Name="headerTextBox" Height="50" Text="Textbox 1"/> <TextBox MinHeight="100" Grid.Row="1" AcceptsReturn="True" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" Text="Textbox 2"> <TextBox.MaxHeight> <MultiBinding Converter="{StaticResource maxHeightConverter}"> <Binding ElementName="rootGrid" Path="ActualHeight"></Binding> <Binding ElementName="headerTextBox" Path="ActualHeight"></Binding> </MultiBinding> </TextBox.MaxHeight> </TextBox> </Grid> </ScrollViewer> 

Basically, a converter with multiple values ​​of values ​​takes the first anchor value as the total available height, and then reduces it to the heights of other related elements, leaving the remaining space for the final text field

Hope this helps

+1
source share

In fact, you can bind to ActualHeight by accessing the ancestor using RelativeSource binding:

 Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Grid}}, Path=RowDefinitions[1].ActualHeight}" 


Update:

If you need this scrollbar only for the second TextBox - place the ScrollViewer just 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> 
0
source share

I came up with something like this, but I'm still testing, just need to know that this is what you imagined?

 <Window x:Class="testWpf.WindowTextBoxInnerScroll" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="200" Width="608" Name="MyWindow" Loaded="MyWindow_Loaded"> <ScrollViewer VerticalScrollBarVisibility="Auto" > <Grid name="MyGrid"> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <TextBox>Test</TextBox> <TextBox Name="TB2" Grid.Row="1" MinHeight="100" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" AcceptsReturn="True" >Test</TextBox> </Grid> </ScrollViewer> 

Code for

 private void MyWindow_Loaded(object sender, RoutedEventArgs e) { if (this.Height > MyGrid.RowDefinitions[0].ActualHeight + MyGrid.RowDefinitions[1].ActualHeight) { TB2.Height = MyGrid.RowDefinitions[1].ActualHeight; } else { TB2.Height = TB2.MinHeight; } } 

UPDATE

If this is not what you want, then I'm not sure what you expect from this question.

Hope this helps.

UPDATE 2

I updated my XAML and the code behind, this works for me. (I think).

0
source share

All Articles