Forum Xamarin.Forms

Helpful Shared Project Image information

MichaelJones.8877MichaelJones.8877 USMember
edited June 2014 in Xamarin.Forms

Fellow devs,

I just wanted to share some information in case it is helpful to you. I am exploring the Shared Projects approach for Xamarin.Forms. In my app, I wanted to be able to put images into the shared project and load them into my UI as needed (ie logos, etc.). This was not obvious to me at all.

I found a great bit of information here which provided most of the clues: link

Important information is this:
1. Images must be marked as an embedded resource (if in shared project)
2. In Shared Projects, the path to the image location translates at run-time to <Assembly Name> + . + imagename (note, this is not the same for PCLs)
3. In order to have shared code find the right image location at run-time, I did the following inside of my view class:

private string _baseResource;
public string BaseResource
{
  get
  {
    return _baseResource ?? (_baseResource = Assembly.GetExecutingAssembly().FullName.Split(',').FirstOrDefault());
  }
}

private ImageSource PlatformImageResource(string resource)
{
  return ImageSource.FromResource(BaseResource + "." + resource);
}

This allowed for me to load the image more easily and have it be platform specific, as follows:

  var logo = new Image
  {
    Source = PlatformImageResource("app_header_logo.jpg")
  };

If there's an easier way, I'd love to hear about it!

I hope this helps others : )

Thanks!
Michael

Posts

  • Hmmm... my post got mangled a bit. The image translates at runtime to AssemblyName + "." + imagename. Sorry about that!

  • ChrisgozdChrisgozd USMember ✭✭

    How would this look to load an image from a shared project in xaml?

  • Great question! I'd love to know the answer as well. I am looking at Xamarin.Forms using Visual Studio 2013 Update 2 and started off with xaml-based views but found the lack of intellisense too frustrating so I switched to code based views.

    If I get time to try something out, I'll let you know.

  • ChrisgozdChrisgozd USMember ✭✭

    Thanks! The only reason i've stuck with purely xaml based views is so that when a UI designer comes out for Xamarin Forms, I will have less work to integrate it into my project (assuming the UI designer is xaml based, which i'm sure it will be).

  • MichaelJones.8877MichaelJones.8877 USMember
    edited June 2014

    OK, slight update to the original post...

    In order for the above code to work, you do need to make sure you look at the properties for each platform project and ensure the Assembly Name is what you expect.

    When creating a new Xamarin.Forms Shared Project, I ended up with some default Assembly Names that were not what I expected. The list below shows the actual project name alongside what was in the project properties for the Assembly Name by default:

    myApp.iOS : "FormsTemplateiOS"

    myApp.Android : "myApp.Droid"

    myApp.WinPhone : "myApp.WinPhone"

    Once I updated the project properties to match, the above code worked perfectly.

    Thanks!
    Michael

  • d_dblud_dblu CAUniversity ✭✭

    @MichaelJones When I updated my default assembly name for iOS from "MyAppiOS" to "MyApp.iOS" I received "iOS executable name must be alphanumeric". Did you just leave the assembly name and change your project name to match? It sounds like what you did is vice versa but it doesn't work for me.

  • d_dblud_dblu CAUniversity ✭✭

    @MichaelJones I found that you can simply set the Resource ID for the image through XS and then use ImageSource.FromResource("resourceid").

  • CraigDunnCraigDunn USXamarin Team Xamurai

    @ChristopherGozdziewski‌ currently there is no built-in TypeConverter for the Image control to use embedded resources in Xaml. For now, this custom markup extension

    using System;
    using Xamarin.Forms.Xaml;
    using Xamarin.Forms;
    namespace WorkingWithImages
    {
        // You exclude the 'Extension' suffix when using in Xaml markup
        [ContentProperty ("Source")]
        public class ImageResourceExtension : IMarkupExtension
        {
            public string Source { get; set; }
            public object ProvideValue (IServiceProvider serviceProvider)
            {
                if (Source == null)
                    return null;
                // Do your translation lookup here, using whatever method you require
                var imageSource = ImageSource.FromResource(Source); 
                return imageSource;
            }
        }
    }
    

    will enable you to refer to embedded images resources like this in Xaml (note the custom xmlns:local definition, and the resource filename is prefixed by the assemblyname and a '.' dot)

    <?xml version="1.0" encoding="UTF-8" ?>
    <ContentPage
        xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:local="clr-namespace:WorkingWithImages;assembly=WorkingWithImages"
        x:Class="WorkingWithImages.EmbeddedImagesXaml">
      <StackLayout VerticalOptions="Center" HorizontalOptions="Center">
        <Label Text="Image Resource Xaml" />
        <!-- uses a custom Extension defined in this project for now -->
        <Image Source="{local:ImageResource WorkingWithImages.beach.jpg}" />
        <Label Text="WorkingWithImages.beach.jpg embedded resource" />
      </StackLayout>
    </ContentPage>
    
  • HimasankarHimasankar USMember
    edited June 2014

    Hi,

    I am using PCL in my project to share the code in IOS, Android, WP 8 devices. I am trying to set the contentpage background image in my code. but the image is not displaying. Please check my sample code below.

    I have set the image property Build Action=Embedded Resource

    Please let me know what is the mistake in this code.

    public class MainPage : ContentPage{
    
        List<string> objEmpCollections = new List<string>();
        public MainPage()
        {
            this.Title = "V V I P's";
            //this.BackgroundColor = Color.Maroon;
            this.BackgroundImage = "EmpMgmtPCL.Images.NPO-Events-bg.png";
    }
    }
    

    Thanks,Himasankar

  • @DerekWinnicki‌ I am using Visual Studio and do not get the same error as you regarding the assembly name. Interesting enough, though, if I make the change for the iOS project properties, I can build and run the app and the images will show up. However, if I close the solution and then re-open it, the Assembly Name gets updated to myAppiOS (no '.'). I then have to re-add the '.' to get it to work, but the change never persists. This doesn't happen with the Android and WinPhone projects. Sounds like a bug to me? Maybe someone from Xamarin can chime in here.

  • @DerekWinnicki‌ Thanks for the ResourceID approach suggestion. Bummer is that there is no way to set the ResourceID within Visual Studio in the properties of the image, just from within Xamarin Studio. I'll have to poke around and see if there's a work around for VS using the resource approach. I like that approach better.

  • @DerekWinnicki‌ OK, so the answer to using resource ids in VS was found in this post here in an answer provided by @msmith‌.

    You have to edit the projitems file by hand (which requires the solution not be open in VS). You simply ensure your embedded resource includes the "LogicalName" child element. The name provided there is what you would use in your

    <EmbeddedResource Include="$(MSBuildThisFileDirectory)Carrot.png"> <LogicalName>Carrot.png</LogicalName> </EmbeddedResource>

    The name used for the LogicalName becomes the resource identifier...

    ImageSource.FromResource("Carrot.png")

  • EricGroverEricGrover USMember ✭✭
    edited June 2014

    @CraigDunn‌ Craig, When I implement the ImageResourceExtension, I get the exception: "Value cannot be null.
    Parameter name: streamSource". When I set a break point and inspect the imageSource, it is not null. The image is a png file that is marked as an embedded resource in the shared project. Any ideas on what I might be doing wrong?

    <Image Source="{local:ImageResource BCCG.Mobile.Images.bccg-logo.png}" HorizontalOptions="Start" VerticalOptions="End" />

  • d_dblud_dblu CAUniversity ✭✭

    @Himasankar‌ When you use ContentPage.BackgroundImage property the image should be placed the respective platforms folder so for iOS it's Resources and Android it is the drawable folder.

  • d_dblud_dblu CAUniversity ✭✭

    @MichaelJones.8877‌ Yes exactly, glad we got that sorted

  • CraigDunnCraigDunn USXamarin Team Xamurai

    @EricGrover‌ not sure - does it work with that exact same Resource ID if you do it in code?

  • CalmBreathsSlowPulseCalmBreathsSlowPulse Member ✭✭
    edited May 2018

    This is a solution that uses a specific search in the manifest for the resource name, which can work in cases where the given code doesn't:

    using System.Linq;
    using System.Reflection;
    using System.Diagnostics;
    using System;
    
    namespace Helpers
    {
        public class EmbeddedSourceror
        {
            public static Xamarin.Forms.ImageSource SourceFor(string pclFilePathInResourceFormat)
            {
                var resources = typeof(EmbeddedSourceror).GetTypeInfo().Assembly.GetManifestResourceNames();
                var resourceName = resources.Single(r => r.EndsWith(pclFilePathInResourceFormat, StringComparison.OrdinalIgnoreCase));
                Debug.WriteLine("EmbeddedSourceror: resourceName string is " + resourceName);
    
                return Xamarin.Forms.ImageSource.FromResource(resourceName);
            }
        }
    }
    

    Some credit due to this post: https://forums.xamarin.com/discussion/73011/xamarin-forms-get-path-of-embedded-file

Sign In or Register to comment.