1
votes

Hello I'm working on a android app in Xamarin forms thats getting json data from a service and its supposed to display that data in The ContactInfo class however when you click on the button that's supposed to get that data and then display it and then take you to the ContactInfo class it hangs and after a minute my galaxy s7 tells me that app isn't responding would you like to close it? I have no idea what i'm doing wrong

here's my 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="ReadyMo.ContactInfo">
              <ListView ItemsSource="{Binding ContactInfoList}" HasUnevenRows="true">
               <ListView.ItemTemplate>
      <DataTemplate>
        <ViewCell>
          <Frame Padding="0,0,0,8" BackgroundColor="#d2d5d7">
            <Frame.Content>
              <Frame Padding="15,15,15,15"   OutlineColor="Gray" BackgroundColor="White">
                <Frame.Content>
                  <StackLayout Padding="20,0,0,0"  Orientation="Horizontal" HorizontalOptions="CenterAndExpand">
                    <Label x:Name="FirstName" Text="{Binding First_Name}" HorizontalOptions="Center">
                        </Label>
                        <Label x:Name ="LastName" Text="{Binding Last_Name}" HorizontalOptions="Center">
                        </Label>
                        <Label x:Name="County" Text="{Binding name}" HorizontalOptions="Center">
                        </Label>
                        <Label x:Name ="Adress" Text="{Binding Address1}" HorizontalOptions="Center">
                        </Label>
                          <Label x:Name ="City" Text="{Binding Address2}" HorizontalOptions="Center">
                        </Label>
                        <Label x:Name="Number"  Text="{Binding BusinessPhone}" HorizontalOptions="Center">
                        </Label>   
                  </StackLayout>
                </Frame.Content>
              </Frame>
            </Frame.Content>
          </Frame>
        </ViewCell>
      </DataTemplate>
    </ListView.ItemTemplate>
  </ListView>
</ContentPage>

Here's my codebehind:

using Newtonsoft.Json;
using ReadyMo.Data;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;

using Xamarin.Forms;

namespace ReadyMo
{
    public partial class ContactInfo : ContentPage
    {
        private County item;


        public static async Task<string> GetContactString(string contactid)
        {

            HttpClient client = new HttpClient();
            var url = $"http://sema.dps.mo.gov/county/service.php?id={contactid}";
            var response = await client.GetAsync(url);
            if (response.IsSuccessStatusCode)
            {
                var responsetext = await response.Content.ReadAsStringAsync();
                return responsetext;
            }
            throw new Exception(response.ReasonPhrase);


        }

        public ContactInfo()
        {
            InitializeComponent();

        }
        List <ContactInfoModel> ContactInfoList;
        public ContactInfo(County item)
        {
            InitializeComponent();
            this.item = item;

            var contactpagetext = ContactManager.GetContactString(item.id);
            var contact = GetContactString("001").Result;
            //need to add update database code 

            //Convert Jason string to object

             ContactInfoList = JsonConvert.DeserializeObject<List<ContactInfoModel>>(contact);
             this.BindingContext = ContactInfoList;





            //Floodplain Administrators



        }
    }
}

any help would be amazing!

Thank in advance!

2

2 Answers

2
votes

Your problem is in this line:

var contact = GetContactString("001").Result;

It's causing a deadlock that I explain in full on my blog.

To fix this, you have to first realize that the UI must display immediately. When the OS calls your code, you can't wait for an HTTP request to complete before responding; that's simply not allowed.

What you can do is start the HTTP request and display something else (like a spinner or "loading..." message) immediately. Then, when the HTTP request completes, update your display with the new information.

I have more details and sample code in my MSDN article on async MVVM data binding.

1
votes

Try moving the async code out of the constructor. A common approach to this is to override OnAppearing() to perform the work. You would also probably want to display an ActivityIndicator in your UI until the data has been loaded:

public partial class ContactInfo : ContentPage
{
    private County item;

    public static async Task<string> GetContactString(string contactid)
    {
        HttpClient client = new HttpClient();
        var url = $"http://sema.dps.mo.gov/county/service.php?id={contactid}";
        var response = await client.GetAsync(url);
        if (response.IsSuccessStatusCode)
        {
            var responsetext = await response.Content.ReadAsStringAsync();
            return responsetext;
        }
        throw new Exception(response.ReasonPhrase);
    }

    public ContactInfo()
    {
        InitializeComponent();
        ContactInfoList = new ObservableCollection<ContactInfoModel>();
    }

    ObservableCollection<ContactInfoModel> ContactInfoList;

    public ContactInfo(County item) : this()
    {
        this.item = item;
        this.BindingContext = ContactInfoList;
    }

    protected override async void OnAppearing ()
    {
        if (item == null)
            return;
        var contact = await GetContactString(item.id);
        var models = JsonConvert.DeserializeObject<List<ContactInfoModel>>(contact);
        foreach (var model in models)
            ContactInfoList.Add(model);
    }
}

To fix the binding problem, recognize that you are assigning the list itself to your page's BindingContext. You can do one of two things:

  1. Change ItemsSource="{Binding ContactInfoList}" in your ListView XAML to ItemsSource="{Binding .}".
  2. Change this.BindingContext = ContactInfoList; in your constructor to this.BindingContext = this;.

Option #1 is preferred.