Forum Xamarin.Forms

Binding a ListView ansynchronously using ViewModel

MacSpinksMacSpinks USMember
edited January 2017 in Xamarin.Forms

Apologies if the answer is obvious as I am brand new to mobile/xamarin dev. I am implementing a simple Employee Directory app and have hit a snag. I have my view which is a content page and my viewmodel for binding. In this view is an ActivityIndicator that shows while the data loads by binding to the ViewModel's IsBusy Property and a ListView to display the data. I can successfully load the data asynchronously. However, I cannot get the data to the ItemsSource of the ListView after loading. Please take a look at my code below and see if you can spot what I'm missing. I'm sure there's a core concept I'm not grasping somewhere.

BaseViewModel.cs

public class BaseViewModel : INotifyPropertyChanged {

  private bool mblnIsBusy;

  public bool IsBusy {
     get { return mblnIsBusy; }
     set {
        mblnIsBusy = value;

        OnPropertyChanged();
     }
  }


  public event PropertyChangedEventHandler PropertyChanged;

  public virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
     var handler = PropertyChanged;
     if(handler != null)
        handler(this, new PropertyChangedEventArgs(propertyName));
  }
}

EmployeeListViewModel.cs

 public class EmployeeListViewModel : BaseViewModel {
  public ObservableCollection<Employee> EmployeeList {
     get {
        return mcolEmployeeList;
     }
     set {
        mcolEmployeeList = value;
        OnPropertyChanged();
     }
  }
  private ObservableCollection<Employee> mcolEmployeeList;

  public EmployeeListViewModel() {
     mcolEmployeeList = new ObservableCollection<Employee>();
  }

  public void LoadEmployees() { 
      string json = string.empty;
        EmployeeCollection colEmployees = JsonConvert.DeserializeObject<EmployeeCollection>(json);
        mcolEmployeeList = new ObservableCollection<Employee>(colEmployees.Employees);
    //left the above json empty to avoid pasting too much code (all the data retrieval) on this thread. I can confirm through debugging that this works
        //and the mcolEmployeeList gets a list of Employees
     }
  }
}

EmployeeListView.cs

public class EmployeeDirectoryListView : BaseView {

  private ListView listView;
  private ToolbarItem toolbarItem;
  private EmployeeListViewModel ViewModel;
  private bool dataLoaded;


  public EmployeeDirectoryListView() {
     this.Title = "Contact List";
     Icon = "Icon.png";
     toolbarItem = new ToolbarItem("?", "Search.png", () => {
        var search = new SearchListView();
        Navigation.PushAsync(search);
     }, 0, 0);

     ToolbarItems.Add(toolbarItem);

     ViewModel = new EmployeeListViewModel();

     var activityIndicator = new ActivityIndicator {
        Color = Color.Black,
        BindingContext = ViewModel
     };
     activityIndicator.SetBinding(ActivityIndicator.IsRunningProperty, "IsBusy");
     activityIndicator.SetBinding(ActivityIndicator.IsEnabledProperty, "IsBusy");
     activityIndicator.SetBinding(ActivityIndicator.IsVisibleProperty, "IsBusy");


     listView = new ListView() {
        IsGroupingEnabled = true,
        GroupHeaderTemplate = new DataTemplate(typeof(GroupHeaderTemplate)),
        ItemTemplate = new DataTemplate(typeof(ListItemTemplate)),
        BindingContext = ViewModel,
        IsVisible = true
     };
     listView.SetBinding(ListView.ItemsSourceProperty, "EmployeeList");


     Content = new StackLayout() {
        HorizontalOptions = LayoutOptions.FillAndExpand,
        VerticalOptions = LayoutOptions.FillAndExpand,
        BackgroundColor = Color.White,
        Children = { activityIndicator, listView }
     };

  }
  async protected override void OnAppearing() {
     if(!dataLoaded) {
        var complete = await LoadEmployees();
        dataLoaded = true;
     }
  }

  private async Task<bool> LoadEmployees() {
     ViewModel.IsBusy = true;
     await Task.Delay(10000)
     .ContinueWith(task => ViewModel.LoadEmployees());
     ViewModel.IsBusy = false;
     return true;
  }

}

ListItemTemplate.cs

public class ListItemTemplate : ViewCell
{
    public ListItemTemplate ()
    {

        var nameLabel = new Label { 
            VerticalTextAlignment = TextAlignment.Center,
            FontAttributes = FontAttributes.None,
            FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)),
        };
        //PreferredName is a propert of object Employee
        nameLabel.SetBinding (Label.TextProperty, "PreferredName");

        var titleLabel = new Label { 
            VerticalTextAlignment = TextAlignment.Center,
            FontAttributes = FontAttributes.None,
            FontSize = Device.GetNamedSize (NamedSize.Micro, typeof(Label)),
        };
                    //Position is a propert of object Employee
        titleLabel.SetBinding (Label.TextProperty, "Position");

        var information = new StackLayout {
            Padding = new Thickness (5, 0, 0, 0),
            VerticalOptions = LayoutOptions.StartAndExpand,
            Orientation = StackOrientation.Vertical,
            Children = { nameLabel, titleLabel }
        };

        View = new StackLayout {
            Orientation = StackOrientation.Horizontal,
            Children = { information }
        };
    }
 }

All pointers are greatly appreciated. Let me know if this needs clarification.

Best Answer

Answers

Sign In or Register to comment.