Bind list with object of base64 images to CardCarouselView

rozman50rozman50 Member ✭✭
edited August 2019 in Xamarin.Forms

I'm hitting my head against the table with this one.

This is my PhotoModel, this one could be omitted, if it represented a huge problem.

    public class PhotoModel
    { 

        public string ID_Photo { get; set; }

        public byte[] Photo { get; set; }

        public byte[] Photo_Smaller { get; set; }

        public int Order_Position { get; set; }
    }

I'm constructing my product in codebehind:
(if I hardcode imagesource, it works fine!)

List<CachedImage> cachedImages = new List<CachedImage>();
            foreach (PhotoModel pm in vm.PhotoList)
            {
                // image in photoList i want to access would be: pm.Photo
                cachedImages.Add(new CachedImage { Source = ImageSource.FromStream(() => new MemoryStream(pm.Photo)), Aspect = Aspect.AspectFill, HeightRequest = 300 });
            }

            // create carouselview 
            var carousel = new CardCarouselView
            {
                HeightRequest = 300,
                ItemsSource = cachedImages,
                IsCyclical = true,
            };

Then I'm appending this to stack layout.
Binding from ViewModel works, because I'm binding to nameLabel.

ViewModel:

private ObservableCollection<PhotoModel> _photoList = new ObservableCollection<PhotoModel>();
        public ObservableCollection<PhotoModel> PhotoList
        {
            get { return _photoList; }
            set { SetProperty(ref _photoList, value); }
        }

This is where I'm getting data:

                async Task ExecuteLoadProductCommand(string parameter)
                {
                    var product = await _productService.GetProductAsync(parameter);

                    Name = product.Name;
                    Number = product.Number;
                    Description = product.Description;
                    Price = product.Price_EUR;
                    PhotoList = new ObservableCollection<PhotoModel>(product.photoList);

                    MaxPhotoIndex = PhotoList.Count;
                }

ExecuteLoadProductCommand is being called in constructor on view load. Binding text to label works, but can't get images to work. They are stored in database as mediumblob, getting them as byte[].

Any help would be appreciated.

Best Answer

  • rozman50rozman50 ✭✭
    Accepted Answer

    After a lot of time banging my head against the desk, I figured it out:
    From database I get byte[], which I store into PhotoModel

    Then I created ValueConverter and saved this class in folder Helpers

     public class ConverterBase64ImageSource : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                if(value != null)
                {
            // it HAS to be converted, otherwise it doesn't work!
                    var ab = System.Text.Encoding.UTF8.GetString((byte[])value);
                    return ImageSource.FromStream(() => new MemoryStream(System.Convert.FromBase64String(ab)));
                }
                return String.Empty;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                throw new NotSupportedException();
            }
        }
    

    Added it to resources for this page (could be added to App.xaml resources)

            <ContentPage.Resources>
                <ResourceDictionary>
                    <helpers:ConverterBase64ImageSource x:Key="Base64ToImageConverter" />
                </ResourceDictionary>
            </ContentPage.Resources>
    

    in view bindingcontext must be set to viewmodel like so:

        <ContentPage.BindingContext>
            <vm:ProductDetailViewModel />
        </ContentPage.BindingContext>
    

    And finally this is how images are displayed in listview:

                        <ListView Grid.Row="0" ItemsSource="{Binding PhotoList}">
                            <ListView.ItemTemplate>
                                <DataTemplate>
                                    <ViewCell>
                                        <forms:CachedImage Source="{Binding Photo, Converter={StaticResource Base64ToImageConverter}, ConverterParameter=Photo}" />
                                    </ViewCell>
                                </DataTemplate>
                            </ListView.ItemTemplate>
                        </ListView>
    

    PS: if anyone wants to make carousel view (swipe left-right images, here is the code, I'm still pimping it to look like I want it to)

     <CarouselView ItemsSource="{Binding PhotoList}">
                            <CarouselView.ItemsLayout>
                                <GridItemsLayout
                                    Orientation="Horizontal"
                                    SnapPointsAlignment="Center"
                                    SnapPointsType="MandatorySingle" />
                            </CarouselView.ItemsLayout>
                            <CarouselView.ItemTemplate>
                                <DataTemplate>
                                    <Frame
                                        Padding="1"
                                        BackgroundColor="Black"
                                        BorderColor="Black"
                                        HeightRequest="300"
                                        VerticalOptions="CenterAndExpand">
    
                                        <forms:CachedImage
                                            Aspect="AspectFill"
                                            HeightRequest="300"
                                            HorizontalOptions="FillAndExpand"
                                            Source="{Binding Photo, Converter={StaticResource Base64ToImageConverter}, ConverterParameter=Photo}"
                                            VerticalOptions="FillAndExpand" />
                                    </Frame>
    
                                </DataTemplate>
                            </CarouselView.ItemTemplate>
                        </CarouselView>
    

    I hope this will help anyone :)

Answers

  • JarvanJarvan Member, Xamarin Team Xamurai

    (if I hardcode imagesource, it works fine!)

    Add breakpoints to check if the Source of CachedImage is valid.

    foreach (PhotoModel pm in vm.PhotoList)
    {
        // image in photoList i want to access would be: pm.Photo
        cachedImages.Add(new CachedImage { Source = ImageSource.FromStream(() => new MemoryStream(pm.Photo))...);
    }
    
  • rozman50rozman50 Member ✭✭
    Accepted Answer

    After a lot of time banging my head against the desk, I figured it out:
    From database I get byte[], which I store into PhotoModel

    Then I created ValueConverter and saved this class in folder Helpers

     public class ConverterBase64ImageSource : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                if(value != null)
                {
            // it HAS to be converted, otherwise it doesn't work!
                    var ab = System.Text.Encoding.UTF8.GetString((byte[])value);
                    return ImageSource.FromStream(() => new MemoryStream(System.Convert.FromBase64String(ab)));
                }
                return String.Empty;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                throw new NotSupportedException();
            }
        }
    

    Added it to resources for this page (could be added to App.xaml resources)

            <ContentPage.Resources>
                <ResourceDictionary>
                    <helpers:ConverterBase64ImageSource x:Key="Base64ToImageConverter" />
                </ResourceDictionary>
            </ContentPage.Resources>
    

    in view bindingcontext must be set to viewmodel like so:

        <ContentPage.BindingContext>
            <vm:ProductDetailViewModel />
        </ContentPage.BindingContext>
    

    And finally this is how images are displayed in listview:

                        <ListView Grid.Row="0" ItemsSource="{Binding PhotoList}">
                            <ListView.ItemTemplate>
                                <DataTemplate>
                                    <ViewCell>
                                        <forms:CachedImage Source="{Binding Photo, Converter={StaticResource Base64ToImageConverter}, ConverterParameter=Photo}" />
                                    </ViewCell>
                                </DataTemplate>
                            </ListView.ItemTemplate>
                        </ListView>
    

    PS: if anyone wants to make carousel view (swipe left-right images, here is the code, I'm still pimping it to look like I want it to)

     <CarouselView ItemsSource="{Binding PhotoList}">
                            <CarouselView.ItemsLayout>
                                <GridItemsLayout
                                    Orientation="Horizontal"
                                    SnapPointsAlignment="Center"
                                    SnapPointsType="MandatorySingle" />
                            </CarouselView.ItemsLayout>
                            <CarouselView.ItemTemplate>
                                <DataTemplate>
                                    <Frame
                                        Padding="1"
                                        BackgroundColor="Black"
                                        BorderColor="Black"
                                        HeightRequest="300"
                                        VerticalOptions="CenterAndExpand">
    
                                        <forms:CachedImage
                                            Aspect="AspectFill"
                                            HeightRequest="300"
                                            HorizontalOptions="FillAndExpand"
                                            Source="{Binding Photo, Converter={StaticResource Base64ToImageConverter}, ConverterParameter=Photo}"
                                            VerticalOptions="FillAndExpand" />
                                    </Frame>
    
                                </DataTemplate>
                            </CarouselView.ItemTemplate>
                        </CarouselView>
    

    I hope this will help anyone :)

Sign In or Register to comment.