How do I observe and update only the changed element in a collection?

ReedReed Member ✭✭

I don't want to call OnPropertyChanged on a collection whenever only one property of a element is updated, I see two ways of doing that:

  1. Use CollectionChanged event handler on ObservableCollection (https://stackoverflow.com/questions/8986105/how-to-detect-if-an-item-in-my-observablecollection-has-changed)
  2. Wrap the model with another class, implement INotifyPropertyChanged, override the setters to call OnPropertyChanged and put the wrapped classes to ObservableCollection and bind to it.

Which way would adhere to MVVM principles more and which method do you prefer?

Tagged:

Answers

  • PatrickJelittoPatrickJelitto Member ✭✭✭

    personally, i use the first method for my apps and never had a problem with that. but i cannot say if it adheres more to MVVM principles or not. greets

  • ReedReed Member ✭✭

    @PatrickJelitto said:
    personally, i use the first method for my apps and never had a problem with that. but i cannot say if it adheres more to MVVM principles or not. greets

    Thanks for your reply. How did you decide to go that way?

    Maybe you know some good blog posts that support this approach?

  • FaizalSaidaliFaizalSaidali USMember ✭✭✭

    Hi @Reed,
    I think you can move with both ways, but I followed the second method. Because in this way we didn't destroy the MVVM architecture. We can simply reuse this model(Item view model) to its detail page as well.

  • yelinzhyelinzh Member, Xamarin Team Xamurai
    edited May 29

    I suggest you implement INotifyPropertyChanged.

    Check the tutorial about Interactive MVVM:
    https://docs.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xaml-basics/data-bindings-to-mvvm#interactive-mvvm

  • ReedReed Member ✭✭

    @FaizalSaidali said:
    Hi @Reed,
    I think you can move with both ways, but I followed the second method. Because in this way we didn't destroy the MVVM architecture. We can simply reuse this model(Item view model) to its detail page as well.

    @yelinzh said:
    I suggest you implement INotifyPropertyChanged.

    Check the tutorial about Interactive MVVM:
    https://docs.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xaml-basics/data-bindings-to-mvvm#interactive-mvvm

    Thanks for you replies, I'm leaning towards this option too.

    How do you deal with having to implement INotifyPropertyChanged methods on every class that implements the interface? Do you have it implemented in a base class and have the classes inherit from it?

  • FaizalSaidaliFaizalSaidali USMember ✭✭✭

    Try this

  • JohnHardmanJohnHardman GBUniversity mod

    @FaizalSaidali - For simple pieces of code like that, can you include them in your post, rather than attach a separate file pls. It makes things easier for people browsing/searching the forums.

  • yelinzhyelinzh Member, Xamarin Team Xamurai

    @Reed

    Do you have it implemented in a base class and have the classes inherit from it?

    If you need to inherit multiple models from INotifyPropertyChanged, you can create a base class. If there is only a model, inherit from INotifyPropertyChanged directly.

  • FaizalSaidaliFaizalSaidali USMember ✭✭✭

    @JohnHardman said:
    @FaizalSaidali - For simple pieces of code like that, can you include them in your post, rather than attach a separate file pls. It makes things easier for people browsing/searching the forums.

    Sure @JohnHardman,

        public class BaseViewModel : INotifyPropertyChanged
        {
            #region INotifyPropertyChanged
            public event PropertyChangedEventHandler PropertyChanged;
            protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
            {
                var changed = PropertyChanged;
                if (changed == null)
                    return;
    
                changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
    
            protected bool SetProperty<T>(ref T backingStore, T value, [CallerMemberName]string propertyName = "", Action onChanged = null)
            {
                if (EqualityComparer<T>.Default.Equals(backingStore, value))
                    return false;
    
                backingStore = value;
                onChanged?.Invoke();
                OnPropertyChanged(propertyName);
                return true;
            }
            #endregion
        }
    
        public class SampleViewModel : BaseViewModel
        {
            #region Fields
            private ObservableCollection<UserItemViewModel> users;
            #endregion
    
            #region Properties
            public Users Users
            {
                get { return user; }
                set { SetProperty(ref user, value); }
            }
            #endregion
    
            public SampleViewModel()
            {
                Users = new ObservableCollection<UserItemViewModel>();
            }
        }
    
        public class UserItemViewModel : BaseViewModel
        {
            #region Fields
            string name;
            string age;
            #endregion
    
            #region Properties
            public string Name
            {
                get { return name; }
                set { SetProperty(ref name, value); }
            }
    
            public string Age
            {
                get { return age; }
                set { SetProperty(ref age, value); }
            }
            #endregion
        }
    
  • PatrickJelittoPatrickJelitto Member ✭✭✭

    @Reed i do it like @FaizalSaidali and implement it in the baseclass

  • ReedReed Member ✭✭

    Do you always implement the INotifyPropertyChanged on the model class itself?

    For example, if you get a list of Order from some API, do you re-map that list to another list of WrappedOrder (that implements INotifyPropertyChanged interface) to keep the model as pure as possible?

  • PatrickJelittoPatrickJelitto Member ✭✭✭

    i would probably register an oncollectionchanged event on that list that gets filled from the api and call OnPropertyChanged() in that event

  • JoeMankeJoeManke USMember ✭✭✭✭✭

    @Reed said:
    Do you always implement the INotifyPropertyChanged on the model class itself?

    For example, if you get a list of Order from some API, do you re-map that list to another list of WrappedOrder (that implements INotifyPropertyChanged interface) to keep the model as pure as possible?

    I make my models as basic as possible (just properties with default { get; set; } accessors) and always wrap them in something that implements INotifyPropertyChanged and anything else it needs for interaction with the UI layer.

  • ReedReed Member ✭✭
    > @JoeManke said:
    > @Reed said:
    > Do you always implement the INotifyPropertyChanged on the model class itself?
    >
    > For example, if you get a list of Order from some API, do you re-map that list to another list of WrappedOrder (that implements INotifyPropertyChanged interface) to keep the model as pure as possible?
    >
    >
    >
    >
    >
    > I make my models as basic as possible (just properties with default { get; set; } accessors) and always wrap them in something that implements INotifyPropertyChanged and anything else it needs for interaction with the UI layer.

    Thanks. One more question.

    Do you literally wrap the model by passing the model to the wrapper class' constructor and delegating all its getters and setters to the wrapped model or f. ex. once you get model from some source, you map it to the "wrapper" class using AutoMapper or something?
  • JoeMankeJoeManke USMember ✭✭✭✭✭

    Depends on the data. If it's something that will be edited and saved I usually keep the model around, if it's just displayed I usually just create the wrapper and throw away the model.

Sign In or Register to comment.