Control toggle button states from ViewModel MVVM

mrw 201 Reputation points
2022-03-20T10:34:05.587+00:00

I would like to control ToggleButton states from view model to be able to communicate to it can it be toggled or not.

Here is xaml for ToggleButton:

  <controls:ToggleButton Command="{Binding StartStopCommand}" WidthRequest="120" Margin="10,10,10,10">
    <VisualStateManager.VisualStateGroups>
      <VisualStateGroup Name="ToggleStates">
        <VisualState Name="ToggledOff">
          <VisualState.Setters>
            <Setter Property="Text" Value="START" />
            <Setter Property="FontSize" Value="14" />
            <Setter Property="BackgroundColor" Value="#474747" />
            <Setter Property="TextColor" Value="White" />
          </VisualState.Setters>
        </VisualState>

        <VisualState Name="ToggledOn">
          <VisualState.Setters>
            <Setter Property="Text" Value="STOP" />
            <Setter Property="FontSize" Value="14" />
            <Setter Property="BackgroundColor" Value="#6e1413" />
            <Setter Property="TextColor" Value="White" />
          </VisualState.Setters>
        </VisualState>
      </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
  </controls:ToggleButton>

C# part of ToggleButton:

  public class ToggleButton : Button
  {
    public event EventHandler<ToggledEventArgs> Toggled;

    public static BindableProperty IsToggledProperty =
        BindableProperty.Create(nameof(IsToggled), typeof(bool), typeof(ToggleButton), false, propertyChanged: OnIsToggledChanged);

    public ToggleButton()
    {
      Clicked += (sender, args) => IsToggled ^= true;
    }

    public bool IsToggled
    {
      set { SetValue(IsToggledProperty, value); }
      get { return (bool)GetValue(IsToggledProperty); }
    }

    protected override void OnParentSet()
    {
      base.OnParentSet();
      VisualStateManager.GoToState(this, "ToggledOff");
    }

    static async void OnIsToggledChanged(BindableObject bindable, object oldValue, object newValue)
    {
      ToggleButton toggleButton = (ToggleButton)bindable;
      bool isToggled = (bool)newValue;

      // Fire event
      toggleButton.Toggled?.Invoke(toggleButton, new ToggledEventArgs(isToggled));

      // Set the visual state
      VisualStateManager.GoToState(toggleButton, isToggled ? "ToggledOn" : "ToggledOff");
    }
  }

Here I would like to "cancel" toggle from ViewModel if certain logical result is not true:

private async Task ActivateStartStopAsync()
{
  if (this.StartStopON == false)
  {
    // Do something
    this.StartStopON = true;
  }
  else
  {
    bool result = await App.Current.MainPage.DisplayAlert("About to be shut down", "Are you sure you want to turn it off?", "OK", "Cancel");

    if (result)
    {
      // Do something
      this.StartStopON = false;
    }
  }
}

public ICommand StartStopCommand { get; }

public MyViewModel()
{
   this.StartStopCommand = new Command(async () => await ActivateStartStopAsync());
}

Basically I need to change button states from ViewModel. How to achieve that? It should happen somehow before click event or should I use something like switch or check box for that? If so, then how can I customize checkbox or Switch to look like a button? Any examples?

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,362 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,997 questions
{count} votes

Accepted answer
  1. Leon Lu (Shanghai Wicresoft Co,.Ltd.) 76,386 Reputation points Microsoft Vendor
    2022-03-22T06:27:51.563+00:00

    Hello,​

    Based on your logic that you want to achieve. As above comment said, this issue is related to the Clicked event in your custom control. So I do following steps.

    Step 1 I delete Clicked += (sender, args) => IsToggled ^= true; line in your ToggleButto's construstor.

    Step 2 I add binding IsToggled="{Binding StartStopON}" property in <controls:ToggleButton> tag like following code.

       <controls:ToggleButton Command="{Binding StartStopCommand}" IsToggled="{Binding StartStopON}" WidthRequest="120" Margin="10,10,10,10">  
    

    Step 3 I add the StartStopON property and extend the BaseViewModel.cs for properties change at runtime. Here is an article about MVVM in the Xamarin, you can refer to it. Here is my viewmodel and baseViewmodel. you can make a test.

       public class MyViewModel:BaseViewModel  
           {  
               private bool startStopON;  
               public bool StartStopON  
               {  
                   get { return startStopON; }  
                   set  
                   {  
                       if (startStopON == value)  
                           return;  
                       startStopON = value;  
                       OnPropertyChanged();  
                   }  
               }  
             
               private async Task ActivateStartStopAsync()  
               {  
                   if (this.StartStopON == false)  
                   {  
                       // Do something  
                       this.StartStopON = true;  
                   }  
                   else  
                   {                 
                       var result = await App.Current.MainPage.DisplayAlert("About to be shut down", "Are you sure you want to turn it off?", "OK", "Cancel");  
                       if (result)  
                       {  
                           // Do something  
                           this.StartStopON = false;  
                       }  
                   }  
               }  
               public ICommand StartStopCommand { get; }  
               public MyViewModel()  
               {  
                   this.StartStopCommand = new Command(async () => await ActivateStartStopAsync());  
               }  
           }  
           public class BaseViewModel : INotifyPropertyChanged  
           {  
               public event PropertyChangedEventHandler PropertyChanged;  
               protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)  
               {  
                   PropertyChangedEventHandler handler = PropertyChanged;  
                   if (handler != null)  
                       handler(this, new PropertyChangedEventArgs(propertyName));  
               }  
           }  
    

    Best Regards,

    Leon Lu


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.