【问题标题】:How to disable a button if textbox and passwordbox is blank in wpf?如果 wpf 中的文本框和密码框为空白,如何禁用按钮?
【发布时间】:2021-08-16 11:23:40
【问题描述】:

我基本上使用了我的ViewModel(CreateAccountViewModel) 中的Model's (UserAccount) Property 来绑定我的View,并调用我的Command (CreateAccountCommand)。


我的Model(UserAccount):

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));
    }
}

我的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
    }
}

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

我的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());
    }
}

我的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)
    }
}

我的问题是,即使我将命令的CanExecute 设置为这样做,我的View 中的按钮也不会更改启用/禁用。我在这里遗漏了一些明显的东西吗?我真的要问,因为我从昨天开始就被困在这里了。 (我的主要目标是在 TextboxPasswordBox 没有输入时禁用 Create Account 按钮。任何解决方案都可以)

【问题讨论】:

  • 另外,您可能应该在视图模型中完全限定您的 CreateAccountCommand 属性并添加 onpropertychanged 事件通知器
  • 您是否将视图数据上下文设置为您的视图模型类?
  • @NoConnection 我会将 onpropertychanged() 放到CreateAccountCommand 的设置器中吗?
  • @NoConnection 我已经在后面的代码中设置了datacontext...

标签: c# wpf button mvvm passwordbox


【解决方案1】:

让我们做一个小的重构。

  • 使用CallerMemberNameAttribute(参见here 如何)在vm 中拥有更短的属性设置器;
  • 编写一次可重用的ICommand 实现并将其用于所有命令,参见DelegateCommand
  • 当您更改命令canExecuted 条件之一时,vm 中的命令CanExecuteChanged 上升;
  • UserAccount 需要通知(你已经在编辑中完成了),如果是模型,那么你需要一个额外的 vm 来充当包装器,否则你将无法捕捉到绑定控件所做的更改;李>
  • 由于UserAccount 的属性是命令canExecuted 的一部分,因此您需要对其进行监控。

所有更改后,使用该命令的按钮应启用/禁用属性。

以下是伪代码(可能包含错误):


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
}

【讨论】:

    【解决方案2】:

    CreateAccountCommand 将事件处理程序连接到视图模型的 PropertyChanged,但是当您设置 UserAccount 对象的 UsernamePassword 属性时,不会引发此类事件。

    UserAccount 中实现INotifyPropertyChanged 或绑定到CreateAccountViewModel 的包装器属性:

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

    如果您决定在UserAccount 中实现INotifyPropertyChanged,您仍然需要在属性更新时通知该命令。

    由于您的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);
    

    【讨论】:

    • 我尝试将INotifyPropertyChanged 放入UserAccount,但是,它仍然没有工作......如果我创建“包装器属性”,那不会让它变得多余吗?因为我们可以使用UserAccount 的属性访问UserAccount 的属性? (这是我第一次制作MVVM 应用程序,所以我很陌生。
    • 如果UserAccount 没有实现INotifyPropertyChanged,则这些属性不是多余的。他们添加了更改通知。
    • 如果你决定在UserAccount中实现INotifyPropertyChanged,仍然需要在属性更新时通知命令。
    • UserAccount 类是一个模型。
    • 我会更新我的帖子(我的新UserAccount
    猜你喜欢
    • 2016-10-28
    • 1970-01-01
    • 1970-01-01
    • 2013-12-05
    • 1970-01-01
    • 2012-10-16
    • 1970-01-01
    • 2012-01-08
    • 2017-09-03
    相关资源
    最近更新 更多