Radovan Kozák
Radovan Kozák

Reputation: 215

WPF MVVM command canexecute enable/disable button

I want to enable RibbonButton when textbox property text isn't null. Disable RibbonButton when textbox property text is null. I want to use CanExecute method in ICommand for it. How can I do it?

View:

 <Custom:RibbonButton
                        LargeImageSource="..\Shared\img\save_diskete.png"
                        Label="Save"
                        Command="{Binding ButtonCommand}">
                    </Custom:RibbonButton>

ViewModel

class KomentarViewModel:BaseViewModel
    {
        #region Data
        private ICommand m_ButtonCommand;
        public ICommand ButtonCommand
        {
            get
            {
                return m_ButtonCommand;
            }
            set
            {
                m_ButtonCommand = value;
            }
        }
        private string textKomentar;
        public string TextKomentar
        {
            get
            {
                return this.textKomentar;
            }
            set
            {
                // Implement with property changed handling for INotifyPropertyChanged
                if (!string.Equals(this.textKomentar, value))
                {
                    textKomentar = value;
                    OnPropertyChanged("TextKomentar");
                }
            }
        }        
        private ObservableCollection<Komentar> allCommentsInc;
        public ObservableCollection<Komentar> AllCommentsInc
        {
            get
            {
                return allCommentsInc;
            }
            set
            {
                allCommentsInc = value;
                OnPropertyChanged("AllCommentsInc");
            }
        }

        public int idIncident { get; private set; }
        public Incident incident { get; private set; }
        #endregion

        #region Constructor
        public KomentarViewModel(int id)
        {
            CC_RK2Entities context = new CC_RK2Entities();
            this.idIncident = id;

            AllCommentsInc = new ObservableCollection<Komentar>(context.Komentar.Where(a => a.Incident_id == idIncident));
            incident = context.Incident.Where(a => a.id == idIncident).First();

            //ButtonCommand = new RelayCommand(new Action<object>(ShowMessage));
        }
        #endregion        

        #region Methods
        //ukaz napsany text
        public void ShowMessage(object obj)
        {
            //MessageBox.Show(obj.ToString());
            MessageBox.Show(this.TextKomentar);
        }
}

RelayCommand

namespace Admin.Shared.Commands
{
    class RelayCommand : ICommand
    {
        private Action<object> _action;

        public RelayCommand(Action<object> action)
        {
            _action = action;
        }

        #region ICommand Members

        public bool CanExecute(object parameter)
        {
            return true;            
        }

        public event EventHandler CanExecuteChanged;

        public void Execute(object parameter)
        {
                _action(parameter);
        }

        #endregion
    }
}

Upvotes: 18

Views: 55265

Answers (4)

Arsen Khachaturyan
Arsen Khachaturyan

Reputation: 8350

In straightforward words, you need the following:

  1. Let's first create our own delegate command:
public class DelegateCommand : DelegateCommandBase
{
    private Action _executeMethod;
    private Func<bool> _canExecute;
    
    public DelegateCommand(Action executeMethod)
        : this(executeMethod, () => true) {}
   
    public DelegateCommand(Action executeMethod, Func<bool> _canExecute): base()
    {
        if (executeMethod == null || _canExecute == null) {         
              throw new ArgumentNullException(nameof(executeMethod),      
                              Resources.DelegateCommandDelegatesCannotBeNull);
        }

        _executeMethod = executeMethod;
        _canExecute = _canExecute;
    }

    public void Execute() => _executeMethod();  
    public bool CanExecute() => _canExecute();
    
    protected override void Execute(object parameter) => Execute();
    protected override bool CanExecute(object parameter) => CanExecute();

    public DelegateCommand ObservesProperty<T>(Expression<Func<T>> propertyExpression)
    {
        ObservesPropertyInternal(propertyExpression);
        return this;
    }
    
    public DelegateCommand ObservesCanExecute(Expression<Func<bool>> canExecuteExpression)
    {
        _canExecute = canExecuteExpression.Compile();
        ObservesPropertyInternal(canExecuteExpression);
        return this;
    }
}

Here, DelegateCommandBase is actually from Prism.Commands namespace.

If you don't use Prism as an MVVM framework for WPF, you can create your own copy of DelegateCommandBase (look for the solution here).

  1. In your View Model, create a member with type DelegateCommand and initialize it in the constructor:
public class MyViewModel
{
    private DelegateCommand _okCommand;
    public DelegateCommand OkCommand
    {
        get => _okCommand;
        set => SetProperty(ref _okCommand, value);
    }
        
    public MyViewModel() 
    {
        OkCommand = new PrismCommands.DelegateCommand(OkCommandHandler,
                                                      OkCanExecuteCommandHandler);
    }
    
    private void OkCommandHandler()
    {
        // ...
    } 
    
    // This is important part: need to return true/false based
    // on the need to enable or disable item
    private bool OkCanExecuteCommandHandler() =>
        return some_condition_to_enable_disable_item;   
}

Note: make sure to raise execution changed event, every time something changes that can affect some_condition_to_enable_disable_item condition behavior.

For example, in the case of Prism, you can call RaiseCanExecuteChanged method once a change happens related to the condition (in our case OkCommand.RaiseCanExecuteChanged();).

Small hint: for Telerik WPF Controls, you need to call InvalidateCanExecute() instead of RaiseCanExecuteChanged().


Finally, our XAML will look like this:

<Button x:Name="btnOk"
        Content="Ok"
        Command="{Binding OkCommand}"/>

Upvotes: 1

yo chauhan
yo chauhan

Reputation: 12315

You need to modify your RelayCommand class like this

  class RelayCommand : ICommand
{
    private Action<object> _action;
    private Func<bool> _func;  

    public RelayCommand(Action<object> action,Func<bool> func)
    {
        _action = action;
        _func = func;
    }

    public void RaiseCanExecuteChanged()
    {
        if(CanExecuteChanged!=null)
            CanExecuteChanged(this,new EventArgs());
    }

    #region ICommand Members

    public bool CanExecute(object parameter)
    {
        if (_func != null)
           return _func();
        return true;
    }



    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        _action(parameter);
    }

    #endregion
}

Initialize ButtonCommand as

ButtonCommand = new RelayCommand((s) => ShowMessage(s),()=>!string.IsNullOrEmpty(TextKomentar));

RaiseCanExcuteChanged from the setter of Text property

        public string TextKomentar
    {
        get
        {
            return this.textKomentar;
        }
        set
        {
            // Implement with property changed handling for INotifyPropertyChanged
            if (!string.Equals(this.textKomentar, value))
            {
                textKomentar = value;
                OnPropertyChanged("TextKomentar");
            }
            ButtonCommand.RaiseCanExecuteChanged();
        }
    }

Upvotes: 22

isxaker
isxaker

Reputation: 9506

Last time I used Microsoft.Practices.Prism.Commands namesapce from Microsoft.Practices.Prism.dll. Class DelegateCommand has own RaiseCanExecuteChanged() method. So the benifit is you don't have to write yout own implementation of ICommand.

XAML:

<StackPanel>
    <CheckBox IsChecked="{Binding IsCanDoExportChecked}" />
    <Button Command="{Binding ExportCommand}" Content="Export" />
</StackPanel>

ViewModel:

public class ViewModel
{
    public DelegateCommand ExportCommand { get; }

    public ViewModel()
    {
        ExportCommand = new DelegateCommand(Export, CanDoExptor);
    }

    private void Export()
    {
        //logic
    }

    private bool _isCanDoExportChecked;

    public bool IsCanDoExportChecked
    {
        get { return _isCanDoExportChecked; }
        set
        {
            if (_isCanDoExportChecked == value) return;

            _isCanDoExportChecked = value;
            ExportCommand.RaiseCanExecuteChanged();
        }
    }

    private bool CanDoExptor()
    {
        return IsCanDoExportChecked;
    }
}

Upvotes: 0

DeshDeep Singh
DeshDeep Singh

Reputation: 1863

implement this for canexecute:

public bool CanExecute(object parameter)
        {if(thistext available)
            return true; 
         else
            return false;  
        }

Since, CanExecuteChanged is raised when the CanExecute method of an ICommand gets changed. it gets invoked when some command that could change canexecute. and can execute changed should be changed to this:

public event EventHandler CanExecuteChanged {
    add {
        CommandManager.RequerySuggested += value;
    }
    remove {
        CommandManager.RequerySuggested -= value;
    }
}

EDIT

in your view model constructor:

m_ButtonCommand= new RelayCommand(Submit, CanSubmit);

now method for this submit:
 private bool CanSubmit(object obj)
        {
            if(thistext available)
                return true; 
             else
                return false;  

        }
 public void Submit(object _)
  {//... code}

 public event EventHandler CanExecuteChanged {
        add {
            CommandManager.RequerySuggested += value;
        }
        remove {
            CommandManager.RequerySuggested -= value;
        }
    }

do it like this.

Upvotes: 3

Related Questions