2

I'm learning Mvvmlight,and quite confused about its canExecute of RelayCommand.

Basically,I have a Button and a PasswordBox in the view,and a Command in the viewModel.What I want is to disable the Button if the PasswordBox is empty.My solution is to pass the PasswordBox as CommandParemeter to the Button,then receive the PasswordBox in the canExecute method and specify if it is null or empty.I first declare a command:

public ICommand CommandClear { get; private set; }

Then instantialize it in the Class Constructor:

CommandConfirm = new RelayCommand<object>((p) => ConfirmExecute(p), (p) => ConfirmCanExecute(p));

Finally implement the canExecute method as below:

private bool ConfirmCanExecute(object parameter)
{
    bool isExecuable = false;
    var passwordBox = parameter as PasswordBox;
    if (!string.IsNullOrEmpty(passwordBox.Password))
        isExecuable = true;
    return isExecuable;
}

The canExecute method above does not work,as an unhandled exception System.Reflection.TargetInvocationException would be thrown in PresentationFramework.dll.

So I try to wrap the code above with try...catch.This time,it works like magic:

        try
        {
            var passwordBox = parameter as PasswordBox;
            if (!string.IsNullOrEmpty(passwordBox.Password))
                isExecuable = true;
            return isExecuable;
        }
        catch
        {
            return isExecuable;
        }

I'm quite confused about such behavior of canExecute,any ideas?

Chenxiao
  • 373
  • 1
  • 3
  • 15
  • So do I get things done the right way?I do not understand why an exception would thrown in the previous `canExecute` method. – Chenxiao May 08 '14 at 03:11
  • 1
    Have you checked that the PasswordBox is passed correctly to your `canExecute` method? The only place in your method that looks like it could throw an exception is `passwordBox.Password`, because you're not checking `passwordBox` for null. – vesan May 08 '14 at 03:12

1 Answers1

3

Are you declaring p on the stack in this line and does it still exist when the handler is called?

CommandConfirm = new RelayCommand<object>((p) => ConfirmExecute(p), (p) => ConfirmCanExecute(p));

Because an incorrect command binding would certainly cause this to return null, creating the error you're seeing.

var passwordBox = parameter as PasswordBox;

Secondly, why have your ViewModel directly manipulate the View in this way? Simply 2-way bind the Password field to your ViewModel and your ViewModel's CanExecute handler becomes a one-liner, no?

return !String.IsNullOrEmpty(this.Password);
  • PasswordBox in WPF does not support binding,so MVVM pattern would inevitably violated.I referenced [this](http://stackoverflow.com/questions/1483892/how-to-bind-to-a-passwordbox-in-mvvm),which manipulate `View` in `ViewModel`.Second,I understand want you mean a bit,perheps there are some tricks to solve it? – Chenxiao May 08 '14 at 03:38
  • 1
    Not a big deal honestly -- following the "viewmodel never touches view" dictum is sometimes just make-work. But okay, the way we typically bind to non-bindable controls in WPF is by creating a "Bindable" dependency property as illustrated here http://wpftutorial.net/PasswordBox.html or here on SO: http://stackoverflow.com/questions/263551/databind-the-source-property-of-the-webbrowser-in-wpf. Just be aware of the security implications of binding to a password field! –  May 08 '14 at 03:49