How can I specify the following layout with RelativePanel? - xaml

+--+---+--+
| |RED| |
+------+ |
| BLUE | |
+------+--+
I have tried a few different combinations of RelativePanel.Align... attached properties, but I can't quite seem to get things to work out the way I want.
<!-- this version overlays Red beneath Blue, but aligned on the right -->
<Rectangle x:Name="Red" RelativePanel.AlignRightWith="Blue" />
<Rectangle x:Name="Blue" />
<!-- this version shoves Blue half off-screen -->
<Rectangle x:Name="Red" />
<Rectangle x:Name="Blue" RelativePanel.AlignRightWith="Red" RelativePanel.Below="Red" />
<!-- this version is a circular dependency -->
<Rectangle x:Name="Red" RelativePanel.AlignRightWith="Blue" />
<Rectangle x:Name="Blue" RelativePanel.Below="Red" />

If I were in your shoes, I would use a Grid.
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle Fill="Red" Grid.Column="1" ... />
<Rectangle Fill="Blue" Grid.Row="1" Grid.ColumnSpan="2" ... />
</Grid>

Related

How to Bind to Height with "auto" value

Working with UWP means I cant use DIP values always. I rely on "auto" sizes, "Stretch" alignments, etc.
I narrowed my problem to this:
How Can I Bind Height and Width of Element to Another Element, which has Height and Width "Auto"?
Sample:
<Grid.RowDefinitions>
<RowDefinition x:Name="CardGriddRow1" Height="2*" />
<RowDefinition x:Name="CardGrdidRow2" Height="1*" />
</Grid.RowDefinitions>
<Rectangle Name="Rec1" Fill="Blue" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Width="auto" Height="auto" Grid.Row="1" Margin="20" />
<Rectangle Name="Rec2" Fill="Yellow" Grid.Row="0" Height="{x:Bind Rec1.ActualHeight, Mode=OneWay }" Width="{x:Bind Rec1.ActualWidth, Mode=OneWay }" HorizontalAlignment="Left" VerticalAlignment="Top" />
When using ActualHeight, I can use only OneWay mode. Height value is NaN.
Rec2 will have 0 values but ActualHeight of Rec1 is more than 0.
Is there way to force Binding to take ActualHeight?
How Can I Bind Height and Width of Element to Another Element, which has Height and Width "Auto"?
ActualHeight is a calculated property. For purposes of ElementName binding, ActualHeight does not post updates when it changes (due to its asynchronous and run-time calculated nature). Do not attempt to use ActualHeight as a binding source for an ElementName binding. If you have a scenario that requires updates based on ActualHeight, use a SizeChanged handler. Details please reference ActualHeight property.
Although updated your code snippet to use binding instead as follows, it seems like worked , but it is not reliable that you should not use it .
<Rectangle Name="Rec1" Fill="Blue" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Row="1" Margin="20" Height="auto" Width="auto" />
<Rectangle Name="Rec2" Height="{Binding Path=ActualHeight,ElementName=Rec1}" Width="{Binding Path=ActualWidth,ElementName=Rec1}" HorizontalAlignment="Left" VerticalAlignment="Top" Fill="Yellow" Grid.Row="0"/>
The correct way as the document mentioned, you could use SizeChanged, for example:
<Rectangle Name="Rec1" SizeChanged="Rec1_SizeChanged" Fill="Blue" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Row="1" Margin="20" Height="auto" Width="auto" />
<Rectangle Name="Rec2" Fill="Yellow" Grid.Row="0" HorizontalAlignment="Left" VerticalAlignment="Top" />
Code behind:
private void Rec1_SizeChanged(object sender, SizeChangedEventArgs e)
{
Rec2.Height = Rec1.ActualHeight;
Rec2.Width = Rec1.ActualWidth;
}
I would try to avoid the SizeChanged event as much as possible as it fires so often when the screen is resized it can cause the UI to start freezing and drop in frame rate. Let the Xaml do as much of the work as possible. I've recreated the scenario you've shown in the link you attached in your comment using Xaml only. See screenshot:
It may not be exactly the same as yours but with some styling it could be.
See code:
<Page>
<Page.Resources>
<Style TargetType="Rectangle">
<Setter Property="VerticalAlignment"
Value="Stretch"/>
<Setter Property="HorizontalAlignment"
Value="Stretch" />
</Style>
</Page.Resources>
<Grid Background="Blue">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="0"
Grid.ColumnSpan="8">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Rectangle Grid.Row="0"
Grid.Column="1"
Fill="Red" />
<Rectangle Grid.Row="0"
Grid.Column="2"
Fill="Yellow" />
</Grid>
<Rectangle Grid.Row="2"
Grid.Column="0"
Fill="Blue" />
<Rectangle Grid.Row="2"
Grid.Column="1"
Fill="Red" />
<Rectangle Grid.Row="2"
Grid.Column="2"
Fill="Green" />
<Rectangle Grid.Row="2"
Grid.Column="3"
Fill="Yellow" />
<Rectangle Grid.Row="2"
Grid.Column="4"
Fill="Brown" />
<Rectangle Grid.Row="2"
Grid.Column="5"
Fill="Pink" />
<Rectangle Grid.Row="2"
Grid.Column="6"
Fill="Purple" />
<Rectangle Grid.Row="2"
Grid.Column="7"
Fill="Orange" />
</Grid>
</Page>

Dynamically expanding Grid also expands fixed height Rows and Columns

I have a dynamically expanding Grid inside another Grid which is separated onto 4 rows and 4 columns.
My main content grid spans 4 rows and 2 columns and dynamically loads different Views, and sometimes expands in height. My problem is, the other rows are also expanded although they have a fixed height, which makes the layout appear weird.
I want to keep all the rows' heights to 36, but just expand the lowest row so all content of my grid is shown.
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="45" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="70" />
<ColumnDefinition Width="110" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="36" />
<RowDefinition Height="36" />
<RowDefinition Height="36" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Button Grid.Row="0" Grid.Column="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
FontSize="14"
Content="X">
</Button>
<Grid x:Name="MainPaymentContentRoot"
Grid.Row="0"
Grid.RowSpan="4"
Grid.Column="1"
Grid.ColumnSpan="2"
Margin="20,0,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollMode="Auto">
</Grid>
<TextBlock Text="MyField" Grid.Row="0" Grid.Column="3" Margin="10,0,0,0" VerticalAlignment="Center" />
<Button Grid.Row="1" Grid.Column="3" Margin="10,0,0,0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Right"
Content="AnotherButton">
</Button>
</Grid>
This is for a Win 8 Development.
Currently, your RowDefinition is set to Auto. This means that the row will calculate the content size, and adjust it's height accordingly.
You need to change it to Height="*".
<RowDefinition Height="*" />
This will force the RowDefinition to expand to the height of the parent. In other words, it will take up all available space.

TextLineBounds snipping bottom of letters

I'm using TextLineBounds in Windows 8.1 to align text of different font sizes on the baseline, such as
With the XAML looking like:
<Grid x:Name="PageHeader">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="15" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="15" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1"
VerticalAlignment="Bottom"
TextLineBounds="TrimToBaseline"
FontSize="50"
Text="OOO Big Font" />
<TextBlock Grid.Column="3"
VerticalAlignment="Bottom"
TextLineBounds="TrimToBaseline"
FontSize="26"
Text="OOO SmallerFont" />
<TextBlock Grid.Column="5"
VerticalAlignment="Bottom"
TextLineBounds="TrimToBaseline"
FontSize="20"
Text="OOO Even Smaller Font" />
</Grid>
</Grid>
When I resize the window, at some point (before the minimum size of 500) the descenders are clipped (and I've also seen the bottom pixel or two across entire line get clipped even when there are no descenders - resulting in artifacts like a flattened O).
The display elements do not change on the resize, there's no modification due to visual state, etc.
It seems like a bug, but not sure if there's a workaround (other than to abandon use of TextLineBounds).

Grid adding unwanted extra vertical space

I have the following xaml inside a user control:
<StackPanel VerticalAlignment="Top">
<Grid Background="LimeGreen">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0" Grid.Row="0" Grid.RowSpan="3" VerticalAlignment="Top" Fill="Yellow" Width="80" Height="80" />
<Rectangle Grid.Column="1" Grid.Row="0" VerticalAlignment="Top" Fill="Red" Width="10" Height="10" />
<Rectangle Grid.Column="1" Grid.Row="1" VerticalAlignment="Top" Fill="Red" Width="10" Height="10" />
<Rectangle Grid.Column="1" Grid.Row="2" VerticalAlignment="Top" Fill="Red" Width="10" Height="10" />
</Grid>
</StackPanel>
and it produces the following layout:
For some reason, this is adding extra unwanted space after the yellow square. I want the following layout instead:
The extra space only occurs when the green grid is inside a stack panel. I can get the correct layout by either:
Putting the green grid inside a grid instead of a stack panel.
Or setting the width of the second column to "Auto". This is undesired, though.
My questions are:
Is the layout in the first picture correct/expected? If so, why is it adding the extra space?
Why does setting the width of the second column to "Auto" get rid of the extra vertical space?
How can I get layout #2 inside a stack panel with width of second column set to * (star)?
I can answer question 3 with this alternative xaml, however it uses a nested grid to bypass using a row span for the yellow square. Ideally this should be possible using just one grid. Anyway, here's the xaml:
<StackPanel VerticalAlignment="Top">
<Grid Background="LimeGreen">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Rectangle VerticalAlignment="Top" Fill="Yellow" Width="80" Height="80" />
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Rectangle Grid.Row="0" VerticalAlignment="Top" Fill="Red" Width="10" Height="10" />
<Rectangle Grid.Row="1" VerticalAlignment="Top" Fill="Red" Width="10" Height="10" />
<Rectangle Grid.Row="2" VerticalAlignment="Top" Fill="Red" Width="10" Height="10" />
</Grid>
</Grid>
</StackPanel>
I'm still stumped on answering questions 1 and 2.

Why doesn't the grid layout work without specifying RowDefinitions and Column Definitions?

<Page
x:Class="AllControls.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AllControls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Rectangle Width="100" Height="100" Fill="Red" Grid.Column="0" Grid.Row="0"/>
<Rectangle Width="100" Height="100" Fill="Orange" Grid.Column="1" Grid.Row="0"/>
<Rectangle Width="100" Height="100" Fill="Yellow" Grid.Column="0" Grid.Row="1"/>
<Rectangle Width="100" Height="100" Fill="Green" Grid.Column="1" Grid.Row="1"/>
<Rectangle Width="100" Height="100" Fill="Blue" Grid.Column="0" Grid.Row="2"/>
<Rectangle Width="100" Height="100" Fill="Indigo" Grid.Column="1" Grid.Row="2"/>
<Rectangle Width="100" Height="100" Fill="Violet" Grid.Column="0" Grid.Row="3"/>
<Rectangle Width="100" Height="100" Fill="Purple" Grid.Column="1" Grid.Row="3"/>
</Grid>
</Page>
I am providing, Row Number and Column number for each element in this case Rectangles. I have also provided height and width for each of them.
I come from an HTML background.
Can someone explain to me how the tags RowDefinitions and ColumnDefinitions work here ?
You have to specify RowDefinitions and ColumnDefinitions so that it knows how to size the rows and columns. Otherwise it doesn't know whether you want auto-sizing or star-sizing. And it also needs to know how many rows and columns you need up front; it doesn't add them on the fly based on your values of Grid.Row and Grid.Column.
I think the code you wrote looks reasonable, and it would be nice if it used a default when you don't specify, but unfortunately it doesn't. Instead, it builds a single-column, single-row grid and stacks all the children on top of each other.
I can think of a couple ways to write some XAML that will do what you want.
One way:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Rectangle Width="100" Height="100" Fill="Red" Grid.Column="0" Grid.Row="0"/>
<Rectangle Width="100" Height="100" Fill="Orange" Grid.Column="1" Grid.Row="0"/>
...
</Grid>
Another way:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition Height="100" />
<RowDefinition Height="100" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<Rectangle Fill="Red" Grid.Column="0" Grid.Row="0"/>
<Rectangle Fill="Orange" Grid.Column="1" Grid.Row="0"/>
...
</Grid>
Like Mr. Andrew pointed out, you'll have to define your layout more to accomplish it using specified RowDefinition / ColumnDefinition to basically manually lay your stuff out. If you're looking for something a bit more fluid with less requirement for defining your layout structure you should look into GridView / VariableSizeWrapGrid (I think is more of what you're looking for.)
Both of which have multiple examples available around the web and are pretty simple to get used to. Hope this helps on your transition from HTML to XAML (ps, in my opinion XAML > HTML)
:)