Xamarin CollectionView Observable Collection not updating with Searchbar - xamarin

My Accounts CollectionView is not updating with the Searchbar. Xaml 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" xmlns:viewmodels="clr-namespace:Pricing051721.ViewModels"
x:Class="Pricing051721.MainPage" Title="KR Pricing"
x:Name="This">
<ContentPage.BindingContext>
<viewmodels:MainPageViewModel/>
</ContentPage.BindingContext>
<StackLayout>
<Button Text="Logout" Command="{Binding LogoutCommand}" Margin="0,5,0,5"/>
<SearchBar x:Name="searchBar"
SearchCommand="{Binding PerformSearch}"
SearchCommandParameter="{Binding Text, Source={x:Reference searchBar}}"/>
<CollectionView ItemsSource="{Binding Accounts}" Margin="5">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Margin="5" >
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.AccountSelected, Source={x:Reference This}}" CommandParameter="{Binding .}"/>
</StackLayout.GestureRecognizers>
<StackLayout >
<Label FontSize="Medium" Text="{Binding Name}" ></Label>
<Label Text="{Binding Address}"></Label>
</StackLayout>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage>
I am trying to search through the Accounts already queried in the view model so I don't have to hit the database again. The search works, but Accounts is not updated.
namespace Pricing051721.ViewModels
{
public class MainPageViewModel : INotifyPropertyChanged
{
public ObservableCollection<Account> Accounts { get; set; }
public INavigation Navigation { get; set; }
public ICommand LogoutCommand { get; set; }
AdAuthenticationService authService;
public ObservableCollection<Account> baseAccountList;
public MainPageViewModel()
{
Accounts = new ObservableCollection<Account> { new Account { AllowUpdate = true, Address = "Wait", Name = "Loading" } };
authService = new AdAuthenticationService();
Task.Run(async () =>
{
if (!authService.IsAuthenticated)
{
var response = authService.Authenticate();
await Update(response.AccessToken, "");
}
else await Update(authService.AccessToken, "");
});
AccountSelected = new Command<Account>(async (a) =>
{
if (!a.AllowUpdate)
return;
await Navigation.PushAsync(new UpdateAccountView(a));
return;
var result = await UserDialogs.Instance.PromptAsync(new PromptConfig
{
InputType = InputType.Name,
OkText = "Change",
Title = "Enter New Column Break",
Text = a.ColumnBreak
});
if (result.Ok && result.Text != null && !result.Text.Trim().Equals(""))
{
a.ColumnBreak = result.Text;
isUpdating = true;
var ok = await crm.Update(a);
var message = ok ? "Account Updated!" : "Unable to update!";
await UserDialogs.Instance.AlertAsync(new AlertConfig
{
Title = "Message",
Message = message,
OkText = "Ok"
});
isUpdating = false;
}
}, _ => !isUpdating);
LogoutCommand = new Command(new Action(() => {
authService.Logout();
Environment.Exit(Environment.ExitCode);
}));
}
//search
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ICommand PerformSearch => new Command<string>((string query) =>
{
Accounts = SearchAccounts(query);
});
private bool isUpdating = false;
private Crm crm;
public ObservableCollection<Account> accounts;
public async Task Update(string accessToken, string query)
{
Crm.Setup(accessToken);
crm = Crm.AuthenticatedCrmService;
var accounts = await crm.GetAccounts();
Accounts.RemoveAt(0);
accounts.ForEach(a => Accounts.Add(a));
}
public ObservableCollection<Account> SearchAccounts(string query)
{
Task.Run(async () =>
{
if (!authService.IsAuthenticated)
{
var response = authService.Authenticate();
await Update(response.AccessToken, "");
}
else await Update(authService.AccessToken, "");
});
baseAccountList = Accounts;
if (!(query == ""))
{
var normalizedQuery = query?.ToLower() ?? "";
List<Account> accountsList = (List<Account>)Accounts.Where(f => f.Name.ToLowerInvariant().Contains(normalizedQuery)).ToList();
ObservableCollection<Account> accounts = new ObservableCollection<Account>(accountsList);
Accounts.Clear();
return accounts;
}
else
{
accounts = Accounts;
return accounts;
}
}
public ICommand AccountSelected { get; set; }
}
}
I don't need a neat solution (as you can tell by my code so far), just something that will work Thanks in advance!

My Accounts CollectionView is not updating with the Searchbar
From your code, you don't post some code about PerformSearch command, I don't know how do you search data by searchbar. I do one sample about search some data by searchbar, display in collectionview, you can modify your code according to the following code.
<SearchBar
x:Name="searchBar"
SearchCommand="{Binding PerformSearch}"
SearchCommandParameter="{Binding Text, Source={x:Reference searchBar}}" />
<CollectionView Margin="5" ItemsSource="{Binding Accounts}">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Margin="5">
<!--<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.AccountSelected, Source={x:Reference This}}" CommandParameter="{Binding .}" />
</StackLayout.GestureRecognizers>-->
<StackLayout>
<Label FontSize="Medium" Text="{Binding Name}" />
<Label Text="{Binding Address}" />
</StackLayout>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
public partial class Page15 : ContentPage
{
public Page15()
{
InitializeComponent();
this.BindingContext = new AccountViewModel();
}
}
public class AccountViewModel
{
public ObservableCollection<Account> AccountList { get; set; }
public ObservableCollection<Account> Accounts { get; set; }
public ICommand PerformSearch { get; set; }
public AccountViewModel()
{
AccountList = new ObservableCollection<Account>();
Accounts = new ObservableCollection<Account>();
for(int i=0;i<30;i++)
{
Account a = new Account();
a.Name = "account" + i;
a.Address = "address " + i;
AccountList.Add(a);
Accounts.Add(a);
}
PerformSearch = new Command(search => {
if(search!=null)
{
string searchtext = (string)search;
if (!string.IsNullOrEmpty(searchtext))
{
Accounts.Clear();
List<Account> list= AccountList.Where((account) => account.Name.ToLower().Contains(searchtext) || account.Address.ToLower().Contains(searchtext)).ToList();
foreach(Account a in list)
{
Accounts.Add(a);
}
}
Accounts = AccountList;
}
else
{
Accounts = AccountList;
}
});
}
}
public class Account
{
public string Name { get; set; }
public string Address { get; set; }
}

Related

Xamarin Forms CollectionView is empty

I have a Xamarin Forms CollectionView, contained within a RefreshView. The binding source is populated but its not showing anything in the CollectionView. I know the binding source is populated as I show a count in the toolbar. Can anyone spot why the list is empty?
Here is my Content Page with my RefreshView and CollectionView:
<ContentPage.ToolbarItems>
<ToolbarItem Text="Add" Command="{Binding AddDogCommand}" />
<ToolbarItem Text="{Binding Dogs.Count}"></ToolbarItem>
</ContentPage.ToolbarItems>
<RefreshView x:DataType="local:MyDogsViewModel" Command="{Binding LoadDogsCommand}" IsRefreshing="{Binding IsBusy, Mode=TwoWay}">
<CollectionView ItemsLayout="HorizontalList" ItemsSource="{Binding Dogs}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid WidthRequest="100">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label Grid.Column="1"
Text="{Binding DogName}"
FontAttributes="Bold" TextColor="Red"/>
<Label Grid.Row="1"
Grid.Column="1"
Text="{Binding Nickname}"
FontAttributes="Italic" TextColor="Green"
VerticalOptions="End" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</RefreshView>
Here is my ViewModel
public class MyDogsViewModel : BaseViewModel
{
private DogModel _selectedItem;
private DogService dogService = new DogService();
private string _dogName;
private string _nickname;
public ObservableCollection<DogModel> Dogs { get; }
public Command LoadDogsCommand { get; }
public Command<DogModel> ItemTapped { get; }
public Command AddDogCommand { get; }
public Command SaveDogCommand { get; }
public string DogName
{
get => _dogName;
set => SetProperty(ref _dogName, value);
}
public string Nickname
{
get => _nickname;
set => SetProperty(ref _nickname, value);
}
public MyDogsViewModel()
{
Title = "My Dogs";
Dogs = new ObservableCollection<DogModel>();
LoadDogsCommand = new Command(async () => await ExecuteLoadDogsCommand());
ItemTapped = new Command<DogModel>(OnItemSelected);
AddDogCommand = new Command(OnAddDog);
SaveDogCommand = new Command(OnSaveDog);
}
async Task ExecuteLoadDogsCommand()
{
IsBusy = true;
try
{
Dogs.Clear();
var dogs = await dogService.GetDogsAsync();
foreach (var d in dogs)
{
Dogs.Add(d);
}
}
catch (Exception ex)
{
Debug.WriteLine("Exception: " + ex);
}
finally
{
IsBusy = false;
}
}
public void OnAppearing()
{
IsBusy = true;
}
private async void OnAddDog(object obj)
{
await Shell.Current.GoToAsync(nameof(AddDogPage));
}
private async void OnSaveDog(object obj)
{
AddDogModel model = new AddDogModel
{
DogName = DogName,
Nickname = Nickname
};
await dogService.AddDog(model);
await Shell.Current.GoToAsync("..");
}
async void OnItemSelected(DogModel dog)
{
if (dog == null)
return;
}
}
DogModel class
public class DogModel
{
public int Id { get; set; }
public string DogName { get; set; }
public string Nickname { get; set; }
}
keep your xaml the same and i will edit your ViewModel and take out the properties that is causing you the issue ;
public class MyDogsViewModel : BaseViewModel
{
private DogModel _selectedItem;
private DogService dogService = new DogService();
private string _dogName;
private string _nickname;
private ObservableCollection<DogModel> dogs;
public ObservableCollection<DogModel> Dogs
{
get{return dogs;}
set{dogs=value;}
}
public Command LoadDogsCommand { get; set; }
public Command<DogModel> ItemTapped { get; set;}
public Command AddDogCommand { get; set;}
public Command SaveDogCommand { get; set;}
public MyDogsViewModel()
{
Title = "My Dogs";
Dogs = new ObservableCollection<DogModel>();
LoadDogsCommand = new Command(async () => await ExecuteLoadDogsCommand());
ItemTapped = new Command<DogModel>(OnItemSelected);
AddDogCommand = new Command(OnAddDog);
SaveDogCommand = new Command<object>(OnSaveDog);
}
async Task ExecuteLoadDogsCommand()
{
IsBusy = true;
try
{
Dogs.Clear();
var dogs = await dogService.GetDogsAsync();
foreach (var d in dogs)
{
Dogs.Add(d);
}
}
catch (Exception ex)
{
Debug.WriteLine("Exception: " + ex);
}
finally
{
IsBusy = false;
}
}
public void OnAppearing()
{
IsBusy = true;
}
private async void OnAddDog(object obj)
{
await Shell.Current.GoToAsync(nameof(AddDogPage));
}
private async void OnSaveDog(object obj)
{
DogModel newdog = obj as DogModel;
await dogService.AddDog(newdog);
await Shell.Current.GoToAsync("..");
}
async void OnItemSelected(DogModel dog)
{
if (dog == null)
return;
}
}
dont forget to bind to the viewmodel. in the xaml.cs add this in the constuctor.
this.BindingContext = new MyDogsViewModel();

How to mark the checkbox in repeater in Xamarin.Forms?

I am using checkbox control under repeater to do a radio button functionality, everything seems to be fine but now stuck on how to bind the checkbox when the page loads. I have saved the radio button text whichever was selected and once user come back to page again I want to bin what he has selected last time. Not getting any hint here how to proceed.
<grial:Repeater
x:Name="PP"
SelectionMode="Single"
InitialSelection="Empty"
ItemSize="100"
HorizontalOptions="Start"
ItemsSource="{Binding BlowerPostions}">
<grial:Repeater.ItemTemplate>
<DataTemplate>
<grial:Checkbox
IsChecked="false"
UncheckedBorderColor="Black">
<Label
TextColor="Black"
Text="{ Binding . }"
Margin="8,0" />
</grial:Checkbox>
</DataTemplate>
</grial:Repeater.ItemTemplate>
<grial:Repeater.SelectedItemTemplate>
<DataTemplate>
<grial:Checkbox
IsChecked="true"
UncheckedBorderColor="Black"
InputTransparent="true">
<Label
TextColor="Black"
Text="{ Binding . }"
Margin="8,0" />
</grial:Checkbox>
</DataTemplate>
</grial:Repeater.SelectedItemTemplate>
</grial:Repeater>
View Model :
public class ProductionViewModel : INotifyPropertyChanged
{
public ObservableCollection<BlowerPostion> _blowerPostions;
public ObservableCollection<BlowerPostion> BlowerPostions
{
get => _blowerPostions;
set
{
_blowerPostions = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new
PropertyChangedEventArgs("BlowerPostions"));
}
}
}
public void LoadData()
{
BlowerPostions = new ObservableCollection<BlowerPostion>();
BlowerPostions.Add(new BlowerPostion("Left", 1));
BlowerPostions.Add(new BlowerPostion("Standard", 1));
}
}
public class BlowerPostion
{
public string Text { get; set; }
public int Id { get; set; }
public BlowerPostion(string _text, int _id)
{
Text = _text;
Id = _id;
}
}
I don't use grial:Repeater,but you can refer to the following code which use CheckBox in ListView item.
Item.cs
public class Item
{
public string Name { get; set; }
public string Type { get; set; }
public string Image { get; set; }
//This field indicates whether or not it is selected
public bool isChecked { get; set; }
}
MyViewModel.cs
public class MyViewModel
{
public ObservableCollection<Item> items { get; private set; }
public MyViewModel() {
items = new ObservableCollection<Item>();
items.Add(new Item { Name = "Tomato", Type = "Fruit", Image = "tomato.png", isChecked = true });
items.Add(new Item { Name = "Romaine Lettuce", Type = "Vegetable", Image = "lettuce.png", isChecked = false });
items.Add(new Item { Name = "Zucchini", Type = "Vegetable", Image = "zucchini.png", isChecked = false });
}
}
TestPage1.xaml
<ContentPage.Content>
<ListView x:Name="listview" ItemsSource="{Binding items}" VerticalOptions="FillAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" Padding="5,0,5,0">
<Label Text="{Binding Name}" HorizontalOptions="StartAndExpand" FontSize="30"/>
<input:CheckBox IsChecked="{Binding isChecked}" Type="Check" Color="White" BoxBackgroundColor="Green" TextColor="White" HeightRequest="40"
CheckChanged="CheckBox_CheckChanged" BindingContext="{Binding .}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
TestPage1.xaml.cs
public partial class TestPage1 : ContentPage
{
public List<Item> selectedItems; // define `selectedItems` as the list of selected items.
public MyViewModel viewModel;
public TestPage1 ()
{
InitializeComponent ();
selectedItems = new List<Item>(); // init the `selectedItems`
viewModel = new MyViewModel();
BindingContext = viewModel;
}
private void CheckBox_CheckChanged(object sender, EventArgs e)
{
var checkbox = (Plugin.InputKit.Shared.Controls.CheckBox)sender;
var ob = checkbox.BindingContext as Item;
if (ob != null)
{
System.Diagnostics.Debug.WriteLine("isChecked = " + ob.isChecked + "<---> Name = " + ob.Name +"<---> Type = " + ob.Type );
if (ob.isChecked)
{
selectedItems.Add(ob);
}
else {
// remove the item
}
}
}
}
Note:
1.add new field isChecked in item model
public bool isChecked { get; set; }
2.Add event CheckChanged for the item.And when we check the CheckBox,we can get the corresponding value isChecked of the CheckBox.
<input:CheckBox IsChecked="{Binding isChecked}" Type="Check" Color="White" BoxBackgroundColor="Green" TextColor="White" HeightRequest="40"
CheckChanged="CheckBox_CheckChanged" BindingContext="{Binding .}" />

Why doesn't CollectionView highlight SelectedItems within RefreshView

I'm getting two collections of data from a web service - the full collection and then a subset of items. The subset should be selected on load. I cannot get the SelectedItems to show as selected in the CollectionView, event though that property is set. I've looked over the Monkeys example and all over the web but can't find an example just like this
View:
<RefreshView IsRefreshing="{Binding IsBusy, Mode=TwoWay}" Command="{Binding LoadItemsCommand}">
<CollectionView x:Name="ExperiencesCollectionView"
ItemsSource="{Binding Experiences}"
SelectedItems="{Binding ProfileExperiences, Mode=TwoWay}"
SelectionMode="Multiple">
<CollectionView.ItemTemplate>
<DataTemplate>
<Label
Text="{Binding Description}"/>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</RefreshView>
</StackLayout>
Code Behind:
public partial class ExperiencesPage : ContentPage
{
ExperiencesViewModel viewModel;
public ExperiencesPage()
{
InitializeComponent();
BindingContext = viewModel = new ExperiencesViewModel();
}
protected override void OnAppearing()
{
base.OnAppearing();
if (viewModel.Experiences.Count == 0)
viewModel.IsBusy = true;
}
ViewModel:
public class ExperiencesViewModel : BaseViewModel
{
public ObservableCollection<Experience> Experiences { get; set; }
public ObservableCollection<object> ProfileExperiences { get; set; }
public Command LoadItemsCommand { get; set; }
public string InterestedIn { get; set; }
public ExperiencesViewModel()
{
Title = "My Experiences";
Experiences = new ObservableCollection<Experience>();
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
}
async Task ExecuteLoadItemsCommand()
{
IsBusy = true;
try
{
Experiences.Clear();
//Get these from the REST service
var experiences = await App.ExperienceManager.GetExperiencesAsync();
var serviceExperiences = await App.ExperienceManager.GetProfileExperiencesAsync();
foreach (var exp in experiences)
{
Experiences.Add(exp);
}
foreach (var profileExp in serviceExperiences)
{
ProfileExperiences.Add(profileExp);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}

Xamarin.Forms block UI when ItemsSource load a big data

I do not load a small data from the API, in C# code, they are loaded in advance and everything seems to be fine, but as soon as I open the page where ItemsSource = "{Binding BigData}", my UI is blocked for 10 seconds.
Are there any ideas to open the page first, then start loading data without blocking the UI?
I would to suggest you can kick off a task in your view models constructor that loads the data. Using Async and await to load bid data.
I do one sample that using ListView to display 100000 records.
<StackLayout>
<Label Text="test ui in xamarin.forms asyn" />
<ActivityIndicator IsRunning="{Binding isBusy}" IsVisible="{Binding isBusy}" />
<ListView x:Name="listview1" ItemsSource="{Binding Items}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding name}" />
<Label HorizontalOptions="CenterAndExpand" Text="{Binding age}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
public partial class Page19 : ContentPage
{
public Page19()
{
InitializeComponent();
this.BindingContext = new ItemsViewModel();
}
}
public class ItemsViewModel:ViewModelBase
{
private bool _isBusy;
public bool isBusy
{
get { return _isBusy; }
set
{
_isBusy = value;
RaisePropertyChanged("isBusy");
}
}
public ObservableCollection<people> Items { get; set; }
public ItemsViewModel()
{
Items = new ObservableCollection<people>();
isBusy = true;
Task.Run(async () => await LoadItems());
}
public async Task LoadItems()
{
var items = new ObservableCollection<people>(); // new collection
if (isBusy)
{
await Task.Delay(10000);
// var loadedItems = ItemsService.LoadItemsDirectory();
//foreach (var item in loadedItems)
// items.Add(item);
for (int i = 0; i < 100000; i++)
{
people p = new people();
p.name = "people " + i;
p.age = i;
items.Add(p); // items are added to the new collection
}
Items = items; // swap the collection for the new one
RaisePropertyChanged(nameof(Items)); // raise a property change in whatever way is right for your VM
isBusy = false;
}
}
}
public class people
{
public string name { get; set; }
public int age { get; set; }
}
ViewModelBase is one class that implementing INotifyPropertyChanged
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

How to move this Selected Item Changed Events from Pages into ViewModel?

I have this code for handling Item Selected on ListView and once selected will redirect to other page with an Id.
I can do this in Pages level BUT I want to move this to ViewModel. How do I do this?
ActivitiesPage.xaml:
<ContentPage.ToolbarItems>
<ToolbarItem x:Name="TbSearch" Icon="search.png" Command="{Binding SearchBtnClicked}"></ToolbarItem>
<ToolbarItem x:Name="TbAdd" Icon="add.png" Command="{Binding AddBtnClicked}"></ToolbarItem>
</ContentPage.ToolbarItems>
<ContentPage.Content>
<!--<ListView ItemsSource="{Binding Items}" CachingStrategy="RecycleElement" ItemSelected="LvActivities_ItemSelected">-->
<ListView ItemsSource="{Binding Items}" CachingStrategy="RecycleElement" SelectedItem="{Binding NameSelectedItem}">
<ListView.Behaviors>
<extended:InfiniteScrollBehavior IsLoadingMore="{Binding IsBusy}" />
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Footer>
<Grid Padding="5" IsVisible="{Binding IsBusy}">
<!-- Footer HeightRequest must be 0 when not visible -->
<Grid.Triggers>
<Trigger TargetType="Grid" Property="IsVisible" Value="False">
<Setter Property="HeightRequest" Value="0" />
</Trigger>
</Grid.Triggers>
<Label Text="Loading..." TextColor="Crimson" FontSize="Large"
VerticalOptions="Center" HorizontalOptions="Center" />
</Grid>
</ListView.Footer>
</ListView>
</ContentPage.Content>
ActivitiesPage.xaml.cs
public partial class ActivitiesPage : ContentPage
{
public ObservableCollection<Activity> Activities;
public ActivitiesPage ()
{
InitializeComponent();
BindingContext = new ActivityViewModel(Navigation);
}
private void LvActivities_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var selectedActivity = e.SelectedItem as Activity;
Navigation.PushAsync(new ActivityDetailPage(selectedActivity.Id));
}
}
ActivityViewModel.cs
public class ActivityViewModel : BaseViewModel
{
private const int PageSize = 10;
private int totalRecords;
private readonly ApiService apiService = new ApiService();
public InfiniteScrollCollection<Activity> Items { get; }
public INavigation Navigation { get; set; }
private bool _isBusy;
public bool IsBusy
{
get => _isBusy;
set
{
_isBusy = value;
OnPropertyChanged();
}
}
public ICommand SearchBtnClicked
{
protected set;
get;
}
public ICommand AddBtnClicked
{
protected set;
get;
}
public ActivityViewModel(INavigation navigation)
{
this.Navigation = navigation;
Items = new InfiniteScrollCollection<Activity>
{
OnLoadMore = async () =>
{
IsBusy = true;
int page = Items.Count / PageSize;
ActivityResult activityResult = await apiService.GetActivities("-CreatedDate", page+1, PageSize);
IsBusy = false;
return activityResult.Results;
},
OnCanLoadMore = () =>
{
return Items.Count < totalRecords;
}
};
DownloadDataAsync();
this.SearchBtnClicked = new Command(async () => await GotoSearchPage());
this.AddBtnClicked = new Command(async () => await GotoAddPage());
}
public async Task GotoSearchPage()
{
await Navigation.PushAsync(new ActivitySearchPage());
}
public async Task GotoAddPage()
{
await Navigation.PushAsync(new ActivityAddPage());
}
private async Task DownloadDataAsync()
{
ActivityResult items = await apiService.GetActivities("-CreatedDate", 1, PageSize);
totalRecords = items.Metadata.TotalRecords;
Items.AddRange(items.Results);
}
Create a SelectedItem property in the view model
private Activity selectedActivity = null;
public Activity SelectedItem {
get { return selectedActivity; }
set {
selectedActivity = value;
NotifyPropertyChanged(); //assumption here
if(selectedActivity != null) {
Navigation.PushAsync(new ActivityDetailPage(selectedActivity.Id));
}
}
}
that can be bound to the list view
<ListView ItemsSource="{Binding Items}"
CachingStrategy="RecycleElement"
SelectedItem="{Binding SelectedItem}">
<!-- ...omitted for brevity -->
</ListView>
This will allow the event handler to be removed from the View's code behind.

Resources