0

Good evening,

I'm currently hitting my head against the wall and I've read just about every article on JSON to ListView using MVVM. Below you'll find the list of places I've looked so far:

convert-json-string-to-c-sharp-object-list

xamarin-forms-mvvm-listview-remains-empty

xamarin-api-json-list-to-listview

json-object-to-xamarin-listview

I'm fairly new to MVVM pattern and something just isn't making sense to me while using ObservableCollection to update the listview?

My current understanding of MVVM is as follows:

  • Models are non-visual classes used to encapsulate the app's data.
  • ViewModels are used to facilitate model data to the view.
  • Views are the layout, structure, and appearance of the UI.

I currently have a working application that I can write the JSON content to the debug view. I for the life of me cannot wrap my head around how to send this data to the View from the ViewModel.. I've added all my code below in hopes someone can shine some light on this.

The Model-View-ViewModel Pattern


Project Notes

URI for API: https://jsonplaceholder.typicode.com/users/

Models

UserModel.cs

    using Newtonsoft.Json;
    
    namespace mvvm_binding_2.Models
    {
        public partial class Users
        {
            [JsonProperty("id")]
            public long Id { get; set; }
    
            [JsonProperty("name")]
            public string Name { get; set; }
    
            [JsonProperty("username")]
            public string Username { get; set; }
    
            [JsonProperty("email")]
            public string Email { get; set; }
    
            [JsonProperty("address")]
            public Address Address { get; set; }
    
            [JsonProperty("phone")]
            public string Phone { get; set; }
    
            [JsonProperty("website")]
            public string Website { get; set; }
    
            [JsonProperty("company")]
            public Company Company { get; set; }
        }
    
        public partial class Address
        {
            [JsonProperty("street")]
            public string Street { get; set; }
    
            [JsonProperty("suite")]
            public string Suite { get; set; }
    
            [JsonProperty("city")]
            public string City { get; set; }
    
            [JsonProperty("zipcode")]
            public string Zipcode { get; set; }
    
            [JsonProperty("geo")]
            public Geo Geo { get; set; }
        }
    
        public partial class Geo
        {
            [JsonProperty("lat")]
            public string Lat { get; set; }
    
            [JsonProperty("lng")]
            public string Lng { get; set; }
        }
    
        public partial class Company
        {
            [JsonProperty("name")]
            public string Name { get; set; }
    
            [JsonProperty("catchPhrase")]
            public string CatchPhrase { get; set; }
    
            [JsonProperty("bs")]
            public string Bs { get; set; }
        }
    }

ViewModels

UserViewModel.cs

    using System;
    using System.Collections.Generic;
    using mvvm_binding_2.Models;
    using System.Net.Http;
    using Newtonsoft.Json;
    using System.Diagnostics;
    
    namespace mvvm_binding_2.ViewModels
    {
        public class UserViewModel
        {
            public UserViewModel()
            {
                try
                {
                    //Get the data?
                    GetUsers();
    
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
    
            public async void GetUsers()
            {
                try
                {
                    //HttpClient setup
                    HttpClient client = new HttpClient();
                    var url = "https://jsonplaceholder.typicode.com/users/";
                    client.BaseAddress = new Uri(url);
                    client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
    
                    //Get data
                    HttpResponseMessage response = await client.GetAsync(url);
    
                    if (response.IsSuccessStatusCode)
                    {
                        //Display status code in debug console
                        Debug.WriteLine(response.StatusCode.ToString());

                        var content = await response.Content.ReadAsStringAsync();

                        //Deserialize JSON
                        var result = JsonConvert.DeserializeObject<List<Users>>(content);

                        //write all usernames and email's to debug console
                        foreach (var item in result)
                        {
                            Debug.WriteLine(item.Username);
                            Debug.WriteLine(item.Email);
                            Debug.WriteLine("------------");
                        }
                    }
                    
                }
                catch (Exception ex)
                {
                    await App.Current.MainPage.DisplayAlert("Error", ex.ToString(), "Ok");
                }
            }
    
        }
    }

Views

Page1.xaml

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="mvvm_binding_2.Views.Page1">
        <ContentPage.Content>
            <StackLayout>
                <Label Text="Test"></Label>
                <ListView x:Name="usersList"
                          ItemsSource="{Binding .}">
    
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <ViewCell>
                                <StackLayout Orientation="Horizontal">
                                    <Label Text="{Binding userId}" TextColor="Red" Padding="5,5,5,5"></Label>
                                    <Label Text="{Binding id}" TextColor="Purple" Padding="5,5,5,5"></Label>
                                    <Label Text="{Binding title}" TextColor="DarkGreen" Padding="5,5,5,5"></Label>
                                    <Label Text="{Binding userId}" TextColor="Orange" Padding="5,5,5,5"></Label>
                                </StackLayout>
                            </ViewCell>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                    
                </ListView>
            </StackLayout>
        </ContentPage.Content>
    </ContentPage>

Views Code

Page1.xaml.cs

    using mvvm_binding_2.ViewModels;
    using Xamarin.Forms;
    using Xamarin.Forms.Xaml;
    
    namespace mvvm_binding_2.Views
    {
        [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class Page1 : ContentPage
        {
            UserViewModel vm;
    
            public Page1()
            {
                InitializeComponent();
                vm = new UserViewModel();
                usersList.BindingContext = vm;
            }
        }
    }

Debug Output

    [0:] Bret
    [0:] Sincere@april.biz
    [0:] ------------
    [0:] Antonette
    [0:] Shanna@melissa.tv
    [0:] ------------
    [0:] Samantha
    [0:] Nathan@yesenia.net
    [0:] ------------
    [0:] Karianne
    [0:] Julianne.OConner@kory.org
    [0:] ------------
    [0:] Kamren
    [0:] Lucio_Hettinger@annie.ca
    [0:] ------------
    [0:] Leopoldo_Corkery
    [0:] Karley_Dach@jasper.info
    [0:] ------------
    [0:] Elwyn.Skiles
    [0:] Telly.Hoeger@billy.biz
    [0:] ------------
    [0:] Maxime_Nienow
    [0:] Sherwood@rosamond.me
    [0:] ------------
    [0:] Delphine
    [0:] Chaim_McDermott@dana.io
    [0:] ------------
    [0:] Moriah.Stanton
    [0:] Rey.Padberg@karina.biz
    [0:] ------------

If you need more information please just ask :)


Updated Code

I've added a Services folder to my solution and put my Api calls in there.

Services

using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Diagnostics;
using System.Net.Http;
using System.Threading.Tasks;
using mvvm_binding_2.Models;

namespace mvvm_binding_2.Services
{
    public class ApiService
    {
        public static async Task GetAllNewsAsync(Action<IEnumerable<Users>> action)
        {

            HttpClient httpClient = new HttpClient();
            HttpResponseMessage response = await httpClient.GetAsync("https://jsonplaceholder.typicode.com/users/");

            Debug.WriteLine(response.Content.ToString());

            if (response.StatusCode == System.Net.HttpStatusCode.OK)
            {
                var list = JsonConvert.DeserializeObject<IEnumerable<Users>>(await response.Content.ReadAsStringAsync());
                Debug.WriteLine("This is the list: " + list);
                action(list);
            }
        }
    }
}

Models

UserModel.cs

No changes!

ViewModels

UserViewModel.cs

using mvvm_binding_2.Models;
using mvvm_binding_2.Services;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace mvvm_binding_2.ViewModels
{
    public class UserViewModel : INotifyPropertyChanged
    {

        private ObservableCollection<Users> items;

        public event PropertyChangedEventHandler PropertyChanged;

        public ObservableCollection<Users> Items
        {
            get { return items; }
            set
            {
                items = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Items)));
            }
        }

        public UserViewModel()
        {

            //This seems to display the list?
            Items = new ObservableCollection<Users>()
            {

            };

            //This seems to call the ApiService
            _ = ApiService.GetAllNewsAsync(list =>
                  {
                      foreach (Users item in list)
                      {
                          Items.Add(item);

                      }
                  });
        }

    }
}

Views

Page1.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="mvvm_binding_2.Views.Page1">
    <ContentPage.Content>
        <StackLayout>
            <Label Text="Test"></Label>
            <ListView x:Name="usersList"
                      ItemsSource="{Binding Items}">

                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <StackLayout Orientation="Horizontal">
                                <Label Text="{Binding Id}" TextColor="Red" Padding="5,5,5,5"></Label>
                                <Label Text="{Binding Username}" TextColor="Purple" Padding="5,5,5,5"></Label>
                                <Label Text="{Binding Email}" TextColor="DarkGreen" Padding="5,5,5,5"></Label>
                            </StackLayout>
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
                
            </ListView>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

Views Code

Page1.xaml.cs

using mvvm_binding_2.ViewModels;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace mvvm_binding_2.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Page1 : ContentPage
    {
        
        public Page1()
        {
            InitializeComponent();
            BindingContext = new UserViewModel();
        }
    }
}

Screenshot

https://ibb.co/Y7KD1pf

llooker
  • 3
  • 4
  • Your `GetUsers()` function should RETURN the result or json string. Then you can pass it further. – Charles Feb 10 '22 at 06:32
  • Ok, I followed along with this newly found article here: https://stackoverflow.com/questions/48282918/binding-itemsource-of-listview-from-viewmodel and here: https://almirvuk.blogspot.com/2017/02/xamarinforms-listview-simple-mvvm.html I've made some changes to the app and I now have a working listview with information pulled from https://jsonplaceholder.typicode.com/users/ I'm still not 100% sure how this is working but will post my new code tomorrow after work.. Need some sleep and I'm relieved. *Note how do I update my code now? should I post an answer? – llooker Feb 10 '22 at 07:29
  • I don't know if this is the correct way to implement this though :/ I'm looking for someone to check the updated code to see what I'm doing right / what I'm doing wrong really.. – llooker Feb 13 '22 at 06:10

0 Answers0