6

I have this:

<Window x:Class="ScrollTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Height="450"
        Width="525">
    <ScrollViewer ScrollViewer.HorizontalScrollBarVisibility="Visible"
                  ScrollViewer.VerticalScrollBarVisibility="Visible">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>

            <GroupBox Grid.Row="0"
                      Header="Stuff"
                      Height="200">
                <TextBlock Text="Lots of controls go here"
                           HorizontalAlignment="Center"
                           VerticalAlignment="Center" />
            </GroupBox>
            <TabControl Grid.Row="1">
                <TabItem Header="Main Tab">
                    <TextBox MinHeight="100"
                             HorizontalAlignment="Stretch"
                             VerticalAlignment="Stretch"
                             HorizontalContentAlignment="Left"
                             VerticalContentAlignment="Top"
                             ScrollViewer.HorizontalScrollBarVisibility="Visible"
                             ScrollViewer.VerticalScrollBarVisibility="Visible"
                             AcceptsReturn="True" />
                </TabItem>
            </TabControl>
        </Grid>
    </ScrollViewer>
</Window>

When I add too many rows into the TextBox, instead of the ScrollViewer of the TextBox being used, the box stretches and the outermost ScrollViewer is used. Can I prevent that without fixing the height of the TextBox or TabControl?

Update:

If I remove MinHeight on the TextBox and set MaxLines to 5, this is what I get:

MinHeight removed and MaxLines set to 5

If I added a 6th line, the scroll bars of the TextBox's ScrollViewer are used, but they still remain centered vertically in the TextBox control.

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
mbursill
  • 2,911
  • 1
  • 32
  • 44

3 Answers3

1

Try looking at the MaxLines and MinLines Properties.

From above link:

Setting this property causes the text box to resize if the number of visible lines exceeds the limit specified by MaxLines. This property applies only to visible lines, and does not constrain the actual number of lines. Depending on its configuration, a text box may contain additional non-visible lines that are accessible by scrolling. If the Height property is explicitly set on a TextBox, the MaxLines and MinLines property values are ignored.

Try Changing:

<TextBox MinHeight="100" 
         HorizontalAlignment="Stretch" 
         VerticalAlignment="Stretch" 
         ...

to

<TextBox MinLines="5" 
         MaxLines="5"
         HorizontalAlignment="Stretch" 
         VerticalAlignment="Stretch"

Edit: Give this a try. It is setting the VerticalContentAlignment of the TabItem. This will keep the text box at the top of the Tab, I also set the maxlines to what your available area is able to hold if you resize your form you may want to adjust that number to use all of the available space.

<TabItem Header="Main Tab" VerticalContentAlignment="Top"  >
     <TextBox 
              ScrollViewer.HorizontalScrollBarVisibility="Visible"   
              ScrollViewer.VerticalScrollBarVisibility="Visible" 
              MinLines="8"
              MaxLines="8"
              HorizontalAlignment="Stretch"   
              VerticalAlignment="Stretch" 
              HorizontalContentAlignment="Stretch"  
              VerticalContentAlignment="Stretch"  
              AcceptsReturn="True" />
</TabItem>

Edit:

After looking into it further, the reason the scrollbars are not showing up on the TextBox is because the TabControl and the TabItem are resizing to the size of the TextBox. What needs to be done is to have a limiting height set either on the TabControl, TabItem or TextBox this will allow the ScrollViewer to work for the TextBox.

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Mark Hall
  • 53,938
  • 9
  • 94
  • 111
  • Good idea, but it had no affect. – mbursill Jan 02 '12 at 00:35
  • @mbursill Did it limit the height at all? It should grow in number of lines up to the value that you set. – Mark Hall Jan 02 '12 at 00:36
  • Nope. I tried setting the MaxLines to 5, and when I exceeded 5 lines, nothing different happened. When I reach 9 lines (if I haven't resized the window beyond the 450 of my example) the TextBox box starts to expand vertically, causing the outermost scroll-viewer to be vertically scrollable. The scroll bars of the TextBox are never used. – mbursill Jan 02 '12 at 00:39
  • Try taking off the minHeight value to check. It states that if the Height property is explicitly set they are ignored. – Mark Hall Jan 02 '12 at 00:42
  • Removing the MinHeight had a strange outcome. The TextBox remained at the * height (the height of the TabItem content, like I want), but the ScrollViewer contained inside the TextBox resized to be 5 lines and centered vertically. The text insertion point was in line with the ScrollViewer. I'll try and update the question with a screen shot. – mbursill Jan 02 '12 at 00:46
  • Your updated code comes close to achieving what I would like, however, I want the TextBox height to size with the TabItem height. Min/Max lines constrains the TextBox height based on the line height. I could see that having having benefits for many layouts but not mine. – mbursill Jan 02 '12 at 01:51
  • My question used a fixed height for the GroupBox as a proof of concept. In my "real" project the GroupBox contains a grid of many rows (roughly 10). Either way, wrapping the GroupBox with the ScrollViewer doesn't allow it to scroll. When the window is resized, or when many lines of text are typed, the height of the GroupBox never changes (nor should it), and therefore those scrollbars will never show. – mbursill Jan 02 '12 at 07:41
1

I was able to get close with this:

<Window x:Class="ScrollTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Width="525">
    <ScrollViewer ScrollViewer.VerticalScrollBarVisibility="Visible"
                  x:Name="Base">
        <Grid Height="{Binding ElementName=Base, Path=ActualHeight, Mode=OneWay}"
              MinHeight="400">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>

            <GroupBox Grid.Row="0"
                      Header="Stuff"
                      Height="200">
                <TextBlock Text="Lots of controls go here"
                           HorizontalAlignment="Center"
                           VerticalAlignment="Center" />
            </GroupBox>
            <TabControl Grid.Row="1">
                <TabItem Header="Main Tab">
                    <Grid x:Name="myInnerGrid">
                        <TextBox MinHeight="100"
                                 MaxHeight="{Binding ElementName=myInnerGrid, Path=ActualHeight, Mode=OneWay}"
                                 HorizontalAlignment="Stretch"
                                 VerticalAlignment="Stretch"
                                 HorizontalContentAlignment="Left"
                                 VerticalContentAlignment="Top"
                                 ScrollViewer.HorizontalScrollBarVisibility="Visible"
                                 ScrollViewer.VerticalScrollBarVisibility="Visible"
                                 AcceptsReturn="True" />
                    </Grid>
                </TabItem>
            </TabControl>
        </Grid>
    </ScrollViewer>
</Window>

Note the binding expression on height for the outside grid and on MaxHeight for the TextBox.

It's still not perfect in that you have to manually set the MinHeight that will trigger the outer most scrollbar. It's probably as close as WPF will allow without writing a new grid control.

The idea was found here: http://social.msdn.microsoft.com/Forums/en/wpf/thread/7b4b0c88-6b8f-4f07-aa8b-8e7018762388

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
mbursill
  • 2,911
  • 1
  • 32
  • 44
0

I found that the best solution to this is to use the Border trick outlined here, applied both vertically and horizontally.

In the following example, a ScrollViewer contains a TextBox, where it is desired to have the TextBox fit all of the available space (vertically and horizontally), and have it scroll vertically before the parent ScrollViewer. The Border PlaceHolderBorder manages the Width of the TextBoxes as the parent window is resized. The Border DescriptionPlaceHolderBorder manages the Height of the Description TextBox as the parent control is resized, and the ScrollViewer of the TextBox kicks in before the parent control.

It is important to have Margins in the placeholder Borders.

<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Background="{StaticResource ControlBackgroundBrush}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="Auto"></RowDefinition>
            <RowDefinition Height="*"></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="{DynamicResource LabelHeader}" Content="Company" />
        <Label Grid.Row="1" Grid.Column="1" Style="{DynamicResource CompanyNameInput}" Content="{Binding CompanyNameLabel}" />
        <Label Grid.Row="2" Grid.Column="1" Style="{DynamicResource DescriptionInput}" Content="{Binding DescriptionLabel}" />
        <Border Name="PlaceHolderBorder" Grid.Column="2" Margin="7"/>
        <TextBox Grid.Row="1" Grid.Column="2" Text="{Binding CompanyName}" MaxLength="255"/>
        <Border Name="DescriptionPlaceHolderBorder" Grid.Row="2" Margin="7"/>
        <TextBox Grid.Row="2" Grid.Column="2" Text="{Binding Description}" VerticalScrollBarVisibility="Auto"
                 TextAlignment="Left" TextWrapping="Wrap" AcceptsReturn="True" MinHeight="60"
                 Width="{Binding ElementName=PlaceHolderBorder, Path=ActualWidth}"
                 Height="{Binding ElementName=DescriptionPlaceHolderBorder, Path=ActualHeight}"
        />
        <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 CloseConfirmCommand}" Content="{Binding CloseButtonLabel}"></Button>
        </StackPanel>
    </Grid>
</ScrollViewer>
Community
  • 1
  • 1
Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72