locked
Xamarin MVVM Delete Listview Item from Another Page RRS feed

  • Question

  • User356319 posted

    Hello new to Xamarin form. I have a listview and I can pull up the information when the item is selected similar to the template provided in Visual Studio 2017 xamarin project.

    Wish the template was more complete because adding and updating works great but I'm having trouble creating the delete method. The data is deleted from SQLite but the ObservableCollection doesn't refresh.

    I tried copying similar logic from the adding and updating to the delete. I tried adding another message center method into my constructor but it's not being hit when debugging like adding. Not sure what I am missing. Over my head still.

    Here is the listview and its working great

        <ContentPage.ToolbarItems>
        <ToolbarItem Text="-" Command="{Binding ExecuteDeleteAllCommand}"></ToolbarItem>
        <ToolbarItem Text="+" Clicked="InsertAuditClicked"></ToolbarItem>
    </ContentPage.ToolbarItems>
    <ContentPage.Padding>
        <OnIdiom>10, 10, 10, 10</OnIdiom>
    </ContentPage.Padding>
    <ContentPage.Content>
        <StackLayout>
            <ListView x:Name="AuditsListView"
                      ItemsSource="{Binding AuditsCollection}"
                      VerticalOptions="FillAndExpand"
                      HasUnevenRows="true"
                      RefreshCommand="{Binding LoadAuditsCommand}"
                      IsPullToRefreshEnabled="true"
                      IsRefreshing="{Binding IsBusy, Mode=OneWay}"
                      CachingStrategy="RecycleElement"
                      ItemSelected="OnAuditSelected">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell>
                            <Grid>
                                <StackLayout Grid.Column="0">
                                    <!-- <Label Text="{Binding Id}"
                                       LineBreakMode="NoWrap"
                                       Style="{DynamicResource ListAuditTextStyle}"
                                       FontSize="16"></Label>-->
                                    <Label Text="{Binding AuditId}"
                                       Style="{DynamicResource ListAuditTextStyle}"
                                       FontSize="16"></Label>
                                    <Label Text="{Binding AuditCommand}"
                                       LineBreakMode="NoWrap"
                                       Style="{DynamicResource ListAuditDetailTextStyle}"
                                       FontSize="13"></Label>
                                </StackLayout>
                                <StackLayout Grid.Column="1" VerticalOptions="CenterAndExpand" HorizontalOptions="EndAndExpand">
                                    <Label Text=">" FontSize="18"></Label>
                                </StackLayout>
                            </Grid>
    
                        </ViewCell>
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        </StackLayout>
    </ContentPage.Content>
    

    Here code behind

    using System;
    

    using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;

    using Xamarin.Forms; using Xamarin.Forms.Xaml;

    using AuditTrack.Models; using AuditTrack.ViewModels;

    namespace AuditTrack.Views { [XamlCompilation(XamlCompilationOptions.Compile)] public partial class AuditsPage : ContentPage { AuditsViewModel ViewModel;

        public AuditsPage()
        {
            InitializeComponent();
    
            BindingContext = ViewModel = new AuditsViewModel();
        }
    
        async void OnAuditSelected(object s, SelectedItemChangedEventArgs e)
        {
            var m = e.SelectedItem as AuditModel;
    
            if (m == null)
                return;
    
            await Navigation.PushAsync(new AuditDetailPage(new AuditDetailViewModel(m))); // (new AuditDetailPage(new AuditDetailViewModel(m)));
    
            AuditsListView.SelectedItem = null;
        }
    
        async void InsertAuditClicked(object sender, EventArgs e)
        {
    
            await Navigation.PushModalAsync(new NavigationPage(new AuditNewPage()));
    
        }
    
        protected override void OnAppearing()
        {
            base.OnAppearing();
    
            if (ViewModel.AuditsCollection.Count == 0)
                ViewModel.LoadAuditsCommand.Execute(null);
        }
    
    
    }
    

    }

    Here is view model

    using System;
    

    using System.Collections.ObjectModel; using System.Diagnostics; using System.Threading.Tasks;

    using Xamarin.Forms;

    using AuditTrack.Models; using AuditTrack.Views;

    namespace AuditTrack.ViewModels { public class AuditsViewModel : BaseViewModel { public ObservableCollection AuditsCollection { get; set; }

        public Command LoadAuditsCommand { get; set; }
    
        public AuditsViewModel()
        {
            Title = "Browse";
            AuditsCollection = new ObservableCollection<AuditModel>();
            LoadAuditsCommand = new Command(async () => await ExecuteLoadAuditsCommand());
    
            MessagingCenter.Subscribe<AuditNewPage, AuditModel>(this, "AddAudit", async (o, a) =>
            {
                var n = a as AuditModel;
                AuditsCollection.Add(n);
                await App.MobileDataBase.InsertAuditAsync(n);
            });
    
            //MessagingCenter.Subscribe<AuditDetailPage, AuditModel>(this, "DeleteAudit", async (o, a) =>
            //{
            //    var n = a as AuditModel;
            //    AuditsCollection.Remove(n);
            //    await App.MobileDataBase.DeleteAuditAsync(n);
    
    
            //});
    
        }
    
        async Task ExecuteLoadAuditsCommand()
        {
            if (IsBusy)
                return;
    
            IsBusy = true;
    
            try
            {
                AuditsCollection.Clear();
                var a = await App.MobileDataBase.GetAuditsAsync(true); // var a = await DataSource.GetAuditsAsync(true);
    
                foreach (var i in a)
                {
                    AuditsCollection.Add(i);
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
            finally
            {
                IsBusy = false;
            }
        }
    
    }
    

    }

    Got a toolbar with a click event. Here is the code behind

    using AuditTrack.Models;
    

    using Xamarin.Forms; using AuditTrack.Views; using System.Threading.Tasks;

    namespace AuditTrack.ViewModels { public class AuditDetailViewModel : BaseViewModel { public AuditModel AuditDetail { get; set; }

        public AuditDetailViewModel(AuditModel m = null)
        {
    
            Title = m.AuditId.ToString(); // m?.AuditId;
            AuditDetail = m;
    
        }
    
        public Command ExecuteEditAuditCommand
        {
            get
            {
                return new Command(async () =>
                {
                    await DataSource.UpdateAuditAsync(AuditDetail);
                });
            }
        }
    
        public Command ExecuteDeleteAuditCommand
        {
            get
            {
                return new Command(async () =>
                {
                    await App.MobileDataBase.DeleteAuditAsync(AuditDetail); // DataSource.DeleteAuditAsync(AuditDetail.AuditId));
                });
            }
        }
    
        public Task TestUpdateCommand()
        {
            Task<bool> tt = App.MobileDataBase.UpdateAuditAsync(AuditDetail);
    
            return tt;
        }
    
        public Task TestDeleteCommand()
        {
            Task<bool> tt = App.MobileDataBase.DeleteAuditAsync(AuditDetail);
            MessagingCenter.Send(this, "DeleteAudit", this.AuditDetail); // await ViewModel.ExecuteDeleteAuditCommand(); // App.MobileDataBase.DeleteAuditAsync();
            return tt;
    
        }
    }
    

    }

    Playing around with different properties and methods but here is the view model for the content page

    using AuditTrack.Models;
    

    using Xamarin.Forms; using AuditTrack.Views; using System.Threading.Tasks;

    namespace AuditTrack.ViewModels { public class AuditDetailViewModel : BaseViewModel { public AuditModel AuditDetail { get; set; }

        public AuditDetailViewModel(AuditModel m = null)
        {
    
            Title = m.AuditId.ToString(); // m?.AuditId;
            AuditDetail = m;
    
        }
    
        public Command ExecuteEditAuditCommand
        {
            get
            {
                return new Command(async () =>
                {
                    await DataSource.UpdateAuditAsync(AuditDetail);
                });
            }
        }
    
        public Command ExecuteDeleteAuditCommand
        {
            get
            {
                return new Command(async () =>
                {
                    await App.MobileDataBase.DeleteAuditAsync(AuditDetail); // DataSource.DeleteAuditAsync(AuditDetail.AuditId));
                });
            }
        }
    
        public Task TestUpdateCommand()
        {
            Task<bool> tt = App.MobileDataBase.UpdateAuditAsync(AuditDetail);
    
            return tt;
        }
    
        public Task TestDeleteCommand()
        {
            Task<bool> tt = App.MobileDataBase.DeleteAuditAsync(AuditDetail);
            MessagingCenter.Send(this, "DeleteAudit", this.AuditDetail); // await ViewModel.ExecuteDeleteAuditCommand(); // App.MobileDataBase.DeleteAuditAsync();
            return tt;
    
        }
    }
    

    }

    xamarin.forms listview mvvm sqlite observablecollection c# android xamarin ios uwp

    Wednesday, October 9, 2019 6:40 PM

Answers

  • User356319 posted

    Fixed.

    Un-commented and changed the message center subscription on the ViewModel constructor to point to the other ViewModel sending instead of the ContentPage which is not sending

            MessagingCenter.Subscribe<AuditDetailViewModel, AuditModel>(this, "DeleteAudit", async (o, a) =>
            {
                var n = a as AuditModel;
                AuditsCollection.Remove(n);
                await App.MobileDataBase.DeleteAuditAsync(n);
    
    
            });
    

    public method on other ViewModel

        public void MethodDeleteCommand()
        {
            MessagingCenter.Send(this, "DeleteAudit", this.AuditDetail);
        }
    
    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Thursday, October 10, 2019 8:32 PM

All replies

  • User382871 posted

    Did the viewModel implement INotifyPropertyChanged? Implementing the interface INotifyPropertyChanged with the event that will notify the ViewModel changes to update UI. Change the itemSource collection, listview will update.

    ``` private void Button_Clicked(object sender, EventArgs e) { viewModel.Items.RemoveAt(0); }

    private void Listview_ItemSelected(object sender, SelectedItemChangedEventArgs e) { var model = (Car)e.SelectedItem; viewModel.Items.Remove(model); } ```

    Tutorial about ListView Simple MVVM Binding Example. https://almirvuk.blogspot.com/2017/02/xamarinforms-listview-simple-mvvm.html

    Thursday, October 10, 2019 6:52 AM
  • User356319 posted

    Thank you for the quick response. Yes both view models implement a property change event via my base view model

    public class AuditDetailViewModel : BaseViewModel
    public class AuditsViewModel : BaseViewModel
    

    Here is the method on my baseviewmodel too. is this enough? should i be using INotifyPropertyChanged instead?

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string p = "")
        {
            var c = PropertyChanged;
            if (c == null)
                return;
    
            c.Invoke(this, new PropertyChangedEventArgs(p));
        }
    

    I am also doing this on my item select to view the detail page

    await Navigation.PushAsync(new AuditDetailPage(new AuditDetailViewModel(m)));
    
    Thursday, October 10, 2019 3:35 PM
  • User356319 posted

    Fixed.

    Un-commented and changed the message center subscription on the ViewModel constructor to point to the other ViewModel sending instead of the ContentPage which is not sending

            MessagingCenter.Subscribe<AuditDetailViewModel, AuditModel>(this, "DeleteAudit", async (o, a) =>
            {
                var n = a as AuditModel;
                AuditsCollection.Remove(n);
                await App.MobileDataBase.DeleteAuditAsync(n);
    
    
            });
    

    public method on other ViewModel

        public void MethodDeleteCommand()
        {
            MessagingCenter.Send(this, "DeleteAudit", this.AuditDetail);
        }
    
    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Thursday, October 10, 2019 8:32 PM