Reputation: 392
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
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
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:
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));
}
IEnumerable<CalendarItem>
and simply set it like:private async void ViewLeaves()
{
ListItems = await FirebaseService.GetAll(_month, _year);
}
Upvotes: 1