1

I am currently working on a image preview list for pictures that have been made on the mobile device itself. Later on those images will be stored in the database. However for the first iteration I was testing the list with images from web. I am using xamarin forms and the target is android. That is a picture of the page on the device I can not pinpoint the issue. I do not get any runtime exception. Maybe there is an issue with the binding.

[Updated the source code and cleaned it up]

Here is the view:

<?xml version="1.0" encoding="utf-8" ?>
<ui:BasePage
    x:Class="AssetManagement.Mobile.Core.UI.OrderItemImageDocumentationPage"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:actionMenu="clr-namespace:Client.ApplicationDataModel.UI.Controls.ActionMenu;assembly=Client.ApplicationDataModel.UI"
    xmlns:controls="clr-namespace:AssetManagement.Mobile.Core.Controls"
    xmlns:controls1="clr-namespace:UI.XF.Controls;assembly=UI"
    xmlns:res="clr-namespace:AssetManagement.Mobile.Core.Resources"
    xmlns:ui="clr-namespace:AssetManagement.Mobile.Core.UI"
    xmlns:valueconverter="clr-namespace:AssetManagement.Mobile.Core.Classes.ValueConverter"
    xmlns:viewmodel="clr-namespace:AssetManagement.Mobile.Core.ViewModels"
    Title="Bild Dokumentation" 
    x:TypeArguments="viewmodel:OrderItemImageDocuViewModel"><!--{res:Translate OrderItemImageDocumentary}-->
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <StackLayout Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3">
            <ListView ItemsSource="{Binding OrderItemImages}"
          HasUnevenRows="true"
          ItemSelected="OnListViewItemSelected"
          ItemTapped="OnListViewItemTapped">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <Grid Padding="10">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto" />
                                    <RowDefinition Height="*" />
                                </Grid.RowDefinitions>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="*" />
                                </Grid.ColumnDefinitions>
                           <Image Grid.RowSpan="2"
                           Source="{Binding ImageData}"
                           Aspect="AspectFit"
                           HeightRequest="200"
                           WidthRequest="200" />
                                <Label Grid.Column="1"
                           Text="{Binding Title}"
                           FontAttributes="Bold" />
                                <Label Grid.Row="1"
                           Grid.Column="1"
                           Text="test"
                           VerticalOptions="End" />
                            </Grid>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </StackLayout>
        <StackLayout Grid.Row="1" Grid.ColumnSpan="3" Grid.Column="0" Orientation="Horizontal">
            <Button x:Name="BtnCancel"
                        AutomationId="BtnCancel"
                        Text="Cancel"
                        Clicked="BtnCancel_OnClicked"
                        Style="{StaticResource DefaultButtonStyle}" />
            <Button x:Name="BtnSave"
                        AutomationId="BtnSave"
                        Text="Save"
                        Clicked="BtnSave_OnClicked"
                        Style="{StaticResource DefaultButtonStyle}" />
        </StackLayout>
    </Grid>



</ui:BasePage>

The associated code behind:

    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class OrderItemImageDocumentationPage : BasePage<OrderItemImageDocuViewModel>
    {
        private IAppConfiguration _appConfig;

        public OrderItemImageDocumentationPage()
        {
            InitializeComponent();
        }

        public OrderItemImageDocumentationPage(OrderItemImageDocuViewModel viewModel, IEventLogService eventLog, ILifetimeScope scope,
            IUserInterfaceService uiService, IAppConfiguration appConfig)
            : base(viewModel, eventLog, scope, uiService)
        {
            InitializeComponent();

            _appConfig = appConfig;
        }

        private void BtnSave_OnClicked(object sender, EventArgs e)
        {
            var test = new OrderItemImageDocData(Title = "test", Xamarin.Forms.ImageSource.FromUri(new Uri(@"https://upload.wikimedia.org/wikipedia/commons/7/7b/Dark_brown.PNG")));
            base.ViewModel.OrderItemImages.Add(test);
            base.ViewModel.OrderItemImages.Last().ImageData = Xamarin.Forms.ImageSource.FromUri(new Uri(@"https://upload.wikimedia.org/wikipedia/commons/7/7b/Dark_brown.PNG"));
        }
    }

My current View Model:

    public class OrderItemImageDocuViewModel : ApplicationDataBaseViewModel, INavigationEventHandler
    {
        #region Klassenvariablen

        private readonly int _pagingSize = 20;
        private IAppConfiguration _appConfig;
        private ILifetimeScope _scope;
        private IBarcodeParserService _barcodeParser;
        private IEventLogService _eventLog;
        private IUserDataService _userData;
        private int _itemsToLoadCount;
        private ObservableCollection<OrderItemImageDocData> _orderItemImages = new ObservableCollection<OrderItemImageDocData>();
        private OrderItem _orderItemContext = null;
        #endregion Klassenvariablen

        #region Konstruktor

        public OrderItemImageDocuViewModel(ILifetimeScope scope, IDataRepositoryService dataRepository, IBarcodeParserService barcodeParserService, IEventLogService eventLog, IUserDataService userData, ITranslationService translationService, IAppConfiguration appConfig)
            : base(dataRepository)
        {
            _eventLog = eventLog.Initialize(typeof(OrderItemImageDocuViewModel));
            _scope = scope;
            _appConfig = appConfig;
            _userData = userData;
            _barcodeParser = barcodeParserService;
            TranslationService = translationService;

            _itemsToLoadCount = _pagingSize;

            //DatabasesMissing = !dataRepository.IsDataStoreFileAvailable(DownloadDataFileType.TransactionData);

            //if (!DatabasesMissing)
            //{
            //    IsLoading = true;
            //}
        }

        #endregion Konstruktor

        #region Properties

        [NavigationParameter]
        public Order Order { get; set; }

        public IEventLogService EventLog => _eventLog;

        public bool IsLoading { get; set; }

        public bool DatabasesMissing { get; set; }



        public ITranslationService TranslationService { get; set; }

        public IList<OrderItem> OrderItems { get; set; }

        public IOrderTypeScanHandling ScanHandler { get; set; }

        public ObservableCollection<OrderItemImageDocData> OrderItemImages 
        {
            get 
            {
                return _orderItemImages;
            }
            set 
            {
                _orderItemImages = value;
                base.OnPropertyChanged("OrderItemImages");
            }
        }
        #endregion Properties

        public override async void OnNavigationParametersProvided(NavigationContext context)
        {
            if (!DatabasesMissing)
            {
                IsLoading = true;

                try
                {
                    //await InitializeData();

                    //await LoadDataOnDemand();
                }
                catch (Exception ex)
                {
                    //ex.TrackError();
                }

                IsLoading = false;
            }
        }

    }

This is the class currently holding the Image data. This is just a quick and dirty placeholder. Since the vs designer is rather poor and not working I needed some class that just provides image data so i can design the ui page the way i want it.

    public class OrderItemImageDocData : INotifyPropertyChanged
    {
        #region Events
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion
        #region Fields
        private string _title = string.Empty;
        private ImageSource _imageData;
        #endregion

        #region Constr
        public OrderItemImageDocData()
        {

        }
        public OrderItemImageDocData(string title, ImageSource imgData) 
        {
            _title = title;
            ImageData = imgData;
        }
        #endregion

        #region Properties
        public string Title 
        {
            get 
            {
                return _title; 
            }
            set 
            {
                _title = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("Title"));
            }
        }
        public ImageSource ImageData
        {
            get
            {
                return _imageData;
            }
            set
            {
                _imageData = value;
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs("ImageData"));
            }
        }
        #endregion
    }

The permissions I have in my android manifest file.

    <uses-permission android:name="android.permission.FLASHLIGHT" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.VIBRATE" />

[Update] I am not sure why I did not notice it earlier. Probably because while being in debug he did not throw an exception. However there must be a runtime exception. The vs output windows shows this whenever I try to add a new list view item (and thatfor a new image):

05-28 08:09:43.563 D/skia    ( 7653): --- SkImageDecoder::Factory returned null
05-28 08:09:43.619 W/art     ( 7653): JNI RegisterNativeMethods: attempt to register 0 native methods for md51558244f76c53b6aeda52c8a337f2c37.CellRenderer_RendererHolder
[0:] ImageLoaderSourceHandler: Could not retrieve image or image data was invalid: Uri: https://upload.wikimedia.org/wikipedia/commons/7/7b/Dark_brown.PNG
05-28 08:09:43.703 I/Choreographer( 7653): Skipped 38 frames!  The application may be doing too much work on its main thread.
Thread finished: <Thread Pool> #2

The information however is rather poor. Tried multiple different images with the same output result.

Latest Update: Sadly I can not post a solution for this problem in case others have similar issues please try the answers from Mihail Duchev. He got some good pointers. However I switch from uri to stream source since i load the images from the database. Apparently that runtime exception only occurs for me while using uri ImageSource.

Dev86
  • 91
  • 9
  • From the posted code, the image is set binding to the AssetImage property. The property is type of Image, binding the value to Image's source will not work. Try to change the type to string or ImageSource. If AssetImage is the web image address, please make sure the Internet permission has been added. – Jarvan Zhang May 28 '20 at 09:14
  • still no success and I have updated the initial post with the current code base. I also posted the permission this app is using. – Dev86 May 28 '20 at 11:57
  • 1
    Due to your update, can you try this solution: https://stackoverflow.com/a/58301205/4090219 Also, I see that there was a logged bug in Xamarin.Forms on the topic. If nothing helps, you can write in the issue - https://github.com/xamarin/Xamarin.Forms/issues/7751 You can take a look here too - https://forums.xamarin.com/discussion/comment/391707/#Comment_391707 – Mihail Duchev May 28 '20 at 12:25
  • Try to use FFImageLoading to load the images the remote urls. Similar issue: https://stackoverflow.com/questions/58299753/xamarin-forms-imageloadersourcehandler-could-not-retrieve-image-or-image-data – Jarvan Zhang May 28 '20 at 14:47

2 Answers2

1

You are correct that the issue is with the binding. Xamarin's Image class expects the Source bindable property to be an ImageSource instance. From Images in Xamarin.Forms docs:

Source - An ImageSource instance, either File, Uri or Resource, which sets the image to display.

You are currently setting your images' source to be of type Image, which is telling nothing to the UI & its binding.

What you need to do it so change this:

Source="{Binding AssetImage}"

to this:

 Source="{Binding ImageData}"
Mihail Duchev
  • 4,691
  • 10
  • 25
  • 32
  • Have been trying "Uri" and "ImageSource" als Binding already. Nothing worked so far. The latest test was with "Image". I found some forum entries that sayed I need "Bitmap". https://stackoverflow.com/questions/30850510/how-to-correctly-use-the-image-source-property-with-xamarin-forms – Dev86 May 28 '20 at 07:34
  • That's strange. Also, you don't need a Bitmap explicitly. – Mihail Duchev May 28 '20 at 07:38
  • Ah, I saw what your issues is. You are passing the data in your ctor, but you are assigning it to your `ImageData` property. I have updated my answer - you need to bind it to `ImageData` property and it will work. Just tried it on my side. – Mihail Duchev May 28 '20 at 07:55
  • In the latest iteration which i posted I did not assign it via constructor. That was my fist attemp. I think you can see the line which is commented in the first post where I create test entries each time i click on the button. I had the binding to ImageData and passed the ImageSource via constructor. After that I tried it with uri and then I tried binding and Image property as Source. You tried it and it worked for you? – Dev86 May 28 '20 at 08:45
  • Yes, I copy-pasted your code and cleaned it up a bit. At the end, it worked. If it doesn't work for you, and you are using ctor initialization, assign the value to the public property and not its private field. Currently, when you are setting `_imageData = imgData;`, the UI doesn't know that it needs to update. If you set it like this in ctor `ImageData = imgData`, then `OnPropertyChanged` will be invoked and it will "tell" the UI to update itself. – Mihail Duchev May 28 '20 at 08:47
  • Thank you for your responses. The constructor is indeed wrong. I updated and cleaned up my code in the first post. As you see in the button code I even try to assign the "ImageData" again after I added the instance to the observable list on the button click. However it sadly all get me the same result. The area where the picture should be is just white. I was thinking maybe it's something with the android version I am using (6.0 is on the device). Or maybe I need permission for the in order to download the image (in my sample i am loading the source from the inet). Could you could your test? – Dev86 May 28 '20 at 09:51
  • 1
    Here is a working sample of your code. Please check it and see where you are doing something wrong and write it back here. I guess that you are setting the binding context incorrectly, in your base page maybe? https://github.com/mduchev/SO_TestImageIssue – Mihail Duchev May 28 '20 at 10:27
  • Thank you for the upload. You won't belive it (as am I) but it's still showing the same issue. I have been using your solution. I will try a different android device and will check again. https://imgur.com/a/7cYQ7Zg – Dev86 May 28 '20 at 11:46
  • tried it in android (orion) simulator as well with the same result. I did add it to imgur.com/a/7cYQ7Zg . – Dev86 May 28 '20 at 11:55
  • When you click the Save button what happens? Doesn't the brownish image appears in the list? – Mihail Duchev May 28 '20 at 12:01
  • no. He adds a entry in the list view. He does reserve the 200x200 area for the image but its empty and on the right he does add the text test to each entry. I tested it on another device and added the result to the imgur collection as well (there are multiple screenshots added). https://imgur.com/a/7cYQ7Zg – Dev86 May 28 '20 at 12:06
  • I have no idea. Here is a screenshot of what I am getting when I run the project and click 3 times on the button: https://imgur.com/WCqfhOH It works flawlessly even on iOS. – Mihail Duchev May 28 '20 at 12:13
  • Hmmm. Apparently he does throw an exception (internally). I've added some details under the second update headline in the initial post. Any chance that you had similar output messages in the past. Because the little info the output windows is giving me, does not really help. – Dev86 May 28 '20 at 12:23
1

However for the first iteration I was testing the list with images from web. I am using xamarin forms and the target is android.

From the posted code, the image is set binding to the AssetImage property. The property is type of Image, binding the value to Image's source will not work. Try to change the type to string.

Check the code:

public class OrderItemImageDocData : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    ...
    private string _assetImage;
    public string AssetImage
    {
        get
        {
            return _assetImage;
        }
        set
        {
            _assetImage = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs("AssetImage"));
        }
    }
}

To load images from web, don't forget to add the Internet permission.

Jarvan Zhang
  • 968
  • 3
  • 11
  • 84
  • Hi. I updated the code in my previous post. I removed the "Image" and the "Uri" property. I only use a ImageSource property now. It should be fine for the Control Property "Image.Source" right? Or does it really only accept string? I checked the permission and they seem to be ok. I have added those as well to the first post. – Dev86 May 28 '20 at 10:03
  • The ImageSource property should also work. Does it work now? – Jarvan Zhang May 28 '20 at 10:10
  • Sadly no. The updated source from my first post does not work. I will try the test from Mihail Duchev in a seperate test app and check out if its working on the android v6 device. If so I will search for differences. Thank you for your response. – Dev86 May 28 '20 at 10:42