在XAML中将isEnabled绑定到按钮时出现错误

3

我对xaml、WPFs、C#和MVVM范式都很新。我从这个示例项目开始创建了一个应用程序,在选定的摘录中,我想在单击身份验证按钮后禁用LoginPageViewModel中的该按钮(如果已经通过身份验证,则单击按钮没有意义)。 我已经使命令绑定工作正常,并且在视图和ViewModel之间实现了文本控件绑定。我的LoginPageViewModel基于继承自INotifyPropertyChanged的抽象类。

AuthenticateButtonEnabled的setter正在工作,但它没有绑定到表单上的isEnabled属性。我的问题是,我可能错过了什么,如何跟踪视图和ViewModel之间的绑定?

LoginPageView.xaml按钮:

        <Button x:Name="authenticateButton" Content="{x:Static res:Strings.LoginPage_authenticateButton_content}" 
            Grid.Column="2" Margin="53,4,0,10" 
            Grid.Row="2" FontSize="16" 
            IsEnabled="{Binding Path=AuthenticateButtonEnabled}"
            Command="{Binding Path=AuthenticateCommand}" HorizontalAlignment="Left" Width="87"/>

视图模型
    private String _username;
    private String _responseTextBlock;
    private String _linkTextBlockURI;
    private String _linkTextBlockText;
    private bool _authenticateButtonEnabled;
    ...
    private async void Authenticate()
    {
        ResponseTextBlock = Strings.LoginPage_responseBlock_content_checking;#this works!
        AuthenticateButtonEnabled = false;
        return;

    }
    ....

    public bool AuthenticateButtonEnabled 
    {
        get { return _authenticateButtonEnabled; }
        set { _authenticateButtonEnabled = value;  OnPropertyChanged("AuthenticateButtonEnabled"); }
    }
    // this is in the abstract class.
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
3个回答

1
如果你想同时拥有命令和AuthenticateButtonEnabled属性,那么只需在CanExecute委托中检查此属性,并在属性设置器中更新命令。以下是使用 DelegateCommand 实现的代码以及一些你可能会发现有用的改进:
bool _isAuthenticateButtonEnabled;
public bool IsAuthenticateButtonEnabled 
{
    get { return _isAuthenticateButtonEnabled; }
    set
    {
        _isAuthenticateButtonEnabled = value;
        OnPropertyChanged();
        AuthenticateCommand.Update();
    }
}

// the base could class could actually implement this
void OnPropertyChanged([CallerMemberName] string property) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));

public DelegateCommand AuthenticateCommand { get; }

// view model constructor
public ViewModel()
{
    AuthenticateCommand = new DelegateCommand(o =>
    {
       ... // some actions when command is executed
    }, o =>
    {
       bool somecondition = ...; // some condition to disable button, e.q. when executing command
       return somecondition && IsAuthenticateButtonEnabled;
    });
}

这将让您具备两个功能:启用/禁用按钮的属性,可在绑定中使用(到另一个控件,例如CheckBox.IsChecked),以及具有独立条件来禁用按钮的命令,当命令不应执行时(通常在async命令委托中,当它执行长时间运行的命令时,但为此,您可能需要查看this答案)。

Sinatr,感谢您的建议。我发现使用MVVM范例非常令人沮丧——一个简单的“.enabled = false”花费了我大部分时间来实现。我不禁想这个范例是否过于复杂而难以使用……当然,将逻辑与表现分离是很好的,但我不能不认为它应该更容易些…… - undefined

1
如果您将按钮的命令属性绑定到ViewModel中的ICommand属性,则无需处理按钮的IsEnabled属性,因为它由ICommand实现的CanExecute方法处理。
请搜索RelayCommand或DelegateCommand。

0

感谢大家的帮助,我想要分享这个可行的解决方案给其他人。我使用了DelegateCommand,但是需要修改loginPageViewModel中的一些部分才能让它正常工作:我还更新了xaml,以便在成功认证后所有控件都处于非活动状态。

loginPage的xaml:

    <Label x:Name="usernameLabel"  Content="{x:Static res:Strings.LoginPage_usernameLabel_content}" HorizontalAlignment="Left" Margin="10,4,0,0" Grid.Row="0" VerticalAlignment="Top" Width="130" FontSize="16"  Height="36" Grid.Column="1"/>
    <TextBox x:Name="usernameTextBox" Grid.Column="2" Grid.Row="0" TextWrapping="Wrap" 
             Text="{Binding Username, UpdateSourceTrigger=PropertyChanged}" 
             IsEnabled="{Binding AuthenticateButtonEnabled}"
             Margin="10,5,0,6" FontSize="16" HorizontalAlignment="Left" Width="130" TextChanged="usernameTextBox_TextChanged"/>
    <Label x:Name="passwordLabel" Content="{x:Static res:Strings.LoginPage_passwordLabel_content}" Margin="10,5,0,0" Grid.Row="1" VerticalAlignment="Top" FontSize="16" Height="36" Grid.RowSpan="2" HorizontalAlignment="Left" Width="130" Grid.Column="1"/>
    <PasswordBox x:Name="passwordBox" Grid.Column="2" Margin="10,0,0,9" 
        PasswordChanged="PasswordBox_PasswordChanged"
        IsEnabled="{Binding AuthenticateButtonEnabled}"
        Grid.Row="1" FontSize="16" HorizontalAlignment="Left" Width="130"/>
    <Button x:Name="authenticateButton" Content="{x:Static res:Strings.LoginPage_authenticateButton_content}" 
            Grid.Column="2" Margin="53,4,0,10" 
            Grid.Row="2" FontSize="16" 
            IsEnabled="{Binding AuthenticateButtonEnabled}"
            Command="{Binding Path=AuthenticateCommand}" HorizontalAlignment="Left" Width="87"/>

登录页面视图模型:

    ....
    private bool _authenticateButtonEnabled;
    private DelegateCommand _authenticateCommand;
    public bool AuthenticateButtonEnabled {
        get { return _authenticateButtonEnabled; }
        set
        {
            _authenticateButtonEnabled = value;
            DynamicOnPropertyChanged(); // this is so named to not content with onPropertyChanged defined elsewhere.
            AuthenticateCommand.Update();
        }
    }
    ...

    public DelegateCommand AuthenticateCommand
    { 
        get {
            if (_authenticateCommand == null)
            {
                _authenticateCommand = new DelegateCommand(Authenticate, AuthenticateEnded);
            }
            return _authenticateCommand;
        }
    }
    private bool AuthenticateEnded(object obj) {
        return _authenticateButtonEnabled;
    }
    private async void Authenticate(object obj)
    {
        AuthenticateButtonEnabled = false;

        ResponseTextBlock = Strings.LoginPage_responseBlock_content_checking;
        i3SoftHttpClient _httpClient = new i3SoftHttpClient();
        i3SoftUser _i3SoftUser;
        AuthenticateCommand.CanExecute(false);
        ....
      // if authentication does not succeed - turn the buttons back on.
        AuthenticateCommand.CanExecute(true);
     }

并且对于委托命令类,我添加了:

    public void Update()
    {
        if (CanExecuteChanged != null)
            CanExecuteChanged(this, EventArgs.Empty);
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接