OK, I give up. 3 days spent trying to accomplish this simple thing.
Here is my scenario (not all on the same XAML file):
<TabbedPage>
<NavigationPage>
<ContentPage/>
<ContentPage/>
<CarouselPage>
<CarouselContentPage>
This is the CarouselContentPage
:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.Layouts.PointInfoDataTemplate"
>
<ContentPage.Content>
<ScrollView Orientation="Vertical" BackgroundColor="Navy" VerticalOptions="Fill">
<StackLayout x:Name="MyStackLayout" HorizontalOptions="StartAndExpand" VerticalOptions="Start" BackgroundColor="Gray">
<Label x:Name="NameLabel" FontSize="Medium" HorizontalOptions="Center"/>
<Label x:Name="DescLabel" FontSize="Medium" HorizontalOptions="Center" />
<AbsoluteLayout x:Name="ImgsContainer" VerticalOptions="Start" BackgroundColor="Green">
<Image x:Name="BackImg" Aspect="AspectFit" VerticalOptions="Start"/>
<Image x:Name="MidImg" Aspect="AspectFit" VerticalOptions="Start"/>
<Image x:Name="FrontImg" Aspect="AspectFit" VerticalOptions="Start"/>
</AbsoluteLayout>
</StackLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>
What I need is simple, a vertical scroll for a page with a few Labels vertically stacked and after that I need to create "one image" which is made of 3 images on top of each other (like 3 layers overlapped). For that I need the AbsoluteLayout
just for the images, right?
I've tried all possible combinations of VerticalOptions
for all the views in this file and nothing works.
The problem is that the scroll always leave a big space under the images. And with the Green color, so it means the AbsoluteLayout
is not shrinking its height after the image resize. The images can have variable sizes and shapes (although the 3 inside each page will all have the same dimensions) and they are all larger than any smartphone screen (zoom feature later). So I need the images to use all the Width available and keep the aspect ratio (AspectFit
I believe).
All the images are Embedded Resources. Here is the code behind CarouselContentPage
:
public PointInfoDataTemplate(PointModel point)
{
InitializeComponent();
NameLabel.Text = point.nome;
DescLabel.Text = point.apelido;
BackImg.Source = ImageSource.FromResource(point.backimg);
MidImg.Source = ImageSource.FromResource(point.midimg);
FrontImg.Source = ImageSource.FromResource(point.frontimg);
//BackImg.SizeChanged += delegate {
// ImgsContainer.Layout(new Rectangle(imgsContainer.Bounds.X, imgsContainer.Bounds.Y, BackImg.Bounds.Width, BackImg.Bounds.Height));
// MyStackLayout.Layout(new Rectangle(imgsContainer.Bounds.X, imgsContainer.Bounds.Y, BackImg.Bounds.Width, BackImg.Bounds.Height));
//};
}
I even tried resizing the Layouts after the SizeChanged
event, but it didn't work either.
So maybe I'm doing something wrong, here's all my code. I'm stuck in it for 3 days, I don't know what else to do.
I tried using a RelativeLayout
for the ScrollView.Content
. And after an entire day trying to understand it (and not sure if I fully did) I had a worse problem. The RelativeLayout
would overflow the Scroll
height and hide part of the images behind the tabs (base app container).
But I'd really like to preserve the StackLayout->items + AbsoluteLayout
approach.
I appreciate any help.
I found out that the problem was on the AbsoluteLayout
not handling its child resize events. Either a problem with the Image element not dispatching the proper events or the AbsoluteLayout
not handling them well.
So all I needed was to create this event handling.
Added a listener to the Image.SizeChanged
to set the AbsoluteLayout
height.
private void BackImg_SizeChanged(object sender, EventArgs e)
{
ImgsContainer.HeightRequest = BackImg.Height;
}
That works for my case, but if you have a more complex child disposition for AbsoluteLayout
you will need something like that:
private void ChildAdded_EventHandler(object sender, EventArgs e) {
double neededHeight = 0;
foreach(var child in sender) {
if(neededHeight < child.Y+child.Height) {
neededHeight = child.Y+child.Height;
}
}
sender.HeightRequest = neededHeight;
}
And you will have to add this event handler to AbsoluteLayout.ChildAdded
and AbsoluteLayout.ChildRemoved
;
If the Grid solution works, that may be easier, but since you asked about AbsoluteLayout...
To get AbsoluteLayout to work, you don't use the same VerticalOptions on the children that you would for other views.
Instead, there are other ways to indicate exactly how to lay out the child with respect to the Absolute Layout. Using C# instead of Xaml, you'd do something like:
AbsoluteLayout.SetLayoutFlags(BackImg,
AbsoluteLayoutFlags.PositionProportional);
AbsoluteLayout.SetLayoutBounds(BackImg,
new Rectangle(0, 0, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
And the same thing for MidImg and FrontImg.
SetLayoutFlags tells the system what aspects of the child's size and/or position to do proportionally to the parent. Then SetLayoutBounds gives the details of how to lay it out. In this case, the code is saying to lay out BackImg at the top left corner.
It's in C# not XAML but it should be easy to convert
Grid imageContainer = new Grid() {
HeightRequest = 400,
Children = {
new Image(), //Image 1
new Image(), //Image 2 on top of image 1
new Image(), //Image 3 on top of image 2 and 1
}
}
Content = new ScrollView() {
Content = new Grid() {
RowDefinitions = {
new RowDefinition(){Height = new GridLength(1, GridUnitType.Auto)}, //label1
new RowDefinition(){Height = new GridLength(1, GridUnitType.Auto)}, //label2
new RowDefinition(){Height = new GridLength(1, GridUnitType.Auto)}, //image container
},
Children ={
{new Label(), 0, 0}, //Label 1
{new Label(), 0, 1}, //Label 2
{imageContainer, 0, 2} //image container
}
}
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With