在不公开模型属性的情况下启用按钮

Enable button without exposing model properties

我在 MVVMHelpers 中使用 MVVM 模式。

我已经制作了登录表单,但我试图仅在用户输入用户名和密码时启用该按钮。

public ICommand LoginCommand { get; set; }

    private string _Username;
    public string Username {
        get { return _Username; }
        set {
            if (_Username != value) {
                _Username = value;
                RaisePropertyChanged();
                Activate();
            }
        }
    }

    private string _Password;
    public string Password {
        get { return _Password; }
        set {
            if (_Password != value) {
                _Password = value;
                RaisePropertyChanged();
                Activate();
            }
        }
    }

    private bool _IsEnabled;
    public bool IsEnabled {
        get { return _IsEnabled; }
        set {
            if (_IsEnabled != value) {
                _IsEnabled = value;
                RaisePropertyChanged();
            }
        }
    }

    public bool Activate() {  
        if (!string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password)) {
            IsEnabled = true;
            return true;
        }

        return false;

    }

    public LoginVM() {
        LoginCommand = new Command(() => {
            MessageBox.Show("LogeIn");    
        });
    }
}

如您所见,我通过将 class 的属性公开给我的 VM 来实现这一点。 我想知道是否有办法在不暴露它的情况下做到这一点? https://github.com/eduardoagr?tab=repositories

可以 仅在 XAML 中实现此目的,方法是在 IsEnabled 属性 上使用 [=57] 上的多重绑定=]登录 Button 到用户名和密码 TextBoxes 以及将绑定的 strings 转换为 bool 的自定义值转换器表示它们是否为空。

但是,在这种情况下这是不可能的,因为您应该避免同时使用 CommandIsEnabled,因为这会导致奇怪的行为。命令提供了一种机制来确定某个操作是否可行,并且 will in turn set the IsEnabled state.

A command can indicate whether an action is possible by implementing the CanExecute method. A button can subscribe to the CanExecuteChanged event and be disabled if CanExecute returns false or be enabled if CanExecute returns true.

如您所见,同时设置 CommandIsEnabled 会相互干扰。虽然它可能有效,但如果您在 XAML 中的 Command 之前指定 IsEnabled,这是一个脆弱的解决方案,因此请选择一个,在这种情况下支持 Command 及其内置机制.

好消息是,不用在 XAML 中执行此操作,您可以在视图模型中实现相同的操作,并通过将 CanExecute 委托传递给 [=26= 来节省一些代码] 如下。

public class LoginVM : ViewModelBase {

    public Command LoginCommand { get; }

    private string _Username;
    public string Username {
        get { return _Username; }
        set {
            if (_Username != value) {
                _Username = value;
                RaisePropertyChanged();
                LoginCommand.RaiseCanExecuteChanged();
            }
        }
    }


    private string _Password;
    public string Password {
        get { return _Password; }
        set {
            if (_Password != value) {
                _Password = value;
                RaisePropertyChanged();
                LoginCommand.RaiseCanExecuteChanged();
            }
        }
    }

    public LoginVM() {
        LoginCommand = new Command(
           () => MessageBox.Show("Log IN"),
           () => !string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password));
    }
}

你的命令的第二个参数是一个方法,它确定命令 是否可以执行 如果它 returns true,绑定的控件返回 false 时,该命令将自动启用或禁用。使用这种方法,您根本不需要 IsEnabled 属性,您可以 将其从 XAML.

中删除

这里唯一需要注意的是,当它需要通过调用其 RaiseCanExecuteChanged 方法(定义在Command class)。为了访问它,将 属性 的类型从 ICommand 更改为 Command 或为其定义一个单独的支持字段。在这种情况下,每次 UsernamePassword 更改时都需要重新评估,因此将其放入它们的设置器中。

目前,用户名和密码的 TextBoxes 仅在失去焦点时更新视图模型属性(例如,单击它或另一个控件之外)。如果您希望 Sign In 按钮对每次击键做出反应,请将 UpdateSourceTrigger 更改为 PropertyChanged

<inputLayout:SfTextInputLayout x:Name="sfTextInputLayout"
                               Hint="Username"
                               Margin="20,20,20,0">
    <TextBox Foreground="BlanchedAlmond"
             Text="{Binding Username, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</inputLayout:SfTextInputLayout>
<controls:CustomPasswordBox Password="{Binding Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />