Automatic Height not recalculating on wpf window resize - wpf

I have a control in a grid row with height set to auto. the control's max height is bound to the actual height of the grid. This is so that the control will resize depending on it's content, but then not grow outside of the size of the grid if there is lots of data - it just gets a scroll bar. This works perfectly fine.
The issue is when I resize the window. If i make the window smaller than the size of the control, then the control doesn't resize to fit in the window.
If I then (with the smaller window size) close the screen and open it back up, the control resizes to the correct size.
When going from a smaller size to a larger size, the control expands to take up more space. It just doesn't resize to take up less space when going from a larger size to a smaller size
<Grid x:Name="MyGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollViewer MaxHeight="{Binding ElementName=MyGrid, Path=ActualHeight}">
<TextBlock Background="Gray" Text="Hello World" Height="700"/>
</ScrollViewer>
</Grid>
When shrinking the window, I expect the control to shrink, but it stays the same height and stays partially off screen

Remove MaxHeight and change Height of RowDefinition to "*"
<Grid x:Name="MyGrid">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ScrollViewer>
<TextBlock
Height="700"
Background="Gray"
Text="Hello World" />
</ScrollViewer>
</Grid>

It depends on exactly what you intend doing here.
You could simplify your markup from:
<Grid x:Name="MyGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollViewer MaxHeight="{Binding ElementName=MyGrid, Path=ActualHeight}">
<TextBlock Background="Gray" Text="Hello World" Height="700"/>
</ScrollViewer>
</Grid>
To:
<ScrollViewer>
<TextBlock Background="Gray" Text="Hello World" Height="700"/>
</ScrollViewer>
You only have one rowdefinition in the grid so there is no point in having the rowdefinition.
The grid only contains the scrollviewer - no point in the grid.
In fact, a textblock is designed to allow you to scroll long content and the template has it's own scrollviewer so you could just have a textblock:
<Window .....
<TextBlock Background="Gray"
Text="Hello World"
Height="700"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
/>
</Window>

Related

How to make TextBox fill all available space without later resizing to its content?

I have the following Grid with a TextBox in it:
<UserControl ...>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30px"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="1px"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="1" Grid.RowSpan="3" AcceptsReturn="True"
TextWrapping="Wrap" HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Visible" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
<!-- content in other cells of the Grid's first column -->
</Grid>
</UserControl>
I want the TextBox to fill all space that is available on the control (width and height-wise) and should the user enter more text than fits into it I want its vertical scrollbar to become active, without resizing the TextBox. What happens instead is that the size of the TextBox changes to accomodate the content and the whole grid grows with it.
How do I achieve what I need?
Edit: Here's an illustration of what happens.
The above screenshot is of the situation when the content fits comfortably into the TextBox. The below screenshot is of the situation when there is not enough room to fit all content - the TextBox is then resized to fit it and this resizes also the grid that it is placed in, making it look broken.
Edit 2: The project that demonstrates this behavior is here.
I have managed to solve this by adding an invisible Border in the same cells of the Grid as the TextBox, then setting TextBox' Width and Height to ActualWidth and ActualHeight of that Border respectively:
<Border x:Name="b" Grid.Column="1" Grid.RowSpan="3"
HorizontalAlignment="Stretch"/>
<TextBox AcceptsReturn="True" TextWrapping="Wrap" Grid.Column="1"
Grid.RowSpan="3" Width="{Binding ActualWidth, ElementName=b}"
Height="{Binding ActualHeight, ElementName=b}"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Visible" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
This then caused the TextBox to stay of fixed Height regardless of its content, but there was another problem with its Width: it grew when the interface was expanded, but didn't shrink afterwards, because of the underlying ScrollViewer. The trick was to set that ScrollViewer's HorizontalScrollBarVisibility to Disabled, instead of Hidden as I had done previously.
I pushed changes of the example project to GitHub, so the solution is now available here.
The TextBox doesn't fill the Grid. You can confirm this yourself by specifying a Background for the Grid:
<UserControl>
<Grid Background="Yellow">
...
This is because the height of the Grid is 30 px + whatever the height of the TextBox is + 1 px. For the contents of the row 2 or 3 to fill the Grid, you need to change the Height of at least one of the RowDefinitions to *:
<UserControl>
<Grid Background="Yellow">
<Grid.RowDefinitions>
<RowDefinition Height="30px"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="1px"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="1" Grid.RowSpan="3" AcceptsReturn="True"
TextWrapping="Wrap" HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Visible" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
</Grid>
</UserControl>

Listbox with gridsplitter resize itself instead of showing a scrollbar

In my application I need to have a listbox to display at the bottom of the screen. The listbox can be displayed of not (via a menu entry), and must be resizable in height. I placed it in a grid and used a gridsplitter to do the resize part, which works as intended.
My problem is, if no manual resize before, once log begins to appear in the listbox, this listbox does not show a scrollbar but instead in begins growing and takes more space. Once I trigger a resize using the gridsplitter, everything works as intended. What can I do to stop this ?
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0"
Grid.Column="1" />
<GridSplitter Grid.Row="1"
Height="5"
HorizontalAlignment="Stretch"
Visibility="{Binding ShowLogWindow,
Converter={StaticResource Bool2Vis}}" />
<ListBox Grid.Row="2"
VerticalAlignment="Stretch"
ItemsSource="{Binding Toolbox.LogEntries}"
MinHeight="50"
Visibility="{Binding ShowLogWindow,
Converter={StaticResource Bool2Vis}}" />
</Grid>
Set the Height of the third RowDefinition (or the ListBox itself) to 50 or whatever fixed height you want it to have.
Auto means size to content, which means that the height of the last row will grow as the ListBox grows. This is not what you want apparently.

WPF:stretch to parent loop

In my WPF application I am trying to connect all the sizes to the app size. So the main grid stretches to the main window, the canvas - to the main grid etc. But this does not work. For example, ScrollViewer that is inside of the canvas, stretches to the main grid.
What is a solution?
Simple code for example:
<Window Height="400" Width="200" ...>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Canvas Grid.Row="0" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ScrollViewer HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
</ScrollViewer>
</Canvas>
</Grid>
</Window>
Just remove the canvas and it will work.
Basically, canvas has infinite size by default, so everything in canvas cannot be aligned, unless you specify size maually.
<Window Height="400" Width="200" ...>
<Grid>
<ScrollViewer>
</ScrollViewer>
</Grid>
</Window>
Note, that you dont have to specify Grid's Column and Row Definitions, if there's only single cell. it works by default. Also, Horizontal and Vertical Alignment hav value "Stretch" by default, so it does not have to be specified explicitelly as well.
Since there is nothing but scrollviewer inside the grid, you could remove the grid as well:
<Window Height="400" Width="200" ...>
<ScrollViewer>
</ScrollViewer>
</Window>

Preserving minimum sizes with multiple WPF grids and splitters

I have a problem getting an arrangement of grids, splitters and an expander to work correctly, especially regarding setting min heights. I’ve followed a number of suggestions posted here (especially regarding how to combine a splitter and an expander), and have got the basic architecture working, but I can’t get the final details to work right. I’m sure that I could get something working by trial-and-error, especially if I do some brute force work in the code-behind, but I’m guessing there are some fundamental principles of how WPF resizing works that I’m missing here.
The following xaml is a simplified version of the actual structure, with colors and borders added for clarity. I’ve removed my attempts at setting MinHeights. (There is currently no code-behind.)
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="450"
Height="600">
<Grid Background="Yellow">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox x:Name="Tree" Grid.Row="0" Text="Tree" BorderThickness="2" BorderBrush="Red" />
<Expander IsExpanded="True" Grid.Row="1" Header="Expand/Collapse Details" ExpandDirection="Down"
BorderThickness="2" BorderBrush="Black" VerticalAlignment="Bottom">
<Expander.Content>
<Grid x:Name="LowerAreaGrid" Margin="4">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<GridSplitter HorizontalAlignment="Stretch" Grid.Row="0" Height="8" Background="DarkGray"
ResizeDirection="Rows" ResizeBehavior="CurrentAndNext" />
<Grid x:Name="DetailsPaneAndSubpane" Margin="4" Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="*" MaxHeight="{Binding ActualHeight, ElementName=DetailsPane}"/>
<RowDefinition Height="Auto" />
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer ScrollViewer.VerticalScrollBarVisibility="Auto" Grid.Row="0">
<TextBox x:Name="DetailsPane" Text="This is the Details Pane. It is 150 pixels high. If the available space is too small, it will grow a scrollbar to see all the content."
TextWrapping="Wrap" BorderThickness="4" BorderBrush="Green" Height="150"/>
</ScrollViewer>
<GridSplitter Grid.Row="1" HorizontalAlignment="Stretch" Height="8" Background="Brown"
ResizeDirection="Rows" ResizeBehavior="PreviousAndNext" />
<Border BorderThickness="4" Grid.Row="2" BorderBrush="Blue">
<Grid x:Name="SubpaneAndButtons" Margin="4" Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Text="This is the Details Subpane. It can grow and shrink."
BorderThickness="2" BorderBrush="Blue"/>
<Grid x:Name="SubpaneButtons" Grid.Row="1" Background="Blue" Margin="4">
<Button Width="200">Do something with subpane</Button>
</Grid>
</Grid>
</Border>
</Grid>
</Grid>
</Expander.Content>
</Expander>
<Grid x:Name="CommandButtons" Grid.Row="2" Background="Pink" Margin="4">
<Button Width="200">Do something with whole window</Button>
</Grid>
</Grid>
</Page>
As you can see, the window has two main areas. There is a tree in the top pane, and when an item in the tree is selected, the bottom area (which is contained in an expander) shows details about the item. There is a splitter that can change the relative sizes of the top and bottom areas. The details area is further divided into two parts: a Details Pane which has a fixed maximum size, and a bottom Details Subpane which can grow and shrink. A second splitter controls the relative sizes of the Details Pane and Details Subpane.
There’s also a button at the bottom of the window that represents a set of buttons that are commands that apply to the whole window. There’s a button at the bottom of the Details Subpane that represents a set of buttons that are commands that apply only to the Details Subpane.
Here’s what doesn’t work:
Problem: the second (brown) splitter can’t be moved up initially. (It’s not expected to be able to move down, since the Details Pane is at its maximum size.) You have to move the gray splitter a little bit, then the brown splitter works fine even if you move the gray splitter back to where it was.
The Tree should have a MinHeight of 75. Problem: putting this in the tree’s RowDefinition causes the rest of the content to slide off the end of the window when you drag the slider up past the 75 pixel mark. (If you don’t have a MinHeight, this sliding occurs when the splitter is dragged up past the top of the window.)
The Details Pane and the Details Subpane should have a MinHeight of 50. Problem: moving either of the splitters down can squish these areas down to nothing.
Neither splitter should affect the “Do something with subpane” button row. It should always be fully visible, unless the expander is collapsed. Problem: again, moving either of the splitters down can squish this area down to nothing.
Here’s what works, and should keep working after all changes are made:
When the “Expand/Collapse Details” expander is collapsed, it collapses everything but the “Do something with whole window” button row. The Tree takes all the remaining space.
The Details Pane is a fixed size (150 pixels). It can’t grow larger than that, and if it is given less space than that, it will grow a scrollbar to allow the entire 150 pixels to be seen still.
What changes need to be made (either in xaml or code-behind) to get this to work?

UserControl Expand Vertically when Window is expanded

I'm having trouble getting my UserControl to expand vertically when my window is expanded.
My UserControl currently sits inside a ItemsControl which is stretching correctly by setting the VerticalAlignment="Stretch" property on the ItemsControl.
I add the following UserControl to the ItemsControl:
<UserControl MinWidth="930">
<Grid VerticalAlignment="Stretch" Background="Red">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="730*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="400" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DockPanel Grid.Column="1" Grid.ColumnSpan="1" Grid.RowSpan="2" Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Pink" LastChildFill="True">
<ItemsControl Name="itemPanelOverview" Grid.Column="1" Background="Black" Margin="0"/>
</DockPanel>
</Grid>
</UserControl>
The UserControl is called in an ItemsControl inside of a TabControl like so:
<TabItem>
<TabItem.Header>
HEADER EG
</TabItem.Header>
<ItemsControl Name="contentItems" Grid.ColumnSpan="2" Grid.Column="1" Grid.Row="1" VerticalAlignment="Stretch" Background="Blue">
<Grid Height="35" Background="{DynamicResource GrayMenuGradient}" >
<Image Source="..." Stretch="None" />
<TextBlock Text="{Binding WelcomeMessage}" />
</Grid>
</ItemsControl>
</TabItem>
It appears that the ItemsControl (contentItems) is stretching as expected, as I can see the blue background stretching correctly.
I haven't set the height for this UserControl anywhere other than the Row Definitions. Is there something I'm missing?
There are at least two aspects at play here:
The first is that when you have items in an ItemsControl, each item is actually inside an ItemContainer, so it is the container that you want to stretch.
You can design the container by declaring an ItemContainerTemplate for your ItemsControl: http://msdn.microsoft.com/en-us/library/system.windows.controls.itemcontainertemplate.aspx
The second consideration is the ItemsPanelTemplate, which determines into what type of panel the items are placed. The ability of the items in the ItemsControl to fill up the available space is going to depend on the type of container as much as on the type of ItemContainer. For example, if you use a StackPanel for the ItemsPanelTemplate, it won't fill up available space because StackPanel grows and shrinks according to its contents. A DockPanel could potentially work, but only the last child would grow to fill available space. Perhaps a UniformGrid could do the trick.

Resources