0
votes

I have been trying to bind my ListView to my View model. The view model successfully retrieves 5 records from the database and the Listview seems to display 5 blank rows, however it is not showing binding for each field within each row.

I have spent a couple of days searching internet but I don't seem to be doing anything different. I was using master detail pages so I thought that it may be the issue so I set my Events page as first navigation page without master/detail scenario but to no avail. Please note that I am using Portable Ninject for my dependencies/IoC.

My App.Xamal.cs is is as follows:

public App (params INinjectModule[] platformModules)
    {
        InitializeComponent();

        var eventsPage = new NavigationPage(new EventsPage());

        //Register core services
        Kernel = new StandardKernel(new MyAppCoreModule(), new MyAppNavModule(eventsPage.Navigation));

        //Register platform specific services
        Kernel.Load(platformModules);

        //Get the MainViewModel from the IoC
        eventsPage.BindingContext = Kernel.Get<EventsViewModel>();
        ((BaseViewModel)(eventsPage.BindingContext)).Init();

        MainPage = eventsPage;
    }

My EventsPage.Xaml is provided below:

    <?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="MyApp.Views.EventsPage"             
             Title="Events">    
    <ContentPage.Content>        
        <ListView x:Name="Events" ItemsSource="{Binding Events}" >            
                <ListView.ItemTemplate>
                    <DataTemplate>
                    <ViewCell>
                        <Label Text="{Binding EventID}" BackgroundColor="Red" TextColor="White"
                           FontAttributes="Bold" />
                    </ViewCell>                        
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>            
    </ContentPage.Content>
</ContentPage>

My EventsPage.xaml.cs is provided below:

namespace MyApp.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class EventsPage : ContentPage, IBaseViewFor<EventsViewModel>
    {
        public EventsPage ()
        {
            InitializeComponent ();
        }
        EventsViewModel _vm;
        public EventsViewModel ViewModel
        {
            get => _vm;
            set
            {
                _vm = value;
                BindingContext = _vm;
            }
        }

    }
}

My EventsViewModel is as follows, it successfully retrieves 5 records and OnPropertyChanged is fired for Events property:

namespace MyApp.ViewModels
{
    public class EventsViewModel : BaseViewModel, IBaseViewModel
    {
        ObservableCollection<Event> _events;
        readonly IEventDataService _eventDataService;

        public ObservableCollection<Event> Events
        {
            get { return _events; }
            set
            {
                _events = value;
                OnPropertyChanged();
            }
        }        
        public EventsViewModel(INavService navService, IEventDataService eventDataService) : base(navService)
        {     
            _eventDataService = eventDataService;
            Events = new ObservableCollection<Event>();                
        }

        public override async Task Init()
        {
            LoadEntries();
        }
        async void LoadEntries()
        {                       
            try
            {                
                var events = await _eventDataService.GetEventsAsync();             
                Events = new ObservableCollection<Event>(events);
            }
            finally
            {

            }           
        }
    }
}

My BaseViewModel is as follows:

namespace MyApp.ViewModels
{
    public abstract class BaseViewModel : INotifyPropertyChanged
    {
        protected INavService NavService { get; private set; }

        protected BaseViewModel(INavService navService)
        {
            NavService = navService;
        }

        bool _isBusy;
        public bool IsBusy
        {
            get
            {
                return _isBusy;
            }
            set
            {
                _isBusy = value;
                OnPropertyChanged();
                OnIsBusyChanged();
            }
        }

        protected virtual void OnIsBusyChanged()
        {

        }

        public abstract Task Init();

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    // Secod BaseViewModel abstract base class with a generic type that will be used to pass strongly typed parameters to the Init method
    public abstract class BaseViewModel<TParameter> : BaseViewModel
    {
        protected BaseViewModel(INavService navService) : base(navService)
        {
        }

        public override async Task Init()
        {
            await Init(default(TParameter));
        }

        public abstract Task Init(TParameter parameter);
    }
}

IBaseViewModel is just a blank interface:

public interface IBaseViewModel
    {
    }

IBaseViewFor is given below:

namespace MyApp.ViewModels
{
    public interface IBaseViewFor
    {
    }
    public interface IBaseViewFor<T> : IBaseViewFor where T : IBaseViewModel
    {
        T ViewModel { get; set; }
    }
}

My Event model is as follows:

namespace MyApp.Models
{
    public class Event
    {        
        public int EventID;    
    }
}

Finally, the image of the output, as you can see that 5 rows are created with red background but EventID is not binding in each row. I have checked the data and EventID is returned. I have even tried to manually add records into Events list but to no avail, see the manual code and image below:

async void LoadEntries()
        {                       
            try
            {               

                Events.Add((new Event() { EventID = 1 }));
                Events.Add((new Event() { EventID = 2 }));
                Events.Add((new Event() { EventID = 3 }));
                Events.Add((new Event() { EventID = 4 }));
                Events.Add((new Event() { EventID = 5 }));

            }
            finally
            {

            }           
        }

enter image description here

I have spent a lot of time on it but unable to find a reason for this anomaly, can someone please cast a fresh eye and provide help!?

1

1 Answers

2
votes

You can only bind to public properties - ie, you need a getter

public class Event
{        
    public int EventID { get; set; } 
}