Pers
Pers

Reputation: 15

Not seeing any data in Xamarin CollectionView

I am learning Xamarin and I'm trying to make an app in which I can browse through some elements in a screen and after that, onclick of an element open a new screen. I tried using the flyout template for it, and for the first screen use the collectionview logic from the template. However, with the same logic, I am not seeing any data shown in my collection view. Here are my model, view, viewmodel and xaml and cs files. Model:

public class VehicleMake
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string Abrv { get; set; }
    }

View:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             BackgroundColor="LightCoral"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:local="clr-namespace:VehicleProject.ViewModels" 
             xmlns:model="clr-namespace:VehicleProject.Models" 
             x:DataType="local:VehicleMakeViewModel"
             x:Class="VehicleProject.Views.VehicleMakePage">
    <RefreshView x:DataType="local:VehicleMakeViewModel" Command="{Binding LoadItemsCommand}" IsRefreshing="{Binding IsBusy, Mode=TwoWay}">
    <CollectionView x:Name="ItemsListView"
                ItemsSource="{Binding VehicleMakes}"
                SelectionMode="None">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <StackLayout Padding="10" x:DataType="model:VehicleMake" BackgroundColor="White">
                    <Label Text="{Binding Id}" 
                           TextColor="White"
                            LineBreakMode="NoWrap" 
                            Style="{DynamicResource ListItemTextStyle}" 
                            FontSize="16" />
                    <Label Text="{Binding Name}" 
                            LineBreakMode="NoWrap"
                            Style="{DynamicResource ListItemDetailTextStyle}"
                            FontSize="13" />
                    <StackLayout.GestureRecognizers>
                        <TapGestureRecognizer 
                                NumberOfTapsRequired="1"
                                Command="{Binding Source={RelativeSource AncestorType={x:Type local:VehicleModelViewModel}}, Path=ItemTapped}"      
                                CommandParameter="{Binding .}">
                        </TapGestureRecognizer>
                    </StackLayout.GestureRecognizers>
                </StackLayout>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>
    </RefreshView>
</ContentPage>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VehicleProject.ViewModels;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace VehicleProject.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class VehicleMakePage : ContentPage
    {
        VehicleMakeViewModel _viewModel;
        public VehicleMakePage()
        {
            InitializeComponent();
            BindingContext = _viewModel = new VehicleMakeViewModel();
        }
        protected override void OnAppearing()
        {
            base.OnAppearing();
            _viewModel.OnAppearing();
        }
    }
}

ViewModel:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using System.Threading.Tasks;
using VehicleProject.Models;
using Xamarin.Forms;

namespace VehicleProject.ViewModels
{
    public class VehicleMakeViewModel: BaseViewModel
    {
        private VehicleMake _selectedItem;
        public ObservableCollection<VehicleMake> VehicleMakes { get; set; } = new ObservableCollection<VehicleMake>();
        public Command LoadItemsCommand { get; }
        public Command AddItemCommand { get; }
        public Command<VehicleMake> ItemTapped { get; }

        public VehicleMakeViewModel()
        {
            Title = "Vehicles";
            VehicleMakes = new ObservableCollection<VehicleMake>();
            LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());

            ItemTapped = new Command<VehicleMake>(OnItemSelected);

            AddItemCommand = new Command(OnAddItem);
        }

        async Task ExecuteLoadItemsCommand()
        {
            IsBusy = true;

            try
            {
                VehicleMakes.Clear();
                var vehicleMakes = await DataStore.GetItemsAsync(true);
                foreach (var vehicleMake in vehicleMakes)
                {
                    Debug.WriteLine(vehicleMake.Id);
                    VehicleMakes.Add(vehicleMake);
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
            finally
            {
                IsBusy = false;
            }
        }
        public void OnAppearing()
        {
            IsBusy = true;
            SelectedItem = null;
        }

        public VehicleMake SelectedItem
        {
            get => _selectedItem;
            set
            {
                SetProperty(ref _selectedItem, value);
                OnItemSelected(value);
            }
        }

        private async void OnAddItem(object obj)
        {
            //await Shell.Current.GoToAsync(nameof(NewItemPage));
        }

        async void OnItemSelected(VehicleMake item)
        {
            if (item == null)
                return;

            // This will push the ItemDetailPage onto the navigation stack
            //await Shell.Current.GoToAsync($"{nameof(ItemDetailPage)}?{nameof(ItemDetailViewModel.ItemId)}={item.Id}");
        }
    }
}

BaseViewModel:

public class BaseViewModel :  INotifyPropertyChanged
    {
        public IDataStore<VehicleMake> DataStore => DependencyService.Get<IDataStore<VehicleMake>>();

    bool isBusy = false;
    public bool IsBusy
    {
        get { return isBusy; }
        set { SetProperty(ref isBusy, value); }
    }

    string title = string.Empty;
    public string Title
    {
        get { return title; }
        set { SetProperty(ref title, value); }
    }

    protected bool SetProperty<T>(ref T backingStore, T value,
        [CallerMemberName] string propertyName = "",
        Action onChanged = null)
    {
        if (EqualityComparer<T>.Default.Equals(backingStore, value))
            return false;

        backingStore = value;
        onChanged?.Invoke();
        OnPropertyChanged(propertyName);
        return true;
    }

    #region INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        var changed = PropertyChanged;
        if (changed == null)
            return;

        changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    #endregion
}

IDataStore:

    public interface IDataStore<T>
        {
            Task<bool> AddItemAsync(T item);
            Task<bool> UpdateItemAsync(T item);
            Task<bool> DeleteItemAsync(string id);
            Task<T> GetItemAsync(string id);
            Task<IEnumerable<T>> GetItemsAsync(bool forceRefresh = false);
        }

MockVehicleMakeStore:

public class MockVehicleMakeStore : IDataStore<VehicleMake>
    {
        readonly List<VehicleMake> vehicleMakes;

        public MockVehicleMakeStore()
        {
            vehicleMakes = new List<VehicleMake>()
            {
                new VehicleMake { Id = Guid.NewGuid().ToString(), Name = "First item", Abrv="This is an item description." },
                new VehicleMake { Id = Guid.NewGuid().ToString(), Name = "Second item", Abrv="This is an item description." },
                new VehicleMake { Id = Guid.NewGuid().ToString(), Name = "Third item", Abrv="This is an item description." },
                new VehicleMake { Id = Guid.NewGuid().ToString(), Name = "Fourth item", Abrv="This is an item description." },
                new VehicleMake { Id = Guid.NewGuid().ToString(), Name = "Fifth item", Abrv="This is an item description." },
                new VehicleMake { Id = Guid.NewGuid().ToString(), Name = "Sixth item", Abrv="This is an item description." },
                new VehicleMake { Id = Guid.NewGuid().ToString(), Name = "Sixth item", Abrv="This is an item description." },
                new VehicleMake { Id = Guid.NewGuid().ToString(), Name = "Sixth item", Abrv="This is an item description." }

            };
        }
        public async Task<bool> AddItemAsync(VehicleMake item)
        {
            vehicleMakes.Add(item);

            return await Task.FromResult(true);
        }
        public async Task<bool> UpdateItemAsync(VehicleMake item)
        {
            var oldItem = vehicleMakes.Where((VehicleMake arg) => arg.Id == item.Id).FirstOrDefault();
            vehicleMakes.Remove(oldItem);
            vehicleMakes.Add(item);

            return await Task.FromResult(true);
        }

        public async Task<bool> DeleteItemAsync(string id)
        {
            var oldItem = vehicleMakes.Where((VehicleMake arg) => arg.Id == id).FirstOrDefault();
            vehicleMakes.Remove(oldItem);

            return await Task.FromResult(true);
        }

        public async Task<VehicleMake> GetItemAsync(string id)
        {
            return await Task.FromResult(vehicleMakes.FirstOrDefault(s => s.Id == id));
        }

        public async Task<IEnumerable<VehicleMake>> GetItemsAsync(bool forceRefresh = false)
        {
            return await Task.FromResult(vehicleMakes);
        }
    }

The logic of my solution is the same of the one in the template, but it seems I'm overseeing something. Please help. Thank you

Upvotes: 0

Views: 361

Answers (1)

Pers
Pers

Reputation: 15

So the problem was that my DependencyService wasn't registered.... In app.xaml.cs I added the this line in its constructor, just after InitializeComponent(); DependencyService.Register();

Upvotes: 1

Related Questions