从 ViewModel MVVM 控制切换按钮状态

Control toggle button states from ViewModel MVVM

我想从视图模型中控制 ToggleButton 状态,以便能够与其进行通信,它是否可以切换。

这里是 ToggleButton 的 xaml:

  <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# 部分:

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

在这里,如果某些逻辑结果不正确,我想从 ViewModel“取消”切换:

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

基本上我需要从 ViewModel 更改按钮状态。如何实现?它应该在点击事件之前以某种方式发生,或者我应该为此使用开关或复选框之类的东西吗?如果是这样,那么如何自定义复选框或 Switch 使其看起来像一个按钮?有例子吗?

我要实现的逻辑:

  1. 按钮为灰色,文字为 START
  2. 按钮被点击,颜色变为红色并带有文本 STOP
  3. 用户点击按钮,弹出 window 出现选项确定和取消
  4. 如果用户单击“确定”-> 按钮变回灰色并带有文本“开始”,弹出窗口 window 消失
  5. 如果用户点击取消 -> 按钮保持相同状态 = 红色文本停止,弹出窗口 window 消失。重复相同的逻辑,直到用户在弹出窗口中单击确定 window。

这是我想出的...

Window.xaml:

为切换按钮和弹出窗口分配数据上下文,一切都在视图模型中处理class (Button_Toggle.cs)

Window.xaml.cs:

同样,不多。

创建视图模型实例 (Button_Toggle.cs) 并分配数据上下文

Button_Toggle.cs:

奇迹发生的地方。

此处您继承了 Button Class,因此您拥有完全控制权并可以提供 Button 的数据上下文。

为弹出窗口创建一个 public 对象,这为弹出窗口提供数据上下文。

当给出任何输入时,会更新 buttonState 对象,然后进行反映 buttonState 的当前状态的更改。

我认为您真的不需要走这么远,可以在每个输入中设置所有内容。

但是对我来说布局有助于将来的扩展和调试。

希望这对您有所帮助并且有意义,如果没有,请告诉我。

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

        }
    }
}