I'm looking to have a Select All checkbox that will update all the other checkboxes in the listview when it's selected or deselected but I can't find a way to make them update. I've tried a foreach statement, as well as a for statement in the ViewModel and to run the task when the Select All checkbox is changed, but they don't seem to update the UI. Any help is appreaciated!
view model:
public class SyncViewModel : BaseViewModel
{
public ObservableCollection<Company> CompaniesCollection { get; set; }
public ObservableCollection<WellGroup> WellGroupCollection { get; set; }
public ObservableCollection<Well> WellsCollection { get; set; }
public SyncViewModel()
{
Title = "Sync";
CompaniesCollection = new ObservableCollection<Company>();
WellGroupCollection = new ObservableCollection<WellGroup>();
WellsCollection = new ObservableCollection<Well>();
}
public async Task InitializeData()
{
var wellDataStore = new WellDataStore();
var companies = await wellDataStore.GetAllGroups();
if (companies != null)
{
CompaniesCollection.Clear();
foreach (var company in companies)
{
CompaniesCollection.Add(company);
}
}
}
public async Task SyncData()
{
IsBusy = true;
// load and process data
IsBusy = false;
}
}
}
xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:viewModel="clr-namespace:SiteVisits.ViewModels" xmlns:model="clr-namespace:SiteVisits.Models"
x:DataType="viewModel:SyncViewModel"
x:Class="SiteVisits.Views.Sync">
<ContentPage.ToolbarItems>
<ToolbarItem Text="Sync" Clicked="Sync_Clicked" />
</ContentPage.ToolbarItems>
<StackLayout>
<ActivityIndicator
IsVisible="{Binding IsBusy}"
IsRunning="{Binding IsBusy}" />
<CheckBox x:Name="SelectAll" Color="Blue" CheckedChanged="CheckAll" />
<Label Text="Select All" FontSize="Large" VerticalOptions="Center"/>
<ListView x:Name="Companies"
ItemsSource="{Binding CompaniesCollection}"
SelectionMode="Single"
HasUnevenRows="True"
ItemTapped="Companies_Selection">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Padding="10" x:DataType="model:Company">
<CheckBox x:Name="Name" Color="Blue" IsChecked="{Binding IsChecked}" />
<Label Text="{Binding Name}"
FontSize="Large"
VerticalOptions="Center"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
xaml.cs:
SyncViewModel viewModel;
public Sync(SyncViewModel viewModel)
{
InitializeComponent();
this.viewModel = viewModel;
BindingContext = this.viewModel;
}
protected override async void OnAppearing()
{
await viewModel.InitializeData();
}
async private void Sync_Clicked(object sender, EventArgs e)
{
await viewModel.SyncData();
}
private void Companies_Selection(object sender, ItemTappedEventArgs e)
{
if (e.Item != null)
{
var company = e.Item as Company;
company.IsChecked = !company.IsChecked;
}
}
Since you are using MVVM, I would use a binding for the checkbox
<CheckBox x:Name="SelectAll" Color="Blue" IsChecked="{Binding AllChecked}" />
So then you will need this bool property, something like this. So when the value changes, it updates all the collection
private bool _allChecked;
public bool AllChecked { get => _allChecked;
set
{
if (value != _allChecked)
{
UpdateCompaniesCollection(value);
OnPropertyChanged(nameof(AllChecked));
}
}
}
And then you will need the method to update the collection. One way would be
void UpdateCompaniesCollection(bool newValue)
{
for(int i = 0; i < CompaniesCollection.Count; i++)
{
var tempCompany = CompaniesCollection[i];
tempCompany.IsChecked = newValue;
CompaniesCollection[i] = tempCompany;
}
}
That should do the trick. This only will trigger the change of the elements inside the collection when the Checkbox is checked. But if you want to also the the other way round (if a item in unchecked, then deselect the allCheck), that would be more complicated.
Related
Below is the code I'm using for checkbox in xamarin forms, but here I'm able to select only one item, I wanted to select multiple items from the checkbox. To the checkbox the data is binded from the database. Please help me
Checkforms.xaml.cs
public partial class Checkforms : ContentPage
{
private ObservableCollection<HelperModel> statusRecords;
string[] statusList;
public Checkforms()
{
InitializeComponent();
GetUserRoles();
}
public async void GetUserRoles()
{
HttpClient client = new HttpClient();
var response = await client.GetStringAsync("http://**********/api/Masters/getRoles");
var details = JsonConvert.DeserializeObject<List<HelperModel>>(response);
ListView1.ItemsSource = details;
}
private async void ListView1_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
if (e.SelectedItem == null) return;
var statusData = e.SelectedItem as HelperModel;
((ListView)sender).SelectedItem = null;
HttpClient client = new HttpClient();
var response = await client.GetStringAsync("http://********/api/Masters/getRoles");
var details = JsonConvert.DeserializeObject<List<HelperModel>>(response);
ListView1.ItemsSource = details;
var item = details.Where(x => x.name == statusData.name).FirstOrDefault();
if (item != null)
item.IsSelected = !item.IsSelected;
}
}
Checkforms.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="Checkbox_listview.Checkforms"
xmlns:lv="clr-namespace:Xamarin.Forms.MultiSelectListView;assembly=Xamarin.Forms.MultiSelectListView" Padding="0,20,0,0">
<ContentPage.Content>
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<!-- Place new controls here -->
<ListView x:Name="ListView1" ItemSelected="ListView1_ItemSelected" lv:MultiSelect.Enable="true">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout HorizontalOptions="FillAndExpand"
Orientation="Horizontal" Padding="10 ">
<Label Text="{Binding name}" HorizontalOptions="StartAndExpand"/>
<Image Source="select.png" IsVisible="{Binding IsSelected}"
VerticalOptions="Center" HeightRequest="40"
WidthRequest="40"/>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
HelperModel.cs
public class HelperModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool isSelected = false;
public string name { get; set; }
public bool IsSelected
{
get { return isSelected; }
set
{
isSelected = value;
OnPropertyChanged("IsSelected");
}
}
//OnProperty changed method
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I'm trying to select multiple items from the checkbox after binding it from the database, from here only one item is selected at a time. please help how to select multiple items
Thanks in advance
You can try use CollectionView to replace the listview like following code. CollectionView have SelectionMode, you can set it to Multiple
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<!-- Place new controls here -->
<CollectionView x:Name="ListView1" ItemsSource="{Binding StatusRecords}" SelectionMode="Multiple"
SelectionChanged="ListView1_SelectionChanged">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout HorizontalOptions="FillAndExpand" Orientation="Horizontal" Padding="10 ">
<Label Text="{Binding name}" HorizontalOptions="StartAndExpand"/>
<Image Source="select.png" IsVisible="{Binding IsSelected}" VerticalOptions="Center" HeightRequest="40" WidthRequest="40"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
Here is running GIF.
=========Update===============
Do you want to Multiple pre-selection result?
If so, you should add the Property in your ViewModel. Note: No matter what is your model, please set the type of ObservableCollection to object
ObservableCollection<object> selectedHelperModels;
public ObservableCollection<object> SelectedHelperModels
{
get
{
return selectedHelperModels;
}
set
{
if (selectedHelperModels != value)
{
selectedHelperModels = value;
OnPropertyChanged("SelectedHelperModels");
}
}
}
Then If the IsSelected was selected to true. I will add it to the SelectedHelperModels.
public MyHelperViewModel()
{
StatusRecords = new ObservableCollection<HelperModel>();
StatusRecords.Add(new HelperModel() { IsSelected=false, name="test1" });
StatusRecords.Add(new HelperModel() { IsSelected = true, name = "test2" });
StatusRecords.Add(new HelperModel() { IsSelected = true, name = "test3" });
StatusRecords.Add(new HelperModel() { IsSelected = true, name = "test4" });
StatusRecords.Add(new HelperModel() { IsSelected = false, name = "test5" });
StatusRecords.Add(new HelperModel() { IsSelected = false, name = "test6" });
SelectedHelperModels = new ObservableCollection<object>();
foreach (var item in StatusRecords)
{
if (item.IsSelected)
{
SelectedHelperModels.Add(item);
}
}
}
In the foreground xaml. Add the SelectedItems="{Binding SelectedHelperModels}" in the CollectionView.
<CollectionView x:Name="ListView1" ItemsSource="{Binding StatusRecords}" SelectedItems="{Binding SelectedHelperModels}" SelectionMode="Multiple"
SelectionChanged="ListView1_SelectionChanged">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout HorizontalOptions="FillAndExpand" Orientation="Horizontal" Padding="10 ">
<Label Text="{Binding name}" HorizontalOptions="StartAndExpand"/>
<Image Source="{Binding IsSelected, Converter={StaticResource imageToBool}}" IsVisible="{Binding IsSelected} " VerticalOptions="Center" HeightRequest="40" WidthRequest="40"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
As you comment, you lack of the ListView1_SelectionChanged event. just add it in the layout background code.
public partial class MainPage : ContentPage
{
MyHelperViewModel myHelperViewModel;
public MainPage()
{
InitializeComponent();
myHelperViewModel= new MyHelperViewModel();
this.BindingContext = myHelperViewModel;
}
private void ListView1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
}
}
=========Update2============
Do you want to achieve the result like following GIF?
If so, I found the SelectionChanged event cannot achieve it easliy, and it cannot meet the MVVM requirement, So I add a TapGestureRecognizer for StackLayout in the CollectionView.
Here is code.
<StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<CollectionView x:Name="ListView1"
ItemsSource="{Binding StatusRecords}"
SelectedItems="{Binding SelectedHelperModels}"
SelectionMode="Multiple"
>
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout HorizontalOptions="FillAndExpand" Orientation="Horizontal" Padding="10">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Command="{Binding BindingContext.ChangeCommand, Source={x:Reference Name=ListView1}}"
CommandParameter="{Binding .}"
/>
</StackLayout.GestureRecognizers>
<Label Text="{Binding name}" HorizontalOptions="StartAndExpand"/>
<Image Source="{Binding IsSelected, Converter={StaticResource imageToBool},Mode=TwoWay}" IsVisible="{Binding IsSelected, Mode=TwoWay}" VerticalOptions="Center" HeightRequest="40" WidthRequest="40"/>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
Here is ViewModel.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Windows.Input;
using Xamarin.Forms;
namespace SelectMutiPlyDemo
{
public class MyHelperViewModel: INotifyPropertyChanged
{
public ObservableCollection<HelperModel> StatusRecords { get; set; }
public ICommand ChangeCommand { protected set; get; }
ObservableCollection<object> selectedHelperModels;
public ObservableCollection<object> SelectedHelperModels
{
get
{
return selectedHelperModels;
}
set
{
if (selectedHelperModels != value)
{
selectedHelperModels = value;
OnPropertyChanged("SelectedHelperModels");
}
}
}
public MyHelperViewModel()
{
StatusRecords = new ObservableCollection<HelperModel>();
StatusRecords.Add(new HelperModel() { IsSelected=false, name="test1" });
StatusRecords.Add(new HelperModel() { IsSelected = true, name = "test2" });
StatusRecords.Add(new HelperModel() { IsSelected = true, name = "test3" });
StatusRecords.Add(new HelperModel() { IsSelected = true, name = "test4" });
StatusRecords.Add(new HelperModel() { IsSelected = false, name = "test5" });
StatusRecords.Add(new HelperModel() { IsSelected = false, name = "test6" });
SelectedHelperModels = new ObservableCollection<object>();
foreach (var item in StatusRecords)
{
if (item.IsSelected)
{
SelectedHelperModels.Add(item);
}
}
ChangeCommand=new Command<HelperModel>((key) =>
{
if (SelectedHelperModels.Contains<object>(key))
{
SelectedHelperModels.Remove(key);
}
else
{
SelectedHelperModels.Add(key);
}
key.IsSelected = !key.IsSelected;
});
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}
I have a list view with one item for this moment.
<ContentPage.Content>
<StackLayout>
<ListView x:Name="MyListView"
CachingStrategy="RecycleElement"
SelectionMode="Single">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding Text}" Detail="{Binding Detail}" Something to identify item individually ??? />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Text="Commencer" VerticalOptions="End" HorizontalOptions="Center" Clicked="StartProcessButton"/>
</StackLayout>
</ContentPage.Content>
I want to get an id or name or something who can individually get this item in my viewModel.
For example If I have an id system on this item :
void Action() {
if (listView.itemSelected.id == 1) {
then ....
}
}
I'm looking for this kind of thing. I checked google but I didn't found something for me.
You could create a id property.
Contact.cs
public class Contacts
{
private int id;
public int Id
{
get { return id; }
set { id = value; }
}
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
private string address;
public string Address
{
get { return address; }
set { address = value; }
}
private string image;
public string Image
{
get { return image; }
set { image = value; }
}
}
MainPage.xaml
<ListView
x:Name="ContactsList"
IsVisible="True"
ItemsSource="{Binding MyList}" ItemSelected="ContactsList_ItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Image
HeightRequest="50"
Source="{Binding Image}"
WidthRequest="50" />
<Label Text="{Binding Id}" />
<Label Text="{Binding Name}" />
<Label Text="{Binding Address}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
MainPage.xaml.cs
public partial class MainPage : ContentPage
{
private ObservableCollection<Contacts> myList;
public ObservableCollection<Contacts> MyList
{
get { return myList; }
set { myList = value; }
}
public MainPage()
{
InitializeComponent();
this.BindingContext = this;
MyList = new ObservableCollection<Contacts>();
for (int i = 1; i < 10; i++)
{
MyList.Add(new Contacts() { Id = i, Name = "Student" + i.ToString(), Address = "Address" + i.ToString(), Image = "usa.png" });
}
ContactsList.ItemsSource = MyList;
}
private void ContactsList_ItemSelected(object sender, SelectedItemChangedEventArgs e)
{
var item = (Contacts)e.SelectedItem;
if (item.Id == 1)
{
//.......do something you want
DisplayAlert("Id", "the id of item is 1", "Cancel");
}
}
}
In the ContactsList_ItemSelected event, you could get the ID from the e.SelectedItem. I use DisplayAlert to show the result.
I'm stuck with passing parameter from view model to page. On view model page i have list of properties which i increase by button , after button click sum is displyed on same page below that i collected that many of smth, my goal is to send this sum collected on this view model page to new page which i want to be responsible for displaying this sum . I'm stuck with passing parameter, it just don't update the value, it looks like the binding is okey becouse app don't throw exception that object has no reference. I'm begginer in xamarin and for any explanation or just direction which i can follow to achive this I would be very appreciated. Thank you in advance :)
ListViewModel code:
public class PersonListViewModel : INotifyPropertyChanged
{
public ObservableCollection<PersonViewModel> Persons { get; set; }
PersonViewModel selectedPerson;
double _sumcollected;
public double SumCollected
{
get => _sumcollected;
set
{
if (_sumcollected != value)
{
_sumcollected = value;
OnPropertyChanged("SumCollected");
}
}
}
public INavigation Navigation { get; set; }
public PersonListViewModel()
{
Persons = new ObservableCollection<PersonViewModel>
{
new PersonViewModel()
{
Name="Test", Surname="Test", Description= "TEsT", Background = "bgtest6.jpg", ProgressCounter =0.1, SavedClicked=0,Weight=1
},
new PersonViewModel()
{
Name="Test", Surname="Test", Description= "TEsT",Background = "bgtest6.jpg", ProgressCounter =0.1, SavedClicked=0,Weight=30
},
new PersonViewModel()
{
Name="Test", Surname="Test", Description= "TEsT",Background = "bgtest6.jpg", ProgressCounter =0.2, SavedClicked=0,Weight=100
},
new PersonViewModel()
{
Name="Test", Surname="Test", Description= "TEsT",Background = "bgtest6.jpg", ProgressCounter =0.3, SavedClicked=0,Weight=27
},
};
NavigateCommand = new Command<PersonViewModel>(NavigatationSolved);
IncreaseProgressCommand = new Command<PersonViewModel>(IncreaseProgress);
GotoCounterCommand = new Command<PersonListViewModel>(GotoNumbersPage);
NavigateSumPageCommand = new Command<PersonListViewModel>(NavigateSumPage);
}
private void NavigateSumPage(object obj)
{
Debug.WriteLine("Navigate to sum page ");
PersonListViewModel personListModel = obj as PersonListViewModel;
Navigation.PushAsync(new SumPage(personListModel));
}
//Passing SumCollected not working
private void GotoNumbersPage(object numbers)
{
PersonListViewModel personList = numbers as PersonListViewModel;
Navigation.PushAsync(new CounterPage(personList));
Debug.WriteLine("Next Page ?");
}
private void IncreaseProgress(object sender)
{
PersonViewModel person = sender as PersonViewModel;
if(person.ProgressCounter >= 1)
{
person.ProgressCounter -= person.ProgressCounter;
Application.Current.MainPage.DisplayAlert("Alert!", "Message after one progress bar", "GO!");
}
else
{
person.ProgressCounter += .2;
}
//Navigation.PushPopupAsync(new GratulationAlertPage());
person.SavedClicked += 1;
Debug.WriteLine("Saved Clicked");
SumCollected += 1;
SumCollected += person.Weight;
Debug.WriteLine("New SumCollected value");
}
}
ListViewModelPage code:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CommandDemo.Views.PersonListPage"
>
<NavigationPage.TitleView>
<StackLayout Orientation="Horizontal">
<Button Text="Numbers"
Command="{Binding Path=BindingContext.GotoCounterCommand}"
CommandParameter="{Binding .}"/>
</StackLayout>
</NavigationPage.TitleView>
<ContentPage.Content>
<StackLayout Padding="10"
Margin="10">
<ListView x:Name="personList"
ItemsSource="{Binding Persons}"
HasUnevenRows="True"
>
<!--SelectedItem="{Binding SelectedPerson}"-->
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<StackLayout.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1"
Command="{Binding Source={x:Reference personList},Path=BindingContext.NavigateCommand}"
CommandParameter="{Binding .}"/>
</StackLayout.GestureRecognizers>
<Label Text="{Binding Name}"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
HorizontalOptions="Center"
VerticalOptions="Center"
Margin="5,5,5,5"/>
<ProgressBar Progress="{Binding ProgressCounter}"/>
<Button Text="Add Progress"
Command="{Binding Source={x:Reference personList},Path=BindingContext.IncreaseProgressCommand}"
CommandParameter="{Binding .}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Label Text="{Binding SumCollected}"
VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
VerticalOptions="Center"
HorizontalOptions="Center"/>
<Button Text="Numbers"
Command="{Binding NavigateSumPageCommand}"
CommandParameter="{Binding .}"/>
</StackLayout>
</ContentPage.Content>
SumViewModel code:
public class CounterViewModel : INotifyPropertyChanged
{
private PersonListViewModel _personListView;
public PersonListViewModel PersonList
{
get => _personListView;
set
{
if (_personListView != value)
{
_personListView = value;
OnPropertyChanged("PersonList");
}
}
}
PersonViewModel _personView;
public PersonViewModel PersonView
{
get => _personView;
set
{
if (_personView != value)
{
_personView = value;
OnPropertyChanged("PersonView");
}
}
}
public double SumCollected
{
get => PersonList.SumCollected;
set
{
if (PersonList.SumCollected != value)
{
PersonList.SumCollected = value;
OnPropertyChanged("SumCollected");
}
}
}
private double _collected;
public double Collected
{
get => _collected;
set
{
if (_collected != value)
{
_collected = value;
OnPropertyChanged("Collected");
}
}
}
public CounterViewModel()
{
PersonList = new PersonListViewModel();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Page where i want to display sum collected from list page:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CommandDemo.Views.SumPage">
<ContentPage.Content>
<StackLayout>
<Label Text="{Binding PersonList.SumCollected}"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage.Content>
Sum page code behind:
public partial class SumPage : ContentPage
{
public SumPage (PersonListViewModel personListModel)
{
InitializeComponent ();
BindingContext = new CounterViewModel();
}
}
You need to receive the object that you are passing in your viewmodel.
public CounterViewModel(PersonListViewModel personList)
{
PersonList = personList;
}
my PropertyChanged event is not updatin my listview in xamarin.
Could someone help me with that?
The Method RefreshListView is triggered when the searchbar text has changed.
My Viewmodel:
public class LebensmittelViewModel : INotifyPropertyChanged
{
private ObservableCollection<Lebensmittel> lebensmittelList = new ObservableCollection<Lebensmittel>();
public List<Lebensmittel> normalLebensmittelList = new List<Lebensmittel>();
public event PropertyChangedEventHandler PropertyChanged;
public LebensmittelViewModel()
{
normalLebensmittelList = App.LebensmittelDatabase.getAllLebensmittel();
}
public void RefreshListView(string searchBarText)
{
LebensmittelList = addItemInCollection(searchBarText);
}
public ObservableCollection<Lebensmittel> addItemInCollection(string searchBarText)
{
if (searchBarText != null)
{
foreach (var item in normalLebensmittelList)
{
if (item.Name.Contains(searchBarText) || item.Name.Contains(searchBarText.First().ToString().ToUpper()))
{
LebensmittelList.Add(item);
};
}
}
return LebensmittelList;
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ObservableCollection<Lebensmittel> LebensmittelList
{
get
{
return lebensmittelList;
}
set
{
lebensmittelList = value;
OnPropertyChanged("LebensmittelList");
}
}
}
Einkaufsliste.xaml
<ContentPage.Content>
<StackLayout Spacing="10" Padding="10">
<SearchBar x:Name="searchBar" Text="{Binding searchBarText}" Placeholder="Lebensmittel suchen..." VerticalOptions="StartAndExpand">
<SearchBar.Behaviors>
<behavior:TextChangedBehavior/>
</SearchBar.Behaviors>
</SearchBar>
<ListView x:Name="listView" ItemsSource="{Binding LebensmittelList}" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout VerticalOptions="FillAndExpand" Orientation="Horizontal" Padding="10">
<Label Text="{Binding Name}" YAlign="Center" Font="Large"/>
<ia:Checkbox HorizontalOptions="EndAndExpand"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
Einkaufsliste.xaml.cs
public Einkaufsliste ()
{
InitializeComponent ();
BindingContext = new LebensmittelViewModel();
}
I think everything should be okay but it's not working.
Hope someone could help me with this,
thanks
Edit for Tom:
public void RefreshListView(string searchBarText)
{
addItemInCollection(searchBarText);
}
public void addItemInCollection(string searchBarText)
{
if (searchBarText != null)
{
foreach (var item in normalLebensmittelList)
{
if (item.Name.Contains(searchBarText) || item.Name.Contains(searchBarText.First().ToString().ToUpper()))
{
AddItemToList(item);
};
}
}
}
private void AddItemToList(Lebensmittel item)
{
lebensmittelList.Add(item);
LebensmittelList = lebensmittelList;
}
How it's implemented the behavior TextChangedBehavior? Do you cast the BindingContext as a LebensmittelViewModel in order to call RefreshListView?
I would get rid of the behavior :/ and add a TextChanged event, create the property SearchBarText, since I don't see where it's declared and you're binding it to the SearchBar, refactor the RefreshListView method... and I think that it should work... :)
Einkaufsliste.xaml.cs
private LebensmittelViewModel vm = new LebensmittelViewModel();
public Einkaufsliste ()
{
InitializeComponent ();
BindingContext = vm;
}
public void OnSeachBarTextChange(object e, TextChangedEventArgs args)
{
vm.RefreshListView();
}
Einkaufsliste.xaml
<ContentPage.Content>
<StackLayout Spacing="10" Padding="10">
<SearchBar x:Name="searchBar" Text="{Binding SearchBarText, Mode=TwoWay}" Placeholder="Lebensmittel suchen..." VerticalOptions="StartAndExpand" TextChanged="OnSeachBarTextChange">
</SearchBar>
<ListView x:Name="listView" ItemsSource="{Binding LebensmittelList}" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout VerticalOptions="FillAndExpand" Orientation="Horizontal" Padding="10">
<Label Text="{Binding Name}" YAlign="Center" Font="Large"/>
<ia:Checkbox HorizontalOptions="EndAndExpand"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
Viewmodel
private string _searchBarText;
public string SearchBarText
{
get
{
return _searchBarText;
}
set
{
_searchBarText = value;
OnPropertyChanged("SearchBarText");
}
}
public void RefreshListView()
{
if (!string.IsNullOrEmpty(searchBarText))
{
var matches = normalLebensmittelList.Where(x => x.Name.Contains(searchBarText) || x.Name.Contains(searchBarText.First().ToString().ToUpper())
foreach (var item in matches)
{
LebensmittelList.Add(item);
}
}
}
Can you try replacing the Following method:
public void RefreshListView(string searchBarText)
{
LebensmittelList.Clear();
addItemInCollection(searchBarText);
}
Since you are already using the Observable Collection you dont need to replace the collection. You can add and it will automatically observed and binded. Let me know if it works!
It looks like you have extra work with LebensmittelList. Try do the following:
public void RefreshListView(string searchBarText)
{
// this can be list clearing, if you need it
addItemInCollection(searchBarText);
OnPropertyChanged("LebensmittelList");
}
public void addItemInCollection(string searchBarText)
{
if (searchBarText != null)
{
foreach (var item in normalLebensmittelList)
{
if (item.Name.Contains(searchBarText) || item.Name.Contains(searchBarText.First().ToString().ToUpper()))
{
LebensmittelList.Add(item);
};
}
}
}
Edit:
I don't know if anyone can call this solution elegant, but it should work.
internal class MyCollection<T> : ObservableCollection<T>
{
public void DoCollectionChanged()
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
... MyCollection<LebensmittelItem> LebensmittelList; // field and property declaration should be changed
public void RefreshListView(string searchBarText)
{
// this can be list clearing, if you need it
addItemInCollection(searchBarText);
LebensmittelList.DoCollectionChanged();
}
Is OnPropertyChanged in the property set called when you do the RefreshListView?
In the addItemToCollection method, you are calling the following line:
LebensmittelList.Add(item);
In this case LebensmittelList is a property, not a variable. The line LebensmittelList.Add(item); is effectively doing the following:
var temporaryList = LebensmittelList; // Gets lebensmittelList and assigns to temporaryList
temporaryList.Add(item); // You add the item to the *variable temporaryList*
Essentially, you are adding to an instance of lebensmittelList (a temporary copy), and not the lebensmittelList variable. Once you have added that item to the temporary list, the whole (temporary) list gets discarded without "saving changes".
To actually update LebensmittelList, you would be better off calling a method like:
private void AddItemToList(object item)
{
lebensmittelList.Add(item);
LebensmittelList = lebensmittelList;
}
I'm fairy new to Xamarin, and I trying to do a switching List. So when button is pressed, it switches to another list. I have CustomLists class which wraps all this lists and exposes ChosenList property, which gives access to list currenly being displayed. When entry in List is deleted Command property gets called
public ICommand DeleteTest
{
get { return new Command<TaskRecord>((s) => OnDelete(s)); }
}
void OnDelete(TaskRecord task)
{
List.Remove(task);
IsUnfinishedChanged_ = !task.IsFinished;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ChosenList"));
System.Diagnostics.Debug.Print("Deleting..");
}
When I delete entry in a first list (those shown at the programm start) it works fine. But in a second, ListView doesn't update for some reason
Here's my XAML code
<ListView ItemsSource="{Binding ChosenList}" IsPullToRefreshEnabled="True"
Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2" SeparatorColor="DimGray">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem Text="Delete" IsDestructive="true"
Command="{Binding Source={x:Reference MainGrid},
Path=BindingContext.DeleteTest}"
CommandParameter="{Binding .}"/>
</ViewCell.ContextActions>
<StackLayout Padding="15,0" VerticalOptions="Center">
<Label Text="{Binding Path=Name}" FontSize="Large" Font="Arial"
FontAttributes="Bold" VerticalOptions="Center"/>
<Label Text="{Binding Path=ShortDescr}" FontSize="Micro" Font="Arial"
VerticalOptions="Center"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Edit: ChosenList is wrapper for ListView to bind to
public List<TaskRecord> ChosenList
{
get
{
return (IsAll_ ? List : Unfinished);
}
}
Edit 2: Putting the whole code up here
private List<TaskRecord> List { get; set; }
private List<TaskRecord> UnfinishedCache_;
private List<TaskRecord> Unfinished
{
get
{
if (IsUnfinishedChanged_)
{
UnfinishedCache_ = new List<TaskRecord>();
foreach (TaskRecord task in List)
{
if (!task.IsFinished) UnfinishedCache_.Add(task);
}
IsUnfinishedChanged_ = false;
}
return UnfinishedCache_;
}
set { UnfinishedCache_ = value; }
}
private bool IsUnfinishedChanged_=true;
private bool IsAll_;
public ICommand ListChangeCommand
{
get { return new Command<string>((s)=>OnListSwitch(s)); }
}
void OnListSwitch(string senderText)
{
if (senderText == "All" && !IsAll_)
{
IsAll_ = true;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ChosenList"));
}
else if (senderText == "Unfinished" && IsAll_)
{
IsAll_ = false;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ChosenList"));
}
}
public ICommand DeleteTest
{
get { return new Command<TaskRecord>((s) => OnDelete(s)); }
}
void OnDelete(TaskRecord task)
{
List.Remove(task);
IsUnfinishedChanged_ = !task.IsFinished;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("ChosenList"));
System.Diagnostics.Debug.Print("Deleting..");
}
public event PropertyChangedEventHandler PropertyChanged;
public List<TaskRecord> ChosenList
{
get
{
return (IsAll_ ? List : Unfinished);
}
}