简体   繁体   English

错误绑定 isEnabled 到 xaml 中的按钮

[英]error binding isEnabled to button in xaml

I am new to xaml, WPFs, C# and the MVVM paradigm.我是 xaml、WPF、C# 和 MVVM 范例的新手。 I have started with an app based on this example project , in the selected excerpts i want to disable the authenticate button from the LoginPageViewModel after the authenticate button has been clicked(There is no point clicking the button if you are authenticated).我已经开始使用基于此示例项目的应用程序,在选定的摘录中,我想在单击身份验证按钮后禁用 LoginPageViewModel 中的身份验证按钮(如果您已通过身份验证,则单击按钮没有意义)。 I have got command binding working, as well as text control binding between the view and ViewModel.我有命令绑定工作,以及视图和 ViewModel 之间的文本控件绑定。 my LoginPageViewModel is based on a abstract class that inherits from INotifyPropertyChanged我的 LoginPageViewModel 基于从 INotifyPropertyChanged 继承的抽象类

The setter AuthenticateButtonEnabled is working, but it is not binding to the isEnabled proprerty on the form. setter AuthenticateButtonEnabled 正在工作,但它没有绑定到表单上的 isEnabled 属性。 My question is, what could I have missed, and How can i trace the binding between a View and a ViewModel?我的问题是,我可能错过了什么,以及如何跟踪 View 和 ViewModel 之间的绑定?

the LoginPageView.xaml button: 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"/>

the viewModel视图模型

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

if you bind the command Property of the Button to an ICommand Property in your Viewmodel, then you do NOT need to handle the IsEnabled Property of the Button because its handled by the CanExecute Method of the ICommand implementation.如果将按钮的命令属性绑定到视图模型中的 ICommand 属性,则不需要处理按钮的 IsEnabled 属性,因为它由 ICommand 实现的 CanExecute 方法处理。

google for RelayCommand or DelegateCommand谷歌为 RelayCommand 或 DelegateCommand

If you want to have both: command and AuthenticateButtonEnabled , then simply check for this property in CanExecute delegate and vise-versa in property setter update command.如果您想同时拥有: command 和AuthenticateButtonEnabled ,那么只需在CanExecute委托中检查此属性,并在属性设置器更新命令中检查反之亦然。

Here is implementation with DelegateCommand and some improvements which you may find useful:以下是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;
    });
}

This will let you to have both: property to enable/disable button, which can be used in binding (to another control, eg CheckBox.IsChecked ) and command which can have independent condition to disable button when command shouldn't be executed (typically in async command delegate, when it performs a long running command, but for this you may want to check this answer.).这将使您同时拥有:启用/禁用按钮的属性,可以用于绑定(到另一个控件,例如CheckBox.IsChecked )和可以具有独立条件以在不应执行命令时禁用按钮的命令(通常在async命令委托中,当它执行长时间运行的命令时,但为此您可能需要检查答案。)。

Thanks to the posters for your help, I wanted to share the working solution for others.感谢海报的帮助,我想为其他人分享工作解决方案。 I used the DelegateCommand, but had to change some parts in the loginPageViewModel to make it work: I also updated the xaml so that the controls were all inactive after a successful authentication.我使用了 DelegateCommand,但必须更改 loginPageViewModel 中的某些部分才能使其工作:我还更新了 xaml,以便在成功验证后控件都处于非活动状态。

the loginPage 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"/>

the loginPageViewModel: loginPageViewModel:

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

and to the Delegate command class i added:和我添加的委托命令类

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

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

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