1

I am trying to convert this working view model to one using ObservableObject from Community Toolkit to reduce boilerplate.

public class HslVM : INotifyPropertyChanged
{
    private float hue, saturation, luminosity;
    private Color color = default!;

    public float Hue
    {
        get => hue;
        set
        {
            if (hue != value)
                Color = Color.FromHsla(value, saturation, luminosity);
        }
    }


    public float Saturation
    {
        get => saturation;
        set
        {
            if (saturation != value)
                Color = Color.FromHsla(hue, value, luminosity);
        }
    }

    public float Luminosity
    {
        get => luminosity;
        set
        {
            if (luminosity != value)
                Color = Color.FromHsla(hue, saturation, value);
        }
    }

    public Color Color
    {
        get => color;
        set
        {
            if (color == value) return;

            color = value;
            OnPropertyChanged();


            if (hue != color.GetHue())
            {
                hue = color.GetHue();
                OnPropertyChanged(nameof(Hue));
            }

            if (saturation != color.GetSaturation())
            {
                saturation = color.GetSaturation();
                OnPropertyChanged(nameof(Saturation));
            }

            if (luminosity != color.GetLuminosity())
            {
                luminosity = color.GetLuminosity();
                OnPropertyChanged(nameof(Luminosity));
            }
        }
    }


    public event PropertyChangedEventHandler? PropertyChanged;
    private void OnPropertyChanged([CallerMemberName] string name = "")
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}

My attempt below does not work.

public partial class HslVM : ObservableObject
{
    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(Color))]
    float hue;


    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(Color))]
    float saturation;


    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(Color))]
    float luminosity;


    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(Hue))]
    [NotifyPropertyChangedFor(nameof(Saturation))]
    [NotifyPropertyChangedFor(nameof(Luminosity))]
    Color color = default!;
}
  • I don't see `Color = Color.FromHsla(value, saturation, luminosity);` anywhere in the revised code. Nor all the logic in Color setter. ObservableProperty can't do magic; your revised code tells anyone listening that `Color` has changed, BUT it doesn't actually correct the color. Nor, when someone sets Color, does it set the other three values to their corrected values. Am I missing something? IMHO, a situation like this is NOT a good candidate for `ObservableProperty`. – ToolmakerSteve Nov 20 '22 at 21:34

1 Answers1

1

Lets look at ObservableProperty attribute doc, and write the equivalent of your new code:

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(Color))]
float hue;

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(Hue))]
[NotifyPropertyChangedFor(nameof(Saturation))]
[NotifyPropertyChangedFor(nameof(Luminosity))]
Color color = default!;

generates properties something like this:

public float Hue
{
  get => hue;
  set {
    if (SetProperty(ref hue, value))
      OnPropertyChanged(nameof(Color);
  }
}

public Color Color
{
  get => color;
  set {
    if (SetProperty(ref color, value))
    {
      OnPropertyChanged(nameof(Hue));
      OnPropertyChanged(nameof(Saturation));
      OnPropertyChanged(nameof(Value));
    }
  }
}

Compare that to your original working version. Its missing all the "smarts" in the setters, that set the correct values of other fields.

All ObservableProperty does is generate that simple "default" property implementation. That isn't useful in this situation. AFAIK, it lacks any way to "inject" the needed smarts.

You might be tempted to keep Color setter from original, and change the others to ObservableProperty. That won't work either. OnPropertyChanged(nameof(Color)), or the equivalent attribute NotifyPropertyChangedFor(nameof(Color)) cannot tell Color HOW to update; it can only tell anyone listening that Color has changed. That won't correct the color.

ToolmakerSteve
  • 18,547
  • 14
  • 94
  • 196