Matt Ward
Matt Ward

Reputation: 1235

Xamarin.Forms: Can't access images in folder

I have a very basic Xamarin.Forms app, where I try to access an Image. This works fine when the image is in the necessary folder e.g

However when I add a folder into the mix it doesn't display anything:

Here is the line I am using: <Image Source="btc.png" HeightRequest="20" WidthRequest="20" />

Here is the line that doesn't work: <Image Source="Logos\btc.png" HeightRequest="20" WidthRequest="20" />

I have triple checked all backslashes and spellings/capital letters.

Upvotes: 4

Views: 4745

Answers (2)

Mark Z.
Mark Z.

Reputation: 2447

Yes unfortunately you can't make use of sub-folders for android images but you can for the other 2 platforms and to account for the difference here's what I typically do.

Use the following AssetPathHelper (modify for your needs, in the below I only use a sub-folder for UWP images, and for lottie animations I use a sub-folder in both UWP and iOS). Also I assume .png, if you use other image types then need to handle that.

public static class AssetPathHelper
{
    public static readonly string UWPimgRoot = @"Assets\Images\";
    public static readonly string UWPanimRoot = @"Assets\Animations\";
    public static readonly string iOSanimRoot = @"Animations/"; //note the different slash here, not sure if it's required but that is what works

    public static string GetImageSource(string resourceName)
    {
        var imgFileName = resourceName + ".png"; //! obviously this requires all images used to be pngs

        if (Device.RuntimePlatform != Device.UWP)
            return imgFileName;

        return UWPimgRoot + imgFileName;
    }
    public static string GetLottieAnimSource(string resourceName)
    {
        var animFileName = resourceName + ".json";

        switch (Device.RuntimePlatform)
        {
            case Device.iOS:
                return iOSanimRoot + animFileName;
            case Device.UWP:
                return UWPanimRoot + animFileName;
        }
        return animFileName;
    }
}

which gets used in the following Converter:

/// <summary>
/// Provides the path to the image taking into account the platform specific location.
/// Can be used without a real binding (i.e. when only a parameter is provided)
/// </summary>
public class ImagePathConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (parameter != null)
            return AssetPathHelper.GetImageSource(parameter.ToString());
        return AssetPathHelper.GetImageSource(value.ToString());
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value;
    }
}

So now in my XAML I can just do this, either as a real binding if the image will be changing or if not, then just passing the image name as the converter parameter:

        <Image
            Source="{Binding ConverterParameter=logo_splash, Converter={StaticResource ImagePathConverter}}"/>

Upvotes: 3

SushiHangover
SushiHangover

Reputation: 74094

TL;DR: Do not use sub-folders. ;-)

On Android when using subfolders within the Drawable folder (and the pixel density folders, i.e: drawable-xxhpdi), the sub-folder name is ignored but the drawable's ID are generated in the Java R. file (C# Resources. in Xamarin.Android), assuming there is no invalid names or clashing. So in native Android & Xamarin.Android those drawable will have an resource ID (integer-based) assigned to them and are usable.

But, Xamarin.Forms will not be able to find those images as a reverse lookup is used, from Name to Android resource ID and thus will be no match.

Also on iOS, the use of the Resource folder for images via BundleResource is deprecated and you should be using "Asset Catalog Image Sets" instead.

For more info: Images in Xamarin.Forms

Upvotes: 4

Related Questions