Я хотел бы контролировать состояния ToggleButton из модели представления, чтобы иметь возможность общаться с ней, может ли она быть переключена или нет.
Вот xaml для 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>
С# часть 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");
}
}
Здесь я хотел бы «отменить» переключение из 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. Как этого добиться? Это должно произойти как-то перед событием щелчка или я должен использовать для этого что-то вроде переключателя или флажка? Если да, то как я могу настроить флажок или переключатель, чтобы он выглядел как кнопка? Есть примеры?
Логика, которую я пытаюсь достичь:
@WenxuLi-MSFT В основном я думаю, что моя функция работает так, как ожидалось, но мне не хватает возможности отменить ее состояния из ViewModel. Например, if (result)
вернет false -> мне нужно оставить кнопку в ее предыдущем состоянии = отменить процесс запуска. Я думаю, что IsEnabled ограничивает любые операции с ним? Мне нужно просто иметь доступ к своим состояниям из ViewModel, а не полностью отключить или включить. Я могу обновить свой вопрос. В конце скоро напечатаю еще несколько пояснений.
поскольку вы упомянули MVVM, вам не нужно было бы привязывать его к свойству модели, которая реализует интерфейс INotifyThisOrTheOther
(не могу вспомнить имя, прошло некоторое время с тех пор, как я играл с этим)
@HiFo, не могли бы вы отреагировать на данный ответ? Если вы прокомментируете это, сказав, что оно вас не удовлетворяет, другим будет предложено добавить дополнительные альтернативы. Если вы просто не отреагируете, я чувствую, что вы больше не заинтересованы в поиске решения.
Вот что я придумал...
Окно.xaml:
Назначьте контекст данных для кнопки-переключателя и всплывающего окна, все обрабатывается в классе модели представления (Button_Toggle.cs).
Окно.xaml.cs:
Опять же, не так много.
Создайте экземпляр модели представления (Button_Toggle.cs) и назначьте контекст данных
Button_Toggle.cs:
Где происходят чудеса.
Здесь вы наследуете класс кнопки, поэтому у вас есть полный контроль и вы можете предоставить контекст данных кнопки.
Создайте общедоступный объект для всплывающего окна, это обеспечивает контекст данных для всплывающего окна.
Когда вводится какой-либо ввод, происходит обновление объекта buttonState, затем вносятся изменения, отражающие текущее состояние buttonState.
Я не думаю, что вам действительно нужно заходить так далеко, вы можете установить все на каждом из входов.
Однако для меня макет помогает в расширении и отладке в будущем.
Надеюсь, это поможет и имеет смысл, если нет, дайте мне знать.
Окно.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>
Окно.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();
}
}
}
Какую логическую операцию вы хотите выполнить, нажав на кнопку? Чтобы указать, можно ли щелкнуть кнопку, вы можете установить свойство IsEnabled в Button.