Home > Mobile >  Control toggle button states from ViewModel MVVM
Control toggle button states from ViewModel MVVM

Time:03-25

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?

Logic I am trying to achieve:

  1. Button is gray colored with text START
  2. Button is clicked, color changed to red with text STOP
  3. User clicks Button, popup window appears with options OK and CANCEL
  4. If user clicks OK -> button turns back into gray colored with text START, popup window disappears
  5. If user clicks CANCEL -> button stays in same state = red colored with text STOP, popup window disappears. Same logic is repeated until user click OK in popup window.

CodePudding user response:

Here is what I came up with...

Window.xaml:

Assign the data context for the toggle button and the popup, everything is handled in the view model class (Button_Toggle.cs)

Window.xaml.cs:

Again, not much.

Create an instance of the view model (Button_Toggle.cs) and assign the data context

Button_Toggle.cs:

Where the magic happens.

Here you are inheriting the Button Class, so you have full control and can supply the Data Context of the Button.

Create a public object for the popup, this supplies the Data Context for the popup.

When any input is given, there is an update to the buttonState object, then changes are made that reflect the current state of the buttonState.

I dont suppose you really need to go this far, could set everything in each of the inputs.

However for me the layout helps with expansion and debugging in the future.

Hope this helps and makes sense, if not, let me know.

Window.xaml:

<Window x:Class="Toggle_Change.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:Toggle_Change"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525" Background="Red">

<Window.DataContext>
    <local:Button_Toggle/>
</Window.DataContext>
<Grid>
    <local:Button_Toggle x:Name="my_toggle_button" Background="#FF727272"/>
   
    <Popup Name="my_popup">
    </Popup>


</Grid>

Window.xaml.cs:

using System.Windows;    

namespace Toggle_Change
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public Button_Toggle buttonToggle;


        public MainWindow()
        {
            InitializeComponent();

            buttonToggle = new Button_Toggle();

            this.my_toggle_button.DataContext = buttonToggle;
            this.my_popup.DataContext = buttonToggle.MyPopUp;

        }
    }
}

Button_Toggle.cs:

using System.Diagnostics;     
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

                                                            namespace Toggle_Change
{
    public class Button_Toggle   :System.Windows.Controls.Button
    {       

        public Popup MyPopUp;


        #region private class

        private enum StateType { NA,START,STOP,OK,CANCEL}       
        private class colors
        {
            public static readonly Brush Grey = new SolidColorBrush(color: System.Windows.Media.Color.FromArgb(0xFF,0x72,0x72,0x72));
            public static readonly Brush Black = new SolidColorBrush(color: System.Windows.Media.Color.FromArgb(0xFF,0x00,0x00,0x00));
            public static readonly Brush Red = new SolidColorBrush(color: System.Windows.Media.Color.FromArgb(0xFF,0xFF,0x00,0x00));
        }

        #endregion

        #region private objects
        private StateType buttonState = StateType.START;

        #endregion
                     
        /// <summary>
        /// update text based on the button state
        /// </summary>
        private void set_text()
        {
            switch (buttonState)
            {
                case StateType.START:

                    this.Content = "START";
                    this.Background = colors.Grey;
                    this.Foreground = colors.Black;

                    break;
                case StateType.STOP:
                    this.Content = "STOP";
                    this.Background = colors.Grey;
                    this.Foreground = colors.Red;  

                    break;
                case StateType.OK:
                    break;
                case StateType.CANCEL:
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// something was clicked, alter the button state based on the current state
        /// </summary>
        private void on_click()
        {
            Debug.WriteLine("CLICK");
            switch (buttonState)
            {
                case StateType.NA:

                    buttonState = 
                        StateType.START;

                    break;
                case StateType.START: 

                    buttonState = 
                        StateType.STOP;

                    break;
                case StateType.STOP:

                    MyPopUp.IsEnabled = true;
                    MyPopUp.IsOpen = true;
                    MyPopUp.Visibility = System.Windows.Visibility.Visible;   

                    break;
                case StateType.OK:

                    buttonState = 
                        StateType.START;

                    MyPopUp.IsEnabled = false;
                    MyPopUp.IsOpen = false;
                    MyPopUp.Visibility = System.Windows.Visibility.Hidden;

                    break;
                case StateType.CANCEL:

                    buttonState =
                        StateType.STOP;

                    MyPopUp.IsEnabled = false;
                    MyPopUp.IsOpen = false;
                    MyPopUp.Visibility = System.Windows.Visibility.Hidden;
                    break;
                default:
                    break;
            }
                                                             
            set_text();
        }
               
        private void ini_popup()
        {                          
            StackPanel _stackPanel;
            Button _btnA;
            Button _btnB;

            _btnA = new Button();
            _btnA.Content = "OK";
            _btnA.Click  = _btnA_Click;

            _btnB = new Button();
            _btnB.Content = "NO";
            _btnB.Click  = _btnB_Click;
                                           

            _stackPanel =
                new StackPanel();

            _stackPanel.Orientation = Orientation.Horizontal;

            _stackPanel.Children.Add(
                _btnA);

            _stackPanel.Children.Add(
                _btnB);   

            MyPopUp = new Popup();

            MyPopUp.Child = _stackPanel;

            MyPopUp.Placement = PlacementMode.Center;
            MyPopUp.PlacementTarget = this;

            MyPopUp.PopupAnimation = PopupAnimation.Slide;
            MyPopUp.Visibility = System.Windows.Visibility.Hidden;   
        }

        private void _btnB_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            buttonState = StateType.CANCEL;                         
            OnClick();    
        } 
        private void _btnA_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            buttonState = StateType.OK;                            
            OnClick(); 
        }

        private void MyPopUp_IsVisibleChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
        {
            Debug.WriteLine("Popup Is Doing Something");
        }

        private void Button_Toggle_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            on_click();
        }

        public Button_Toggle() {

            ini_popup();

            MyPopUp.IsVisibleChanged  = MyPopUp_IsVisibleChanged; 

            this.Click  = Button_Toggle_Click;
            this.set_text();   

        }
    }
}
  • Related