Premo
Premo

Reputation: 61

How to update a ListView after deleting an item?

In my application I drag and drop an item from the ListView in the trash button and it removes the item. To see the change I have to go back a page and then open again the next page to see that the item was deleted. What I want is when I drop the item to the trash button to show the update the same time.

With the code I have right now, when I go to the page the ListView loads for a second and then it disappears.

Can anyone tell me how to solve this? Here is my code:

public sealed partial class BasketPage : Page, INotifyPropertyChanged
{

    private MobileServiceCollection<Information, Information> tempItem;
    private ObservableCollection<Information> items;
    private ObservableCollection<Information> RefreshedItems { get; set; }
    private IMobileServiceTable<Information> informationTable = App.MobileService.GetTable<Information>();
    private Information selectedInformation;


    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public BasketPage()
    {
        this.InitializeComponent();
        this.items = new ObservableCollection<Information>();
        this.refreshedItems = new ObservableCollection<Information>();

    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        getListData();

        base.OnNavigatedTo(e);
    }

    private async void getListData()
    {
        var query = App.conn.Table<Information>();

        foreach (var item in query)
        {

            tempItem = await informationTable
                .Where(todoItem => todoItem.Id == item.Name)
                .ToCollectionAsync();

            RefreshedItems.Add(tempItem.ElementAt(0));
        }           
    }
     private void BackButton_Click(object sender, RoutedEventArgs e)
    {
        if (Frame.CanGoBack)
        {
            Frame.GoBack();
        }
    }

    private void CollectedItemsListView_ItemClick(object sender, ItemClickEventArgs e)
    {
        selectedInformation = (Information)e.ClickedItem;

        Frame.Navigate(typeof(MediaViewPage), selectedInformation);
    }

    private void CollectedItemsListView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
    {
        var item = string.Join(",", e.Items.Cast<Information>().Select(i => i.Id));
        e.Data.SetText(item);
        e.Data.RequestedOperation = DataPackageOperation.Move;
    }

    private void TrashButton_DragOver(object sender, DragEventArgs e)
    {
        if (e.DataView.Contains(StandardDataFormats.Text))
        {
            e.AcceptedOperation = DataPackageOperation.Move;
        }
    }

    private async void TrashButton_Drop(object sender, DragEventArgs e)
    {
        if (e.DataView.Contains(StandardDataFormats.Text))
        {
            var id = await e.DataView.GetTextAsync();
            var query = App.conn.Table<Information>();
            var itemToDelete = query.Where(p => p.Name == id).FirstOrDefault();

            RefreshedItems.Remove(itemToDelete);

            App.conn.Delete(itemToDelete);
            App.conn.Commit();   
        }
    }
}

Here is the binding I do in the XAML file:

 <ListView x:Name="CollectedItemsListView"
                  ItemsSource="{Binding refreshedItems}"
...

Upvotes: 0

Views: 2783

Answers (1)

MikeT
MikeT

Reputation: 5500

The problem is the viewModel is deleting directly from the Model, not itself

i would suggest the following changes

firstly you need to delete or refresh one of your ObservableCollections as part of the delete process (refreshedItems), this will then immediately notify anything bound to it that a change has occurred

so remove CollectedItemsListView.ItemsSource = refreshedItems; and update the collection instead

refreshedItems.Remove(deletedItem);

second remove all setters from the ObservableCollection properties, you need to update the content of the collections not replace the collections

public sealed partial class BasketPage : Page, INotifyPropertyChanged
{
    private MobileServiceCollection<Information, Information> tempItem;
    private ObservableCollection<Information> Items{get;private set;}
    private ObservableCollection<Information> RefreshedItems{get;private set;}
    private IMobileServiceTable<Information> informationTable = App.MobileService.GetTable<Information>();
    private Information selectedInformation;


    public event PropertyChangedEventHandler PropertyChanged;

    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public BasketPage()
    {
        this.InitializeComponent();
        this.Items = new ObservableCollection<Information>();
        this.RefreshedItems = new ObservableCollection<Information>();

    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        getListData();

        base.OnNavigatedTo(e);
    }

    private async void getListData()
    {
        var query = App.conn.Table<Information>();

        foreach (var item in query)
        {

            tempItem = await informationTable
                .Where(todoItem => todoItem.Id == item.Name)
                .ToCollectionAsync();

            RefreshedItems.Add(tempItem.ElementAt(0));

        }
    }

    private void BackButton_Click(object sender, RoutedEventArgs e)
    {
        if (Frame.CanGoBack)
        {
            Frame.GoBack();
        }
    }

    private void CollectedItemsListView_ItemClick(object sender, ItemClickEventArgs e)
    {
        selectedInformation = (Information)e.ClickedItem;

        Frame.Navigate(typeof(MediaViewPage), selectedInformation);
    }

    private void CollectedItemsListView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
    {
        var item = string.Join(",", e.Items.Cast<Information>().Select(i => i.Id));
        e.Data.SetText(item);
        e.Data.RequestedOperation = DataPackageOperation.Move;
    }

    private void TrashButton_DragOver(object sender, DragEventArgs e)
    {
        if (e.DataView.Contains(StandardDataFormats.Text))
        {
            e.AcceptedOperation = DataPackageOperation.Move;
        }
    }

    private async void TrashButton_Drop(object sender, DragEventArgs e)
    {
        if (e.DataView.Contains(StandardDataFormats.Text))
        {
            var id = await e.DataView.GetTextAsync();
            var query = App.conn.Table<Information>();
            var itemToDelete = query.Where(p => p.Name == id).FirstOrDefault();

            RefreshedItems.Remove(itemToDelete);
            App.conn.Delete(itemToDelete);
            App.conn.Commit();   
        }
    }
}

EDIT: As your having trouble with the suggested changes here is a complete working example

XAML

<Window x:Class="CollectionBindingDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CollectionBindingDemo"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:CollectionList x:Name="CollectionList"/>
    </Window.DataContext>
    <StackPanel>
        <Button Click="Add_Click">add</Button>
        <Button Click="Remove_Click">remove</Button>
        <ListView x:Name="ListVeiw" ItemsSource="{Binding IntegerNumbers}"/>
    </StackPanel>
</Window>

Code Behind

namespace CollectionBindingDemo
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Add_Click(object sender, RoutedEventArgs e)
        {
            CollectionList.Add();
        }

        private void Remove_Click(object sender, RoutedEventArgs e)
        {
            var i = ListVeiw.SelectedItem as int?;
            if(i.HasValue)
                CollectionList.Remove(i.Value);
        }
    }
}

ViewModel

namespace CollectionBindingDemo
{
    public class CollectionList
    {
        private Random rnd = new Random();
        public ObservableCollection<int> IntegerNumbers { get; } = new ObservableCollection<int>();


        public void Add()
        {
            IntegerNumbers.Add(rnd.Next(1000));
        }
        public void Remove(int i)
        {
            IntegerNumbers.Remove(i);
        }
    }
}

Upvotes: 3

Related Questions