从 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 使其看起来像一个按钮?有例子吗?
我要实现的逻辑:
- 按钮为灰色,文字为 START
- 按钮被点击,颜色变为红色并带有文本 STOP
- 用户点击按钮,弹出 window 出现选项确定和取消
- 如果用户单击“确定”-> 按钮变回灰色并带有文本“开始”,弹出窗口 window 消失
- 如果用户点击取消 -> 按钮保持相同状态 = 红色文本停止,弹出窗口 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();
}
}
}
我想从视图模型中控制 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 使其看起来像一个按钮?有例子吗?
我要实现的逻辑:
- 按钮为灰色,文字为 START
- 按钮被点击,颜色变为红色并带有文本 STOP
- 用户点击按钮,弹出 window 出现选项确定和取消
- 如果用户单击“确定”-> 按钮变回灰色并带有文本“开始”,弹出窗口 window 消失
- 如果用户点击取消 -> 按钮保持相同状态 = 红色文本停止,弹出窗口 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();
}
}
}