1

I have these 3 classes

[AddINotifyPropertyChangedInterface]
public abstract class BaseViewModel
{

}

public abstract class XXXViewModelBase : BaseViewModel
{
   public int SelectedAreaId { get; set; }
}

public class XXXSelectorViewModel : XXXViewModelBase
{
    //public new int SelectedAreaId { get => base.SelectedAreaId; set => base.SelectedAreaId = value; }
    public void OnSelectedAreaIdChanged()
    {
        // Does not fire
    }
}

With the code as shown, OnSelectedAreaIdChanged does not fire when SelectedAreaId is changed. If I uncomment the code, then OnSelectedAreaIdChanged does fire.

Is that expected behaviour? Is there any way around this other than the way I've done it?

Neil
  • 11,059
  • 3
  • 31
  • 56
  • *Is that expected behaviour?* yes .. add abstract `OnSelectedAreaIdChanged` in your `XXXViewModelBase` then fody-propertychanged should generate OnSelectedAreaIdChanged call in setter – Selvin Jul 29 '20 at 13:12
  • @selvin That's an alternative, but it's a code smell in my book, as other implementations don't require it. – Neil Jul 29 '20 at 13:19
  • *other implementations don't require it.* what? how on the FSM sake "property change tool" would know that you ever will extend your `XXXViewModelBase` class with `OnSelectedAreaIdChanged` method? `XXX` prop and `OnXXXChanged` method should be in the same class – Selvin Jul 29 '20 at 13:21
  • On the other implementations, the UI uses `SelectedAreaId` but doesn't require notification of that property changing. – Neil Jul 29 '20 at 13:22
  • Then instead abstract add empty virtual method ... and you will override it in XXXSelectorViewModel – Selvin Jul 29 '20 at 13:25
  • eventually you can add `public XXXSelectorViewModel() { PropertyChanged += (....) { check if prop name is SelectedAreaId then call OnSelectedAreaIdChanged } }` – Selvin Jul 29 '20 at 13:27
  • Yes, that's a way around it, but it feels more like a limitation of Fody: 'the OnXXXChanged must be in the same class as the property it's notified on'. – Neil Jul 29 '20 at 13:28

2 Answers2

3

Yes it is expected behavior, because Fody doesn't support this. More Info Here

Visual Sharp
  • 3,093
  • 1
  • 18
  • 29
-1

Does this give you what you want?

public class BaseViewModel : INotifyPropertyChanged
{

    /// <summary>
    ///     Worker function used to set local fields and trigger an OnPropertyChanged event
    /// </summary>
    /// <typeparam name="T">Parameter class</typeparam>
    /// <param name="backingStore">Backing field referred to</param>
    /// <param name="value">New value</param>
    /// <param name="propertyName">Property that this value is applied to </param>
    /// <param name="onChanged">Event handler to invoke on value change</param>
    /// <param name="onChanging">Event handler to invoke on value changing</param>
    /// <returns></returns>
    protected bool SetProperty<T>(
        ref T backingStore, T value,
        [CallerMemberName] string propertyName = "",
        Action onChanged = null,
        Action<T> onChanging = null)
    {
        if (EqualityComparer<T>.Default.Equals(backingStore, value)) return false;

        onChanging?.Invoke(value);
        OnPropertyChanging(propertyName);

        backingStore = value;
        onChanged?.Invoke();
        OnPropertyChanged(propertyName);
        return true;
    }


    #region INotifyPropertyChanging implementation
    /// <summary>
    /// INotifyPropertyChanging event handler 
    /// </summary>
    public event PropertyChangingEventHandler PropertyChanging;

    /// <summary>
    /// INotifyOnPropertyChanging implementation
    /// </summary>
    /// <param name="propertyName">Class property that is changing</param>
    protected void OnPropertyChanging([CallerMemberName] string propertyName = "")
    {
        var changing = PropertyChanging;

        changing?.Invoke(this, new PropertyChangingEventArgs(propertyName));
    }

    #endregion

    #region INotifyPropertyChanged
    /// <summary>
    /// INotifyPropertyChanged event handler
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// INotifyPropertyChanged implementation
    /// </summary>
    /// <param name="propertyName">Class property that has changed</param>
    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        var changed = PropertyChanged;

        changed?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion
}

Example of use:

public class SomeViewModel: BaseViewModel
{
    private int _someValue;

    public int SomeValue
    {
        get => _someValue;
        set => SetProperty(ref _someValue, value);
    }
}
ChrisBD
  • 9,104
  • 3
  • 22
  • 35
  • Fody handles all of that for you. No need to implement INotifyPropertyChanged manually. – Neil Jul 29 '20 at 15:45
  • Fair enough. Mine was only a suggestion. I'll stick with my way as it would appear that in order for Fody to work you're not really writing any less code in your derived classes than I do currently. – ChrisBD Jul 30 '20 at 09:41
  • I came up with a 'simple' wrapper for all that functionality a few years ago (for a different client). Basically each property is defined as `PropertyWrapper` and it handles everything you have written here, including `OnChanged` delegates etc. I'd use it on this current project if I wasn't just adding a new feature. – Neil Jul 30 '20 at 11:23