3

I'm trying parse this JSON object and bind it to my ListView in Xamarin.Forms.

I'm just totally lost on how to handle it as i'm completely new to Xamarin Forms.

Is there a easier way to do this?

My returned JSON Object

[
  {
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret"
  },
  {
    "id": 2,
    "name": "Ervin Howell",
    "username": "Antonette"
  }
]

Here is the my Code to handle the REST json response

public class RestClient
    {
        public RestClient ()
        {
        }

        public async Task<User[]> GetUsersAsync () {

            var client = new System.Net.Http.HttpClient ();

            client.BaseAddress = new Uri("http://jsonplaceholder.typicode.com");

            var response = client.GetAsync("users");

            var usersJson = response.Result.Content.ReadAsStringAsync().Result;

            var rootobject = JsonConvert.DeserializeObject<Rootobject>(usersJson);

            return rootobject.Users;

        }
    }

Users.cs

    public class Rootobject
        {
            public User[] Users { get; set; }
        }

        public class User
        {
            public string id { get; set; }
            public string username { get; set; }
        }

ListView Form Code
var sv = new RestClient ();
            var es = sv.GetUsersAsync();
            Xamarin.Forms.Device.BeginInvokeOnMainThread (() => {
                Debug.WriteLine("Found " + es.Result.Length + " users");
                listView.ItemsSource = es.Result;
            });

XAML

public ListViewPage ()
        {
            Title = "Users";

            var sv = new RestClient ();
            var es = sv.GetUsersAsync();
            Xamarin.Forms.Device.BeginInvokeOnMainThread (() => {
                Debug.WriteLine("Found " + es.Result.Length + " users");
                listView.ItemsSource = es.Result;
            });


            listView = new ListView ();
            listView.ItemTemplate = new DataTemplate(typeof(TextCell));
            listView.ItemTemplate.SetBinding(TextCell.TextProperty, "username");

            listView.ItemTemplate = new DataTemplate(typeof(ItemCell));

            Content = new StackLayout { 
                Children = {
                    listView
                }
            };
        }

2 Answers 2

4

Your async call is not correct. If you have to do it in the constructor (which isn't the best place to do this) you would want to use ContinueWith as using Task.Result should not be used. Also because the Result is a blocking call you are assigning the item source before the list view is constructed and you are getting a null reference exception.

Try this:

public class ListViewPage : ContentPage
{
    private readonly ListView listView;

    public ListViewPage()
    {
        Title = "Users";

        this.listView = new ListView {ItemTemplate = new DataTemplate(typeof (TextCell))};
        this.listView.ItemTemplate.SetBinding(TextCell.TextProperty, "username");

        Content = new StackLayout
        {
            Children = { this.listView }
        };

        var sv = new RestClient();
        var es = sv.GetUsersAsync().ContinueWith(t =>
        {
            if (t.Status == TaskStatus.RanToCompletion)
            {
                Debug.WriteLine("Found {0} users.", t.Result.Length);
                Device.BeginInvokeOnMainThread(() => this.listView.ItemsSource = t.Result);
            }
        });
    }
}

A slightly better option (but not perfect either) would be to override the appearing method and mark is async. This way you can use await on the async REST call method. Note that this would get called every time the view appears unless additional code is added.

    protected override async void OnAppearing()
    {
        base.OnAppearing();

        try
        {
            var sv = new RestClient();
            // activate/show spinner here
            this.listView.ItemsSource = await sv.GetUsersAsync();
            // inactivate/hide spinner here
        }
        catch (Exception exception)
        {
            this.DisplayAlert("Error", exception.Message, "OK");
        }
    }
Sign up to request clarification or add additional context in comments.

4 Comments

How should I implement a progress spinner (thinking spinner) so that the View loads fast and shows only the spinner until it has loaded the JSON?
I edited the OnAppearing overload to show where you would show/hide the spinner. Here is the API for the indicator: iosapi.xamarin.com/?link=T%3aXamarin.Forms.ActivityIndicator
Just a minor correction, you would want to inactivate the spinner in finally {} block of the try-catch so in case of an error it would hide.
FYI I had 2 async API calls (diff domains) on a single page. I thought the quick call could run in Device.BeginInvokeOnMainThread in the constructor and the bulky one in OnAppearing. I had to put both in OnAppearing for the page to work consistently. Funnily enough there was no pattern on which listView would show data on each page load before the fix. Cheers @SKall.
0

Looks like you are not awaiting sv.GetUsersAsync and I'm not sure if Result will contain all data without waiting for the operation to be completed.

Though it's fine to use any collection and any objects as data source for list view it's better to use ObservableCollection and make your User class implement INotifyPropertyChanged (take a look on Fody.PropertyChanged nuget package).

Could you share xaml with us?

EDIT 1

You define your ItemTemplate twice.

listView.ItemTemplate = new DataTemplate(typeof(TextCell)); //first definition
listView.ItemTemplate.SetBinding(TextCell.TextProperty, "username");

listView.ItemTemplate = new DataTemplate(typeof(ItemCell)); //second definition, remove it

Plus it's better to use MVVM with Xamarin.Forms and load your data in view-model and not in your page's constructor. Here is good article about mvvm and data loading in Xamarin.Forms.

EDIT 2

Why do you use async/await in such a strange way? Instead of reading Task.Result property it's better to use async/await pair. Example.

3 Comments

Have added the XAML now. But I would love to know if there is an easier way to load the JSON on load and bind it to my ListView
Added comment about async/await. Sorry if this is just your style of using it.
Not at all, I just found a example on the net doing this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.