Reputation: 706
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
}
};
}
Upvotes: 3
Views: 8869
Reputation: 5234
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");
}
}
Upvotes: 4
Reputation: 3167
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.
Upvotes: 0