2

I am new to xaml, WPFs, C# and the MVVM paradigm. 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). I have got command binding working, as well as text control binding between the view and ViewModel. my LoginPageViewModel is based on a abstract class that inherits from INotifyPropertyChanged

The setter AuthenticateButtonEnabled is working, but it is not binding to the isEnabled proprerty on the form. My question is, what could I have missed, and How can i trace the binding between a View and a ViewModel?

the LoginPageView.xaml button:

        <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));
    }
pgee70
  • 3,707
  • 4
  • 35
  • 41

3 Answers3

1

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.

google for RelayCommand or DelegateCommand

blindmeis
  • 22,175
  • 7
  • 55
  • 74
1

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.

Here is implementation with DelegateCommand and some improvements which you may find useful:

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, e.g. 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.).

Community
  • 1
  • 1
Sinatr
  • 20,892
  • 15
  • 90
  • 319
  • Sinatr thanks for your suggestions, I find it quite frustrating to use this MVVM paradigm - a simple .enabled = false has taken me the better part of a day to implement - I can't help thinking the paradigm is too complex to be usable..... sure it is good to abstract logic from presentation but i can't help think it should be easier.... – pgee70 Oct 02 '15 at 14:24
0

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.

the 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:

    ....
    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);
    }
pgee70
  • 3,707
  • 4
  • 35
  • 41