I have simple XAML page with code like this:
<StackLayout BindableLayout.ItemsSource="{Binding Data}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Label BackgroundColor="{Binding Color}"
Text="{Binding Text}"
FontAttributes="Italic"
FontSize="20"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
HeightRequest="105"
Margin="25" />
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
In the view page code behind I have:
public partial class DataView
{
public DataView()
{
InitializeComponent();
BindingContext = new ViewModel();
}
...
}
Data is:
public class ViewModel
{
public ViewModel()
{
Data = new ObservableCollection()<Model> {
new Model { Text = "Pink", Color = Color.DeepPink },
new Model { Text = "Crimson", Color = Color.Crimson },
new Model { Text = "Aqua", Color = Color.Aqua },
new Model { Text = "Blue", Color = Color.DeepSkyBlue },
new Model { Text = "BurlyWood", Color = Color.BurlyWood }, };
}
public ObservableCollection<Model> Data { get; set; }
}
public class Model
{
public Color Color { get; set; }
public string Text { get; set; }
}
However, on compile, I get:
Binding: Property "Color" not found on "MyComp.ViewModel".
Seems like its searching for Color in ViewModel instead of parent (Data).
I'm using the latest Xamarin.Forms 4.8.0, .NET Standard 2.0 and Visual Studio 2019 16.7.4.
I tested the xaml code, it still works fine.Check the gif: https://us.v-cdn.net/5019960/uploads/editor/pd/aa0fp5pwnvkl.gif
Here is the related code, you could refer to it.
Page.xaml
<StackLayout x:Name="expanderLayout" BindableLayout.ItemsSource="{Binding Data}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Expander>
<Expander.Header>
<Grid>
<Label BackgroundColor="{Binding Color}"/>
</Grid>
</Expander.Header>
<Expander.ContentTemplate>
<DataTemplate>
<Label
Text="{Binding Text}"
FontAttributes="Italic"
FontSize="20"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
HeightRequest="105"
Margin="25" />
</DataTemplate>
</Expander.ContentTemplate>
</Expander>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
Page.xaml.cs
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new ViewModel();
}
}
Model class and ViewModel class
public class ViewModel
{
public ViewModel()
{
Data = new System.Collections.ObjectModel.ObservableCollection<Model>() {
new Model { Text = "Pink", Color = Color.DeepPink },
new Model { Text = "Crimson", Color = Color.Crimson },
new Model { Text = "Aqua", Color = Color.Aqua },
new Model { Text = "Blue", Color = Color.DeepSkyBlue },
new Model { Text = "BurlyWood", Color = Color.BurlyWood }, };
}
public ObservableCollection<Model> Data { get; set; }
}
public class Model
{
public Color Color { get; set; }
public string Text { get; set; }
}
I tried run your code and see nothing wrong with it
Apart from some slight typo in your question, I have it as this
public ViewModel()
{
Data = new ObservableCollection<Model>
{
new Model {Text = "Pink", Color = Color.DeepPink},
new Model {Text = "Crimson", Color = Color.Crimson},
new Model {Text = "Aqua", Color = Color.Aqua},
new Model {Text = "Blue", Color = Color.DeepSkyBlue},
new Model {Text = "BurlyWood", Color = Color.BurlyWood},
};
}
, maybe try clean/rebuild solution and run again?
Related
I am trying to get an image to show in my custom ViewCell, however, setting it manually doesn't work.
I am first creating a list of my custom view cells and setting the image through there. After I have all the view cells I need, I add them to a list and set that list to be the ItemSource for the listview. However; the image doesn't display even though it should through some very simplistic code. Am I missing something?
The following is the ContentPage that I am loading the view cells in.
public partial class InAppStorePage : ContentPage
{
private List<ViewCell> cells;
private Store inAppStore;
public InAppStorePage()
{
InitializeComponent();
InitializeObjects();
}
private void InitializeObjects()
{
cells = new List<ViewCell>();
inAppStore = AppStore.CurrentStore;
}
protected override void OnAppearing()
{
SetListViewTemplate();
LoadProductsIntoListView();
SetListViewItemSource();
}
private void LoadProductsIntoListView()
{
LoadPurchasedProductsIntoListView();
LoadPendingProductsIntoListView();
LoadNonPurchasedProductsIntoListView();
}
private void SetListViewTemplate()
{
InAppProductsListView.ItemTemplate = new DataTemplate(typeof(InAppStoreViewCell));
}
private void LoadPurchasedProductsIntoListView()
{
List<ViewCell> purchasedProductCells = new List<ViewCell>();
foreach (InAppProduct purchasedProduct in inAppStore.GetListOfPurchasedProducts())
{
InAppStoreViewCell purchasedProductViewCell = new InAppStoreViewCell();
//The Line in Question
purchasedProductViewCell.ProductImage.Source = purchasedProduct.GetIcon().Source;
purchasedProductCells.Add(purchasedProductViewCell);
}
cells.AddRange(purchasedProductCells);
}
private void LoadPendingProductsIntoListView()
{
List<ViewCell> pendingPurchasedProductCells = new List<ViewCell>();
foreach (InAppProduct pendingPurchaseProduct in inAppStore.GetListOfPendingPurchaseProducts())
{
InAppStoreViewCell pendingPurchaseProductCell = new InAppStoreViewCell();
//The Line in Question
pendingPurchaseProductCell.ProductImage.Source = pendingPurchaseProduct.GetIcon().Source;
pendingPurchasedProductCells.Add(pendingPurchaseProductCell);
}
cells.AddRange(pendingPurchasedProductCells);
}
private void LoadNonPurchasedProductsIntoListView()
{
List<ViewCell> nonPurchasedProductCells = new List<ViewCell>();
foreach (InAppProduct nonPurchasedProduct in inAppStore.GetListOfProductsThatHaventBeenPurchased())
{
InAppStoreViewCell nonPurchasedProductCell = new InAppStoreViewCell();
//The Line in Question
nonPurchasedProductCell.ProductImage.Source = nonPurchasedProduct.GetIcon().Source;
nonPurchasedProductCells.Add(nonPurchasedProductCell);
}
cells.AddRange(nonPurchasedProductCells);
}
private void SetListViewItemSource()
{
InAppProductsListView.ItemsSource = null;
InAppProductsListView.ItemsSource = cells;
}
}
And the following is the C# file of the custom viewcell and its accompanying xaml file
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class InAppStoreViewCell : ViewCell
{
public InAppStoreViewCell()
{
InitializeComponent();
}
public Image ProductImage
{
get
{
return CellProductIcon;
}
set
{
CellProductIcon = value;
}
}
public void SetColor(Color color)
{
ProductImage.BackgroundColor = color;
}
public Label ProductNameLabel
{
get
{
return CellProductNameLabel;
}
set
{
CellProductNameLabel = value;
}
}
public Label ProductStatusLabel
{
get
{
return CellProductStatus;
}
set
{
CellProductStatus = value;
}
}
public Label ProductPriceLabel
{
get
{
return CellProductPriceLabel;
}
set
{
CellProductPriceLabel = value;
}
}
}
The Xaml file
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Alan.Views.UIElements.InAppStoreViewCells.InAppStoreViewCell">
<ViewCell.View>
<RelativeLayout>
<RelativeLayout x:Name="TopContainer"
BackgroundColor="CornflowerBlue"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=0.75}">
<Image x:Name="CellProductIcon"
BackgroundColor="Indigo"
Aspect="Fill"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1.0}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=1.0}"/>
</RelativeLayout>
<RelativeLayout x:Name="BottomContainer"
BackgroundColor="Orange"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=1}"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=0.25}"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=0.75}">
<Label x:Name="CellProductNameLabel"/>
<Label x:Name="CellProductStatus"/>
<Label x:Name="CellProductPriceLabel"/>
</RelativeLayout>
</RelativeLayout>
</ViewCell.View>
</ViewCell>
Any help would be seriously appreciated because this super simplistic thing is driving me crazy :/
I think there's something wrong with the way you use it.
For example:
1.You didn't set the BindableProperty for your ViewCell.
2.Why do you assign cells to InAppProductsListView.ItemsSource while the type of its child element is ViewCell?
private List<ViewCell> cells;
cells = new List<ViewCell>();
InAppProductsListView.ItemsSource = cells;
We should assign our special data list to the ItemsSource of listView.
You can refer to the following sample code:
public ListPageCS()
{
Title = "BindingContextChanged Code Demo";
Padding = 10;
var customCell = new DataTemplate(typeof(CustomCell));
customCell.SetBinding(CustomCell.NameProperty, "Name");
customCell.SetBinding(CustomCell.AgeProperty, "Age");
customCell.SetBinding(CustomCell.LocationProperty, "Location");
var listView = new ListView
{
ItemTemplate = customCell
};
var button = new Button { Text = "Change Binding Context" };
button.Clicked += (sender, e) =>
{
listView.ItemsSource = Constants.People;
};
Content = new StackLayout
{
Children = {
listView,
button
}
};
}
For how to use custom ViewCell , you can check document Customizing ListView Cell Appearance.
And you can check up the sample included above link.
The sample is here: https://github.com/xamarin/xamarin-forms-samples/tree/main/UserInterface/ListView/BindingContextChanged .
I have a collectionview which has a [Label(day of the week] - [checkbox] - [picker with times].
I want the user to be able to select a day and time, after that I want to then pass those values to my database. So far I am able to select the day and pass this value on. However I am struggling to refence the value in the picker. I think this is due to it being a list in my object. I have tried booking.Listtimes but that is just a list. I want the value selected.
My ViewModel:
public class RepeatMonthly
{
public string Day { get; set; }
public bool Selected { get; set; }
public List<WalkTimes> _ListTimes { get; set; }
}
private WalkTimes _selectedTimes;
public WalkTimes SelectedTimes
{
get
{
return _selectedTimes;
}
set
{
SetProperty(ref _selectedTimes, value);
}
}
public ObservableCollection<RepeatMonthly> DayList { get; set; }
public CreateWeeklyScheduleViewModel()
{
ListTimes = WalkTimesService.GetTimes().OrderBy(c => c.Key).ToList();
DayList = new ObservableCollection<RepeatMonthly>()
{
new RepeatMonthly(){Day="Every Monday", Selected = false, _ListTimes = ListTimes},
new RepeatMonthly(){Day="Every Tuesday", Selected = false, _ListTimes = ListTimes},
new RepeatMonthly(){Day="Every WednesDay", Selected = false, _ListTimes = ListTimes},
new RepeatMonthly(){Day="Every Thursday", Selected = false, _ListTimes = ListTimes},
new RepeatMonthly(){Day="Every Friday", Selected = false, _ListTimes = ListTimes},
new RepeatMonthly(){Day="Every Saturday", Selected = false, _ListTimes = ListTimes},
new RepeatMonthly(){Day="Every Sunday", Selected = false, _ListTimes = ListTimes}
};
source = new ObservableCollection<PetProfile>();
}
My Xaml:
<CollectionView x:Name="RepeatCollectionView" HorizontalOptions="Center" ItemsSource="{Binding DayList}" HeightRequest="350">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid Padding="0" RowDefinitions="25, 20" ColumnDefinitions="*,*">
<Label Text="{Binding Day}" FontAttributes="Bold" FontSize="Medium"
Margin="5,0"
VerticalTextAlignment="Center" HorizontalTextAlignment="Start"/>
<CheckBox x:Name="SelectDayCheckBox" Grid.Row="0" HorizontalOptions="End" IsChecked="{Binding Selected, Mode=TwoWay}" BindingContext="{Binding .}" CheckedChanged="SelectDayCheckBox_CheckedChanged"/>
<Picker x:Name="SelectTimeOfWalkPicker" Title="--Select Walk Start Time--" Grid.Column="1" ItemsSource="{Binding _ListTimes}" ItemDisplayBinding="{Binding Value}" VerticalTextAlignment="Center" HorizontalTextAlignment="Center" SelectedItem="{Binding SelectedTimes}" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
my .cs page:
private async void SubmitBtn_Clicked(object sender, EventArgs e)
{
foreach (RepeatMonthly booking in (BindingContext as CreateWeeklyScheduleViewModel).DayList)
{
if (booking.Selected)
{
await passtoDataBase(booking.Day, "where time variable goes");
}
}
await DisplayAlert("Success", "Booked Successfully", "OK");
await Shell.Current.GoToAsync($"//{nameof(MyBookingsPage)}");
}
properties of WalkTimes object:
public class WalkTimes
{
public int Key { get; set; }
public string Value { get; set; }
}
I have a question here (I imagine it is for beginners: P).
I have a listview and a searchbar already working. I can make the right filter.
Then my question comes in, this listview has more than one column.
I can't think of a way to make 2 complementary filters.
For example:
Filter column 1 and then filter the result of that initial filter by column 2 (with another searchbar), that is, a filter on top of another filter.
My ListViewItem is like this with the filter:
C#
void InitList()
{
Items = new List<ListViewItem>
{
new ListViewItem { Name = "Guilherme", Bairro = "BOTAFOGO"},
new ListViewItem { Name = "João", Bairro = "FLAMENGO"},
new ListViewItem { Name = "Maria", Bairro = "CENTRO"}
}
}
void InitSearchBarBloco()
{
sb_search_bloco.TextChanged += (s, e) => FilterItem(sb_search_bloco.Text);
sb_search_bloco.SearchButtonPressed += (s, e) =>
FilterItem(sb_search_bloco.Text);
}
private void FilterItem(string filter)
{
exampleListView.BeginRefresh();
if (string.IsNullOrWhiteSpace(filter))
{
exampleListView.ItemsSource = Items;
}
else
{
exampleListView.ItemsSource = Items.Where(x => x.Name.ToLower().Contains(filter.ToLower()));
}
exampleListView.EndRefresh();
}
XAML
<SearchBar x:Name="sb_search_bloco" Placeholder="Nome..." />
<ListView x:Name="exampleListView" RowHeight="22" SelectedItem="{Binding Name}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell >
<Grid>
<Label Text="{Binding Name}" LineBreakMode="TailTruncation" />
<Label Grid.Column="1" Text="{Binding Bairro}" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
With this structure I can implement this ... "filtrate filter"?
thanks
I guess the below code should do fine for you. You just need to alter your linq code inside the FilterItem(string filter) method to achieve your requirement.
Note:
I have used OR condition inside the where clause to search if the enter text is available in both Name and Bairro. However, you can modify the condition as you require.
private void FilterItem(string filter)
{
exampleListView.BeginRefresh();
if (string.IsNullOrWhiteSpace(filter))
{
exampleListView.ItemsSource = Items;
}
else
{
//Alter the condition like below or based on requirement to achieve the desired result.
exampleListView.ItemsSource = Items.Where(x => x.Name.ToLower().Contains(filter.ToLower()) || x.Bairro.ToLower().Contains(filter.ToLower()));
}
exampleListView.EndRefresh();
}
I make a sample code for your referece.
xmal:
<StackLayout>
<StackLayout>
<SearchBar x:Name="sb_search_bloco" Placeholder="Nome..." TextChanged="sb_search_bloco_TextChanged"/>
<SearchBar x:Name="searchBar2" Margin="0,10" TextChanged="searchBar2_TextChanged" />
</StackLayout>
<ListView
x:Name="exampleListView"
RowHeight="22"
ItemsSource="{Binding Items}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Label LineBreakMode="TailTruncation" Text="{Binding Name}" />
<Label Grid.Column="1" Text="{Binding Bairro}" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
ListViewItem.cs
public class ListViewItem
{
public string Name { get; set; }
public string Bairro { get; set; }
}
MainPage.xml.cs
public partial class MainPage : ContentPage
{
public List<ListViewItem> Items { get; set; }
public MainPage()
{
InitializeComponent();
Items = new List<ListViewItem>
{
new ListViewItem { Name = "AAAA", Bairro = "BBCFS"},
new ListViewItem { Name = "ABBB", Bairro = "SSDCA"},
new ListViewItem { Name = "AAAA", Bairro = "AAAD"},
new ListViewItem { Name = "CCCC", Bairro = "SSS"},
new ListViewItem { Name = "DAAB", Bairro = "CCC"},
new ListViewItem { Name = "DDDC", Bairro = "QWDAS"}
};
this.BindingContext = this;
}
private void sb_search_bloco_TextChanged(object sender, TextChangedEventArgs e)
{
exampleListView.ItemsSource = FilterItem1(e.NewTextValue);
}
IEnumerable<ListViewItem> FilterItem1(string filter = null)
{
if (string.IsNullOrEmpty(filter))
return Items;
return Items.Where(p => p.Name.StartsWith(filter));
}
private void searchBar2_TextChanged(object sender, TextChangedEventArgs e)
{
exampleListView.ItemsSource = FilterItem2(e.NewTextValue);
}
IEnumerable<ListViewItem> FilterItem2(string filter = null)
{
if (string.IsNullOrEmpty(filter))
return Items;
return Items.Where(p => p.Bairro.StartsWith(filter));
}
}
I am working on an Android app with Xamarin, using Telerik UI.
The following error is raised when trying to bind a property to a Telerik ListViewTextCell in a RadListView:
[0:] Binding: 'Author' property not found on 'Book', target property: 'Telerik.XamarinForms.DataControls.ListView.ListViewTextCell.Detail'
This happens in even the most minimal cases. Below is an example, drawn largely from the ListView documentation itself.
PageTest.cs:
using System.Collections.Generic;
using System.ComponentModel;
using Xamarin.Forms;
using Telerik.XamarinForms.DataControls;
using Telerik.XamarinForms.DataControls.ListView;
namespace MyTelerikApp
{
[DesignTimeVisible(false)]
public partial class PageTest : ContentPage
{
public PageTest()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
var listView = new RadListView
{
ItemsSource = new ViewModel().Source,
ItemTemplate = new DataTemplate(() =>
{
var cell = new ListViewTextCell
{
TextColor = Color.Black,
DetailColor = Color.Gray,
};
cell.SetBinding(ListViewTextCell.TextProperty, new Binding(nameof(Book.Title)));
cell.SetBinding(ListViewTextCell.DetailProperty, new Binding(nameof(Book.Author)));
return cell;
}),
LayoutDefinition = new ListViewLinearLayout { ItemLength = 70 }
};
MainPageContent.Children.Add(listView);
}
}
}
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
}
public class ViewModel
{
public ViewModel()
{
this.Source = new List<Book>{
new Book{ Title = "The Fault in Our Stars ", Author = "John Green"},
new Book{ Title = "Divergent", Author = "Veronica Roth"},
new Book{ Title = "Gone Girl", Author = "Gillian Flynn"},
new Book{ Title = "Clockwork Angel", Author = "Cassandra Clare"},
new Book{ Title = "The Martian", Author = "Andy Weir"},
new Book{ Title = "Ready Player One", Author = "Ernest Cline"},
new Book{ Title = "The Lost Hero", Author = "Rick Riordan"},
new Book{ Title = "All the Light We Cannot See", Author = "Anthony Doerr"},
new Book{ Title = "Cinder", Author = "Marissa Meyer"},
new Book{ Title = "Me Before You", Author = "Jojo Moyes"},
new Book{ Title = "The Night Circus", Author = "Erin Morgenstern"},
};
}
public List<Book> Source { get; set; }
}
PageText.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="GeoGIS.views.PageTest">
<StackLayout x:Name="MainPageContent">
</StackLayout>
</ContentPage>
After some searching, it seems that a BindingContext is necessary, but I couldn't get that to work either.
I didn't found BindingContext from your code.And I guess you confused the two usages of ContentPage(XAML and C# ).
When we created a contentpage,we have two choices(XAML and C#) as follows:
1.When we choose the ContentPage(c#),in this case, there is no xaml.And we can do like this:
public class TestPage1 : ContentPage
{
public TestPage1 ()
{
var listView = new RadListView
{
BackgroundColor = Color.White,
ItemsSource = new ViewModel().Source,
ItemTemplate = new DataTemplate(() =>
{
var cell = new ListViewTextCell
{
TextColor = Color.Black,
DetailColor = Color.Gray,
};
cell.SetBinding(ListViewTextCell.TextProperty, new Binding(nameof(Book.Title)));
cell.SetBinding(ListViewTextCell.DetailProperty, new Binding(nameof(Book.Author)));
return cell;
}),
LayoutDefinition = new ListViewLinearLayout { ItemLength = 70 },
};
Content = new StackLayout {
Children = {
listView
}
};
}
}
2.When we choose the ContentPage,in this case, code has xaml.We can do like this.
Put the followinging code in your xaml
<StackLayout>
<telerikDataControls:RadListView ItemsSource="{Binding Source}" BackgroundColor="White" x:Name="listView">
<telerikDataControls:RadListView.BindingContext>
<local:ViewModel />
</telerikDataControls:RadListView.BindingContext>
<telerikDataControls:RadListView.ItemTemplate>
<DataTemplate>
<telerikListView:ListViewTextCell Text="{Binding Title}" Detail="{Binding Author}" TextColor="Black" DetailColor="Gray" />
</DataTemplate>
</telerikDataControls:RadListView.ItemTemplate>
<telerikDataControls:RadListView.LayoutDefinition>
<telerikListView:ListViewLinearLayout ItemLength="70" />
</telerikDataControls:RadListView.LayoutDefinition>
</telerikDataControls:RadListView>
</StackLayout>
And remove the method OnAppearing() from your code.
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
//BindingContext = new ViewModel();
}
protected override void OnAppearing()
{
base.OnAppearing();
}
}
From above code,we can found the BindingContext,it is necessary.
<telerikDataControls:RadListView.BindingContext>
<local:ViewModel />
</telerikDataControls:RadListView.BindingContext>
And we can also BindingContext like this(Any one is ok.):
BindingContext = new ViewModel();
The result is the same:
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 -
The response received from Firebase is serialized and then a custom calendar is created based on the response.
To create the calendar, I am using a Model which will contain all the necessary bindings to be displayed in the View. e.g Color, title, counts etc.
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; }
}
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);
}
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);
}