How to make FlexLayout's height adjust to its content? - xaml

In my Xamarin Forms application, I have a list of strings (minimum 1, maximum 4) that I want to display evenly in a column layout. Each column needs to have the same width and the content should expand so that the whole text is wrapped and visible. I know how I can do it using the Grid control using Width="*" on its columns.
I want to achieve the same result using FlexLayout, so I can bind the list of strings to BindableLayout and easily add and remove columns (I will not be displaying strings but a more complex layout in each column).
Using FlexLayout.Grow and FlexLayout.Basis I can get the FlexLayout to display evenly sized columns. The problem is making the FlexLayout's height fit all the displayed labels. Only the first row of text is displayed.
Both the Grid and FlexLayout are wrapped in a StackLayout:
<StackLayout>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Text="First label" Grid.Column="0" BackgroundColor="Aqua"
LineBreakMode="WordWrap" />
<Label Text="Second label" Grid.Column="1" BackgroundColor="Red"
LineBreakMode="WordWrap" />
<Label Text="Third label but with longer text" Grid.Column="2" BackgroundColor="Aqua"
LineBreakMode="WordWrap"/>
<Label Text="Fourth label" BackgroundColor="Red"
Grid.Column="3" LineBreakMode="WordWrap" />
</Grid>
<BoxView BackgroundColor="Blue"></BoxView>
<FlexLayout AlignItems="Stretch">
<Label Text="First label"
FlexLayout.Grow="1"
FlexLayout.Basis="0"
BackgroundColor="Aqua"
LineBreakMode="WordWrap" />
<Label Text="Second label"
FlexLayout.Grow="1"
FlexLayout.Basis="0"
BackgroundColor="Red"
LineBreakMode="WordWrap" />
<Label Text="Third label but with longer text"
FlexLayout.Grow="1"
FlexLayout.Basis="0"
BackgroundColor="Aqua"
VerticalOptions="FillAndExpand"
LineBreakMode="WordWrap" />
<Label Text="Fourth label"
FlexLayout.Grow="1"
FlexLayout.Basis="0"
BackgroundColor="Red"
LineBreakMode="WordWrap" />
</FlexLayout>
<BoxView BackgroundColor="Blue"></BoxView>
</StackLayout>
Grid and FlexLayout displayed
I figured out that when setting the HeightRequest of the FlexLayout to a specific number (e.g. 150), everything works as expected - the row has a height of 150 and all the labels stretch out to fit that. So what I need is somehow specify HeightRequest="Auto" so that the row fits all the column's content without being set to a specific value
Is there a way to achieve this?

FlexLayout will cut its child Elements . So in your case use Grid is the best solution .
so I can bind the list of strings to BindableLayout and easily add and remove columns
If you want to display a collection of data I suggest that you could use ListView or CollectionView(if you want to let the collection scroll in Horizontal or display multi Columns in the same row) .
<ContentPage.Resources>
<ResourceDictionary>
<local:WidthConverter x:Key="WidthConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout x:Name="stack">
<CollectionView x:Name="list" >
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Horizontal" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid WidthRequest="{Binding Source={x:Reference stack},Path=Width,Converter={StaticResource WidthConverter}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Text="111111" BackgroundColor="LightBlue" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
in code behind
public class WidthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var width = (double)value;
if(width>0)
{
return width * 0.25;
}
return 100;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return 0;
}
}
For more details you could check https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/collectionview/layout#horizontal-list

Related

Xamarin Grid does not split evenly

I want two labels contained within a ListView DataTemplate Cell to split evenly horizontally across the screen.
From my understanding,
if you use a Grid and set the two ColumnDefinitions to 1* it should work. I tried this and it does not display as expected.
I had to add a large WidthRequest to the first Label to get it to work.
I also tried setting the HorizonalOptions on the Grid and labels to FillAndExpand and that had no results.
<ViewCell>
<Frame HasShadow="true" Margin="2">
<StackLayout Orientation="Horizontal" Margin="0,0,0,0" >
<StackLayout WidthRequest="20" BackgroundColor="{Binding RYGStatusColor}" >
<Label Text="{Binding RYGStatusLetter}" HorizontalTextAlignment="Center"
FontSize="Medium" FontAttributes="Bold" TextColor="White" />
</StackLayout>
<StackLayout Orientation="Vertical">
<Label Text="{Binding ProgramName}" FontSize="Medium" FontAttributes="Bold" />
<Label Text="{Binding DesignCustomerName}" FontSize="Small" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0"
Text="{Binding EstimatedTotalValue, Converter={StaticResource CurrencyDisplay}}"
FontSize="Small" FontAttributes="Bold" WidthRequest="1024" />
<!-- WidthRequest is a hack to force the width to be equal across the screen. -->
<Label Grid.Row="0" Grid.Column="1" FontSize="Small">
<Label.FormattedText>
<FormattedString>
<Span Text="Modified: " />
<Span Text="{Binding ModifiedDate, StringFormat='{0:M/d/yy}', Converter={StaticResource LocalTime}}}" />
</FormattedString>
</Label.FormattedText>
</Label>
</Grid>
</StackLayout>
</StackLayout>
</Frame>
</ViewCell>
Without the WidthRequest hack, I get these results: Actual Results
Row 1
"Label1 Label2 "
Row 2
"Label1 Label2 "
With the hack, I get expected results:Expected Results
Row 1
"Label1 Label2 "
Row 2
"Label1 Label2 "
I suspect the Grid is splitting itself correctly, but it is not taking up the full space that you are expecting. Try adding HorizontalOptions="FillAndExpand" to the Grid itself and report back.
I want two labels contained within a ListView DataTemplate Cell to split evenly horizontally across the screen.From my understanding, if you use a Grid and set the two ColumnDefinitions to 1* it should work. I tried this and it does not display as expected.
I use your code in ListView datetemplate, and don't use widthrequest, I get the following result. Xamarin.Form version is 3.4.0.1008975.
Here is the code:
<ContentPage.Content>
<ListView ItemsSource="{Binding models}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Label
Grid.Row="0"
Grid.Column="0"
BackgroundColor="Blue"
FontAttributes="Bold"
FontSize="Small"
Text="{Binding str1}" />
<!-- WidthRequest is a hack to force the width to be equal across the screen. -->
<Label
Grid.Row="0"
Grid.Column="1"
BackgroundColor="Yellow"
FontSize="Small">
<Label.FormattedText>
<FormattedString>
<Span Text="Modified: " />
<Span Text="{Binding str2}" />
</FormattedString>
</Label.FormattedText>
</Label>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>

How to make 3 column Xamarin.Forms FlexLayout with expanding center column and variable width left/right columns?

I'm trying to create a FlexLayout in Xamarin.Forms that will allow me to have the left and right columns be a variable width, and have the center column (and its contents) fill the remaining space and be centered on the screen.
Here is my current code, and here is what it's producing. Notice that "CENTER TEXT" in blue is centered within its StackLayout, but the StackLayout is not centered on the screen since the left and right columns have different widths.
Is FlexLayout a good choice for this, or should I use Grid or something else? Ideally, each column will expand to fit its content, with the center column's content being centered on the screen.
Note that the contents of each column is dynamic, so the widths of the left and right columns is also dynamic.
Thank you!
Code:
<FlexLayout x:Name="titleBar"
MinimumHeightRequest="40"
Padding="10"
JustifyContent="SpaceBetween"
AlignItems="Center"
AlignContent="Center">
<StackLayout x:Name="leftActionButton"
VerticalOptions="Center"
BackgroundColor="Red"
Orientation="Horizontal">
<Image x:Name="leftActionImg"
Margin="0, 0, 5, 0"
HeightRequest="40"
VerticalOptions="Center" />
<Label x:Name="leftActionLabel"
VerticalOptions="Center" />
</StackLayout>
<StackLayout VerticalOptions="Center"
BackgroundColor="Blue"
FlexLayout.Grow="1"
FlexLayout.Shrink="0">
<Label x:Name="title"
HorizontalTextAlignment="Center"/>
</StackLayout>
<StackLayout x:Name="rightActionButton"
BackgroundColor="Yellow"
VerticalOptions="Center"
Orientation="Horizontal">
<Label x:Name="rightActionLabel"
VerticalOptions="Center"
HorizontalOptions="End"
HorizontalTextAlignment="End" />
<Image x:Name="rightActionImg"
HeightRequest="40"
VerticalOptions="Center"
HorizontalOptions="End" />
</StackLayout>
</FlexLayout>
Results:
I was struggling with the same problem recently. So I was investing a day to find a solution. The result is disillusioning and I wouldn't call it a proper solution. I'm posting my thoughts here, because I couldn't find anything similar on the net.
I created a component which is responsible for balancing all three columns: It subscribes to width changes of the left and right column and propagates the relative difference to the center column.
The center column takes the relative difference as correction by applying a padding.
MainPage: In the Resources part you see that I'm declaring an instance of ColumnBalancer. Later I subscribe Width changes to ColumnBalancer, so ColumnBalancer gets to known when the very left and very right column (content, actually) changes. The PaddingOffset binding is retrieved from the same ColumnBalancer instance whenever the Width values have changed.
<ContentPage
x:Class="App7863.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:cb="clr-namespace:ColumnBalancing"
mc:Ignorable="d">
<ContentPage.Resources>
<ResourceDictionary>
<cb:ColumnBalancer x:Key="ColumnBalancer" x:Name="ColumnBalancer" />
</ResourceDictionary>
</ContentPage.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Header -->
<Grid
Grid.Row="0"
Padding="10"
BackgroundColor="LightGray">
<Grid.RowDefinitions>
<RowDefinition Height="39" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- Navigate Back Button -->
<Label
Grid.Column="0"
Width="{Binding Source={x:Reference ColumnBalancer}, Path=ReferenceWidthLeft}"
Text="<"
FontSize="20"
BackgroundColor="Magenta"
WidthRequest="39"
HeightRequest="39"
VerticalOptions="CenterAndExpand"
VerticalTextAlignment="Center"
HorizontalOptions="CenterAndExpand"
HorizontalTextAlignment="Center" />
<!-- Page Title -->
<StackLayout
Grid.Column="1"
Margin="0"
Padding="{Binding Source={x:Reference ColumnBalancer}, Path=PaddingOffset}"
BackgroundColor="LightYellow"
Spacing="0">
<Label
Text="Center Title"
FontSize="20"
BackgroundColor="Magenta"
HeightRequest="39"
VerticalOptions="CenterAndExpand"
VerticalTextAlignment="Center"
HorizontalOptions="CenterAndExpand"
HorizontalTextAlignment="Center"
LineBreakMode="TailTruncation" />
</StackLayout>
<!-- Toolbar Items -->
<StackLayout
Grid.Column="2"
Width="{Binding Source={x:Reference ColumnBalancer}, Path=ReferenceWidthRight}"
Margin="0"
Padding="0"
BackgroundColor="LightGreen"
Orientation="Horizontal"
Spacing="6">
<Label
x:Name="ToolbarT1"
Text="T1"
FontSize="20"
BackgroundColor="Green"
WidthRequest="39"
HeightRequest="39"
VerticalOptions="CenterAndExpand"
VerticalTextAlignment="Center"
HorizontalOptions="CenterAndExpand"
HorizontalTextAlignment="Center"
IsVisible="False" />
<Label
x:Name="ToolbarT2"
Text="T2"
FontSize="20"
BackgroundColor="Green"
WidthRequest="39"
HeightRequest="39"
VerticalOptions="CenterAndExpand"
VerticalTextAlignment="Center"
HorizontalOptions="CenterAndExpand"
HorizontalTextAlignment="Center"
IsVisible="False" />
</StackLayout>
<Label
Grid.Row="2"
Grid.ColumnSpan="3"
Text="Subtitle with more info"
FontSize="20"
BackgroundColor="LightBlue"
HeightRequest="39"
VerticalOptions="CenterAndExpand"
VerticalTextAlignment="Center"
HorizontalOptions="StartAndExpand"
HorizontalTextAlignment="Start" />
</Grid>
<!-- Content -->
<Grid
Grid.Row="1"
Padding="40"
BackgroundColor="LightCoral">
<StackLayout BackgroundColor="LightBlue">
<Button Text="Toogle T1" Clicked="Button_ToogleT1" />
<Button Text="Toogle T2" Clicked="Button_ToogleT2" />
</StackLayout>
</Grid>
</Grid>
</ContentPage>
ColumnBalancer: Exposes a ReferenceWidthLeft and ReferenceWidthRight which take the Width values from the left resp. right column content. Whenever the ReferenceWidth* properties change, a new PaddingOffset is set.
public class ColumnBalancer : BindableObject
{
public static readonly BindableProperty PaddingOffsetProperty = BindableProperty.Create(
nameof(PaddingOffset),
typeof(Thickness),
typeof(ColumnBalancer),
default(Thickness),
BindingMode.OneWay);
public Thickness PaddingOffset
{
get => (Thickness)this.GetValue(PaddingOffsetProperty);
set => this.SetValue(PaddingOffsetProperty, value);
}
public static readonly BindableProperty ReferenceWidthRightProperty = BindableProperty.Create(
nameof(ReferenceWidthRight),
typeof(double),
typeof(ColumnBalancer),
default(double),
BindingMode.OneWayToSource,
null,
OnReferenceWidthRightPropertyChanged);
public double ReferenceWidthRight
{
get => (double)this.GetValue(ReferenceWidthRightProperty);
set => this.SetValue(ReferenceWidthRightProperty, value);
}
public static readonly BindableProperty ReferenceWidthLeftProperty = BindableProperty.Create(
nameof(ReferenceWidthLeft),
typeof(double),
typeof(ColumnBalancer),
default(double),
BindingMode.OneWayToSource,
null,
OnReferenceWidthLeftPropertyChanged);
public double ReferenceWidthLeft
{
get => (double)this.GetValue(ReferenceWidthLeftProperty);
set => this.SetValue(ReferenceWidthLeftProperty, value);
}
private static void OnReferenceWidthLeftPropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
{
if (!(bindable is ColumnBalancer columnBalancer) || !(newvalue is double newLeftValue && newLeftValue >= 0))
{
return;
}
UpdatePaddingOffset(columnBalancer, newLeftValue, columnBalancer.ReferenceWidthRight);
}
private static void OnReferenceWidthRightPropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
{
if (!(bindable is ColumnBalancer columnBalancer) || !(newvalue is double newRightValue && newRightValue >= 0))
{
return;
}
UpdatePaddingOffset(columnBalancer, columnBalancer.ReferenceWidthLeft, newRightValue);
}
private static void UpdatePaddingOffset(ColumnBalancer columnBalancer, double left, double right)
{
if (left < 0)
{
left = 0;
}
if (right < 0)
{
right = 0;
}
var relativePadding = Math.Abs(left - right);
if (right > left)
{
columnBalancer.PaddingOffset = new Thickness(relativePadding, 0, 0, 0);
}
else
{
columnBalancer.PaddingOffset = new Thickness(0, 0, relativePadding, 0);
}
}
}
I was able to get this to work by using a 2-column grid and overlaying the "center" column on top by using Grid.ColumnSpan="2".
I realize this has the potential for the content in the center to overlap the content on the left and right, but I'm OK with working around this limitation if that's the best I can do. Still welcome other, more robust suggestions, though!
<Grid x:Name="titleBar"
MinimumHeightRequest="40"
Padding="10"
HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackLayout x:Name="leftActionButton"
VerticalOptions="Center"
Orientation="Horizontal"
HorizontalOptions="Start"
Grid.Column="0">
<Image x:Name="leftActionImg"
Margin="0, 0, 5, 0"
HeightRequest="40"
VerticalOptions="Center" />
<Label x:Name="leftActionLabel"
VerticalOptions="Center" />
</StackLayout>
<StackLayout x:Name="rightActionButton"
VerticalOptions="Center"
HorizontalOptions="End"
Orientation="Horizontal"
Grid.Column="1">
<Label x:Name="rightActionLabel"
VerticalOptions="Center"
HorizontalOptions="End"
HorizontalTextAlignment="End" />
<Image x:Name="rightActionImg"
HeightRequest="40"
VerticalOptions="Center"
HorizontalOptions="End" />
</StackLayout>
<StackLayout VerticalOptions="Center"
HorizontalOptions="CenterAndExpand"
Grid.Column="0"
Grid.ColumnSpan="2">
<Label x:Name="title"
HorizontalTextAlignment="Center"/>
</StackLayout>
</Grid>

xamarin.forms XAML CODE TO MINIMIZE DISTANCE BETWEEN Label and button

I am developing a xamarin.forms app and here as shown in the images I am getting wide vertical spacing between label field and buttons, so I need to decrease them and align them in proper order, here is the XAML code so please suggest me what changes do I need to make?
<ScrollView>
<StackLayout Padding="5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".3*"></ColumnDefinition>
<ColumnDefinition Width=".5*"></ColumnDefinition>
<ColumnDefinition Width=".2*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
>
<Label Text="Name" Grid.Column="0" Grid.Row="4" Style="
{DynamicResource SizedLabel}" TextColor="Black"
VerticalOptions="CenterAndExpand" HorizontalOptions="StartAndExpand"
HorizontalTextAlignment="Start"/>
<Entry x:Name="name" Grid.Column="1" Grid.Row="4"
Placeholder="" WidthRequest="100" FontAttributes="None" FontSize="Small"
BackgroundColor="Transparent" VerticalOptions="CenterAndExpand"
HorizontalOptions="FillAndExpand"></Entry>
<Label x:Name="qty_lbl" Text=" Quantity"
Grid.Column="0" Grid.Row="5" Style="{DynamicResource SizedLabel}"
TextColor="Black" VerticalOptions="CenterAndExpand"
HorizontalOptions="StartAndExpand" HorizontalTextAlignment="Start"/>
**<Entry x:Name="quantity" Grid.Column="1" Grid.Row="6"
Keyboard="Numeric" Placeholder="" WidthRequest="100"
FontAttributes="None" FontSize="Small" BackgroundColor="Transparent"
VerticalOptions="CenterAndExpand" HorizontalOptions="FillAndExpand">
</Entry>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".1*"></ColumnDefinition>
<ColumnDefinition Width=".1*"></ColumnDefinition>
<ColumnDefinition Width=".1*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Button x:Name="save_btn" BackgroundColor="DodgerBlue"
Grid.Column="0" Grid.Row="1" TextColor="White" Text="Next/Save"
Clicked="OnSave" BorderColor="Black"/>
<Button x:Name="cancel_btn" BackgroundColor="DodgerBlue"
Grid.Column="1" Grid.Row="1" TextColor="White" Text="Cancel"
Clicked="OnCancel" BorderColor="Black"/>
<Button x:Name="close_btn" BackgroundColor="DodgerBlue"
Grid.Column="2" Grid.Row="1" TextColor="White" Text="Close"
Clicked="OnClose" BorderColor="Black"/>
</Grid>**
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<StackLayout Orientation="Horizontal" HeightRequest="40"
HorizontalOptions="FillAndExpand" Grid.Column="0" Grid.Row="0"
BackgroundColor="LightGray">
<Label x:Name="count_label" Text="" Style="
{DynamicResource SizedLabel}" TextColor="Gray"
HorizontalTextAlignment="Start" VerticalOptions="CenterAndExpand"
HorizontalOptions="FillAndExpand"></Label>
</StackLayout>
</Grid>
</StackLayout>
</ScrollView>
</StackLayout>
To make the buttons move up what changes do I need to make?
thanks
The problem lays in the use you make of the Grid.Row attached property. In the first grid, you define 2 rows but, then, you assign to some of its children controls the property Grid.Row to 4, 5 and 6. This implicitly force the creation of additional rows, and you end up with a grid with 7 rows (because of course rows are 0 based).
When the rows aren't given an explicit height, they are given "*", which means "equally proportional", that is, all of the same size. That's alone create a lot of extra space.
Also, in the second Grid, the one with the buttons, you define it with only one row, but assign Grid.Row = "1" to all the three buttons, this way forcing the creation of a second row. Again, because you don't specify an height, the newly created row has the same size of the row containing the buttons.
To fix all this you have to:
Assign Grid.Row = "0" to the first Label and the first Entry.
Assign Grid.Row="1" to the second Label and the second Entry.
Assign Grid.Row="0" to all the three buttons.
If you instead want the labels to appear over the relative entry, then assign Grid.Row = "0" to the first Label, Grid.Row = "1" to the first Entry, Grid.Row = "2" to the second Label and Grid.Row = "3" to the second Entry.
Also, for clarity, define four Rows in the Grid, with Height="Auto" or Height="*".
To make your code more concise, please note:
0 is the default for both Grid.Row and Grid.Column, so you can get rid of such assignments;
You don't have to define a Grid.RowDefinitions property if your Grid has only one row. The same, you don't have to define a Grid.ColumnDefinitions property if your Grid has only one column.
There is no use in using decimal notation when expressing height or width with "*" values. Star values indicate a proportion between rows (or columns), so for example if a row is "2*" and a second row is "*", the first row will be twice as tall than the second. The sum of the stars assigned to the rows (or the columns) can be any number, because it has no particular meaning, what is important is only the relative proportions between rows (or columns). To clarify even more, in the example made before, you could have assigned "100*" to the first row and "50* to the second: the result would have been the same.

Xamarin forms vertically center text over image

I'm learning Xamarin and I want to display an image as background with some texto over this image...
Just that, I manage to acomplish, but the text is displayed at the top left corner of the image.
I'd like the text to be placed at the vertical center of the image, but stick to the left side of it.
My code so far XAML:
<Grid RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="20*" ></RowDefinition>
<RowDefinition Height="65*"></RowDefinition>
<RowDefinition Height="15*"></RowDefinition>
</Grid.RowDefinitions>
<RelativeLayout Grid.Row="0">
<Image Source="login.png" Aspect="Fill" />
<Label Text="Bem vindo" TextColor="White" FontSize="22"></Label>
</RelativeLayout>
<Label Grid.Row="1" Text="linha 2" BackgroundColor="Coral"></Label>
<Button Grid.Row="2" Text="linha 3" BackgroundColor="DarkRed"></Button>
</Grid>
I've tried verticalOptions and such, but with no effect.
One thing that kind of worked is settin a Maring property to the label, but that way, the label would be centered only to the device I'm testing on. Other devices, smaller or greater, the label could (and probably would) be placed at the wrong place.
Any help?
You don't need a RelativeLayout inside the Grid. The Grid itself already is able to handle views overlay besides let your code clearer.
It may works:
<Grid RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="20*"></RowDefinition>
<RowDefinition Height="65*"></RowDefinition>
<RowDefinition Height="15*"></RowDefinition>
</Grid.RowDefinitions>
<Image Grid.Row="0" Grid.Column="0"
Source="login.png"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
Aspect="Fill" />
<Label Grid.Row="0" Grid.Column="0"
Text="Bem vindo"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
HorizontalTextAlignment="Start"
VerticalTextAlignment="Center"
TextColor="White"
FontSize="22"/>
<Label Grid.Row="1"
Text="linha 2"
BackgroundColor="Coral"/>
<Button Grid.Row="2"
Text="linha 3"
BackgroundColor="DarkRed"/>
</Grid>

Xaml alternative to grid layout

I've been using grids to hold my controls for a new app. Such as;
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Label1:" />
<ListBox Grid.Row="0" Grid.Column="1" />
<Label Grid.Row="1" Grid.Column="0" Content="Label2:" />
<ComboBox Grid.Row="1" Grid.Column="1" />
<Label Grid.Row="2" Grid.Column="0" Content="Label3:" />
<TextBox Grid.Row="2" Grid.Column="1" />
</Grid>
This works fine however I've now got a situation where I only want to show my third row based upon the selectedvalue from the combobox in the second row.
With a grid this seems a bit messy too set the visibilty of the entire row to be collapsed. I think I'd have to do it by setting the hight of the contents of the row to zero.
Is there a more flexible layout that the grid. I thought about the stackpannel but wasn't sure about having multiple columns and keeping the rows in synch.
This is probably a really simple question but I'm interested to get input from other people before I do anything.
I wouldn't recommend setting the height of controls to zero - for one thing, it would still be possible to tab to a 0-height control which would be confusing for users to say the least :)
As an alternative, try binding the visibility of any affected controls to the combobox selection, e.g:
<UserControl xmlns:cnv="clr-namespace:your_namespace_here">
<Grid Margin="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Label1:" />
<ListBox Grid.Row="0" Grid.Column="1" />
<Label Grid.Row="1" Grid.Column="0" Content="Label2:" />
<ComboBox Name="cbo" Grid.Row="1" Grid.Column="1" />
<Label Grid.Row="2" Grid.Column="0" Content="Label3:"
Visibility="{Binding ElementName=cbo, Path=SelectedIndex,
Converter={cnv:IntToVisibilityConverter}}" />
<TextBox Grid.Row="2" Grid.Column="1" />
</Grid>
In code, put together a converter which returns the relevant Visibility type:
namespace your_namespace_here
{
public class IntToVisibilityConverter : MarkupExtension, IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
int _value = (int)value;
return (_value > 0) ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
}
Note that in this example, the converter will return Visiblity.Collapsed if the first item in the combo is selected, otherwise Visiblity.Visible.
Untested code, but the method is sound. Hope this is of some use!