0

There is a observable collection in my class and I want to be notified when the observable collection changes.

I searched on stackoverflow and found this code

 private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (var item in e.OldItems)
                {
                    //Removed items
                    
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Add)
            {
                foreach (var item in e.NewItems)
                {
                    //Added items
                }
            }
            else if(e.Action == NotifyCollectionChangedAction.Replace){
                foreach (var item in e.NewItems)
                {

                }
            }
        }

Every thing was working fine. I get notified when something created and deleted. but I didnt get notified when an instance edited. what did I do wrong?

Edit: I changed the observable collection to binding list but nothing is happeng when I edit something.

 <ListView Grid.Row="1" ItemsSource="{Binding Contacts}">
        <ListView.View>
        <GridView>
            <GridView.Columns>
                <GridViewColumn>
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                                <TextBox Text={Binding Name}></TextBox>
                                <TextBox Text={Binding Email}><TextBox>
                            </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
            </GridView.Columns>
        </GridView>
        </ListView.View>
    </ListView>

And my viewModel:

    private BindingList<ContactViewModel> _contacts;
    public IEnumerable<ContactViewModel> Contacts=>_contacts;
    public ContactListing()
    {
        _contacts.ListChanged +=_contacts_ListChanged;          

    }

    private void _contacts_ListChanged(object sender, ListChangedEventArgs e)
    {
        //Get Notified When somthing is edited
    }

in the xaml I have some texboxes that when the text of them changes, The binding list or observable collection also changes(I set a breakpoint and the list was changing everytime something edited) but the ListChanged event doesnot call.

Eboy
  • 61
  • 7
  • 1
    The observable collection notification is about adding and removing the items in the collection. Inotifypropertychanged is the thing for property changes. You should always have that interface on any viewmodel. You can add an isdirty flag to a base viewmodel, handle the change notification event and set isdirty to true. Inherit from that base viewmodel for any viewmodels you want to know if they were edited. Then you can filter / search that collection for those which the user edited. – Andy Jan 15 '23 at 11:46
  • 1
    You can let the items implement the `INotifyPropertyChanged` interface and the listen to the `PropertyCHanged` event to get notified about changed properties. If the items serve as a binding source, for example to bind to elements of a DataTemplate, your items should have already implemented this interface ([How to: Implement Property Change Notification](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/how-to-implement-property-change-notification?view=netframeworkdesktop-4.8#example)). Alternatively create custom event(s) that the class can raise to notify about changes. – BionicCode Jan 15 '23 at 13:35
  • ObservableCollection itself does not track its contained items for changes. It only tracks its own changes. – BionicCode Jan 15 '23 at 13:35

1 Answers1

2

The collection element must implement INotifyPropertyChanged. And the collection needs to be replaced with BindingList< T>. Then in the ListChanged event you will receive a notification about the change in the properties of the elements of the collection. You will also have access to the AddingNew event, which occurs BEFORE an element is added to the collection.

It is also not working for editing. Its onliy working for creating and deleting.

Here is an example showing that it works when adding elements, and when changing any property of an element.

using Simplified;

namespace Core2023.SO.Eboy
{
    public class ItemInpc : BaseInpc
    {
        private int _value;
        private string title = string.Empty;

        public int Id { get; }
        public string Title { get => title; set => Set(ref title, value ?? string.Empty); }
        public int Value { get => _value; set => Set(ref _value, value); }

        public override string? ToString()
            => $"{Id}: {Title}-{Value}";

        public ItemInpc(int id)
        {
            Id = id;
        }
        public ItemInpc() : this(-1) { }
    }
}
using Simplified;
using System.ComponentModel;
using System.Linq;
using System.Text;

namespace Core2023.SO.Eboy
{
    public class ItemsViewModel : BaseInpc
    {
        private string _listChangedArgs = string.Empty;

        public BindingList<ItemInpc> Items { get; }

        public ItemsViewModel()
        {
            Items = new BindingList<ItemInpc>(
                "And the collection needs to be replaced"
                .Split()
                .Select((title, id) => new ItemInpc(id) { Title = title, Value = id * 23 })
                .ToList());
            Items.AddingNew += OnAddingNew;
            Items.ListChanged += OnListChanged;
        }

        public string ListChangedArgs { get => _listChangedArgs; set => Set(ref _listChangedArgs, value); }

        private void OnAddingNew(object? sender, AddingNewEventArgs e)
        {
            int id = Items.Max(it => it.Id) + 1;
            ItemInpc item = new ItemInpc(id);
            e.NewObject = item;
        }

        int i = 0;
        private void OnListChanged(object? sender, ListChangedEventArgs e)
        {
            StringBuilder builder = new StringBuilder();
            builder.Append(i);
            i++;
            builder.AppendLine(new string('-', 40));
            builder.AppendLine(e.ListChangedType.ToString());
            builder.AppendLine(e.PropertyDescriptor?.Name);
            builder.AppendLine(e.NewIndex.ToString());
            if (e.NewIndex < 0)
                builder.AppendLine($"{e.NewIndex}");
            else
                builder.AppendLine($"{e.NewIndex} {Items[e.NewIndex]}");
            if (e.OldIndex < 0)
                builder.AppendLine($"{e.OldIndex}");
            else
                builder.AppendLine($"{e.OldIndex} {Items[e.OldIndex]}");

            builder.AppendLine(ListChangedArgs);

            ListChangedArgs = builder.ToString();
        }
    }
}
<Window x:Class="Core2023.SO.Eboy.ItemChangedWindow"
        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:Core2023.SO.Eboy"
        mc:Ignorable="d"
        Title="ItemChangedWindow" Height="450" Width="800">
    <Window.DataContext>
        <local:ItemsViewModel/>
    </Window.DataContext>
    <UniformGrid Columns="2">
        <DataGrid ItemsSource="{Binding Items}"/>
        <TextBlock Text="{Binding ListChangedArgs}"/>
    </UniformGrid>
</Window>

enter image description here

BaseInpc class from here: https://stackoverflow.com/a/67617794/13349759 .

EldHasp
  • 6,079
  • 2
  • 9
  • 24
  • It is also not working for editing. Its onliy working for creating and deleting. – Eboy Jan 16 '23 at 06:06
  • @Eboy , I supplemented my answer with an implementation example. – EldHasp Jan 17 '23 at 19:01
  • Thank you for your time! Can you please put it on github that I could see the whole project? because I think the mvvm app that I wrote were wrong. – Eboy Jan 18 '23 at 08:57
  • @Eboy , In the example, only the BaseInpc class code is missing. I have added a link to it. I don’t have a ready solution with this example, I made an example in a large general project and then deleted it from there. But if you can not assemble the solution yourself as a whole - write. I'll create a complete example over the weekend in a standalone Solution and upload it to GitHub. – EldHasp Jan 18 '23 at 14:30
  • Oh! But what about the Set function in the 12th line? what are you using? – Eboy Jan 19 '23 at 06:25
  • 1
    @Eboy , The `Set(ref ...)` method is declared in the `BaseInpc` class. I gave a link to the source code of the `BaseInpc` class at the very end of my answer. Take a closer look. – EldHasp Jan 19 '23 at 15:28