TinTan19
TinTan19

Reputation: 57

How to delete item from CollectionView with SwipeView in Xamarin Forms?

I am currently making a note-taking app. I am new so excuse my lack of knowledge. I am using the default flyout menu template given with changes made to cater for my needs. I am using a SwipeView in my CollectionView so when you swipe on a 'note' the item will delete on execute.

I have the swipe working but I cannot get the item to delete once swiped.

This is my ItemsPage.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"
             x:Class="xamarinMobileTest.Views.ItemsPage"
             Title="{Binding Title}"
             xmlns:local="clr-namespace:xamarinMobileTest.ViewModels"  
             xmlns:model="clr-namespace:xamarinMobileTest.Models"  
             x:Name="BrowseItemsPage">

    <ContentPage.ToolbarItems>
        <ToolbarItem Text="Add Note" Command="{Binding AddItemCommand}" />
    </ContentPage.ToolbarItems>

    <ContentPage.BindingContext>
        <local:ItemsViewModel/>
    </ContentPage.BindingContext>

    <RefreshView x:DataType="local:ItemsViewModel" Command="{Binding LoadItemsCommand}" IsRefreshing="{Binding IsBusy, Mode=TwoWay}">
        <CollectionView x:Name="ItemsListView"
                ItemsSource="{Binding Items}"
                SelectionMode="None">
            <CollectionView.ItemTemplate>
                <DataTemplate>
                     <SwipeView>
                        <SwipeView.RightItems>
                            <SwipeItems Mode="Execute">
                                <SwipeItem Text="Delete" 
                                           BackgroundColor="Red"
                                           Command="{Binding DeleteItemCommand}"
                                           CommandParameter="{Binding .}"/>
                            </SwipeItems>
                        </SwipeView.RightItems>
                        <StackLayout Padding="10" x:DataType="model:Item">
                            <Label Text="{Binding Text}"
                                   LineBreakMode="NoWrap" 
                                   Style="{DynamicResource ListItemTextStyle}" 
                                   FontSize="16"
                                   FontAttributes="Bold"
                                   TextColor="Black"/>
                            <Label Text="{Binding Description}" 
                                   LineBreakMode="NoWrap"
                                   Style="{DynamicResource ListItemDetailTextStyle}"
                                   FontSize="13"
                                   TextColor="Black"/>
                            <Label Text="{Binding DueDate}"
                            Style="{DynamicResource ListItemDetailTextStyle}"
                                   FontSize="13"
                                   TextColor="Black"/>
                            <StackLayout.GestureRecognizers>
                                <TapGestureRecognizer 
                                NumberOfTapsRequired="1"
                                Command="{Binding Source={RelativeSource AncestorType={x:Type local:ItemsViewModel}}, Path=ItemTapped}"     
                                CommandParameter="{Binding .}">
                                </TapGestureRecognizer>
                            </StackLayout.GestureRecognizers>
                       </StackLayout>
                    </SwipeView>
                </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>
    </RefreshView>
</ContentPage>

ItemsViewModel:

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
    using System.Linq;
    using System.Threading.Tasks;
    using Xamarin.Forms;
    
    using xamarinMobileTest.Models;
    using xamarinMobileTest.Views;

    namespace xamarinMobileTest.ViewModels
    {
    public class ItemsViewModel : BaseViewModel
    {
        private Item _selectedItem;

        public ObservableCollection<Item> Items { get; }
        public Command LoadItemsCommand { get; }
        public Command AddItemCommand { get; }
        public Command<Item> ItemTapped { get; }

        public Command<Item> DeleteItemCommand { get; }

        public ItemsViewModel()
        {
            Title = "Notes";
            Items = new ObservableCollection<Item>();
            LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());

            ItemTapped = new Command<Item>(OnItemSelected);

            AddItemCommand = new Command(OnAddItem);

            DeleteItemCommand = new Command<Item>(OnDeleteItem);
    }

        async Task ExecuteLoadItemsCommand()
        {
            IsBusy = true;

            try
            {
                Items.Clear();
                var items = await DataStore.GetItemsAsync(true);
                foreach (var item in items)
                {
                    Items.Add(item);
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
            finally
            {
                IsBusy = false;
            }
        }

        public void OnAppearing()
        {
            IsBusy = true;
            SelectedItem = null;
        }

        public Item SelectedItem
        {
            get => _selectedItem;
            set
            {
                SetProperty(ref _selectedItem, value);
                OnItemSelected(value);
            }
        }

        private async void OnAddItem(object obj)
        {
            await Shell.Current.GoToAsync(nameof(NewItemPage));
        }

        async void OnItemSelected(Item item)
        {
            if (item == null)
                return;

            // This will push the ItemDetailPage onto the navigation stack
            await Shell.Current.GoToAsync($"{nameof(ItemDetailPage)}?{nameof(ItemDetailViewModel.ItemId)}={item.Id}");
        }

        private void OnDeleteItem(Item item)
        {
            Items.Remove(item);
        }

    }
}

*Other code needed can be found by opening up default flyout menu template

How do I delete an Item on a swipe in the observable collection so that when I delete the Item it automatically is seen that the item is deleted?

Items.Remove(item); does not seem to work (no error, just does not remove the item from the CollectionView) Why is this?

await DataStore.DeleteItemAsync(item); has the same issue of nothing happening when the item is swiped.

Any help is appreciated.

Upvotes: 0

Views: 4786

Answers (5)

David Morrow
David Morrow

Reputation: 294

Instead use CollectionView Selection with multiple. use OnCollectionViewSelectionChanged event in codebehind to delete items

      <CollectionView ItemsSource="{Binding Monkeys}"
      SelectionMode="Multiple"
      SelectionChanged="OnCollectionViewSelectionChanged">
      ...
          </CollectionView>
           CollectionView collectionView = new CollectionView
          {
             SelectionMode = SelectionMode.Multiple
           };
          collectionView.SetBinding(ItemsView.ItemsSourceProperty, "Monkeys");
          collectionView.SelectionChanged += 
           OnCollectionViewSelectionChanged;


               void OnCollectionViewSelectionChanged(object sender, SelectionChangedEventArgs e)
               {
                    var previous = e.PreviousSelection;
                    var current = e.CurrentSelection;
                     ...
                }

Upvotes: 0

David Morrow
David Morrow

Reputation: 294

Is SwipeView even supported by Command?

Goto page 221 in this PDF found this in Xamarin.Forms Documentation. download here

https://opdhsblobprod03.blob.core.windows.net/contents/332e36c8b2484d748610a3dd6b6e98d6/190868cccec033e90406d3cafdb40d3d?skoid=29100048-1fa1-4ada-b0e0-e2aa294fc66a&sktid=975f013f-7f24-47e8-a7d3-abc4752bf346&skt=2022-09-18T13%3A52%3A55Z&ske=2022-09-25T13%3A57%3A55Z&sks=b&skv=2020-10-02&sv=2020-08-04&se=2022-09-20T23%3A36%3A35Z&sr=b&sp=r&sig=0myHYJIb3aEzMUIuCtmXn4VN%2F0MC8gzG1k7%2BQCMZVno%3D

To allow ViewModels to be moreindependent of particular user interface objects but still allow methods to be

called within the ViewModel,a command interfaceexists.This command interface is supported by the following elements in Xamarin.Forms: Button MenuItem ToolbarItem SearchBar TextCell (and hencealso ImageCell ) ListView TapGestureRecognizer With theexception of the SearchBar and ListView element, theseelements definetwo properties: Command of type System.Windows.Input.ICommand CommandParameter of type Object The SearchBar defines SearchCommand and SearchCommandParameter properties, whilethe ListView defines a RefreshCommand property of type ICommand . The ICommand interface defines two methods and oneevent: void Execute(object arg) bool CanExecute(object arg) event EventHandler CanExecuteChange

Upvotes: 0

David Morrow
David Morrow

Reputation: 294

Trying same thing with Tabbed page from default project. Noticed that Command is not running at all not matter what I do. (using Debug)

   Try putting delete button in ItemDetailPage.
   In ItemDetailPageViewModel put this code.

        public Command DeleteCommand { get; set; }

             public ItemDetailViewModel()
             {
                 DeleteCommand = new Command(OnDelete);
              }

                 private async void OnDelete()
                  {
                    await DataStore.DeleteItemAsync(Id);
                    await Shell.Current.GoToAsync("..");
                   }



                       with ItemDetailPageViewModel you have the Id to delete 

              ItemDetailPage code here

                <?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:local="clr-namespace:XamarinDeleteItem.ViewModels"
         x:Class="XamarinDeleteItem.Views.ItemDetailPage"
         Title="{Binding Title}">

             <ContentPage.BindingContext>
                  <local:ItemDetailViewModel />
             </ContentPage.BindingContext>

<StackLayout Spacing="20" Padding="15">
    <Label Text="Text:" FontSize="Medium" />
    <Label Text="{Binding Text}" FontSize="Small"/>
    <Label Text="Description:" FontSize="Medium" />
    <Label Text="{Binding Description}" FontSize="Small"/>
    <Button Text="Delete" Command="{Binding DeleteCommand}" />
</StackLayout>

Upvotes: 0

DeveloperLV
DeveloperLV

Reputation: 1781

Change your parameter to this

 CommandParameter="{Binding Source={RelativeSource Self}, Path=BindingContext}"

Upvotes: 3

Jason
Jason

Reputation: 89129

the code you have in OnDeleteItem is nonsensical. It should look something like this

private void OnDeleteItem(Item item)
{
    Items.Remove(item);
}

Upvotes: 0

Related Questions