astralmaster
astralmaster

Reputation: 2465

Xamarin Forms Image with 100% of parent container width and auto Height to maintain aspect ratio

Is it possible to have an Image take 100% width of its parent container while actually fitting the whole image within this container without clipping, and while having height automatically adjusted to preserve aspect ratio?

I have read similar questions both on SO and Xamarin Forums but apparently this cannot be done without implementing custom renderers or manually calculating correct sizes in code. But to calculate this in code you would need either image dimensions or aspect ratio. For applications where neither of these are known before head, this is a problem.

In terms of CSS, the solution I am looking for is similar to having

width:100%; height:auto;

Implementing a custom renderer for such a trivial task is an overkill and a huge embarrassment for Xamarin in my opinion; unless I am understanding something wrong.

Upvotes: 3

Views: 3604

Answers (4)

Craig
Craig

Reputation: 61

I have searched for an answer to this problem for a long time. As others have noted, I did not find a Xaml-only solution. I choose a different route and present my solution for those who might find it instructive and as the seed for changes to FFImageLoading to solve this correctly.

I created a subclass of CachedImage and overrided OnMeasure. In order for CachedImage.OnMeasure to work in the aspect modes (not fill mode), it has to return a SizeRequest which matches the ratio of the underlying image. So the solution I chose was simply to use the provided widthConstraint and calculate the height as widthConstraint / ratio. This description only addresses one of the many cases: where the widthConstraint is not infinity and the desire is to answer the specific question posed here (width of 100% and auto height).

A subclass which implements this case:

public class CachedImage2 : CachedImage
{
    protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
    {
        var sr = base.OnMeasure(widthConstraint, heightConstraint);
        if (sr.Request.IsZero)
            return sr;

        var ratioWH = sr.Request.Width / sr.Request.Height;
        var sr2 = new SizeRequest(new Size(widthConstraint, widthConstraint / ratioWH));
        return sr2;
    }
}

Upvotes: 5

astralmaster
astralmaster

Reputation: 2465

I was unable to find a pure XAML solution to this problem and therefore decided to use ffimageloading's Success event to find out the original width and height of loaded image and get aspect ratio from these values, store it, and use it in SizeAllocated event to maintain aspect ratio of the image while making sure its width is 100% of the parent container.

Sample code:

    private void testImage_OnSuccess(object sender, CachedImageEvents.SuccessEventArgs e)
    {
        var image = sender as CachedImage;
        double width = e.ImageInformation.OriginalWidth;
        double height = e.ImageInformation.OriginalHeight;

        ratio = width / height; // store it in a variable

    }


    protected override void OnSizeAllocated(double width, double height)
    {
        base.OnSizeAllocated(width, height);

        if (this.Width > 0 && ratio != 0) // width of the parent container
            testImage.HeightRequest = this.Width / ratio;
    }

Upvotes: 4

Wendy Zang - MSFT
Wendy Zang - MSFT

Reputation: 10938

Put you Image in a frame. And then set the height and width of image accprding to the frame.

 <Frame>
        <Image></Image>
    </Frame>

You could change the width and height according to your frame via binding Value Converters.

Binding Value Converters: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/data-binding/converters

Set the name to your Frame first.

<Frame
…………
x:Name="frame"/>

Create the MyConverter. You could change the percentage of value in Convert method. MyConverter.cs

public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
    return (double)value;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
    throw new NotImplementedException();
}
}

Set the StaticResource.

<ContentPage.Resources>
<ResourceDictionary>
    <local:MyConverter x:Key="MyConverter" />
</ResourceDictionary>

Binding to your image.

 <Image WidthRequest="{Binding Source={x:Reference frame},Path=Width,Converter={StaticResource MyConverter}}"
                    HeightRequest="{Binding Source={x:Reference frame},Path=Height,Converter={StaticResource MyConverter}}"></Image>

Upvotes: 0

James Mallon
James Mallon

Reputation: 1127

Presuming when you say:

and while having height automatically adjusted..

You mean the height of the container. Yes this is completely possible in Xamarin.Forms.

Let's imagine I have a Grid as my parent container. Here is how I would do it.

 <!-- remove all the padding & margin & spacing on Grid -->
<Grid RowSpacing="0"
      ColumnSpacing="0"
      Margin="0"
      Padding="0">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" /> <!-- the containers height will now adjust --> 
        <RowDefinition Height="56"/> <!-- This one is for the other content in your view etc -->
    </Grid.RowDefinitions>
    <!-- Put your image inside your parent container and apply properties -->
    <Image Source="some_source.png"
           HorizontalOptions="FillAndExpand"
           VerticalOptions="FillAndExpand"/>
</Grid>

The Horizontal and vertical options are as if you are setting width:100% and height: 100% in CSS.

Upvotes: 1

Related Questions