简体   繁体   English

如果 wpf 中的文本框和密码框为空白,如何禁用按钮?

[英]How to disable a button if textbox and passwordbox is blank in wpf?

I basically used a Model 's (UserAccount) Property from my ViewModel (CreateAccountViewModel) to bind to my View , and call to my Command (CreateAccountCommand).我基本上使用我的ViewModel (CreateAccountViewModel) 中的Model的 (UserAccount) Property绑定到我的View ,并调用我的Command (CreateAccountCommand)。


My Model (UserAccount):我的Model (用户帐户):

public class UserAccount : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;


    private int _id;
    private string _username;
    private string _password;
    private DateTime _dateTime;

    public int Id
    {
        get { return _id; }
        set { _id = value; OnPropertyChanged(nameof(Id)); }
    }


    public string Username
    {
        get { return _username; }
        set { _username = value; OnPropertyChanged(nameof(Username)); }
    }



    public string Password
    {
        get { return _password; }
        set { _password = value; OnPropertyChanged(nameof(Password)); }
    }


    public DateTime DateCreated
    {
        get { return _dateTime; }
        set { _dateTime = value; OnPropertyChanged(nameof(DateCreated)); }
    }

    public virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

My ViewModel (CreateAccountViewModel):我的ViewModel (CreateAccountViewModel):

public class CreateAccountViewModel: ViewModelBase
{
    private UserAccount _userAccount;

    public UserAccount CurrentUserAccount
    {
        get { return _userAccount; }
        set { _userAccount = value; OnPropertyChanged(nameof(CurrentUserAccount)); }
    }

    public ICommand CreateAccountCommand{ get; }

    public CreateAccountViewModel()
    {
        CreateAccountCommand= new CreateAccountCommand(this, Test);
        CurrentUserAccount = new UserAccount();
    }

    public void Test()
    {
        MessageBox.Show("Random Message");
        //I'm going to put my Create functionality here
    }
}

My View (CreateAccountView):我的View (CreateAccountView):

<!--The TextBox for username-->
<TextBox Grid.Column="1"
         Margin="10,0,0,0"
         Text="{Binding Path=CurrentUserAccount.Username, UpdateSourceTrigger=PropertyChanged}" />

<!--The PasswordBox for password-->
<components:BindablePasswordBox Grid.Column="1"
                                Margin="10,0,0,0"
                                Password="{Binding Path=CurrentUserAccount.Password, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
                                Grid.ColumnSpan="2" />

<!--The Create user button-->
<Button Grid.Row="2"
        Margin="0,20,0,0"
        HorizontalAlignment="Center"
        Command="{Binding CreateAccountCommand}"
        Content="Create Account" />

My Command (CreateAccountCommand):我的Command (CreateAccountCommand):

public class CreateAccountCommand: ICommand
{
    private readonly CreateAccountViewModel _viewModel;
    private readonly Action RunCommand;

    public CreateAccountCommand(CreateAccountViewModel viewModel , Action runCommand)
    {
        _viewModel = viewModel;
        _viewModel.PropertyChanged += ViewModel_PropertyChanged;

        RunCommand = runCommand;
    }

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    {        
        //This is supposed to check whether the Username textbox and Password passwordbox is blank (if both of them are blank, the button should be disabled, else disabled
        return !string.IsNullOrEmpty(_viewModel.CurrentUserAccount.Username) && !string.IsNullOrEmpty(_viewModel.CurrentUserAccount.Password);
    }

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

    private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        CanExecuteChanged?.Invoke(this, new EventArgs());
    }
}

My PasswordBox is bindable because I created a custom PasswordBox with DependencyProperty :我的PasswordBox是可绑定的,因为我使用DependencyProperty创建了一个自定义PasswordBox

public partial class BindablePasswordBox : UserControl
{

    public static readonly DependencyProperty PasswordProperty =
        DependencyProperty.Register("Password", typeof(string), typeof(BindablePasswordBox),
            new PropertyMetadata(string.Empty));

    public string Password
    {
        get { return (string)GetValue(PasswordProperty); }
        set { SetValue(PasswordProperty, value); }
    }

    public BindablePasswordBox()
    {
        InitializeComponent();
    }

    //This method will notify us, whenever a password in our passwordBox changes
    private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
    {
        Password = passwordBox.Password; //sets the value of the DependencyProperty (PasswordProperty)
    }
}

My problem here, is that, the button in my View does not change enable/disable even if I set my command's CanExecute to do so.我的问题是,即使我将命令的CanExecute设置为这样做,我的View中的按钮也不会更改启用/禁用。 Am I missing something obvious here?我在这里遗漏了一些明显的东西吗? I really have to ask because I've been stuck here since yesterday.我真的要问,因为我从昨天开始就被困在这里了。 (My Main goal here is to disable the Create Account button if the Textbox and PasswordBox have no input. Any solutions are okay) (我的主要目标是在TextboxPasswordBox没有输入时禁用Create Account按钮。任何解决方案都可以)

Lets do a small refactoring.让我们做一个小的重构。

  • use CallerMemberNameAttribute (see here how) to have shorter property setters in vm;使用CallerMemberNameAttribute (见这里如何)在 vm 中有更短的属性设置器;
  • write once reusable ICommand implementation and use it for all commands, see DelegateCommand ;编写一次可重用的ICommand实现并将其用于所有命令,请参阅DelegateCommand
  • rise command CanExecuteChanged in vm when you change one of command canExecuted condition;当您更改命令canExecuted条件之一时,在 vm 中上升命令CanExecuteChanged
  • UserAccount needs notifications (you have done it in the edit), if it's a model, then you need an extra vm to act as a wrapper, otherwise you wouldn't be able to catch changes done by the bound controls; UserAccount需要通知(您已在编辑中完成),如果它是 model,那么您需要一个额外的 vm 来充当包装器,否则您将无法捕获绑定控件所做的更改;
  • Since the properties of UserAccount are part of command canExecuted , you need to monitor for them.由于UserAccount的属性是命令canExecuted的一部分,因此您需要监视它们。

With all changes your button using the command should be property enabled/disabled.通过所有更改,您使用命令的按钮应启用/禁用属性。

Below is pseudo-code (can contain mistakes):以下是伪代码(可能包含错误):


public class CreateAccountViewModel: ViewModelBase
{
    UserAccount _userAccount;
    public UserAccount CurrentUserAccount
    {
        get => _userAccount;
        set
        {
            // unsubscribe
            if(_userAccount != null)
                _userAccount -= UserAccount_PropertyChanged;
            _userAccount = value;
            // subscribe
            if(_userAccount != null)
                _userAccount += UserAccount_PropertyChanged;
            // notifications
            OnPropertyChanged(); // shorter syntax with CallerMemberNameAttribute
            CreateAccountCommand.RaiseCanExecuteChanged();
        }
    }

    public ICommand CreateAccountCommand { get; }

    public CreateAccountViewModel()
    {
        CurrentUserAccount = new UserAccount();
        CreateAccountCommand = new DelegateCommand(Test,
            o => !string.IsNullOrEmpty(CurrentUserAccount.Username) && !string.IsNullOrEmpty(CurrentUserAccount.Password));
    }

    void Test(object parameter)
    {
        MessageBox.Show("Random Message");
        //I'm going to put my Create functionality here
    }

    void UserAccount_PropertyChanged(object sender, NotifyPropertyChangedEventArgs e) =>
        CreateAccountCommand.RaiseCanExecuteChanged(); // rise always of check for specific properties changes
}

The CreateAccountCommand hooks up en event handler to the view model's PropertyChanged but there is no such event raised when you set the Username and Password properties of the UserAccount object. CreateAccountCommand将事件处理程序连接到视图模型的PropertyChanged ,但是当您设置UserAccount object 的Username名和Password属性时,不会引发此类事件。

Either implement INotifyPropertyChanged in UserAccount or bind to wrapper properties of the CreateAccountViewModel :UserAccount中实现INotifyPropertyChanged或绑定到CreateAccountViewModel的包装器属性:

public string Username
{
    get { return _userAccount?.Username; }
    set
    {
        if (_userAccount != null)
            _userAccount.Username = value;
        OnPropertyChanged();
    }
}

If you decide to implement INotifyPropertyChanged in UserAccount , you still need to notify the command when the properties have been updated.如果您决定在UserAccount中实现INotifyPropertyChanged ,您仍然需要在属性更新时通知该命令。

Since your CurrentUserAccount property may be set to a new value dynamically, you should remove and add the event handler dynamically:由于您的CurrentUserAccount属性可能会动态设置为新值,因此您应该动态删除和添加事件处理程序:

private UserAccount _userAccount;
public UserAccount CurrentUserAccount
{
    get { return _userAccount; }
    set
    {
        if (_userAccount != null)
            _userAccount.PropertyChanged -= OnUserAccountPropertyChanged;
        _userAccount = value;
        if (_userAccount != null)
            _userAccount.PropertyChanged += OnUserAccountPropertyChanged;
        OnPropertyChanged(nameof(CurrentUserAccount));
    }
}

private void OnUserAccountPropertyChanged(object sender, PropertyChangedEventArgs e) =>
    OnPropertyChanged(null);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM