Updating a view with new ObservableCollection causes duplicates in layout

Updating a view with new ObservableCollection causes duplicates in layout

cannot change observablecollection during a collectionchanged event
how to remove items from observablecollection in c#
observablecollection remove
wpf observablecollection collectionchanged not firing
c# observablecollection reset
observablecollection contains
wpf datagrid not updating observablecollection
observablecollection inotifypropertychanged

I have a simple app that displays content from an ObservableCollection of Labels depending on which button in an ObservableCollection of buttons is clicked. With the press of a 'Refresh' button, I want to replace the labels and buttons, and display those instead. For this, I have bindings to respective ObservableCollections, and I do .Clear() on each of the collections before refreshing. The ObservableCollections update correctly, but the elements that are rendered on the screen are both the old items along with the new ones. My layout does not seem to update itself, rather add a new layout beside it with the updated items. The following screenshots illustrate the problem:

On Load:

On Refresh:

How do I stop the old items (first button 1 and 2, Initial item 1) from displaying?

MainPage.xaml:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    x:Class="TestApp.MainPage"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:TestApp">

    <StackLayout>
        <Button Command="{Binding LoadCommand}" Text="Load" />
        <Button Command="{Binding RefreshCommand}" Text="Refresh" />
        <local:BindableStackLayout
            HorizontalOptions="FillAndExpand"
            Items="{Binding ButtonCollection}"
            Orientation="Horizontal"
            VerticalOptions="Start" />
        <local:BindableStackLayout
            HorizontalOptions="FillAndExpand"
            Items="{Binding ItemCollection}"
            VerticalOptions="FillAndExpand" />
    </StackLayout>
</ContentPage>

MainPage.xaml.cs:

using Xamarin.Forms;

namespace TestApp
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
            this.BindingContext = new MyViewModel();
        }
    }
}

MyViewModel.cs:

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Xamarin.Forms;

namespace TestApp
{
    class MyViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public ObservableCollection<View> ButtonCollection { get; set; }
        public ObservableCollection<View> ItemCollection { get; set; }
        public Command LoadCommand { get; set; }
        public Command RefreshCommand { get; set; }
        public List<string> ItemList { get; set; }
        public int SelectedItem { get; set; }
        public MyViewModel()
        {
            ButtonCollection = new ObservableCollection<View>();
            ItemCollection = new ObservableCollection<View>();
            LoadCommand = new Command(Load);
            RefreshCommand = new Command(Refresh);
        }

        public void SelectItem(int item)
        {
            SelectedItem = item;
            for (int i = 0; i < ItemList.Count; i++)
            {
                if (item == i)
                {
                    ItemCollection[i].IsVisible = true;
                    ButtonCollection[i].BackgroundColor = Color.Yellow;
                }
                else
                {
                    ItemCollection[i].IsVisible = false;
                    ButtonCollection[i].BackgroundColor = Color.LightGray;
                }
            }
        }

        public void Load()
        {
            ItemList = new List<string> { "Initial item 1", "Initial item 2" };
            for (int i = 0; i < ItemList.Count; i++)
            {
                int copy = i;
                ButtonCollection.Add(new Button { Command = new Command(() => { SelectItem(copy); }), FontSize = 32, Text = (i + 1).ToString(), HeightRequest = 100, HorizontalOptions = LayoutOptions.FillAndExpand });
                ItemCollection.Add(new Label { Text = ItemList[i] });
            }
            SelectItem(0);
        }

        public void Refresh()
        {
            ItemList = new List<string> { "Updated item 1", "Updated item 2", "Updated item 3" };
            ItemCollection.Clear();
            ButtonCollection.Clear();
            for (int i = 0; i < ItemList.Count; i++)
            {
                int copy = i;
                ButtonCollection.Add(new Button { Command = new Command(() => { SelectItem(copy); }), FontSize = 32, Text = (i + 1).ToString(), HeightRequest = 100, HorizontalOptions = LayoutOptions.FillAndExpand });
                ItemCollection.Add(new Label { Text = ItemList[i] });
            }


      System.Diagnostics.Debug.WriteLine(ItemCollection.Count);
        SelectItem(0);
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

}

BindableStackLayout.cs

using Xamarin.Forms;
using System.Collections.Specialized;
using System.Collections.ObjectModel;

namespace TestApp
{
    class BindableStackLayout : StackLayout
    {
        public static readonly BindableProperty ItemsProperty =
            BindableProperty.Create(nameof(Items), typeof(ObservableCollection<View>), typeof(BindableStackLayout), null,
                propertyChanged: (b, o, n) =>
                {
                    (n as ObservableCollection<View>).CollectionChanged += (coll, arg) =>
                    {
                        switch (arg.Action)
                        {
                            case NotifyCollectionChangedAction.Add:
                                foreach (var v in arg.NewItems)
                                    (b as BindableStackLayout).Children.Add((View)v);
                                break;
                            case NotifyCollectionChangedAction.Remove:
                                foreach (var v in arg.NewItems)
                                    (b as BindableStackLayout).Children.Remove((View)v);
                                break;
                        }
                    };
                });

        public ObservableCollection<View> Items
        {
            get { return (ObservableCollection<View>)GetValue(ItemsProperty); }
            set { SetValue(ItemsProperty, value); }
        }
    }
}

There's a seperate Action for when the list is cleared. So this should probably work:

switch (arg.Action)
{
    case NotifyCollectionChangedAction.Add:
        foreach (var v in arg.NewItems)
            (b as BindableStackLayout).Children.Add((View)v);
        break;
    case NotifyCollectionChangedAction.Remove:
        foreach (var v in arg.NewItems)
            (b as BindableStackLayout).Children.Remove((View)v);
        break;
    case NotifyCollectionChangedAction.Reset:
        (b as BindableStackLayout).Children.Clear();
        break;
}

https://docs.microsoft.com/en-us/dotnet/api/system.collections.specialized.notifycollectionchangedaction?view=netcore-2.0

[Bug] CollectionView dynamic item resizing duplicates sizing on , Updating a view with new ObservableCollection causes duplicates in layout. how to update observablecollection in wpf observablecollection update item notify The problem is that after you swap out one ObservableCollection for a new one, the view is still bound to the old one. A common “fix” for this problem is to fire a PropertyChanged event to notify the view that the collection has been replaced. This causes the view to unbind from the old one and rebind to the new one.


Calling Clear() on an ObservableCollection<T> doesn't raise several CollectionChanged events with an Action of NotifyCollectionChangedAction.Remove.

Try to handle NotifyCollectionChangedAction.Reset in your BindableStackLayout class or remove the items one by one in your view model:

public void Refresh()
{
    for (int i = ItemCollection.Count - 1; i >= 0; i--)
        ItemCollection.RemoveAt(i);
    for (int i = ButtonCollection.Count - 1; i >= 0; i--)
        ButtonCollection.RemoveAt(i);

    ItemList = new List<string> { "Updated item 1", "Updated item 2", "Updated item 3" };
    for (int i = 0; i < ItemList.Count; i++)
    {
        int copy = i;
        ButtonCollection.Add(new Button { Command = new Command(() => { SelectItem(copy); }), FontSize = 32, Text = (i + 1).ToString(), HeightRequest = 100, HorizontalOptions = LayoutOptions.FillAndExpand });
        ItemCollection.Add(new Label { Text = ItemList[i] });
    }

    System.Diagnostics.Debug.WriteLine(ItemCollection.Count);
    SelectItem(0);
}

Common mistakes while using ObservableCollection, Forms CollectionView View Recycling Issues #9365 Inserting a new item in to the bound observable collection causes some items below to  I subscribe the CollectionChanged event in the Form Class, the values are update in the listView but several time (they are duplicate, sometimes triplicate, sometimes in the right number). To explain better: if 12 items are update, sometimes i can see 12 item in the list view, sometimes 19, and so on.


After clear, just re-initialize the collections like below,

 ItemCollection = new ObservableCollection<View>();
 ButtonCollection = new ObservableCollection<View>();

Forms Bugs, This causes the view to unbind from the old one and rebind to the new one. In this situation, the view can't adjust to just the items that were  I had the same problem while reloading an observableCollection from an event (on DataReceived) raised by the serial port class. I used MVVM; I tried to update the collection with the BackgroundWorker, but it raised the "This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread.".


Update installation wizard package causing duplicate renderings in , 20663 – animating buttons inside a stack layout causes layout issues in 22277 – VS: Adding a new Class Library (Xamarin Forms Portable) project does not 23585 – [Android] ListView not updated when ObservableCollection is modified Forms apps have duplicate view at the end of the screen on Nexus 4 devices. I had a situation when data was being updated continuously, and I tried just replacing all items in an ObservableCollection with the new items. This would have worked except that the DataTemplate had a Button , and the Button was not triggering the ICommand Binding .


A LINQ Tutorial: Adding/Updating/Deleting Data, Sign up or log in to view your list. There is a strange thing that the Sitecore Update Installation Wizard does for Standard Values items And maybe one of the four fields that it calculates the entire layout from is difference between The main reason why you will have duplicate renderings is that the Item ID is different. Modifying an ObservableCollection causes UI updates to happen on the same thread that performed the modifications. If the thread is not the primary UI thread, it will cause an exception. If the thread is not the primary UI thread, it will cause an exception.


ListView Data Sources, You can add new records to the database by creating new objects and adding them to the appropriate Table collection in your DataContext . If  Hello i have a problem when i add new item to observablecollection it duplicate the last item but when i scroll up and come back down the item appear i don't know what is happening i am using xamarin forms c# i din't want use insert because i want the item to be the last in the list .