未调用 WPF-Prism CanExecute 方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2444927/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 21:34:33  来源:igfitidea点击:

WPF-Prism CanExecute method not being called

wpfprismcommand

提问by Naresh

I am coding a simple login UserControl with two TextBoxes (Username and Password) and a Login button. I want the Login button to be enabled only when the username and password fields are filled in. I am using Prism and MVVM. The LoginViewModel contains a property called LoginCommand that is bound to the Login button. I have a CanLoginExecute() method in my ViewModel but it fires only when the application comes up and then never again. So the Login button is never enabled. What am I missing?

我正在编写一个带有两个文本框(用户名和密码)和一个登录按钮的简单登录 UserControl。我希望仅在填写用户名和密码字段时启用登录按钮。我使用的是 Prism 和 MVVM。LoginViewModel 包含一个名为 LoginCommand 的属性,该属性绑定到 Login 按钮。我的 ViewModel 中有一个 CanLoginExecute() 方法,但它仅在应用程序启动时才触发,然后再也不会触发。所以登录按钮永远不会启用。我错过了什么?

Here's my xaml:

这是我的 xaml:

<TextBox x:Name="username"
    Text="{Binding Path=Username, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
<TextBox x:Name="password"
    Text="{Binding Path=Password, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
<Button Content="Login"
    cmnd:Click.Command="{Binding LoginCommand}" />

Here's my ViewModel

这是我的视图模型

class LoginViewModel : IDataErrorInfo, INotifyPropertyChanged
{
    public LoginViewModel()
    {
        this.LoginCommand =
            new DelegateCommand<object>(
                this.LoginExecute, this.CanLoginExecute);
    }

    private Boolean CanLoginExecute(object dummyObject)
    {
        return (string.IsNullOrEmpty(Username) ||
                string.IsNullOrEmpty(Password)) ? false : true;
    }

    private void LoginExecute(object dummyObject)
    {
        if (CheckCredentials(Username, Password))
        {
            ....
        }
    }

    #region IDataErrorInfo Members

    public string Error
    {
        get { throw new NotImplementedException(); }
    }

    public string this[string columnName]
    {
        get
        {
            string result = null;
            if (columnName == "Username")
            {
                if (string.IsNullOrEmpty(Username))
                    result = "Please enter a username";
            }
            else if (columnName == "Password")
            {
                if (string.IsNullOrEmpty(Password))
                    result = "Please enter a password";
            }
            return result;
        }
    }

    #endregion // IDataErrorInfo Members

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion // INotifyPropertyChanged Members

    #region Properties

    private String _username;
    public String Username
    {
        get { return _username; }
        set
        {
            if (value == _username)
                return;
            _username = value;
            this.OnPropertyChanged("Username");
        }
    }

    private String _password;
    public String Password
    {
        get { return _password; }
        set
        {
            if (value == _password)
                return;
            _password = value;
            this.OnPropertyChanged("Password");
        }
    }

    public ICommand LoginCommand { get; private set; }

    #endregion // Properties
}

回答by olli-MSFT

It is most likely that the bound control is never asking for the CanExecutestate again. You need to call the RaiseCanExecuteChangedmethod on the DelegateCommand whenever you detect a condition that changes the command's CanExecutestate. This signals the bound control to update the CanExecutestate.

绑定控件很可能永远不会再次请求CanExecute状态。每当您检测到更改命令的CanExecute状态的条件时,您都需要在 DelegateCommand 上调用RaiseCanExecuteChanged方法。这向绑定控件发出信号以更新CanExecute状态。

回答by Naresh

Code for RaiseCanExecuteChanged:

RaiseCanExecuteChanged 的​​代码:

    private void RaiseCanExecuteChanged()
    {
        DelegateCommand<object> command = LoginCommand as DelegateCommand<object>;
        command.RaiseCanExecuteChanged();
    }

    public const string UsernameProperty = "Username";
    private String _username;
    public String Username
    {
        get { return _username; }
        set
        {
            _username = value;
            this.NotifyPropertyChanged(UsernameProperty);
            RaiseCanExecuteChanged();
        }
    }

回答by VRage

Starting with Prism6 the DelegateCommandcan "observe" your propertys. Means everytime your property is changing the CanExecute-Method is called. The good thing is you get rid of RaiseCanExecuteChangedin the Propertysetter. You can also chain-call that method if you want to observe more properties:

从 Prism6 开始,DelegateCommand可以“观察”您的属性。意味着每次您的属性更改时,都会调用 CanExecute-Method。好消息是你RaiseCanExecuteChanged在 Propertysetter 中摆脱了。如果您想观察更多属性,也可以链调用该方法:

public LoginViewModel()
{
    this.LoginCommand =
        new DelegateCommand<object>(
            this.LoginExecute, this.CanLoginExecute).ObservesProperty(() => Username).ObservesProperty(() => Password);
}

Furthermore if you just want your DelegateCommand be called depending on the state of a boolean property you can use .ObservesCanExecute(()=> BoolProp)

此外,如果您只想根据可以使用的布尔属性的状态调用 DelegateCommand .ObservesCanExecute(()=> BoolProp)

public LoginViewModel()
{
    this.LoginCommand =
        new DelegateCommand<object>(
            this.LoginExecute).ObservesCanExecute(()=> IsServerOnline).ObservesProperty(() => Username).ObservesProperty(() => Password);
}

You dont need this.CanLoginExecuteanymore.

this.CanLoginExecute不再需要了。

回答by Glutwolf

Here's a little workaround for Prism (tested with Prism.Core 7.1.0.431):

这是 Prism 的一些解决方法(使用 Prism.Core 7.1.0.431 测试):

public class RelayCommand : DelegateCommand
{
    public RelayCommand(Action executeMethode) : base(executeMethode)
    {

    }

    public RelayCommand(Action executeMethode, Func<bool> canExecuteMethode) : base(executeMethode, canExecuteMethode)
    {

    }

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