thomasclaudiushuber / mvvmgen

MvvmGen is a lightweight MVVM library for XAML applications. It generates your ViewModels on-the-fly for you via a Roslyn-based C# Source Generator.
MIT License
254 stars 21 forks source link

Raise CommandInvalidate when adding or removing elements on collection #87

Open Daimonion1980 opened 10 months ago

Daimonion1980 commented 10 months ago

Hello Thomas.

Is it possible to raise a CommandInvalidate method when changing the number of elements on an ObservableCollection?

In my code i have an ObservableCollection where i can add or remove elements via command methods. Now I want to implement a clear button which will become enabled when the ObservableCollection has more than zero elements.

[ViewModel]
[ViewModelGenerateFactory]
public partial class VM_ParameterChooser 
{
    [Property] ObservableCollection<VM_Parameter> selectedParameterList = new ObservableCollection<VM_Parameter>();

    [Command(CanExecuteMethod = nameof(SelectedListCanBeCleared))]
    public void Clear()
    {
        SelectedParameterList.Clear();
    }

    //this method is only called when SelectedParameterList is changed itself
    //but should be called if a element is added or removed from the collection
    [CommandInvalidate(nameof(SelectedParameterList))]  
    public bool SelectedListCanBeCleared() => SelectedParameterList.Count > 0;

    [Command]
    public void AddSelectedParameter(object param)
    {
        var selectedParam = param as VM_Parameter;
        if (selectedParam is null) return;
        SelectedParameterList.Add(selectedParam);
    }

    [Command]
    public void RemoveSelectedParameter(object param)
    {
        var selectedParam = param as VM_Parameter;
        if (selectedParam is null) return;
        SelectedParameterList.Remove(selectedParam);
    }
}
Telespaz commented 10 months ago

Hi,

Two things come to my mind what you could do here. Call 'ClearCommand.RaiseCanExecuteChanged();' at the end of your Add and Remove method or overwrite the OnInitialize method and subscribe to the 'CollectionChanged' event of your selectedParameterList and if fired call the 'ClearCommand.RaiseCanExecuteChanged();' .

Daimonion1980 commented 10 months ago

Hey @Telespaz

Thank you for your answer.

Call 'ClearCommand.RaiseCanExecuteChanged();' at the end of your Add and Remove method

Yep, i added a call to my Add and Remove Method and the button enables and disables as expected.

InvalidateCommands(nameof(SelectedParameterList));
thomasclaudiushuber commented 10 months ago

@Daimonion1980 This sounds like a great approach that you took now.

Alternatively, you can call as suggested by @Telespaz

ClearCommand.RaiseCanExecuteChanged();

But in your case, your call to InvalidateCommands might be more clear from a semantical point of view. Both ways are good. 👍

Your idea is great to include this in MvvmGen, but it gets super complex very soon. The items in the ObservableCollection could be INotifyPropertyChanged objects, then you need to track these changes as well etc. I think handling it in your own code is the best way, as the generator might lead to a 90% solution.

thomasclaudiushuber commented 10 months ago

Btw, if you don't set the ObservableCollection to a new instance from your UI, there's no reason to use a field with the Property attribute. Instead, you can define the property directly in your code like this (I included a bit of C# 12 sugar, the [] to initialize the collection 😊):

[ViewModel]
[ViewModelGenerateFactory]
public partial class VM_ParameterChooser
{
    public ObservableCollection<VM_Parameter> SelectedParameterList = [];

    [Command(CanExecuteMethod = nameof(SelectedListCanBeCleared))]
    public void Clear()
    {
        SelectedParameterList.Clear();
    }

    [CommandInvalidate(nameof(SelectedParameterList))]
    public bool SelectedListCanBeCleared() => SelectedParameterList.Count > 0;

    [Command]
    public void AddSelectedParameter(object param)
    {
        var selectedParam = param as VM_Parameter;
        if (selectedParam is null) return;
        SelectedParameterList.Add(selectedParam);
        InvalidateCommands(nameof(SelectedParameterList));
    }

    [Command]
    public void RemoveSelectedParameter(object param)
    {
        var selectedParam = param as VM_Parameter;
        if (selectedParam is null) return;
        SelectedParameterList.Remove(selectedParam);
        InvalidateCommands(nameof(SelectedParameterList));
    }
}