9 Replies - 504 Views - Last Post: 13 September 2019 - 06:18 AM Rate Topic: -----

#1 ScottinTexas   User is offline

  • D.I.C Regular

Reputation: 12
  • View blog
  • Posts: 310
  • Joined: 13-March 12

Help Detect CollectionChanged

Posted 23 August 2019 - 07:52 AM

I'll try to describe this as clear as possible. I have an ObservableCollection<LoadSpecimenViewModel> to which I bind a DataGrid. This observable collection is a property in the TestResultViewModel. The test result view model has properties for averages of specimen values in the load specimens collection.



TestResultView
                <DataGrid DockPanel.Dock="Top"
                          Style="{StaticResource DataGridStyle}"
                          ItemsSource="{Binding LoadSpecimens}"
                          AutoGenerateColumns="False"
                          AlternatingRowBackground="Gray"
                          HorizontalAlignment="Stretch"
                          VerticalAlignment="Stretch"
                          SelectionMode="Single">
                    <DataGrid.Columns>
                        <DataGridCheckBoxColumn Binding="{Binding Exclude}" 
                                                Header="X"
                                                HeaderStyle="{StaticResource DataGridHeaderStyle}"/>
                        <DataGridTextColumn Binding="{Binding SpecimenNumber}" 
                                            Width=".05*" 
                                            Header="No."
                                            HeaderStyle="{StaticResource DataGridHeaderStyle}"/>
                        <DataGridTextColumn Binding="{Binding Thickness, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" 
                                            Width=".05*" 
                                            Header="Thick (mm)"
                                            HeaderStyle="{StaticResource DataGridHeaderStyle}"/>
                    </DataGrid.Columns>
                </DataGrid>




Load Specimen View Model

        public ObservableCollection<LoadSpecimenViewModel> LoadSpecimens
        {
            get { return loadSpecimens; }
            set{ loadSpecimens = value;}
        }

        public double LoadThicknessAverage
        {
            get
            {
                if(loadSpecimens.Count > 0)
                    return LoadSpecimens.Select(x => x.Thickness).Average();
                return 0;
            }
        }

        public double LoadThicknessStdDev
        {
            get
            {
                if (loadSpecimens.Count > 0)
                    return StdDev(LoadSpecimens.Select(x => x.Thickness).ToList());
                return 0;
            }
        }

        private void LoadSpecimensChanged(object sender, System.Collections.Specialized.NotifyCollectionchangedEventArgs e)
        {
            if(e.NewItems!=null && e.NewItems.Count > 0)
            {
                foreach (INotifyPropertyChanged item in e.NewItems.OfType<INotifyPropertyChanged>())
                {
                    item.PropertyChanged += LoadSpecimen_PropertyChanged;
                }
            }

            if (e.OldItems != null && e.OldItems.Count > 0)
            {
                foreach (INotifyPropertyChanged item in e.NewItems.OfType<INotifyPropertyChanged>())
                {
                    item.PropertyChanged -= LoadSpecimen_PropertyChanged;
                }
            }
        }

        private void LoadSpecimen_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            RaisePropertyChanged("LoadThicknessAverage");
        }

       public TestResultViewModel(TestResult tr)
        {
            testResult = tr;
            LoadSpecimens = new ObservableCollection<LoadSpecimenViewModel>(testResult.LoadSpecimens.Select(sp => new LoadSpecimenViewModel(sp)));
            LoadSpecimens.Collectionchanged += LoadSpecimensChanged;

            TestDirection = tr.TestDirection;
        }



When I change the thickness value in the data grid I want to recalculate the average and standard deviation. You can see where I tried to use the Collection Changed event to call on the Load average to refresh by invoking the property changed. But that didn't even get hit. A break point set at the get and set was never touched. When I change the value in the data grid, the property in the ViewModel is properly chaged and that calls PropertyChanged. But that should have fired ObservableCollectionchanged. I attached the code to respond to the collection changed event to the LoadSpecimens collection and expected this to be executed when the property in the collection changed. But no go. I have two similar datagrids and each has a cell that may need changing. When the cells change the other values in the view model change. The binding handles everything as expected, just can't get the calculated properties to refresh.

Thanks

Is This A Good Question/Topic? 0
  • +

Replies To: Help Detect CollectionChanged

#2 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 7056
  • View blog
  • Posts: 23,994
  • Joined: 05-May 12

Re: Help Detect CollectionChanged

Posted 23 August 2019 - 09:20 PM

It's currently very late for me right now so I can't do the digging for reference for you right now, but the short story is I've been bitten by this issue before. The collection changed event only fires when items are added or deleted from the collection, or the order of items in the collection changes. It does not fire when the properties of individual items within the collection change.
Was This Post Helpful? 0
  • +
  • -

#3 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 7056
  • View blog
  • Posts: 23,994
  • Joined: 05-May 12

Re: Help Detect CollectionChanged

Posted 24 August 2019 - 10:55 AM

Looking at your code above, it looks like you already knew that you needed to watch each item's properties for changes. So the questions are:
Are you getting the property change events?
If you are not getting the property change events, are you even registering for events? e.g. Is line 33 being hit?

As an aside, it looks like there is a typo on line 39. You did you really mean to iterate over the new items when your outer if statement on line 37 was guarding over the old items.
Was This Post Helpful? 0
  • +
  • -

#4 ScottinTexas   User is offline

  • D.I.C Regular

Reputation: 12
  • View blog
  • Posts: 310
  • Joined: 13-March 12

Re: Help Detect CollectionChanged

Posted 26 August 2019 - 02:04 PM

Skydiver, thanks for your replies. Line 33 was not getting hit and, no, I wasn't getting the property changed events. But I did fix the typo and then re-organized and simplified. I got rid of the unnecessary viewmodel for the loads. A load is a very simple class with 7 properties, one of which is thickness. This is the value that is displayed in the DataGrid and may be changed by the user.

The collection of specimens is in an ObservableCollection in a Result Class. I tried adding the Event handler in the result class. After all, that's where the collection lives. But the event handler was not even seen when the individual items were added to the collection made for them. Besides, the ViewModel is the proper place for that sort of logic. So I put it in the ResultViewModel.

When the user selects a result, it is the parameter for a new ResultViewModel

       public ResultViewModel(Result res)
       {
           LoadSpecimens=res.LoadSpecimens; 
       }

       public ObservableCollection<LoadSpecimen> LoadSpecimens
        {
            get
            {
                if (loadSpecimens == null)
                {
                    loadSpecimens = new ObservableCollection<LoadSpecimen>();
                    loadSpecimens.Collectionchanged += this.LoadSpecimens_Collectionchanged;
                }
                return loadSpecimens;
            }
            set
            {
                loadSpecimens = value;
                RaisePropertyChanged();
            }

        private void LoadSpecimens_Collectionchanged(object sender, NotifyCollectionchangedEventArgs e)
        {
            foreach (INotifyPropertyChanged item in e.NewItems.OfType<INotifyPropertyChanged>())
            {
                item.PropertyChanged += LoadSpecimen_PropertyChanged;
            }

            foreach (INotifyPropertyChanged item in e.OldItems.OfType<INotifyPropertyChanged>())
            {
                item.PropertyChanged -= LoadSpecimen_PropertyChanged;
            }
        }

        private void LoadSpecimen_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            RaisePropertyChanged("LoadThicknessAverage");
        }


.

So I am missing something real obvious right there, aren't I?

What else do you need to know? Just ask.
Was This Post Helpful? 0
  • +
  • -

#5 ScottinTexas   User is offline

  • D.I.C Regular

Reputation: 12
  • View blog
  • Posts: 310
  • Joined: 13-March 12

Re: Help Detect CollectionChanged

Posted 26 August 2019 - 02:24 PM

One small change. I moved the line "loadSpecimens.Collectionchanged += this.LoadSpecimens_Collectionchanged;" outside the if. When I ran it LoadSpecimens_Collectionchanged was executed. It didn't cycle through the properties and add the PropertyChanged event. So nothing happened when I changed the thickness value. However, the value is persistent.
Was This Post Helpful? 0
  • +
  • -

#6 Skydiver   User is offline

  • Code herder
  • member icon

Reputation: 7056
  • View blog
  • Posts: 23,994
  • Joined: 05-May 12

Re: Help Detect CollectionChanged

Posted 26 August 2019 - 07:06 PM

Well, yes, the collection changed event does not fire because you've not added or remove items from the collection. I think that you'll need to forcibly have to register for property notifications on each item in the collection when you initially construct the collection.

Alternatively, can you first create an empty observable collection. Register for collection change events, and then finally load the items into the collection. That way the collection change event fires, and that event handler will register for property change events on the items.
Was This Post Helpful? 1
  • +
  • -

#7 ScottinTexas   User is offline

  • D.I.C Regular

Reputation: 12
  • View blog
  • Posts: 310
  • Joined: 13-March 12

Re: Help Detect CollectionChanged

Posted 27 August 2019 - 05:05 AM

I took the second option. In the ctor I create the collection, then I register the Collectionchanged event, then I assign the values to the collection. It is as if there was no event handler registered to the collection. When I assign the values to the collection, nothing. Crickets. I think I heard an owl in the distance. So. I went with your first suggestion that said "I think that you'll need to forcibly have to register for property notifications on each item in the collection when you initially construct the collection."

    //in the constructor
   loadSpecimens = new ObservableCollection<LoadSpecimen>();
   loadSpecimens.Collectionchanged += this.LoadSpecimens_Collectionchanged;
   loadSpecimens = tr.LoadSpecimens;
   RegisterPropertyChangedEvents();

    //new method 

   private void RegisterPropertyChagedEvents()
   {
       foreach (INotifyPropertyChanged item in loadSpecimens)
       {
           item.PropertyChanged += LoadSpecimen_PropertyChanged;
       }
   }




This worked. Thank you for the help!
Was This Post Helpful? 0
  • +
  • -

#8 ScottinTexas   User is offline

  • D.I.C Regular

Reputation: 12
  • View blog
  • Posts: 310
  • Joined: 13-March 12

Re: Help Detect CollectionChanged

Posted 27 August 2019 - 06:23 AM

I spoke too soon. I did work. Twice. Then it began throwing "System.InvalidCastException: 'Unable to cast object of type '*****.Model.LoadSpecimen' to type 'System.ComponentModel.INotifyPropertyChanged'.'" exception. WTF? What I posted really did work.
Was This Post Helpful? 0
  • +
  • -

#9 h4nnib4l   User is offline

  • The Noid
  • member icon

Reputation: 1434
  • View blog
  • Posts: 2,037
  • Joined: 24-August 11

Re: Help Detect CollectionChanged

Posted 27 August 2019 - 08:13 AM

LoadSpecimen implements the interface, right? What happens if you use var or LoadSpecimen in your foreach instead of explicitly assigning it as INotifyPropertyChanged?

This post has been edited by h4nnib4l: 27 August 2019 - 09:08 AM

Was This Post Helpful? 0
  • +
  • -

#10 ScottinTexas   User is offline

  • D.I.C Regular

Reputation: 12
  • View blog
  • Posts: 310
  • Joined: 13-March 12

Re: Help Detect CollectionChanged

Posted 13 September 2019 - 06:18 AM

I meant to reply on the same day, but I got side tracked. As h4nnib4l suggested I had implemented INotifyPropertyChanged on the model and called propertychanged on the property. This made everything works as it was intended.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1