ObervableCollection, CollectionChanged and PropertyChanged

266 Views Asked by At

I've been trying to figure out how to solve my problem for days.

I have a data structure similar to this (I use PropertyChanged.Fody library):

public class Item1 : INotifyPropertyChanged
{
     public event PropertyChangedEventHandler PropertyChanged;

     public int Somma { get => this.Lista.Sum(o => o.Valore);}
     public ObservableCollection<Item2> Lista {get; set;}
     /*Other fields*/
}

public class Item2 : INotifyPropertyChanged
{
     public event PropertyChangedEventHandler PropertyChanged;

     public int Valore {get; set;}
     /*Other fields*/
}

In my View I have two ListView like (lista is an ObservableCollection of Item1):

<mr:ListView x:Name="listView1" ItemsSource="{Binding lista}" SelectedItem="{Binding listView1Selected, Mode=TwoWay}">
    <mr:ListView.ItemTemplate>
        <DataTemplate>
            <mr:ViewCell>
                <StackLayout Orientation="Horizontal">
                    <Label Text="{Binding Somma}" TextColor="Black" />
                </StackLayout>
            </mr:ViewCell>
        </DataTemplate>
    </mr:ListView.ItemTemplate>
</mr:ListView>
<mr:ListView x:Name="listView2" ItemsSource="{Binding listView1Selected.Lista}" SelectedItem="{Binding listView2Selected, Mode=TwoWay}">
    <mr:ListView.ItemTemplate>
        <DataTemplate>
            <mr:ViewCell>
                <StackLayout Orientation="Vertical" >
                    <Label Text="{Binding Valore}" TextColor="Black" />
                </StackLayout>
            </mr:ViewCell>
        </DataTemplate>
    </mr:ListView.ItemTemplate>
</mr:ListView>

When I call in my ViewModel.cs something that change the value of the field Valore of some Item2, I would like the Sum of Item1 to be recalculated and automatically binded. I tried replacing ObservableCollection with a class like:

public class ItemsChangeObservableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
    {       
        public ItemsChangeObservableCollection() : base()
        {            
        }

        public ItemsChangeObservableCollection(IEnumerable<T> list) : base(list)
        {            
            RegisterPropertyChanged(list.ToList());            
        }

        public ItemsChangeObservableCollection(List<T> list): base(list)
        {            
            RegisterPropertyChanged(list);
        }  
     
        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                RegisterPropertyChanged(e.NewItems);
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                UnRegisterPropertyChanged(e.OldItems);
            }
            else if (e.Action == NotifyCollectionChangedAction.Replace)
            {
                UnRegisterPropertyChanged(e.OldItems);
                RegisterPropertyChanged(e.NewItems);
            }

            base.OnCollectionChanged(e);            
        }

        protected override void ClearItems()
        {
            UnRegisterPropertyChanged(this);
            base.ClearItems();
        }

        private void RegisterPropertyChanged(IList items)
        {
            foreach (INotifyPropertyChanged item in items)
            {
                if (item != null)
                {
                    item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
                }
            }
        }

        private void UnRegisterPropertyChanged(IList items)
        {
            foreach (INotifyPropertyChanged item in items)
            {
                if (item != null)
                {
                    item.PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
                }
            }
        }

        private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }       
    }

But it still doesn't work. Can anyone please help me?

EDIT

Using the ItemsChangeObservableCollection class I can arrive into his method item_PropertyChanged when I update Valore field. So I would expect that calling

base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));

a CollectionChanged it's generated...but it isn't. If I add in Item1 constructor like

public Item1()
{
    Lista= new ItemsChangeObservableCollection<Item2>();
    Lista.CollectionChanged += MyCollectionChanged;     
}

MyCollectionChanged is never fired. Why????

1

There are 1 best solutions below

0
On

I realized what was wrong. The CollectionChanged hadler, that I tried to add in the Item1 Constructor, was overridden when I've instantiated the list in my viewmodel.

Adding CollectionChanged after the initialization in my view model resolve the problem.