Dragging GridSplitter causes cell to grow when using Auto height/width - wpf

I am using a GridSplitter to resize a cell in a grid however its behaviour is not what I am expecting and I cannot find a solution. It is a grid of three rows, the first has a row definition set to Auto and contains some elements. The second row has some data in it and has a row definition of * to fill the remaining space. The last row is a status bar that needs to be resizable, and so has a grid splitter in it and a row definition height of Auto and MinHeight of 30.
The problem is when you drag the GridSplitter all the way to the top, it will make the cell overflow. I wish for it to STOP once it gets to the top. The desired behaviour can be achieved by removing the Height=Auto from the last row, but that makes the bottom cell expand to equal height with the middle row.
Here is a XAML Pad example.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Grid ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" MinHeight="20" />
<RowDefinition Height="Auto" MinHeight="30" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Foo" />
<TextBlock Grid.Row="1" Text="Bar" />
<GridSplitter Canvas.ZIndex="1" VerticalAlignment="Top" Grid.Row="2" Background="Cyan" Height="5" HorizontalAlignment="Stretch" />
<TextBlock VerticalAlignment="Bottom" Grid.Row="2" TextWrapping="Wrap">LOL<LineBreak/>LOL<LineBreak/>LOL</TextBlock>
</Grid>
</Page>
When you drag to the top, you will notice the bottom text disappears.
I have tried various things, such as putting the grid splitter in its own cell, and Binding Height to another objects ActualHeight etc but none really work that well.
I know it isn't the most well explained question, but any ideas would be greatly appreciated.
Edit:
I have made the GridSplitter with its own row as posted below, but as I mentioned earlier the problem still remains. I have the ResizeBehavior and ResizeDirection also set here.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:sys="clr-namespace:System;assembly=mscorlib" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Grid ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" MinHeight="20" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" MinHeight="30" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Foo" />
<TextBlock Grid.Row="1" Text="Bar" />
<GridSplitter ResizeDirection="Rows" ResizeBehavior="PreviousAndNext" Grid.Row="2" Background="Cyan" Height="5" HorizontalAlignment="Stretch" />
<TextBlock VerticalAlignment="Bottom" Grid.Row="3" TextWrapping="Wrap">LOL<LineBreak/>LOL<LineBreak/>LOL</TextBlock>
</Grid>
</Page>
An example of what does work is removing the last rows Height="Auto" and changing it to * like so
This however makes the last row equal in size to the row before it and not to the requested size of the cell.

GridSplitter should lie at its own row or column. Experiment with GridSplitter.ResizeDirection and GridSplitter.ResizeBehavior properties.
Take a look at the following articles:
How to: Resize Rows with a GridSplitter
How to: Resize Columns with a GridSplitter
UPDATE
You may provide "star coefficients" to GridLength object. For example:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="95*" MinHeight="20" /> <!--here we are using 95*-->
<RowDefinition Height="Auto" />
<RowDefinition Height="5*" MinHeight="30"/> <!--and here we are using 5*-->
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Foo" />
<TextBlock Grid.Row="1" Text="Bar" />
<GridSplitter ResizeDirection="Rows" Grid.Row="2" Background="Cyan" Height="5" HorizontalAlignment="Stretch" />
<TextBlock VerticalAlignment="Bottom" Grid.Row="3" TextWrapping="Wrap">LOL<LineBreak/>LOL<LineBreak/>LOL</TextBlock>
</Grid>
</Page>
So we have the layout as you need without GridSplitter unclear behaviour.

Drat, beat me to it. I might as well post what I have. Your issue is with the third row definition. When you start scrolling up and the text disappears, the row's height keeps increasing. You could try setting the max height to some restriction, if Eugene's solution doesn't work.

Related

Grid layout and expanding controls (TextBox)

I seem to not understand the layout behavior in my application. In the following sketch I have a TextBox that expands as text is being entered. How can I prevent that from happening and have the Height stay constant and get a Scrollbar instead? The size of the right TextBox should the size the left three TextBoxes take.
Initial state:
After adding several values:
Here's the XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="LightBlue">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBox Grid.Row="0" Grid.Column="0" Text="One" Margin="5" />
<TextBox Grid.Row="1" Grid.Column="0" Text="Two" Margin="5" />
<TextBox Grid.Row="2" Grid.Column="0" Text="Three" Margin="5" />
<TextBox
Grid.RowSpan="3" Grid.Column="1" Margin="5"
AcceptsReturn="True"
Text="Hello World!"
VerticalScrollBarVisibility="Auto"
/>
</Grid>
<Rectangle Grid.Row="1" Fill="Blue" />
</Grid>
Simple Answer
Assuming that the font size and margins of the TextBoxes are going to remain constant, and that the TextBoxes on the left are single-line only, you can just set a fixed height for the top RowDefinition:
<Grid.RowDefinitions>
<RowDefinition Height="84" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
Complicated Answer
If the left TextBoxes are multiline, or if the font size or margins can change, then you'll have to get a bit more complicated. I don't think there's an existing Panel that can handle that, you'd have to manually set the Height of the right TextBox to match the sum of the ActualHeight + Margin.Top + Margin.Bottom for all of the left TextBoxes. This would need to be done whenever SizeChanged was raised on any of the left TextBoxes.

How to make GridView wrap its contents in XAML?

As the title of the post, I want to make a GridView which can wrap all of it's contents inside, it means if I have 3 items, each of them has 50px of height, so how to make a GridView contains these item that has 50*3=150px of height.
How to do that, please help me!
P/s: I want to do it by configuring XAML code, not in C# code, thanks!
I found out the answer! Just set the height of the GridView to Auto by make it inside a Grid row.
<Grid Grid.Row="0"
Margin="12,24">
<Grid.RowDefinitions>
<RowDefinition Height="64" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Opacity=".8"
Background="White"
Height="64">
<Grid Margin="8">
<TextBlock Text="Students"
VerticalAlignment="Center"
FontSize="24" />
</Grid>
<Grid Height="2"
VerticalAlignment="Bottom"
Background="#B0BEC5" />
</Grid>
<GridView Background="White"
Name="grvMatchGA"
Grid.Row="1"
ItemTemplate="{ThemeResource groupItemTemplate}"
Opacity=".8"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
VerticalAlignment="Stretch">
</GridView>
</Grid>
If you don't provide height for gridview but it's children, it automatically takes the height equivalent to sum of children's height.
For example, you can do something like this-
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0"/>
<TextBox Grid.Row="1"/>
<Button Grid.Row="2"/>
</Grid>

How to have one control expand/fill up to maxheight, then expand/fill another control in WPF?

I have the following XAML source to demonstrate what I am working on.
I want, when resizing the group vertically, is to have the first groupbox expand, up to its max height, then, when that is reached, expand the third groupbox.The third groupbox has a min height property, as well.
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Name="Screen_1_Name"
x:Class="TestExpansionScreens.Screen_1"
Width="400" Height="400">
<Grid x:Name="LayoutRoot" Background="White" Margin="0,0,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<GroupBox Header="Thing1" Background="LightGreen" Grid.Row="0" Grid.Column="0" MaxHeight="350">
<Button Content="Stuff1" />
</GroupBox>
<GroupBox Header="Thing2" Background="LightBlue" Grid.Row="1" Grid.Column="0">
<TextBox Text="Stuff2" Height="60" />
</GroupBox>
<GroupBox Header="Thing3" Background="Pink" Grid.Row="2" Grid.Column="0">
<TextBox Text="Stuff3" />
</GroupBox>
</Grid>
</UserControl>
Normally, when I just want a single control expanded to fill the available space, I use a DockPanel. I've built this example with all kinds of assortments of grids and dockpanels, however, I have been unable to resolve how to make it work. Any idea on how to make it happen?
Thanks
You have to set the MaxHeight on your first RowDefinition, not on the GroupBox. The row will grow up to that height and then all excess space will be occupied by the third row. You can also add a MinHeight to the third row.
<Grid x:Name="LayoutRoot" Background="White" Margin="0,0,0,0">
<Grid.RowDefinitions>
<RowDefinition MaxHeight="350" />
<RowDefinition Height="Auto"/>
<RowDefinition MinHeight="150" />
</Grid.RowDefinitions>
<GroupBox Header="Thing1" Background="LightGreen" Grid.Row="0" Grid.Column="0">
<Button Content="Stuff1" />
</GroupBox>
<GroupBox Header="Thing2" Background="LightBlue" Grid.Row="1" Grid.Column="0">
<TextBox Text="Stuff2" Height="60" />
</GroupBox>
<GroupBox Header="Thing3" Background="Pink" Grid.Row="2" Grid.Column="0">
<TextBox Text="Stuff3" />
</GroupBox>
</Grid>

WPF: Inner content wont scroll

I have a Window with a Grid inside:
<Grid KeyboardNavigation.TabNavigation="Local">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" MinHeight="70" />
<RowDefinition Height="*" />
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" Content="{Binding ChildViewModel.View}" />
<DockPanel Grid.Row="1" Visibility="{Binding SearchResultViewVisibility}">
<GridSplitter DockPanel.Dock="Top" Background="LightGray" Height="5" HorizontalAlignment="Stretch" VerticalAlignment="Top" IsTabStop="False"/>
<Views:SearchResultView DataContext="{Binding SearchResultViewModel}" />
</DockPanel>
<UserControls:GradientBackgroundControl Grid.Row="2" Height="25">
<Validators:FocusSummaryControl x:Name="FocusSummary" ValidateOnlyFocusedElement="False" />
</UserControls:GradientBackgroundControl>
</Grid>
The ContentControl gets a UserControl with this Grid set:
<Grid KeyboardNavigation.TabNavigation="Local">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="35" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="80" />
<ColumnDefinition Width="80" />
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Row="0" Grid.ColumnSpan="4">
<StackPanel>
...
</StackPanel>
</ScrollViewer>
The problem now is, that the ScrollViewer in the UserControl doesn't scroll. The content of the UserControl set to the ContentControl is heigher and the overflow ist just hidden.
If I am not wrong, StackPanel requires a Height to be set for scroll functionality to work because StackPanel, by design, grows in one direction (based on Orientation).
To confirm whether this is the cause of your problem, please test by setting the height of StackPanel to a fixed height. Alternately, you may want to replace the StackPanel with say DockPanel and see the behaviour. Also there is a ScrollViewer.CanContentScroll property that you may want to fiddle with.
Let us know the result of this test.
I think you need to rearrange things a little bit. My suggestions (I'm sure there are infinite variations that would work):
First, add a new row to your grid (Height="Auto") and set the height of your top row (with your ContentControl in it) to "*"
<Grid.RowDefinitions>
<RowDefinition Height="*" MinHeight="70" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="25" />
</Grid.RowDefinitions>
Second, move your GridSplitter out of the DockPanel. Put the splitter in row 1 and the dockpanel in row 2.
<ContentControl Grid.Row="0" Content="{Binding ChildViewModel.View}" />
<GridSplitter Grid.Row="1" Background="LightGray" Height="5" HorizontalAlignment="Stretch" VerticalAlignment="Top" IsTabStop="False" ResizeBehavior="PreviousAndNext"/>
<DockPanel Grid.Row="2" Visibility="{Binding SearchResultViewVisibility}">
<Views:SearchResultView DataContext="{Binding SearchResultViewModel}" />
</DockPanel>
Note that you'll probably also have to set the ResizeBehavior for your GridSplitter as shown above. I hope this will get you close to what you want.

Prevent a control in a grid to change its column width

I can't figure out how to manage properly the width of a grid column with a user control in one of its cell. I have this xaml for a window:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Content="Button" Width="100" HorizontalAlignment="Left"/>
<TextBox Grid.Row="1" />
<local:UserControl1 Grid.Row="2"
HorizontalAlignment="Left" VerticalAlignment="Top"/>
</Grid>
and the user control is a drawing set to stretch as needed:
<UserControl x:Class="WpfApplication1.UserControl1"
Height="auto" Width="auto">
<Grid Background="Aqua">
<Path Fill="Coral" Stroke="Black" StrokeThickness="1"
Stretch="Uniform"
HorizontalAlignment="Left" VerticalAlignment="Top">
<Path.Data>
<RectangleGeometry Rect="40,20, 140,30" RadiusX="10" RadiusY="10" />
</Path.Data>
</Path>
</Grid>
</UserControl>
To help I have set the user control background in blue.
I would like to have the width of the column ignoring the "natural" size of the user control. I mean I would like the user control to not determine the width during the layout of the column, but to adjust its width to whatever is the width of the column.
In the example, initially the Grid would set the column width to the value of the button, and the user control would use the column width to resize itself. Then if a long text is entered in the Textbox, as soon as the Textbox starts to be wider than the button, and the column starts to be resized as well, in turn the user control would adjust to maintain the same size than the column.
I've tried combinations of stretch values, and also have used MeasureOverride() in the user control. The latter doesn't work because the AvalaibleSize received is Infinity, not the column width.
I was able to achieve what (I believe) you're looking for by giving the TextBox a name, and binding the width of the UserControl to the ActualWidth of the TextBox. Here's the code for the Window's Grid:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Content="Button" Width="100" HorizontalAlignment="Left"/>
<TextBox Grid.Row="1" x:Name="entryTextBox" />
<local:UserControl1 Grid.Row="2"
Width="{Binding ElementName=entryTextBox, Path=ActualWidth}"
HorizontalAlignment="Left" VerticalAlignment="Top"/>
</Grid>
Hope that helps!
Old question, so it will probably not help OP anymore, but it may help others:
Put UserControl1 inside a Canvas. Then bind the Width to the ActualWidth of the parent Grid.
Example:
<Grid Name="mainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Something Grid.Row="0"/>
<Canvas Grid.Row="1">
<MyControl Width="{Binding ActualWidth, ElementName=mainGrid, Mode=OneWay}"/>
</Canvas>
</Grid>
The canvas will take the available width, but will never ask Grid for more.
The controls inside the canvas get no size from Canvas, so you must give it a size manually. In this case, we want to give it the size of the parent Grid.

Resources