2つのTextBox(ユーザー名とパスワード)とログインボタンを使用して、単純なログインUserControlをコーディングしています。ユーザー名とパスワードのフィールドに入力した場合にのみログインボタンを有効にしたい。PrismとMVVMを使用しています。 LoginViewModelには、LoginボタンにバインドされたLoginCommandというプロパティが含まれています。 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}" />
これが私の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
}
バインドされたコントロールがCanExecute状態を再度要求することはほとんどありません。コマンドのCanExecute状態を変更する条件を検出するたびに、DelegateCommandでRaiseCanExecuteChangedメソッドを呼び出す必要があります。これは、バインドされたコントロールに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();
}
}
Prism6以降、DelegateCommand
はプロパティを「監視」できます。プロパティが変更されるたびに、CanExecute-Methodが呼び出されることを意味します。良いことは、プロパティセッターのRaiseCanExecuteChanged
を取り除くことです。より多くのプロパティを監視する場合は、そのメソッドをチェーン呼び出しすることもできます。
_public LoginViewModel()
{
this.LoginCommand =
new DelegateCommand<object>(
this.LoginExecute, this.CanLoginExecute).ObservesProperty(() => Username).ObservesProperty(() => Password);
}
_
さらに、ブールプロパティの状態に応じてDelegateCommandを呼び出すだけの場合は、.ObservesCanExecute(()=> BoolProp)
を使用できます。
_public LoginViewModel()
{
this.LoginCommand =
new DelegateCommand<object>(
this.LoginExecute).ObservesCanExecute(()=> IsServerOnline).ObservesProperty(() => Username).ObservesProperty(() => Password);
}
_
もう_this.CanLoginExecute
_は必要ありません。
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; }
}
}