locked
Couple of problems with ListView context actions (UWP) RRS feed

  • Question

  • User22916 posted

    I'm having a problem with the list view context action (delete) on a UWP project. I have no idea of the same issues apply on iOS or Android.

    1) If you add three items to a list, use the context action to delete one of them, then add another new item to the list, then you cannot delete the third item on the list any longer even tho the context menu appears. The context action command parameter references the previously deleted item even tho it no longer exists in the datasource. You can delete any of the other items, but then you get a similar unable to delete issue with any other new ones that you add.

    2) If the list view's viewcell is a stack layout of horizontal labels, swiping on whitespace between labels does not display the context menu. Using a Grid behaves as expected.

    <?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="Views.TestPage2">
    
      <StackLayout>
        <Button Text="Button"  Command="{Binding ButtonCommand}" />
        <Label Text="{Binding Counter}"/>
    
        <ListView x:Name="ItemsListView" ItemsSource="{Binding DataSource}"
                                         HorizontalOptions = "LayoutOptions.FillAndExpand"
                                         VerticalOptions = "LayoutOptions.FillAndExpand">
          <ListView.ItemTemplate>
            <DataTemplate>
              <ViewCell>
                <ViewCell.ContextActions>
                  <MenuItem Clicked="OnDelete" CommandParameter="{Binding .}"  Text="Delete" IsDestructive="True" />
                </ViewCell.ContextActions>
    
                <StackLayout Orientation="Horizontal"  Padding="10,0,10,0">
                  <Label   Text="{Binding Title}"  FontSize="Medium" HorizontalOptions="StartAndExpand"/>
                  <Label  Text="{Binding Quantity}" FontSize="Medium" HorizontalOptions="EndAndExpand"/>
                </StackLayout>
              </ViewCell>
            </DataTemplate>
          </ListView.ItemTemplate>
        </ListView>
      </StackLayout>
    </ContentPage>
    
    
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    using Xamarin.Forms;
    
    namespace Views
    {
        public partial class TestPage2 : ContentPage
        {
            public TestPage2()
            {
                InitializeComponent();
                var model = new TestListViewModel();
                this.BindingContext = model;
            }
    
            public void OnDelete(object sender, EventArgs e)
            {
                var mi = ((MenuItem)sender);
                ((TestListViewModel)this.BindingContext).RemoveItem((TestListViewDataItem)mi.CommandParameter);
            }
        }
    
        public class TestListViewModel : INotifyPropertyChanged
        {
            public TestListViewModel()
            {
                DataSource = new ObservableCollection<Views.TestListViewDataItem>();
    
                ButtonCommand = new Command(() =>
                {
                    Counter++;
                    DataSource.Add(new Views.TestListViewDataItem(string.Format("item : {0}", Counter), 0));
                });
            }
    
            public void RemoveItem(TestListViewDataItem item)
            {
                DataSource.Remove(item);
            }
    
            public Command ButtonCommand { get; set; }
    
            public ObservableCollection<TestListViewDataItem> DataSource { get; set; }
    
            public TestListViewDataItem SelectedItem { get; set; }
    
            public int Counter { get; set; }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected void OnPropertyChanged(string PropertyName)
            {
                PropertyChangedEventHandler handler = PropertyChanged;
    
                if (handler != null)
                    handler(this, new PropertyChangedEventArgs(PropertyName));
            }
    
        }
    
        public class TestListViewDataItem 
        {
            public TestListViewDataItem(string title, int quantity)
            {
                Title = title;
                Quantity = quantity;
            }
    
            public string Title { get; set; }
    
            public int Quantity { get; set; }
        }
    }
    
    Monday, January 16, 2017 2:12 PM

All replies

  • User50078 posted

    To your first problem I found a workaround because I struggled with the same problem. The MenuItem of an old ViewCell still existed even after the entry was removed and a new was inserted. I found out that I have to clear the ContextActions list of a ViewCell when I remove an entry in the ListView.

    Steps to do:

    1. Give the ViewCell a name and set the BindingContext of the MenuItem to the ViewCell

    <ViewCell x:Name="ViewCell"> <ViewCell.ContextActions> <MenuItem Clicked="OnDelete" CommandParameter="{Binding Source{x:Reference ViewCell}}" Text="Delete" IsDestructive="True" /> </ViewCell.ContextActions> ... </ViewCell>

    1. In the OnDelete method of the code behind file you have to do following adjustments

    public void OnDelete(object sender, EventArgs e) { var mi = (MenuItem)sender; var viewCell = (ViewCell)mi.BindingContext; var listViewDataItem = viewCell.BindingContext; viewCell.ContextActions.Clear(); ((TestListViewModel)this.BindingContext).RemoveItem((TestListViewDataItem)listViewDataItem); }

    The BindingContext of the ViewCell should be the same like before at the MenuItem. It is your TestListViewDataItem.

    Wednesday, July 12, 2017 11:33 AM
  • User50078 posted

    To your first point I have a workaround because I struggled with the same problem. When I deleted an entry in a ListView and added a new one the bound CommandParameter was the deleted object which was a problem when I tried to delete it again.

    To make it work for you you have to do the following adjustments:

    1. Give the ViewCell a name and set the BindingContext of the MenuItem to this ViewCell

    xaml <ViewCell x:Name="ViewCell"> <ViewCell.ContextActions> <MenuItem Clicked="OnDelete" BindingContext="{Binding {x:Reference ViewCell}}" Text="Delete" IsDestructive="True" /> </ViewCell.ContextActions> <!--...--> </ViewCell>

    1. The next step is to adjust the OnDelete method of the code behind file

    c public void OnDelete(object sender, EventArgs e) { var mi = (MenuItem)sender; var viewCell = (ViewCell)mi.BindingContext; var listViewDataItem = viewCell.BindingContext; viewCell.ContextActions.Clear(); ((TestListViewModel)this.BindingContext).RemoveItem((TestListViewDataItem)listViewDataItem); }

    To get your TestListViewDataItem you can use the BindingContext of the ViewCell - it should be the same. Now it should work. Notice: It is just a workaround. I hope in the future (with an update) you can remove this workaround and use it like you did it before.

    Wednesday, July 12, 2017 11:53 AM