Issue
I am having a ListView
in which items are added incrementally using ItemAppearing
. I want it to implement it through my ViewModel. ItemAppearing
only calls a method from View.cs hence, is there any way I could implement it in my ViewModel class.
Please note that I can load incrementally items when adding it from View.cs. I just want to load more items from ViewModel.
Here is my XAML code:
<ListView ItemsSource="{Binding JobsList}" HasUnevenRows="True"
SelectedItem="{Binding SelectedJob}" ItemAppearing="LoadMoreItems">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding Title}" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Solution
This answer will be moderately lengthy, but you will not find it anywhere else. I have tried for 2 days.
This answer is lengthy because I have demonstrated 3 things:
- Binding
ItemAppearing
toCommand
and then incrementally loading items. - Selecting an Item from
ListView
and displaying it. - Showing animation while loading new items incrementally.
In MVVM, ViewModel is supposed to be ignorant of the View. Hence ViewModel must not know if the ListView
inside the View is scrolled to the last item or whether LoadMoreItems should be called on scrolling.
We need to convert ItemAppearing
Event to Command
and Bind this Command to the ItemAppearing
event.
For this purpose we need to install Xamarin.CommunityToolkit Nuget Package. This package is supported by .NetFoundation, Xamarin Community and Microsoft, and is authored by Microsoft. This is the official package and is necessary for most of the advanced Xamarin.Forms. Check more on Nuget.org, Download latest stable release: https://www.nuget.org/packages/Xamarin.CommunityToolkit (Install in all your projects Shared, Android, iOS, UWP, WPF, Tizen, etc)
Assume your Model:
public class Job
{
public int Id { get; set; }
public string Title { get; set; }
}
Now in your XAML
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
...
xmlns:xct="http://xamarin.com/schemas/2020/toolkit">
<StackLayout>
<RefreshView IsRefreshing="{Binding IsLoading}">
<ListView ItemsSource="{Binding JobsList}" SelectedItem="{Binding SelectedJob}">
<ListView.Behaviors>
<xct:EventToCommandBehavior EventName="ItemAppearing"
Command="{Binding LoadMoreItemsCommand}"
CommandParameter="{Binding ItemVisibilityEventArgs}"/>
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding Id}" />
<Label Text="{Binding Title}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
In your code behind set BindingContext to instance of the ViewModel
public partial class ListPage : ContentPage
{
ListPageViewModel ListPageVM;
public JobsListPage()
{
InitializeComponent();
ListPageVM = new ListPageViewModel();
BindingContext = ListPageVM;
}
}
In Your ViewModel
public class ListPageViewModel : INotifyPropertyChanged
{
public ObservableCollection<Models.Job> JobsList
{
get { return jobsList; }
set { jobsList = value; OnPropertyChanged(nameof(JobsList)); }
}
public ICommand LoadMoreItemsCommand { get; private set; }
// Used to show Loading Animation in Refresh View
public bool IsLoading
{
get { return isLoading; }
set { isLoading = value; OnPropertyChanged(nameof(IsLoading)); }
}
public Models.Job SelectedJob
{
get { return selectedJob; }
set
{
if (value != null)
{
selectedJob = value;
var page = Application.Current.MainPage;
page.DisplayAlert("Alert", $"Selected: {selectedJob.JobTitle}", "OK");
OnPropertyChanged(nameof(SelectedJob));
}
}
}
ObservableCollection<Models.Job> jobsList;
Models.Job selectedJob;
bool isLoading;
public ListPageViewModel() // ViewModel Constructor
{
// Initialize your List
JobsList = new ObservableCollection<Models.Job>
{
new Models.Job() { Id = 0001, Title = "Product Manager" },
new Models.Job() { Id = 0002, Title = "Senior Executive" },
}
LoadMoreItemsCommand = new Command<ItemVisibilityEventArgs>(
execute: async (ItemVisibilityEventArgs args) =>
{
if ((args.Item as Models.Job).Id >= JobsList[JobsList.Count - 1].Id)
{
IsLoading = true;
for (int i = 0; i < 10; i++)
{
JobsList.Add(new Models.Job()
{
Id = JobsList.Count + 1, JobTitle = JobsList[i].Title
});
}
await System.Threading.Tasks.Task.Delay(2000); // Fake delay
IsLoading = false;
}
});
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Answered By - Juned Khan Momin
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.