0
votes

So i am new to xamarin forms and i have a problem with my listview. So here is my code.

Xaml:

    <RefreshView x:Name="MyContactRefreshView">
    <ListView x:Name="MyContactList"
        Margin="5"   
        ItemsSource="{Binding GroupedData}" 
        IsGroupingEnabled="True"        
        GroupDisplayBinding="{Binding Key}"
        HasUnevenRows="True"
        ItemTapped="GoToContactDetail"
        >
        <ListView.ItemTemplate>
            <DataTemplate>
                <TextCell Text="{Binding Name}" Detail="{Binding Phone1}" x:Name="ContactItem"/>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
    </RefreshView>

Here is my contactList page code in C#:

    public partial class ContactList : ContentPage
    {
        private readonly ContactListGroupingViewModel _contacts;
        private bool _myContactListItemSourceIsBound;
        public ICommand RefreshCommand { get { return new Command(async() => await RefreshList()); } }

        public ContactList()
        {
            InitializeComponent();
            _contacts = new ContactListGroupingViewModel();
            MyContactRefreshView.Command = RefreshCommand;
            MyContactRefreshView.IsRefreshing = App.ViewModel.isBusy;
            _myContactListItemSourceIsBound = false;
        }
        protected override void OnAppearing()
        {
            MyContactRefreshView.IsRefreshing = true;
            base.OnAppearing();
        }
        private void GoToContactDetail(object sender, ItemTappedEventArgs e)
        {
            new DataLayer.Cummon.Commands.NavigateToContactCommand().Execute(e.Item as ContactObservableModel);
        }
        public async Task RefreshList()
        {
            MyContactRefreshView.IsRefreshing = true;
            MyContactList.SelectedItem = null;
            await _contacts.RefreshList();
            if (_myContactListItemSourceIsBound == false)
            {
                MyContactList.ItemsSource = _contacts.GroupedData;
                _myContactListItemSourceIsBound = true;
            }
            MyContactRefreshView.IsRefreshing = false;
        }
    }

So the problem is i have this contact listview that is gruoped in alphabetical order. i have this add button that navigates to contact add or edit page and after you add contact it comes back to this page again. So after it comes back to this page it does not update the list. i have been searching for days. I implemented INotifyPropertyChanged in all of my objects. All of my Collections are Observable and as you can see i tried RefreshView too. I checked my whole code with breakpoints and i found out that everything works fine except my list. The thing is the updated data is there, the updated data is bound to MyContactList.ITemSource but my list wont update untill i close the page and open it again. Is it Because of the OnAppearing() event? please help me :(

Sorry my code is a mess right now because i have been trying everything i found on the internet.

EDIT : this is how i add a contact to mylist:

The Button is a ToolbarItem Here is the code:

    <ContentPage.ToolbarItems>
        <ToolbarItem Text="افزودن مخاطب" IconImageSource="AddIcon.png" Command="{StaticResource NavigateToContactAddOrEditPageCommand}" Order="Primary"/>
    </ContentPage.ToolbarItems>

when clicked this command gets executed and then goes to contact add or edit page

    public class NavigateToContactAddOrEditPageCommand : ICommand
    {
        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter)
        {
            return true;
        }
        public void RaiseCanExecuteChanged()
        {
            var handler = CanExecuteChanged;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }

        public void Execute(object parameter)
        {
            NavigateAsync();
        }
        public async void NavigateAsync()
        {
            await App.MainNavigation.PushAsync(new Pages.ContactAddOrEditPage(), true);
        }
    }

in that page i have entries to enter the data then another toolbaritem to add the contact

        <ContentPage.ToolbarItems>
        <ToolbarItem IconImageSource="SubmitIcon.png" Text="ثبت" Order="Primary" Clicked="AddOrEditButton"/>
    </ContentPage.ToolbarItems>

when clicked this happen

        private void AddOrEditButton(object sender, EventArgs e)
        {
            ContactObservableModel target = new ContactObservableModel()
            {
                Name = contactName.Text,
                Phone1 = contactPhone1.Text,
                Phone2 = contactPhone2.Text,
                AccountNumber = contactAccountNumber.Text,
                PhoneSocial = contactPhone2.Text
            };
            new DataLayer.Cummon.Commands.AddToContactsCommand().Execute(target);
       }

And the command is

 public class AddToContactsCommand : ICommand
    {
        private bool _isBusy = false;
        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter)
        {
            return !_isBusy;
        }
        public void RaiseCanExecuteChanged()
        {
            var handler = CanExecuteChanged;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }

        public void Execute(object parameter)
        {
            AddToContacts(parameter as ContactObservableModel);
            App.MainNavigation.PopAsync();
        }
        public async void AddToContacts(ContactObservableModel contact)
        {
            this._isBusy = true;
            this.RaiseCanExecuteChanged();
            App.ViewModel.isBusy = true;
            await App.ViewModel.ContactListAsync.AddAsync(contact);
            this._isBusy = false;
            this.RaiseCanExecuteChanged();
            App.ViewModel.isBusy = false;
        }
    }

and then it gets added to the database and now i have to call my refresh list method which is this

        public async Task RefreshList()
        {
            this.Items.Clear();
            this.GroupedData.Clear();
            await App.ViewModel.GetContactsItemsAsync();
            var contacts = App.ViewModel.ContactListAsync.OrderBy(n => n.Name).ToList();
            foreach (var contact in contacts)
            {
                this.Items.Add(contact);
            }
            var groupedContacts = Items.OrderBy(p => p.Name)
               .GroupBy(p => p.Name[0].ToString())
               .Select(p => new ObservableGroupCollection<string, ContactObservableModel>(p)).ToList();
            foreach (var contact in groupedContacts)
            {
                this.GroupedData.Add(contact);
            }
        }

and the list is bound to the GroupedData property. and the RefreshList method is on my ContactGroupingViewModel is this

    class ContactListGroupingViewModel : ObservableBase
    {
        public ContactListGroupingViewModel()
        {
            Items = new ObservableCollection<ContactObservableModel>();
            GroupedData = new List<ObservableGroupCollection<string, ContactObservableModel>>();
        }
        public async Task RefreshList()
        {
            this.Items.Clear();
            this.GroupedData.Clear();
            await App.ViewModel.GetContactsItemsAsync();
            var contacts = App.ViewModel.ContactListAsync.OrderBy(n => n.Name).ToList();
            foreach (var contact in contacts)
            {
                this.Items.Add(contact);
            }
            var groupedContacts = Items.OrderBy(p => p.Name)
               .GroupBy(p => p.Name[0].ToString())
               .Select(p => new ObservableGroupCollection<string, ContactObservableModel>(p)).ToList();
            foreach (var contact in groupedContacts)
            {
                this.GroupedData.Add(contact);
            }
        }
        private IList<ContactObservableModel> _items;
        public IList<ContactObservableModel> Items
        {
            get { return _items; }
            set { this.SetProperty(ref this._items, value); }
        }
        private List<ObservableGroupCollection<string, ContactObservableModel>> _groupedData;
        public List<ObservableGroupCollection<string, ContactObservableModel>> GroupedData
        {
            get { return _groupedData; }
            set { this.SetProperty(ref this._groupedData, value); }
        }
    }
1
you specifically say the problem occurs when adding an item, but you have not shown any code that does that. Also, there is no such thing as INotifyMeProperty, do you mean INotifyPropertyChanged?Jason
yea i meant that one sorry i am so tired at the moment that i make a lot of mistakes now. i will edit that. i can show you the code but the problem occurs when i delete a contact too and it wont refresh the list until i get out of the page but im pretty sure both add and delete operations are working fine after spending a lot of time debugging and after putting a lot of breakpoints i saw that the updated data is bound to the list but the list wont refresh. i still can edit the post and add the add and delete operations if you want to.Fardin Farnezhad
i edited the post and added the add operation.Fardin Farnezhad
GroupedData should be an ObservableCollectionJason
it is an ObservableCollection.Fardin Farnezhad

1 Answers

0
votes

You need to use ObservableCollection to notify ListView of any changes.

From the document:

If you want the ListView to automatically update as items are added, removed and changed in the underlying list, you'll need to use an ObservableCollection. ObservableCollection is defined in System.Collections.ObjectModel and is just like List, except that it can notify ListView of any changes:

ObservableCollection<Employee> employees = new ObservableCollection<Employee>();
listView.ItemsSource = employees;

//Mr. Mono will be added to the ListView because it uses an ObservableCollection
employees.Add(new Employee(){ DisplayName="Mr. Mono"});