Search code examples
c#wpfmvvmicommand

C# WPF Declare an ICommand in a single line


I'm in the process of learning WPF and the MVVM design pattern. Currently the code in my ViewModel for a delete customer command looks like this:

    public class vmCustomers : INotifyPropertyChanged
    {
...
        private ICommand _commandDeleteCustomer = null;
...
        public ICommand CommandDeleteCustomer
        {
            get
            {
                if (_commandDeleteCustomer == null)
                    _commandDeleteCustomer = new RelayCommand<object>(DeleteCustomerAction, DeleteCustomerPredicate);
                return _commandDeleteCustomer;
            }
        }

        private void DeleteCustomerAction(object o)
        {
            ...stuff...
        }

        private bool DeleteCustomerPredicate(object o)
        {
            ...stuff...
            return true;
        }
    }

I'd like to slim down the declaration of the ICommand to something like this so that I can reduce the coding overhead for each command:

public readonly ICommand CommandDeleteCustomer = new RelayCommand((obj) => DeleteCustomerAction(obj), (obj) => DeleteCustomerPredicate(obj));

But I get this error:

A field initializer cannot reference the non-static field, method, or property vmCustomers.DeleteCustomerAction(object)

Is there a way I can declare the ICommand in a single line of code so that I can simply focus on business-related code rather than repeated infrastructure code.


Solution

  • In case your RelayCommand.CanExecuteChanegd hooks onto the CommandManager.RequerySuggested event you can drop the constructor initialization and initialize the property as read-only computed property in a single line statement:

    // Creates a *new instance* on every Get() access
    public ICommand CommandDeleteCustomer => new RelayCommand(...);
    
    // The following is the verbose form of the previous single line property declaration:
    public ICommand CommandDeleteCustomer
    {
      get => new RelayCommand(...);
    }
    

    To ensure that the same instance is returned from the property, use

    a) the null coalescing assignment operator (C# 8.0)

    // Backing field required 
    private ICommand commandDeleteCustomer;
    public ICommand CommandDeleteCustomer => this.commandDeleteCustomer ??= new RelayCommand(...);
    
    // The following is the verbose form of the previous property declaration
    // and uses the null coalescing operator:
    public ICommand CommandDeleteCustomer => this.commandDeleteCustomer = this.commandDeleteCustomer ?? new RelayCommand(...);
    

    b) a property initializer (single line statement):

    // Returns the *same instance* on every Get() access.
    // Note that you can't reference instance members like fields and properties from the initializer. 
    // This means the command delegates would have to be defined as 'private static'.
    public ICommand CommandDeleteCustomer { get; } = new RelayCommand<object>(DeleteCustomerAction, DeleteCustomerPredicate);
    
    private static void DeleteCustomerAction(object o)
    {
      ...stuff...
    }
    
    private static bool DeleteCustomerPredicate(object o)
    {
      ...stuff...
      return true;
    }