Search code examples
c#xamarinxamarin.uwpxamarin.windows

How to load images from specific location, not from related assembly?


I have PCL based Xamarin solution. What I need to do is to load images into view in application projects. These images are in another PCL project.

Basically I have some GridView xamarin view. This view should display something like 'cards'. All data are loaded well, but images. Honestly I tried everything I could emagine - absolute path, embedded resources, relative path, windows ms-appx/// prefix. I didn't try only stream which is not very usable for me. Only web source worked.

My code:

[assembly: ExportRenderer(typeof(GridViewRoles), typeof(GridViewRolesRenderer))]
namespace StrangerParty.Windows.Renderers
{
    public class GridViewRolesRenderer : ViewRenderer<GridViewRoles, GridView>
    {
        private GridView gridView;

        protected override void OnElementChanged(ElementChangedEventArgs<GridViewRoles> e)
        {
            base.OnElementChanged(e);

            if (this.Control == null)
            {
                this.gridView = new GridView();

                StringBuilder sb = new StringBuilder();
                sb.Append("<DataTemplate xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">");
                sb.Append("<Grid Height=\"100\" Width=\"100\" >"); /*Background =\"#FF2E2E2E\"*/
                sb.Append("<Image x:Name=\"ImageRoleImage\" Stretch=\"UniformToFill\" Source=\"sampleForTesting.png\"/>");
                sb.Append("</Grid>");
                sb.Append("</DataTemplate>");

                DataTemplate datatemplate = (DataTemplate)XamlReader.Load(sb.ToString());

                this.gridView.ItemTemplate = datatemplate;

                SetNativeControl(this.gridView);
            }

            this.gridView.ItemsSource = Core.Instance.GameHandler.Game.Players;
        }
    }    
}

Thanks in regards for any help


Solution

  • You can load image file from PCL by using Assembly.GetManifestResourceStream. According to your requirement you need to use Converter like code behind.

    Coverter

    public object Convert(object value, Type targetType, object parameter, string language)
    {
        var temp = new BitmapImage();
        var assembly = typeof(ImageTest.App).GetTypeInfo().Assembly;
        Stream stream = assembly.GetManifestResourceStream(value as string);
        if (stream != null)
        {
            using (var memStream = new MemoryStream())
            {
                stream.CopyTo(memStream);
                memStream.Position = 0;
                temp.SetSource(memStream.AsRandomAccessStream());
            }
        }
        return temp;
    }
    

    The next step is to create DataTemplate, To leverage converter the datatemplate can't not be loaded by XamlLoader. So you can create <ResourceDictionary> for datatemplate like codes below.

    ....
     xmlns:local="using:ImageTest.UWP"
        RequestedTheme="Light">
        <Application.Resources>
            <ResourceDictionary>
                <DataTemplate x:Key="MyDataTemplate">
                    <Grid >
                        <Grid.Resources>
                            <local:DateToStringConverter x:Key="MyConvert"/>
                        </Grid.Resources>
                        <Image Name="MyImage" Source="{Binding imageSource , Mode=OneWay, Converter={StaticResource MyConvert}}" />
                    </Grid>
                </DataTemplate>
            </ResourceDictionary>
        </Application.Resources>
    

    MainPage.xaml

     <GridView Name="MyGridView" ItemTemplate="{StaticResource MyDataTemplate}"/>
    

    MainPage.xaml.cs

    public MainViewModel viewModel { get; set; }
      public MainPage()
      {
          this.InitializeComponent();      
          viewModel = this.DataContext as MainViewModel;
          viewModel.items.Add(new Model() { imageSource = "ImageTest.hello.jpg" });
          viewModel.items.Add(new Model() { imageSource= "ImageTest.hello.jpg" });
              MyGridView.ItemsSource = viewModel.items;
      }
    

    So you can use the same way to create your DataTemplate by loading <Application.Resources> just like code behind.

    var ItemTemplate = App.Current.Resources["MyDataTemplate"] as Windows.UI.Xaml.DataTemplate
    

    If it doesn’t work. Please have a try of my demo.