Home > Software design >  Xamarin Forms - toggle between button states
Xamarin Forms - toggle between button states

Time:02-06

I have this form in my Xamarin.Forms application where I have two buttons, that are both meant to update a boolean value. Depending on whether that value is true or false, I want only one of the buttons to be enabled. Think of them as a "door": one button sets the "exit" boolean to true and the other to "false". So when the "enter" button is clicked I want it to be disabled until the user "exits" by clicking the "exit" button.

CanExecute/ChangeCanExecute should be the way to go here, at least by my own knowledge - and that's what I've tried. But it doesn't seem to be working, even when I abstract that functionality on a simpler content page.

I have attached a sample of my ViewModel's code, simplified for clarity.

I can't understand why I'm stumped by something that is so simple outside of MVVM conventions.

public bool _hasWorkerExit;
public bool hasWorkerExit
{
    get { return _hasWorkerExit; }
    set
    {
        _hasWorkerExit = value;
        OnPropertyChanged();
        EnterCommand?.ChangeCanExecute();
        ExitCommand?.ChangeCanExecute();
    }
}

public Command EnterCommand => new Command(SendWorkerEntry,WorkerCanEnter());
public Command ExitCommand => new Command(SendWorkerExit,WorkerCanExit());

private Func<bool> WorkerCanEnter()
{
    return new Func<bool>(() => hasWorkerExit);
}

private Func<bool> WorkerCanExit()
{
    return new Func<bool>(() => !hasWorkerExit);
}

private void SendWorkerEntry()
{
    // Do the work you're meant to do
    hasWorkerExit = false;
}

private void SendWorkerExit()
{
    // Do the work you're meant to do
    hasWorkerExit = true;
}

Here's the .xaml code for the buttons

<dxe:SimpleButton Grid.Column="0"
                  FontSize="13"
                  Text="Enter"
                  BorderThickness="0"
                  BackgroundColor="{StaticResource ButtonColour}"
                  PressedBackgroundColor="{StaticResource PressedButtonColour}"
                  TextColor="{StaticResource ButtonTextColour}"
                  PressedTextColor="{StaticResource ButtonTextColour}"
                  DisabledBackgroundColor="{StaticResource DisabledButtonColour}"
                  CornerRadius="0"
                  CornerMode="Round"                                             
                  Command="{Binding EnterCommand}"></dxe:SimpleButton>
<dxe:SimpleButton Grid.Column="1"
                  FontSize="13"
                  Text="Exit"
                  BorderThickness="0"
                  BackgroundColor="{StaticResource ButtonColour}"
                  PressedBackgroundColor="{StaticResource PressedButtonColour}"
                  TextColor="{StaticResource ButtonTextColour}"
                  PressedTextColor="{StaticResource ButtonTextColour}"
                  DisabledBackgroundColor="{StaticResource DisabledButtonColour}"
                  CornerRadius="0"
                  CornerMode="Round"                                             
                  Command="{Binding ExitCommand}"></dxe:SimpleButton>

CodePudding user response:

You can try:

Button 1:

IsEnabled="{Binding HasWorkerExit}"

Button 2:

IsEnabled="{Binding HasWorkerExit, Converter={Helpers:InverseBoolConverter}}"}"

One property alone should solve your problem.

public bool _hasWorkerExit;
    public bool HasWorkerExit
    {
        get { return _hasWorkerExit; }
        set
        {
            _hasWorkerExit = !_hasWorkerExit;
            OnPropertyChanged();
        }
    }

Invert Helper code:

public class InverseBoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return !((bool)value);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value;
        //throw new NotImplementedException();
    }
}

CodePudding user response:

Try this instead of using the Func<bool>, you can use a simple method group for the Commands which should suffice:

public Command EnterCommand => new Command(SendWorkerEntry, WorkerCanEnter);
public Command ExitCommand => new Command(SendWorkerExit, WorkerCanExit);

private bool WorkerCanEnter() => hasWorkerExit;
private bool WorkerCanExit() => !hasWorkerExit;

Alternatively, this should also work:

public Command EnterCommand => new Command(SendWorkerEntry, () => hasWorkerExit);
public Command ExitCommand => new Command(SendWorkerExit, () => !hasWorkerExit);
  • Related