diff --git a/src/Avalonia.Controls/Primitives/ToggleButton.cs b/src/Avalonia.Controls/Primitives/ToggleButton.cs
index e7cd0697a0..247e55a347 100644
--- a/src/Avalonia.Controls/Primitives/ToggleButton.cs
+++ b/src/Avalonia.Controls/Primitives/ToggleButton.cs
@@ -7,8 +7,14 @@ using Avalonia.Data;
namespace Avalonia.Controls.Primitives
{
+ ///
+ /// Represents a control that a user can select (check) or clear (uncheck). Base class for controls that can switch states.
+ ///
public class ToggleButton : Button
{
+ ///
+ /// Defines the property.
+ ///
public static readonly DirectProperty IsCheckedProperty =
AvaloniaProperty.RegisterDirect(
nameof(IsChecked),
@@ -17,9 +23,30 @@ namespace Avalonia.Controls.Primitives
unsetValue: null,
defaultBindingMode: BindingMode.TwoWay);
+ ///
+ /// Defines the property.
+ ///
public static readonly StyledProperty IsThreeStateProperty =
AvaloniaProperty.Register(nameof(IsThreeState));
+ ///
+ /// Defines the event.
+ ///
+ public static readonly RoutedEvent CheckedEvent =
+ RoutedEvent.Register(nameof(Checked), RoutingStrategies.Bubble);
+
+ ///
+ /// Defines the event.
+ ///
+ public static readonly RoutedEvent UncheckedEvent =
+ RoutedEvent.Register(nameof(Unchecked), RoutingStrategies.Bubble);
+
+ ///
+ /// Defines the event.
+ ///
+ public static readonly RoutedEvent IndeterminateEvent =
+ RoutedEvent.Register(nameof(Indeterminate), RoutingStrategies.Bubble);
+
private bool? _isChecked = false;
static ToggleButton()
@@ -27,14 +54,49 @@ namespace Avalonia.Controls.Primitives
PseudoClass(IsCheckedProperty, c => c == true, ":checked");
PseudoClass(IsCheckedProperty, c => c == false, ":unchecked");
PseudoClass(IsCheckedProperty, c => c == null, ":indeterminate");
+
+ IsCheckedProperty.Changed.AddClassHandler((x, e) => x.OnIsCheckedChanged(e));
+ }
+
+ ///
+ /// Raised when a is checked.
+ ///
+ public event EventHandler Checked
+ {
+ add { AddHandler(CheckedEvent, value); }
+ remove { RemoveHandler(CheckedEvent, value); }
+ }
+
+ ///
+ /// Raised when a is unchecked.
+ ///
+ public event EventHandler Unchecked
+ {
+ add { AddHandler(UncheckedEvent, value); }
+ remove { RemoveHandler(UncheckedEvent, value); }
+ }
+
+ ///
+ /// Raised when a is neither checked nor unchecked.
+ ///
+ public event EventHandler Indeterminate
+ {
+ add { AddHandler(IndeterminateEvent, value); }
+ remove { RemoveHandler(IndeterminateEvent, value); }
}
+ ///
+ /// Gets or sets whether the is checked.
+ ///
public bool? IsChecked
{
get { return _isChecked; }
set { SetAndRaise(IsCheckedProperty, ref _isChecked, value); }
}
+ ///
+ /// Gets or sets a value that indicates whether the control supports three states.
+ ///
public bool IsThreeState
{
get => GetValue(IsThreeStateProperty);
@@ -47,18 +109,78 @@ namespace Avalonia.Controls.Primitives
base.OnClick();
}
+ ///
+ /// Toggles the property.
+ ///
protected virtual void Toggle()
{
if (IsChecked.HasValue)
+ {
if (IsChecked.Value)
+ {
if (IsThreeState)
+ {
IsChecked = null;
+ }
else
+ {
IsChecked = false;
+ }
+ }
else
+ {
IsChecked = true;
+ }
+ }
else
+ {
IsChecked = false;
+ }
+ }
+
+ ///
+ /// Called when becomes true.
+ ///
+ /// Event arguments for the routed event that is raised by the default implementation of this method.
+ protected virtual void OnChecked(RoutedEventArgs e)
+ {
+ RaiseEvent(e);
+ }
+
+ ///
+ /// Called when becomes false.
+ ///
+ /// Event arguments for the routed event that is raised by the default implementation of this method.
+ protected virtual void OnUnchecked(RoutedEventArgs e)
+ {
+ RaiseEvent(e);
+ }
+
+ ///
+ /// Called when becomes null.
+ ///
+ /// Event arguments for the routed event that is raised by the default implementation of this method.
+ protected virtual void OnIndeterminate(RoutedEventArgs e)
+ {
+ RaiseEvent(e);
+ }
+
+ private void OnIsCheckedChanged(AvaloniaPropertyChangedEventArgs e)
+ {
+ var newValue = (bool?)e.NewValue;
+
+ switch (newValue)
+ {
+ case true:
+ OnChecked(new RoutedEventArgs(CheckedEvent));
+ break;
+ case false:
+ OnUnchecked(new RoutedEventArgs(UncheckedEvent));
+ break;
+ default:
+ OnIndeterminate(new RoutedEventArgs(IndeterminateEvent));
+ break;
+ }
}
}
}
diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/ToggleButtonTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/ToggleButtonTests.cs
index 4f4ab47b0a..9acd42aba6 100644
--- a/tests/Avalonia.Controls.UnitTests/Primitives/ToggleButtonTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/Primitives/ToggleButtonTests.cs
@@ -1,5 +1,4 @@
using Avalonia.Data;
-using Avalonia.Markup.Data;
using Avalonia.UnitTests;
using Xunit;
@@ -63,6 +62,54 @@ namespace Avalonia.Controls.Primitives.UnitTests
Assert.Null(threeStateButton.IsChecked);
}
+ [Fact]
+ public void ToggleButton_Events_Are_Raised_On_Is_Checked_Changes()
+ {
+ var threeStateButton = new ToggleButton();
+
+ bool checkedRaised = false;
+ threeStateButton.Checked += (_, __) => checkedRaised = true;
+
+ threeStateButton.IsChecked = true;
+ Assert.True(checkedRaised);
+
+ bool uncheckedRaised = false;
+ threeStateButton.Unchecked += (_, __) => uncheckedRaised = true;
+
+ threeStateButton.IsChecked = false;
+ Assert.True(uncheckedRaised);
+
+ bool indeterminateRaised = false;
+ threeStateButton.Indeterminate += (_, __) => indeterminateRaised = true;
+
+ threeStateButton.IsChecked = null;
+ Assert.True(indeterminateRaised);
+ }
+
+ [Fact]
+ public void ToggleButton_Events_Are_Raised_When_Toggling()
+ {
+ var threeStateButton = new TestToggleButton { IsThreeState = true };
+
+ bool checkedRaised = false;
+ threeStateButton.Checked += (_, __) => checkedRaised = true;
+
+ threeStateButton.Toggle();
+ Assert.True(checkedRaised);
+
+ bool indeterminateRaised = false;
+ threeStateButton.Indeterminate += (_, __) => indeterminateRaised = true;
+
+ threeStateButton.Toggle();
+ Assert.True(indeterminateRaised);
+
+ bool uncheckedRaised = false;
+ threeStateButton.Unchecked += (_, __) => uncheckedRaised = true;
+
+ threeStateButton.Toggle();
+ Assert.True(uncheckedRaised);
+ }
+
private class Class1 : NotifyingBase
{
private bool _foo;
@@ -80,5 +127,10 @@ namespace Avalonia.Controls.Primitives.UnitTests
set { nullableFoo = value; RaisePropertyChanged(); }
}
}
+
+ private class TestToggleButton : ToggleButton
+ {
+ public new void Toggle() => base.Toggle();
+ }
}
}