3
votes

I have a ListView and populate it via DataBinding to a Lists Property in my ViewModel. Additionally, I have a menu for the ListView, with a Delete Command, also bound to my ViewModel. My Problem is now, if I have the ListView initialized, I can delete the lists in it. If I add new lists, I can delete all lists. But then, if I add new items, I can't delete them, because the List I get from the DeleteCommand is the old, already deleted list.

So, after deleting lists, they seem to be somehow, somewhere still present and I can only delete new lists, if the total amount of current lists is higher, than any previous amount of deleted lists.

I hope this is a somehow understandable explanation of my problem.

The Binding is working and Lists Property in my ViewModel holds the correct values, but the "sender" ItemList in the DeleteListCommand is the old ItemList.

Here is my XAML for my ListView:

<ListView x:Name="listView" ItemsSource="{Binding Lists}" >
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell x:Name="viewCell">
                <ViewCell.ContextActions>
                    <MenuItem Command="{Binding BindingContext.RenameListCommand, Source={x:Reference listView}}" CommandParameter="{Binding .}" Text="Rename" />
                    <MenuItem Command="{Binding BindingContext.DeleteListCommand, Source={x:Reference listView}}" CommandParameter="{Binding .}" IsDestructive="True" Text="Delete" />
                </ViewCell.ContextActions>
                <ContentView Margin="0,2,0,2"
                                    HeightRequest="50"
                                    BackgroundColor="{Binding Color}">

                    <ContentView.GestureRecognizers>
                        <TapGestureRecognizer BindingContext="{Binding Source={x:Reference listView}, Path=BindingContext}"
                                                        Command="{Binding ListTappedCommand}" 
                                                        CommandParameter="{Binding Source={x:Reference viewCell}, Path=BindingContext}" />
                    </ContentView.GestureRecognizers>
                    <ContentView.Content>

                        <Label Text="{Binding Name}"
                                    HorizontalTextAlignment="Center"
                                    VerticalTextAlignment="Center"
                                    TextColor="White" 
                                    IsEnabled="True"/>
                    </ContentView.Content>

                </ContentView>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

And here is my ViewModel:

...
public ObservableCollection<ItemList> lists = new ObservableCollection<ItemList>();
        public ObservableCollection<ItemList> Lists
        {
            get { return lists; }
            set
            {
                lists = value;
                OnPropertyChanged("Lists");
            }
        }
public event PropertyChangedEventHandler PropertyChanged;

...

this.DeleteListCommand = new Command<ItemList>((sender) =>
            {
                    OnDeleteList(sender);
            });

...

public ICommand DeleteListCommand { get; set; }
private void OnDeleteList(ItemList itemList)
        {
            Lists.Remove(itemList);
        }

...

protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
1
What is "ItemList"? is it observable too?Yuri S
It is just a normal class, used as a model for my DataBase for sqlite-net-pcl with string and bool properties. I also use this class for the ObservableCollection so that I don't have to cast to different types. How should I make it observable?mKay
so, it is not a list? I understood you are using nested lists. is that correct?Yuri S
I think this will answer your question. Unfortunately I cannot post it as an answer because I cannot take credit for that forums.xamarin.com/discussion/86578/…Yuri S
And the bug is already reported on bugzilla with the id 42516: bugzilla.xamarin.com/show_bug.cgi?id=42516mKay

1 Answers

3
votes

Based on sample project here is what you need to do: Name you cell "viewCell" because you need to pass cell to your model to be able to reset ContextAction. Change binding on menu item to pass the cell instead of ItemList. Then in model reset context action and get an item from binding context of the cell

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:local="clr-namespace:TobyList_XamarinForms"
                 xmlns:localVM="clr-namespace:TobyList_XamarinForms.ViewModels"
                 Title="Toby" 
             x:Class="TobyList_XamarinForms.Views.MasterPage">

    <StackLayout Padding="5" VerticalOptions="FillAndExpand" BackgroundColor="#F9F9F9">

        <StackLayout.BindingContext>
            <localVM:MasterPageViewModel />
        </StackLayout.BindingContext>


        <ListView x:Name="listView" ItemsSource="{Binding Lists}" CachingStrategy="RecycleElement">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell x:Name="viewCell">
                    <ViewCell.ContextActions>
                            <MenuItem Command="{Binding Path=BindingContext.DeleteListCommand, Source={x:Reference Name=listView}}" CommandParameter="{Binding Source={x:Reference viewCell}}" Text="Delete" />
                        </ViewCell.ContextActions>
                    <ContentView Margin="0,2,0,2"
                                        HeightRequest="50"
                                        BackgroundColor="{Binding Color}">

                        <ContentView.GestureRecognizers>
                            <TapGestureRecognizer BindingContext="{Binding Source={x:Reference listView}, Path=BindingContext}"
                                                            Command="{Binding ListTappedCommand}" 
                                                            CommandParameter="{Binding Source={x:Reference viewCell}, Path=BindingContext}" />
                        </ContentView.GestureRecognizers>
                        <ContentView.Content>

                            <Label Text="{Binding Name}"
                                        HorizontalTextAlignment="Center"
                                        VerticalTextAlignment="Center"
                                        TextColor="White"/>
                        </ContentView.Content>

                    </ContentView>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

        <StackLayout Orientation="Horizontal" HeightRequest="30" Margin="7">
            <Label Text="Add">
                <Label.GestureRecognizers>
                    <TapGestureRecognizer Command="{Binding AddListCommand}" />
                </Label.GestureRecognizers>
            </Label>

        </StackLayout>

    </StackLayout>

</ContentPage>

Model:

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Input;
using TobyList_XamarinForms.Models;
using Xamarin.Forms;
using System.Linq;

namespace TobyList_XamarinForms.ViewModels
{
    public class MasterPageViewModel : INotifyPropertyChanged
    {
        public ObservableCollection<ItemList> lists = new ObservableCollection<ItemList>();
        public ObservableCollection<ItemList> Lists
        {
            get { return lists; }
            set
            {
                lists = value;
                OnPropertyChanged("Lists");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public MasterPageViewModel()
        {
            this.AddListCommand = new Command(() =>
            {
                OnAddList();
            });
            //this.DeleteListCommand = new Command<ItemList>((sender) =>
            //{
            //  OnDeleteList(sender);
            //});

            this.DeleteListCommand = new Command<ViewCell>((sender) =>
            {
                OnDeleteList(sender);
            });

        }

        public ICommand AddListCommand { get; protected set; }
        private void OnAddList()
        {
            ItemList itemList = new ItemList() { Id = Guid.NewGuid().ToString().ToUpper(), Name = "Lorem Ipsum", Color = "#000000" };
            Lists.Add(itemList);
        }

        public ICommand DeleteListCommand { get; set; }
        //public void OnDeleteList(ItemList itemList)
        //      {
        //          Lists.Remove(itemList);
        //      }
        public void OnDeleteList(ViewCell viewCell)
        {
            viewCell.ContextActions.Clear();
            Lists.Remove((ItemList)viewCell.BindingContext);
        }

        protected void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}