Search code examples
c#xamlxamarinxamarin.formsimagesource

Xaml - how to use ImageSource from ViewModel


I have an ObservableCollection<Item> and an Item contains both a file path and an ImageSource of some image from the disk.

public ObservableCollection<Item> Items { get; set; } = new ObservableCollection<MediaListItem>();
public class Item
{
    public string Image { get; set; } // Full path to the image
    public ImageSource ImageSource { get; set; }
}

However, I am unable to access any of the two attributes and display an image via XAML.

<ListView ItemsSource="{Binding MediaList}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <StackLayout Padding="10">
                    <Image Source="{Binding ImageSource}" VerticalOptions="Fill"></Image>
                </StackLayout>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Now I am confused because I am able to output the string Image via XAML through a label, but can't display an image from the path at all.


Solution

  • You can use ImageSource.FromFile() or ImageSource.FromUri() like this

    In Xaml

    <Image  Source="{Binding  ImageSource}"  Aspect="AspectFit"  ></Image>
    

    In Code

    public ObservableCollection<Item> MediaList { get; set; } = new ObservableCollection<Item>()
            {
                new Item
                {
                    ImageSource = ImageSource.FromFile("xamarin.png")
                },
                new Item
                {
                    ImageSource = ImageSource.FromUri(new Uri("https://i.sstatic.net/4mMod.png"))
                }
            };
    

    The result

    enter image description here

    Update

    Depending on Microsoft-Local Images

    Image files can be added to each application project and referenced from Xamarin.Forms shared code...

    To use a single image across all apps, the same filename must be used on every platform, and it should be a valid Android resource name (ie. only lowercase letters, numerals, the underscore, and the period are allowed).

    For more information take a look at the answer here

    Now if you want to display an image without adding it to each platform, you should use Converter.

    For example:

    1. Create a new folder called ImagesFolder in shared code then add the image to it

      enter image description here

    2. In Code, create a new class called ByteArrayToImageSourceConverter like this

      public class ByteArrayToImageSourceConverter : IValueConverter 
      {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {   
          byte[] bytes;
          var assembly = GetType().GetTypeInfo().Assembly; // using System.Reflection;
      
          var stream = assembly.GetManifestResourceStream((string)value); // value = "App1.ImagesFolder.ninja.png"
          using (var ms = new MemoryStream()) // using System.IO; 
          {
              stream.CopyToAsync(ms);
              bytes = ms.ToArray();
          }
          return ImageSource.FromStream(() => new MemoryStream(bytes));
        }
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
          return null;
        }
      }
      

      View Model

      public ObservableCollection<Item> MediaList { get; set; } = new ObservableCollection<Item>()
      {
          new Item
          {
             Image  = "App1.ImagesFolder.ninja.png"
          }
      };
      
    3. In Xaml

      <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
           xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
           xmlns:local="clr-namespace:App1"
           x:Class="App1.MainPage">
      
        <ContentPage.Resources>
          <ResourceDictionary>
             <local:ByteArrayToImageSourceConverter  x:Key="ByteArrayToImageSourceConverter" />
         </ResourceDictionary>
       </ContentPage.Resources>
      
       <ListView ItemsSource="{Binding MediaList}" >
          <ListView.ItemTemplate>
            <DataTemplate>
              <ViewCell>
                 <Image Source="{Binding Image, Converter={StaticResource ByteArrayToImageSourceConverter}}" />
              </ViewCell>
            </DataTemplate>
         </ListView.ItemTemplate>
       </ListView>
      
      </ContentPage>
      

    Related Links

    ByteArrayToImageSourceConverter

    Shared Resources

    How to load binary images in Xamarin?