WPF why are all row heights expanding when last row is * - wpf

I have a form with several columns and rows defined as follows:
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="280" />
<ColumnDefinition Width="120" />
<ColumnDefinition Width="280" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="35" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
Then I have a StackPanel which spans columns and rows:
<StackPanel Name="foo" Margin="0,0,0,0" Grid.Column="2" Grid.ColumnSpan="3" Grid.Row="2" Grid.RowSpan="7"></StackPanel>
When StackPanel "foo" contains items which cause the total height to be more than all the rows combined, I want only the last row height to expand. Instead, all the row heights are expanding equally, creating a lot of space between the controls.
Before "foo" is filled with data:
After "foo" is filled with data:
How can I code this so that only that last row with height="*" expands, and the other row heights stay the same?

This is more of a workaround than a solution, but what I ended up doing was to remove the ScrollViewer which wrapped around my grid, and wrapped the StackPanel with a ScrollViewer instead. This prevented the StackPanel from expanding the grid.

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 build a 3x3 grid with squared center cell in XAML

I am trying to build a 3x3 grid with XAML (for a Windows Phone application) where the center cell should be a square. I have tried the following but it does not work:
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5*" x:Name="centerColumnDefinition" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="{Binding ElementName=centerColumnDefinition, Path=ActualWidth}" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Canvas Grid.Row="1" Grid.Column="1">
...
</Canvas>
</Grid>
Any suggestions for a working solution?
Greetings from Germany,
Tobias
Try this
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition x:Name="centerColumnDefinition"
Width="5*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Rectangle x:Name="CenterRect"
Grid.Row="1"
Grid.Column="1"
Height="{Binding ElementName=CenterRect,
Path=ActualWidth}"
HorizontalAlignment="Stretch" />
</Grid>
It can be tricky to reference column and row definitions because they are not "real" objects in the visual tree. This method avoids the problem by using a separate object in the center cell that can be used to get the proper sizes.
You should be able to replace the rectangle with another type of control if you want, or just leave it and embed your content inside the rectangle.
ActualHeight and ActualWidth are not set until the control is measured and arranged. Usually there is nothing in InitializeComponent() that causes a measure, so you will need to set the Height of your rows after it's calculated. You can do the re-sizing on Loaded event.
Source: https://stackoverflow.com/a/1695518/546896

Fill Middle Grid Column with Slider

I have a 3 column Grid -
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
I am trying to fill the * column with a slider control - I have tried DockPanel and StackPanel - cannot get the slider width to fill that middle column. The slider is for a media element position changer that loads an .avi to a set height in xaml - so the aspect ratio is taken care of. But...Width changes to the correct ratio...the Grid size changes so I cant set a Width in xaml for the slider control.
I am not sure if that is exactly what you need, because it looks so obvious to me !
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Slider Grid.Column="1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
</Grid>
that gonna fill the second column :

WPF: Rectangle in Grid ignores margin

In a user control, which I'm embedding in my main view,
I've defined the following layout:
<Grid x:Name="RootGrid" Margin="0,10,0,0" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30*" />
<ColumnDefinition Width="20*" />
<ColumnDefinition Width="20*" />
<ColumnDefinition Width="30*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Rectangle Margin="5,0,5,0"
Grid.RowSpan="5"
Grid.ColumnSpan="4"
Panel.ZIndex="-1"
Stroke="Blue"
Fill ="Black"
StrokeThickness="2"
/>
In the preview in Visual Studio, it looks like expected:
- that is the margin 5(for right adjustment) is taken into account.
Unfortunately, during runtime it is another story. I can set the right adjustment (margin) as high as I want, the right border of the rectangle is missing.
Can somebody tell me, what I am doing wrong here? I do not want to work with absolute width for the rectangle (that's working).
Update:
According to the proposal of Erno, I've used a border (and this is indeed much simpler):
<Border HorizontalAlignment="Stretch" Margin="10,0,10,0" Style="{StaticResource StatusPanelBorder}">
<Grid x:Name="RootGrid" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30*" />
<ColumnDefinition Width="20*" />
<ColumnDefinition Width="20*" />
<ColumnDefinition Width="30*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
But the issue is still the same.
I'm embedding this view in a main view, which has the following layout:
<Grid Width="1600" Height="Auto" HorizontalAlignment="Stretch" Background="{StaticResource NoiseBackground}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30*" />
<ColumnDefinition Width="90*" />
<ColumnDefinition Width="40*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="600" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
The subview is embedded into the last column of the grid.
If I do not use the 'Stretch' alignment it works, but I want to 'stretch' the UI elements.
Second Update:
The issue was just that the shell view had a smaller width. Problem solved!
The best way to add a border to a grid is to take a border and nest a grid inside of it:
<Border>
<Grid>
...
</Grid>
</Border>
This way the grid and the border will resize the way you probably want, you can control the margins and you do not have to keep the textblocks' margins in sync with the rectangle's border.
EDIT
Looking at the xaml you added to the question I guess you set the width of the window to 1600. If so, the width of the Grid that you also set to 1600 doesn't fit because the width of the window INCLUDES the left and right borders. So forcing the grid's width to 1600 will cut it off at the right.
My advice: do not use hard-coded sizes, use star-sizes for columns and rows and use maximized for windows; Grids will stretch their contents automatically.

Grid Item Not Visible When Margin Set to Display in Bottom Half Grid

I have a grid with an overlaying separator which I am using as a cursor for the grid. I am programmatically moving the separator on the grid by setting the margin to a percentage of the grid ActualHeight. This works fine until the margin is set to more than (grid.ActualHeight / 2) at which point the separator is no longer visible.
I have tried setting the vertical alignment (which is defaulted to Top) to Bottom or Center and changing the location accordingly with similar results.
Is there some reason why the grid hides the separator? And how can I ensure the separator is visible?
Thanks,
Stuart
Edit:
Here is the XAML for the grid. Borders are added to the grid programmatically, which is why the grid contains the rows and columns.
<Grid Name="graph1" Grid.Row="1" Margin="2" MouseLeftButtonDown="MouseDown" Background="#00000000">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Separator Name="graph1Cursor" Grid.RowSpan="5" Grid.ColumnSpan="6" VerticalAlignment="Top"/>
</Grid>
And the code I am using for setting the margin:
graph1Cursor.Margin = new Thickness(0, y, 0, y);
where y is the height of the cursor location with respect to the ActualHeight of the grid.
I found a solution, although I'm not sure why the solution works over the original... Instead of putting the separator in the Grid directly, I nested it within a StackPanel. All the code other than that remained the same.
Here is the XAML for the Grid with nested StackPanel:
<Grid Name="graph1" Grid.Row="1" Margin="2" MouseLeftButtonDown="MouseDown" Background="#00000000">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Grid.RowSpan="5" Grid.ColumnSpan="6">
<Separator Name="graph1Cursor" VerticalAlignment="Top" />
</StackPanel>
</Grid>
I will mark this as answered. If anyone has a better solution, I will remark it.
A other solution is to set the Grid.Row-Property from the Separator instead the Grid.RowSpan-Property and than in your code only set again the Row-Property:
Xaml:
<Grid Name="graph1" Grid.Row="1" Margin="2" Background="#00000000">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Click="Button_Click"/>
<Separator Name="graph1Cursor" Grid.Row="1" Grid.ColumnSpan="6" VerticalAlignment="Top"/>
</Grid>
In your code, yo set the Row-Property from the graph1Cursor:
Grid.SetRow(graph1Cursor, 4);

Resources