Kshitij
Kshitij

Reputation: 392

Custom ListView binding not working in Xamarin.Forms

I am trying to bind a response to a custom ListView in Xamarin.Forms but the ListView remains blank even when the ObservableCollection contains results. Please point out my mistake in the code below.

In the below code, I am trying to achieve following result -

I am using Prism MVVM architecture for building the app.

View

<ListView  ItemsSource="{Binding ListItems}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <Frame BorderColor="LightGray" Padding="5">
                        <StackLayout Orientation="Horizontal">
                            <!--<BoxView BackgroundColor="{Binding HighlightColor}" WidthRequest="10"/>-->
                            <Label Text="{Binding Date}" FontSize="Medium" Margin="20,0,0,0"/>
                            <Label Text="{Binding Day}" FontSize="Medium" Margin="20,0,0,0"/>
                            <Label Text="{Binding LeaveCount}" HorizontalOptions="EndAndExpand" HorizontalTextAlignment="End"/>
                        </StackLayout>
                    </Frame>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

ViewModel

public ObservableCollection<CalendarItem> ListItems { get; set; }    



public LeaveViewerPageViewModel(INavigationService navigationService, IFirebaseService firebaseService)
            : base(navigationService, firebaseService)
        {
            Title = "View Leaves";
            ViewCommand = new DelegateCommand(ViewLeaves);
        }

    private async void ViewLeaves()
    {
        ListItems = new ObservableCollection<CalendarItem>();

        var x = await FirebaseService.GetAll(_month, _year);

        foreach (var item in x)
        {
            ListItems.Add(item);
        }
    }

Service

public async Task<List<CalendarItem>> GetAll( string month, string year)
{
    List<CalendarItem> ItemList = new List<CalendarItem>();

    int iterationLimit = DateTime.DaysInMonth(int.Parse(year), (DateTime.ParseExact(month.Substring(0, 3), "MMM", CultureInfo.InvariantCulture)).Month);

    for (int i = 1; i <= iterationLimit; i++)
    {
       CalendarItem ITEM = new CalendarItem();

        DateTime dateTime = new DateTime(int.Parse(year), DateTime.ParseExact(month.Substring(0, 3), "MMM", CultureInfo.InvariantCulture).Month, i);
        var res = await GetLeavesAsync(i, month, year);

        ITEM.Date = dateTime.Day.ToString();
        ITEM.Day = dateTime.DayOfWeek.ToString();
        ITEM.LeaveCount = res.Count;
        ITEM.Leaves = res;
        if (res.Count > 0)
            ITEM.HighlightColor = res.Count < 5 ? System.Drawing.Color.Yellow : System.Drawing.Color.Tomato;

        ItemList.Add(ITEM);
    }

    return ItemList;
}

Model

 public class CalendarItem
    {
        public Color HighlightColor { get; set; }
        public string Date { get; set; }
        public string Day { get; set; }
        public int LeaveCount { get; set; }
        public List<LeaveDetail> Leaves { get; set; }
    }

Upvotes: 0

Views: 504

Answers (2)

Nikhil
Nikhil

Reputation: 3387

Please try modifying your ListItems as follows:-

    private ObservableCollection<CalendarItem> _listItems = new ObservableCollection<CalendarItem>();
    public ObservableCollection<CalendarItem> ListItems
    {
        get => _listItems;
        set => SetProperty(ref _listItems, value);
    }

Upvotes: 1

Dan Siegel
Dan Siegel

Reputation: 5799

Based on the code you have here there are actually a couple of issues:

1) Your ListItems property in the ViewModel is not itself implementing INotifyPropertyChanged.

public class ViewAViewModel : BindableBase
{
    private ObservableCollection<CalendarItem> _listItems;
    public ObservableCollection<CalendarItem> ListItems
    {
        get => _listItems;
        set => SetProperty(ref _listItems, value);
    }
}

2) You are newing up a ListItems in the ViewLeaves method and then adding each item one at a time. The result here is that if you followed the first step when you set ListItems it would try to update the UI, and then every time you add an item it will try to update the UI.

3) You have two options for optimizing this:

  • Use ObservableRangeCollection which James Montemagno has in his MvvmHelpers library. This will allow you to new up the collection in the ctor and then simply dump a collection in like:
private async void ViewLeaves()
{
    ListItems.ReplaceRange(await FirebaseService.GetAll(_month, _year));
}
  • Simply implement the property as shown in the first issue I pointed out but change it to IEnumerable<CalendarItem> and simply set it like:
private async void ViewLeaves()
{
    ListItems = await FirebaseService.GetAll(_month, _year);
}

Upvotes: 1

Related Questions