mvvmwinui-3winuicommunity-toolkit-mvvm

How to use set when binding with MVVM community sdk on a property


The proper way to create an observable property in mvvm is

  [ObservableProperty]
  private string _name;

However, in certain scenarios you need to alter the set. How does one do this properly without declaring the Public?

private double _currentProgressPct;

public double CurrentProgressPct
{
    get => _currentProgressPct;
    set
    {
        _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High, () =>
        {
            SetProperty(ref _currentProgressPct, value);
        });
    }
}

From what I have understood the first approach is the correct according to mvvm, but not the latter?

Also, according to this article you need to implement the INotifyPropertyChanged interface. However, is this necessary when using [ObservableProperty]? From what I have understood, the community toolkit needs the class to be an observableobject?

The reason I am asking the last questions is because it doesn't look like the bindings are released upon inspecting with performance profiler with memory focus. According to another thread I found this can be because INotifyProperyChanged is not implemented?


Solution

  • First of all, ObservableObject and ObservableProperty come from the CommunityToolkit.Mvvm NuGet package.

    Now in general, you should go with the first one:

    [ObservableProperty]
    private string _name;
    

    then forget about the _name field and use the generated Name property.

    The second one:

    private double _currentProgressPct;
    
    public double CurrentProgressPct
    {
        get => _currentProgressPct;
        set
        {
            _dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High, () =>
            {
                SetProperty(ref _currentProgressPct, value);
            });
        }
    }
    

    is using the DispatcherQueue to ensure that the value will be updated on the UI thread. Also, might not be a big deal in most cases but it's worth noting that if you set CurrentProgressPct on the UI thread, CurrentProgressPct will be enqueued instead of updating it immediately.

    If you have cases that you won't be updating CurrentProgressPct on the UI thread, you can easily just:

    _ = _dispatcherQueue.TryEnqueue(
            DispatcherQueuePriority.High,
            () =>
            {
                CurrentProgressPct = value;
            });
    }
    

    UPDATE

    I'm not saying that you shouldn't use the second one. There are cases where you might need it. Let me show you another case:

    private double _currentProgressPct;
    
    public double CurrentProgressPct
    {
        get => _currentProgressPct;
        set
        {
            // Only update `_currentProgressPct` when the value is 
            // greater than `0`.
            if (value > 0)
            {
                SetProperty(ref _currentProgressPct, value);
            }
        }
    }