1
votes

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.

2
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 - MSFT
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
Due to your update, can you try this solution: 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 - github.com/xamarin/Xamarin.Forms/issues/7751 You can take a look here too - forums.xamarin.com/discussion/comment/391707/#Comment_391707Mihail Duchev
Try to use FFImageLoading to load the images the remote urls. Similar issue: stackoverflow.com/questions/58299753/…Jarvan Zhang - MSFT

2 Answers

1
votes

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}"
1
votes

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.