未调用WPF Prism CanExecute方法

未调用WPF Prism CanExecute方法,wpf,prism,command,Wpf,Prism,Command,我用两个文本框(用户名和密码)和一个登录按钮编写了一个简单的登录用户控件。我希望只有在填写用户名和密码字段时才启用登录按钮。我正在使用Prism和MVVM。LoginViewModel包含一个名为LoginCommand的属性,该属性绑定到Login按钮。我的ViewModel中有一个CanLoginExecute()方法,但它只在应用程序启动时启动,然后再也不会启动。因此,登录按钮从未启用。我错过了什么 这是我的xaml: <TextBox x:Name="username" T

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

这是我的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}" />

这是我的视图模型

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
}
类LoginView模型:IDataErrorInfo,INotifyPropertyChanged
{
公共登录视图模型()
{
这个.LoginCommand=
新的DelegateCommand(
this.LoginExecute,this.CanLoginExecute);
}
私有布尔值CanLoginExecute(对象dummyObject)
{
返回(string.IsNullOrEmpty(用户名)||
IsNullOrEmpty(密码))?false:true;
}
私有void LoginExecute(对象dummyObject)
{
如果(检查凭证(用户名、密码))
{
....
}
}
#区域IDataErrorInfo成员
公共字符串错误
{
获取{抛出新的NotImplementedException();}
}
公共字符串此[string columnName]
{
得到
{
字符串结果=null;
如果(columnName==“用户名”)
{
if(string.IsNullOrEmpty(用户名))
result=“请输入用户名”;
}
else if(columnName==“密码”)
{
if(string.IsNullOrEmpty(密码))
result=“请输入密码”;
}
返回结果;
}
}
#endregion//IDataErrorInfo成员
#区域INotifyProperty更改成员
公共事件属性更改事件处理程序属性更改;
void OnPropertyChanged(字符串propertyName)
{
if(PropertyChanged!=null)
PropertyChanged(这是新的PropertyChangedEventArgs(propertyName));
}
#endregion//INotifyPropertyChanged成员
#区域属性
私有字符串\u用户名;
公共字符串用户名
{
获取{return\u username;}
设置
{
如果(值==\u用户名)
返回;
_用户名=值;
此.OnPropertyChanged(“用户名”);
}
}
私有字符串\u密码;
公共字符串密码
{
获取{return\u password;}
设置
{
如果(值==\u密码)
返回;
_密码=值;
此.OnPropertyChanged(“密码”);
}
}
public ICommand LoginCommand{get;private set;}
#endregion//属性
}

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

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();
        }
    }
private void raisecancecutechanged()
{
DelegateCommand=LoginCommand as DelegateCommand;
command.RaiseCanExecuteChanged();
}
public const string UsernameProperty=“Username”;
私有字符串\u用户名;
公共字符串用户名
{
获取{return\u username;}
设置
{
_用户名=值;
this.NotifyPropertyChanged(UsernameProperty);
RaiseCanExecuteChanged();
}
}

从Prism6开始,
DelegateCommand
可以“观察”您的财产。意味着每次属性更改时,都会调用CanExecute方法。好的方面是您可以在Propertysetter中去掉
RaiseCanceTechChanged
。如果要观察更多属性,也可以链调用该方法:

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

您不再需要
这个.CanLoginExecute

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

公共类RelayCommand:DelegateCommand
{
public RelayCommand(Action ExecuteMethod):基本(ExecuteMethod)
{
}
public RelayCommand(Action ExecuteMethod,Func CanExecuteMethod):基本(ExecuteMethod,CanExecuteMethod)
{
}
公共重写事件处理程序CanExecuteChanged
{
添加{CommandManager.RequerySuggested+=value;}
删除{CommandManager.RequerySuggested-=value;}
}
}

什么是cmnd:Click.Command=,它是Prism特有的吗。我通常是这样做的,cmnd:Click.Command=is prism-specific:xmlns:cmnd=“clr namespace:Microsoft.Practices.Composite.Presentation.Commands;assembly=Microsoft.Practices.Composite.Presentation”因为我使用的是prism的DelegateCommand,所以我认为这将是兼容的命令绑定机制。我还尝试了直接命令=“{Binding LoginCommand}”-它的工作原理完全相同。就记录而言(这发生在我身上),我没有找到
raisecancecutechanged
方法,因为我使用的是ICommand接口。这个方法是在DelegateCommand实现中定义的,所以我需要强制转换它。
public LoginViewModel()
{
    this.LoginCommand =
        new DelegateCommand<object>(
            this.LoginExecute).ObservesCanExecute(()=> IsServerOnline).ObservesProperty(() => Username).ObservesProperty(() => Password);
}
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; }
    }
}