Reputation: 15
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
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