Browse Source

Merge pull request #7451 from robloo/robloo-button

Button Refactoring
pull/7455/head
Max Katz 4 years ago
committed by GitHub
parent
commit
9dcb061cdb
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 197
      src/Avalonia.Controls/Button.cs

197
src/Avalonia.Controls/Button.cs

@ -59,13 +59,13 @@ namespace Avalonia.Controls
AvaloniaProperty.Register<Button, object>(nameof(CommandParameter));
/// <summary>
/// Defines the <see cref="IsDefaultProperty"/> property.
/// Defines the <see cref="IsDefault"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsDefaultProperty =
AvaloniaProperty.Register<Button, bool>(nameof(IsDefault));
/// <summary>
/// Defines the <see cref="IsCancelProperty"/> property.
/// Defines the <see cref="IsCancel"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsCancelProperty =
AvaloniaProperty.Register<Button, bool>(nameof(IsCancel));
@ -76,6 +76,9 @@ namespace Avalonia.Controls
public static readonly RoutedEvent<RoutedEventArgs> ClickEvent =
RoutedEvent.Register<Button, RoutedEventArgs>(nameof(Click), RoutingStrategies.Bubble);
/// <summary>
/// Defines the <see cref="IsPressed"/> property.
/// </summary>
public static readonly StyledProperty<bool> IsPressedProperty =
AvaloniaProperty.Register<Button, bool>(nameof(IsPressed));
@ -95,13 +98,12 @@ namespace Avalonia.Controls
static Button()
{
FocusableProperty.OverrideDefaultValue(typeof(Button), true);
CommandProperty.Changed.Subscribe(CommandChanged);
CommandParameterProperty.Changed.Subscribe(CommandParameterChanged);
IsDefaultProperty.Changed.Subscribe(IsDefaultChanged);
IsCancelProperty.Changed.Subscribe(IsCancelChanged);
AccessKeyHandler.AccessKeyPressedEvent.AddClassHandler<Button>((lbl, args) => lbl.OnAccessKey(args));
}
/// <summary>
/// Initializes a new instance of the <see cref="Button"/> class.
/// </summary>
public Button()
{
UpdatePseudoClasses(IsPressed);
@ -112,8 +114,8 @@ namespace Avalonia.Controls
/// </summary>
public event EventHandler<RoutedEventArgs> Click
{
add { AddHandler(ClickEvent, value); }
remove { RemoveHandler(ClickEvent, value); }
add => AddHandler(ClickEvent, value);
remove => RemoveHandler(ClickEvent, value);
}
/// <summary>
@ -121,8 +123,8 @@ namespace Avalonia.Controls
/// </summary>
public ClickMode ClickMode
{
get { return GetValue(ClickModeProperty); }
set { SetValue(ClickModeProperty, value); }
get => GetValue(ClickModeProperty);
set => SetValue(ClickModeProperty, value);
}
/// <summary>
@ -130,8 +132,8 @@ namespace Avalonia.Controls
/// </summary>
public ICommand Command
{
get { return _command; }
set { SetAndRaise(CommandProperty, ref _command, value); }
get => _command;
set => SetAndRaise(CommandProperty, ref _command, value);
}
/// <summary>
@ -139,8 +141,8 @@ namespace Avalonia.Controls
/// </summary>
public KeyGesture HotKey
{
get { return GetValue(HotKeyProperty); }
set { SetValue(HotKeyProperty, value); }
get => GetValue(HotKeyProperty);
set => SetValue(HotKeyProperty, value);
}
/// <summary>
@ -148,8 +150,8 @@ namespace Avalonia.Controls
/// </summary>
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}
/// <summary>
@ -158,8 +160,8 @@ namespace Avalonia.Controls
/// </summary>
public bool IsDefault
{
get { return GetValue(IsDefaultProperty); }
set { SetValue(IsDefaultProperty, value); }
get => GetValue(IsDefaultProperty);
set => SetValue(IsDefaultProperty, value);
}
/// <summary>
@ -168,18 +170,21 @@ namespace Avalonia.Controls
/// </summary>
public bool IsCancel
{
get { return GetValue(IsCancelProperty); }
set { SetValue(IsCancelProperty, value); }
get => GetValue(IsCancelProperty);
set => SetValue(IsCancelProperty, value);
}
/// <summary>
/// Gets or sets a value indicating whether the button is currently pressed.
/// </summary>
public bool IsPressed
{
get { return GetValue(IsPressedProperty); }
private set { SetValue(IsPressedProperty, value); }
get => GetValue(IsPressedProperty);
private set => SetValue(IsPressedProperty, value);
}
/// <summary>
/// Gets or sets the Flyout that should be shown with this button
/// Gets or sets the Flyout that should be shown with this button.
/// </summary>
public FlyoutBase Flyout
{
@ -187,7 +192,8 @@ namespace Avalonia.Controls
set => SetValue(FlyoutProperty, value);
}
protected override bool IsEnabledCore => base.IsEnabledCore && _commandCanExecute;
/// <inheritdoc/>
protected override bool IsEnabledCore => base.IsEnabledCore && _commandCanExecute;
/// <inheritdoc/>
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
@ -224,6 +230,7 @@ namespace Avalonia.Controls
}
}
/// <inheritdoc/>
protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e)
{
if (_hotkey != null) // Control attached again, set Hotkey to create a hotkey manager for this control
@ -240,6 +247,7 @@ namespace Avalonia.Controls
}
}
/// <inheritdoc/>
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
{
// This will cause the hotkey manager to dispose the observer and the reference to this control
@ -358,12 +366,14 @@ namespace Avalonia.Controls
}
}
}
/// <inheritdoc/>
protected override void OnPointerCaptureLost(PointerCaptureLostEventArgs e)
{
IsPressed = false;
}
/// <inheritdoc/>
protected override void OnLostFocus(RoutedEventArgs e)
{
base.OnLostFocus(e);
@ -371,119 +381,93 @@ namespace Avalonia.Controls
IsPressed = false;
}
/// <inheritdoc/>
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{
base.OnPropertyChanged(change);
if (change.Property == IsPressedProperty)
{
UpdatePseudoClasses(change.NewValue.GetValueOrDefault<bool>());
}
else if (change.Property == FlyoutProperty)
if (change.Property == CommandProperty)
{
// If flyout is changed while one is already open, make sure we
// close the old one first
if (change.OldValue.GetValueOrDefault() is FlyoutBase oldFlyout &&
oldFlyout.IsOpen)
if (((ILogical)this).IsAttachedToLogicalTree)
{
oldFlyout.Hide();
if (change.OldValue.GetValueOrDefault() is ICommand oldCommand)
{
oldCommand.CanExecuteChanged -= CanExecuteChanged;
}
if (change.NewValue.GetValueOrDefault() is ICommand newCommand)
{
newCommand.CanExecuteChanged += CanExecuteChanged;
}
}
}
}
protected override void UpdateDataValidation<T>(AvaloniaProperty<T> property, BindingValue<T> value)
{
base.UpdateDataValidation(property, value);
if (property == CommandProperty)
CanExecuteChanged(this, EventArgs.Empty);
}
else if (change.Property == CommandParameterProperty)
{
if (value.Type == BindingValueType.BindingError)
CanExecuteChanged(this, EventArgs.Empty);
}
else if (change.Property == IsCancelProperty)
{
var isCancel = change.NewValue.GetValueOrDefault<bool>();
if (VisualRoot is IInputElement inputRoot)
{
if (_commandCanExecute)
if (isCancel)
{
_commandCanExecute = false;
UpdateIsEffectivelyEnabled();
ListenForCancel(inputRoot);
}
else
{
StopListeningForCancel(inputRoot);
}
}
}
}
/// <summary>
/// Called when the <see cref="Command"/> property changes.
/// </summary>
/// <param name="e">The event args.</param>
private static void CommandChanged(AvaloniaPropertyChangedEventArgs e)
{
if (e.Sender is Button button)
else if (change.Property == IsDefaultProperty)
{
if (((ILogical)button).IsAttachedToLogicalTree)
var isDefault = change.NewValue.GetValueOrDefault<bool>();
if (VisualRoot is IInputElement inputRoot)
{
if (e.OldValue is ICommand oldCommand)
if (isDefault)
{
oldCommand.CanExecuteChanged -= button.CanExecuteChanged;
ListenForDefault(inputRoot);
}
if (e.NewValue is ICommand newCommand)
else
{
newCommand.CanExecuteChanged += button.CanExecuteChanged;
StopListeningForDefault(inputRoot);
}
}
button.CanExecuteChanged(button, EventArgs.Empty);
}
}
/// <summary>
/// Called when the <see cref="CommandParameter"/> property changes.
/// </summary>
/// <param name="e">The event args.</param>
private static void CommandParameterChanged(AvaloniaPropertyChangedEventArgs e)
{
if (e.Sender is Button button)
else if (change.Property == IsPressedProperty)
{
button.CanExecuteChanged(button, EventArgs.Empty);
UpdatePseudoClasses(change.NewValue.GetValueOrDefault<bool>());
}
}
/// <summary>
/// Called when the <see cref="IsDefault"/> property changes.
/// </summary>
/// <param name="e">The event args.</param>
private static void IsDefaultChanged(AvaloniaPropertyChangedEventArgs e)
{
var button = e.Sender as Button;
var isDefault = (bool)e.NewValue;
if (button?.VisualRoot is IInputElement inputRoot)
else if (change.Property == FlyoutProperty)
{
if (isDefault)
{
button.ListenForDefault(inputRoot);
}
else
// If flyout is changed while one is already open, make sure we
// close the old one first
if (change.OldValue.GetValueOrDefault() is FlyoutBase oldFlyout &&
oldFlyout.IsOpen)
{
button.StopListeningForDefault(inputRoot);
oldFlyout.Hide();
}
}
}
/// <summary>
/// Called when the <see cref="IsCancel"/> property changes.
/// </summary>
/// <param name="e">The event args.</param>
private static void IsCancelChanged(AvaloniaPropertyChangedEventArgs e)
/// <inheritdoc/>
protected override void UpdateDataValidation<T>(AvaloniaProperty<T> property, BindingValue<T> value)
{
var button = e.Sender as Button;
var isCancel = (bool)e.NewValue;
if (button?.VisualRoot is IInputElement inputRoot)
base.UpdateDataValidation(property, value);
if (property == CommandProperty)
{
if (isCancel)
{
button.ListenForCancel(inputRoot);
}
else
if (value.Type == BindingValueType.BindingError)
{
button.StopListeningForCancel(inputRoot);
if (_commandCanExecute)
{
_commandCanExecute = false;
UpdateIsEffectivelyEnabled();
}
}
}
}
@ -566,6 +550,9 @@ namespace Avalonia.Controls
}
}
/// <summary>
/// Updates the visual state of the control by applying latest PseudoClasses.
/// </summary>
private void UpdatePseudoClasses(bool isPressed)
{
PseudoClasses.Set(":pressed", isPressed);

Loading…
Cancel
Save