1

Can't display data in list view after json deserialization. How do i need to set it up in order to see it?

I'm data binding my reponse from http client using binding context so i can use it in my list view.

MainPage.xaml

<ContentPage
        Title="Home"
     >
        <Grid>
            <ListView ItemsSource="{Binding .}">
            <ListView.ItemTemplate>
                    <DataTemplate>
                        <StackLayout>
                            <Label Text="{Binding Description}"/>
                            <Label Text="{Binding Value}"/>
                        </StackLayout>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </Grid>
    </ContentPage>

MainPage.cs

public MainPage()
        {
            DataService ds = new DataService();

            BindingContext = ds.GetBillsAsync();

            InitializeComponent();

            Chart1.Chart = new BarChart { Entries = entries, LabelTextSize = (float)Convert.ToDouble(Device.GetNamedSize(NamedSize.Large, typeof(Label))), BackgroundColor = SKColors.Transparent };



        }

DataService.cs

public class DataService
    {
        HttpClient client = new HttpClient();

        public async Task<List<Bill>> GetBillsAsync()
        {
            try
            {
                string url = "my url";

                var response = await client.GetStringAsync(url).ConfigureAwait(continueOnCapturedContext: false); ;
                var bills = JsonConvert.DeserializeObject<List<Bill>>(response);
                return bills;
            }
            catch (Exception e)
            {
                throw e;
            }
        }
    }

I'm not getting any error messages, except for this output message "Binding: System.Threading.Tasks.Task`1[System.Collections.Generic.List`1[Test.Models.Bill]] can not be converted to type 'System.Collections.IEnumerable'" which i don't think it is the problem since i can see my list being populated correctly when i'm debugging.

Can you help me please?

Thank you in advance

Speedy
  • 69
  • 8
  • could it work by binding it to the listview ItemsSource like below ? – Leo Zhu Oct 31 '19 at 07:24
  • It's really simple and yes for now i just wanted to display that information but i cannot inherit ContentPage in MainPage. It gives me this error ```Partial declarations of 'MainPage' must not specify different base classes``` – Speedy Oct 31 '19 at 07:33
  • what's your MainPage ?is it a ContentPage ?if it's a content page,it's xaml would like below i update – Leo Zhu Oct 31 '19 at 07:45
  • you try to let your MainPage implement the BottomBarPage,like `public partial class MainPage: BottomBarPage`.then have a try – Leo Zhu Oct 31 '19 at 08:08
  • Yes, i got it to implement it, thanks. I'm gonna try and use your approach – Speedy Oct 31 '19 at 08:10
  • ListView.ItemsSource = await ds.GetBillsAsync(); is giving me this ```An object reference is required for the non-static field, method, or property 'ItemsView.ItemsSource'``` – Speedy Oct 31 '19 at 08:23
  • not ListView,you should use the Name of ListView which you define in xaml,for example,in my answer below,i define the ListView `x:Name ="listview"`,so i use `listview.ItemSource` – Leo Zhu Oct 31 '19 at 08:47
  • Forgot to give it a name x:Name = "listview" – Speedy Oct 31 '19 at 08:49
  • Now it's not showing any errors but when it finally compiles ends with this exception ```InvalidCastException: Specified cast is not valid``` – Speedy Oct 31 '19 at 09:05
  • which line throw the error ? – Leo Zhu Oct 31 '19 at 09:09
  • make sure `var bills = JsonConvert.DeserializeObject>(response)` is correct – Leo Zhu Oct 31 '19 at 09:16
  • It's actually this: ```The application may be doing too much work on its main thread. **System.InvalidCastException: 'Specified cast is not valid.' ``` It's skipping frames because it has some heavy processing stuff for some reason. It appears in here [link](https://stackoverflow.com/questions/14678593/the-application-may-be-doing-too-much-work-on-its-main-thread) – Speedy Oct 31 '19 at 09:18
  • i test with an local list data,so i could not reproduce your problem, maybe you can see if you did some time-consuming operations on the main thread. – Leo Zhu Oct 31 '19 at 09:39
  • Thanks for all your help, i will mark it as the accepted answer either way. If i can solve i'll let you know if you want – Speedy Oct 31 '19 at 09:46
  • yes,if you solve it,you could tell me – Leo Zhu Oct 31 '19 at 09:48
  • Fixed the problem, it turns out the dropping frames where because i was debugging xD The real problem was a stack layout inside datatemplate, without a viewcell, you can see more here [link](https://stackoverflow.com/questions/40575870/xamarin-forms-binding-specified-cast-is-not-valid) – Speedy Nov 03 '19 at 11:25

2 Answers2

1

Nikhil's method is one way ,if you only want to set the List which request from http client to the listview,you could binding to the ListView's ItemsSource property.

in the MainPage.xaml.cs :

public partial class MainPage: ContentPage
{
   DataService ds = new DataService();
   public MainPage()
    {

        InitializeComponent();
        Chart1.Chart = new BarChart { Entries = entries, LabelTextSize = (float)Convert.ToDouble(Device.GetNamedSize(NamedSize.Large, typeof(Label))), BackgroundColor = SKColors.Transparent };
    }

   protected override async void OnAppearing()
    {
        base.OnAppearing();
        listview.ItemsSource = await ds.GetBillsAsync();
    }
}

in MianPage.xaml:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:d="http://xamarin.com/schemas/2014/forms/design"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         mc:Ignorable="d"
         Title="Home"
         x:Class="App18.MainPage">
  <ContentPage.Content>
    <StackLayout>
        <ListView x:Name="listview">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <StackLayout>
                        <Label Text="{Binding Description}"/>
                        <Label Text="{Binding Value}"/>
                    </StackLayout>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
 </ContentPage.Content>
</ContentPage>
Leo Zhu
  • 15,726
  • 1
  • 7
  • 23
0

You need to change the GetBillsAsync Method to retrieve the Array and then convert into List using Array.ToList() with System.Linq.

You need to implement a ViewModel for this. You can use the NuGet MVVM helpers for the BaseViewModel or implement your own.

using System.Windows.Input;
using MvvmHelpers.Interfaces;

namespace NameSpace.ViewModel
{
    public partial class HomeViewModel : BaseViewModel
    {

        private ObservableCollection<Bill> _listOfbills = new ObservableCollection<Bill>();
        public ObservableCollection<Bill> ListOfbills
        {
            get => _listOfbills;
            set => SetProperty(ref _listOfbills, value);
        }
        public HomeViewModel()
        {
            DataService ds = new DataService();
            ListOfbills = ds.GetBillsAsync()
        }
    }
}

Then your MainPage becomes:-

HomeViewModel vm; public MainPage() {

            BindingContext = vm = new HomeViewModel();

            InitializeComponent();

            Chart1.Chart = new BarChart { Entries = entries, LabelTextSize = (float)Convert.ToDouble(Device.GetNamedSize(NamedSize.Large, typeof(Label))), BackgroundColor = SKColors.Transparent };



        }

Then in the Xaml you can change this line as follows :-

<ListView ItemsSource="{Binding ListOfbills}">

These changes should work. Let me know if you face difficulties. Thanks!

Nikhil
  • 3,387
  • 1
  • 8
  • 16
  • It's giving me 2 errors: 1. ```"Cannot implicitly convert type 'System.Threading.Tasks.Task>' to 'System.Collections.ObjectModel.ObservableCollection'"``` 2. public HomeViewModel must have a return type – Speedy Oct 31 '19 at 06:18
  • Update the following things in the code :- 1. ListOfbills = new ObservableCollection( ds.GetBillsAsync()); 2. Remove the keyword partial from "public partial class HomeViewModel : BaseViewModel" – Nikhil Oct 31 '19 at 06:37
  • Update: The second was just a naming issue sorry xD. I'm already converting it using linq in GetBillsAsync but it's giving me the same error. – Speedy Oct 31 '19 at 06:42
  • You need to write ListOfbills = new ObservableCollection( ds.GetBillsAsync()); because to get an ObservableCollection from a List we need to do this – Nikhil Oct 31 '19 at 06:45
  • Sorry I might be missing something. I've implemented as you said ```ListOfbills = new ObservableCollection( ds.GetBillsAsync()); ``` and it gives me now this conversion error ```cannot convert from 'System.Threading.Tasks.Task>' to 'System.Collections.Generic.IEnumerable'``` – Speedy Oct 31 '19 at 06:54
  • You need to change it to ListOfbills = new ObservableCollection( await ds.GetBillsAsync()); Await is missing in this case. – Nikhil Oct 31 '19 at 08:56