I am currently making an app to display and interact with a company's employees. I have a list of departments and when a department is selected, I am trying to show only a list of employees in that department. I am pulling the departments and employees from two different JSON urls. I am trying to use linq to pull the employees where the department is the same as the selected department. I am using James Montemagno's Monkey Finder workshop to help build my app. However, when I try to load the contacts I get an object reference not set to an instance of an object error. I will put my code below for reference on what I am doing as well as screenshots of the error. Does anyone know how to fix this or what I am doing wrong? Thank you!
App Screenshots:
Code:
Model:
Contact.cs
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace ContactNavigation.Model
{
public partial class Contact
{
[JsonProperty("FirstName")]
public string FirstName { get; set; }
[JsonProperty("LastName")]
public string LastName { get; set; }
[JsonProperty("Department")]
public string Department { get; set; }
[JsonProperty("Position")]
public string Position { get; set; }
[JsonProperty("PhoneNumber")]
public string PhoneNumber { get; set; }
[JsonProperty("Email")]
public string Email { get; set; }
[JsonProperty("Picture")]
public Uri Picture { get; set; }
[Newtonsoft.Json.JsonIgnore]
public string FullName { get { return FirstName + " " + LastName; } }
}
public partial class Contact
{
public static Contact[] FromJson(string json) => JsonConvert.DeserializeObject<Contact[]>(json, ContactNavigation.Model.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this Contact[] self) => JsonConvert.SerializeObject(self, ContactNavigation.Model.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
}
Services:
IDataService.cs
using ContactNavigation.Model;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace ContactNavigation.Services
{
public interface IDataService
{
Task<IEnumerable<Contact>> GetContactsAsync();
Task<IEnumerable<Department>> GetDepartmentsAsync();
}
}
WebDataService
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Xamarin.Forms;
using Newtonsoft.Json;
using System.Text;
using System;
using System.Linq;
using ContactNavigation.Model;
using ContactNavigation.Services;
[assembly: Dependency(typeof(WebDataService))]
namespace ContactNavigation.Services
{
public class WebDataService : IDataService
{
HttpClient DepartmentsHttpClient;
HttpClient DepartmentClient => DepartmentsHttpClient ?? (DepartmentsHttpClient = new HttpClient());
HttpClient ContactsHttpClient;
HttpClient ContactClient => ContactsHttpClient ?? (ContactsHttpClient = new HttpClient());
public async Task<IEnumerable<Contact>> GetContactsAsync()
{
var json = await ContactClient.GetStringAsync("MyContactURLHere");
var all = Contact.FromJson(json);
return all;
}
public async Task<IEnumerable<Department>> GetDepartmentsAsync()
{
var json = await DepartmentClient.GetStringAsync("MyDepartmentURLHere");
var all = Department.FromJson(json);
return all;
}
}
}
ViewModel:
ContactViewModel.cs
using ContactNavigation.Model;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;
using System.Diagnostics;
namespace ContactNavigation.ViewModel
{
public class ContactViewModel : BaseViewModel
{
public ObservableCollection<Contact> EmployeeContacts { get; }
public Command GetContactsCommand { get; }
public ContactViewModel()
{
GetContactsCommand = new Command(async () => await GetContactsAsync());
}
public ContactViewModel(Department department)
: this()
{
Department = department;
Title = $"{Department.DepartmentName}";
}
Department department;
public Department Department
{
get => department;
set
{
if (department == value)
return;
department = value;
OnPropertyChanged();
}
}
async Task GetContactsAsync()
{
if (IsBusy)
return;
try
{
IsBusy = true;
var contacts = await DataService.GetContactsAsync();
EmployeeContacts.Clear();
foreach (var contact in contacts.Where(x => x.Department == department.DepartmentName))
EmployeeContacts.Add(contact);
}
catch(Exception ex)
{
await Application.Current.MainPage.DisplayAlert("Error!", ex.Message, "OK");
}
finally
{
IsBusy = false;
}
}
}
}
View:
ContactPage.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"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:local="clr-namespace:ContactNavigation"
xmlns:viewmodel="clr-namespace:ContactNavigation.ViewModel"
xmlns:circle="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
x:Class="ContactNavigation.View.ContactPage"
ios:Page.UseSafeArea="True"
Title="{Binding Title}"
BackgroundColor="#57565B">
<ContentPage.BindingContext>
<viewmodel:ContactViewModel/>
</ContentPage.BindingContext>
<Grid RowSpacing="0" ColumnSpacing="5">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListView ItemsSource="{Binding EmployeeContacts}"
CachingStrategy="RecycleElement"
HasUnevenRows="True"
Grid.ColumnSpan="2">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid ColumnSpacing="10"
Padding="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<circle:CircleImage Source="{Binding Picture}"
HorizontalOptions="Center"
VerticalOptions="Center"
BorderColor="{StaticResource PrimaryDark}"
BorderThickness="3"
WidthRequest="60"
HeightRequest="60"
Aspect="AspectFill"/>
<StackLayout Grid.Column="1"
VerticalOptions="Center">
<Label Text="{Binding FullName}"
TextColor="White"/>
<Label Text="{Binding Position}"
TextColor="White"/>
</StackLayout>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Text="Load Contacts"
TextColor="White"
BackgroundColor="{StaticResource PrimaryDark}"
Command="{Binding GetContactsCommand}"
IsEnabled="{Binding IsNotBusy}"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"/>
<ActivityIndicator IsVisible="{Binding IsBusy}"
IsRunning="{Binding IsBusy}"
HorizontalOptions="FillAndExpand"
VerticalOptions="CenterAndExpand"
Grid.RowSpan="2"
Grid.ColumnSpan="2"/>
</Grid>
</ContentPage>
ContactPage.xaml.cs
using ContactNavigation.Model;
using ContactNavigation.ViewModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace ContactNavigation.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ContactPage : ContentPage
{
public ContactPage()
{
InitializeComponent();
}
public ContactPage(Department department)
{
InitializeComponent();
BindingContext = new ContactViewModel(department);
}
}
}