diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs index 1ec6f8dabc..f48d7a7cc1 100644 --- a/src/Avalonia.Controls/Button.cs +++ b/src/Avalonia.Controls/Button.cs @@ -1,11 +1,9 @@ using System; -using System.Diagnostics; using System.Linq; using System.Windows.Input; using Avalonia.Automation.Peers; using Avalonia.Controls.Metadata; using Avalonia.Controls.Primitives; -using Avalonia.Controls.Templates; using Avalonia.Data; using Avalonia.Input; using Avalonia.Interactivity; @@ -48,9 +46,8 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly DirectProperty CommandProperty = - AvaloniaProperty.RegisterDirect(nameof(Command), - button => button.Command, (button, command) => button.Command = command, enableDataValidation: true); + public static readonly StyledProperty CommandProperty = + AvaloniaProperty.Register(nameof(Command), enableDataValidation: true); /// /// Defines the property. @@ -85,8 +82,8 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly StyledProperty IsPressedProperty = - AvaloniaProperty.Register(nameof(IsPressed)); + public static readonly DirectProperty IsPressedProperty = + AvaloniaProperty.RegisterDirect(nameof(IsPressed), b => b.IsPressed); /// /// Defines the property @@ -94,10 +91,10 @@ namespace Avalonia.Controls public static readonly StyledProperty FlyoutProperty = AvaloniaProperty.Register(nameof(Flyout)); - private ICommand? _command; private bool _commandCanExecute = true; private KeyGesture? _hotkey; private bool _isFlyoutOpen = false; + private bool _isPressed = false; /// /// Initializes static members of the class. @@ -138,8 +135,8 @@ namespace Avalonia.Controls /// public ICommand? Command { - get => _command; - set => SetAndRaise(CommandProperty, ref _command, value); + get => GetValue(CommandProperty); + set => SetValue(CommandProperty, value); } /// @@ -185,8 +182,8 @@ namespace Avalonia.Controls /// public bool IsPressed { - get => GetValue(IsPressedProperty); - private set => SetValue(IsPressedProperty, value); + get => _isPressed; + private set => SetAndRaise(IsPressedProperty, ref _isPressed, value); } /// @@ -248,7 +245,7 @@ namespace Avalonia.Controls { if (_hotkey != null) // Control attached again, set Hotkey to create a hotkey manager for this control { - HotKey = _hotkey; + SetCurrentValue(HotKeyProperty, _hotkey); } base.OnAttachedToLogicalTree(e); @@ -267,7 +264,7 @@ namespace Avalonia.Controls if (HotKey != null) { _hotkey = HotKey; - HotKey = null; + SetCurrentValue(HotKeyProperty, null); } base.OnDetachedFromLogicalTree(e); @@ -291,17 +288,17 @@ namespace Avalonia.Controls break; case Key.Space: - { - if (ClickMode == ClickMode.Press) { - OnClick(); + if (ClickMode == ClickMode.Press) + { + OnClick(); + } + + IsPressed = true; + e.Handled = true; + break; } - IsPressed = true; - e.Handled = true; - break; - } - case Key.Escape when Flyout != null: // If Flyout doesn't have focusable content, close the flyout here CloseFlyout(); @@ -592,7 +589,7 @@ namespace Avalonia.Controls { flyout.Opened -= Flyout_Opened; flyout.Closed -= Flyout_Closed; - } + } } /// @@ -671,7 +668,7 @@ namespace Avalonia.Controls void ICommandSource.CanExecuteChanged(object sender, EventArgs e) => this.CanExecuteChanged(sender, e); void IClickableControl.RaiseClick() => OnClick(); - + /// /// Event handler for when the button's flyout is opened. /// diff --git a/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.Properties.cs b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.Properties.cs index 5ff04b1a99..1454b4ab6c 100644 --- a/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.Properties.cs +++ b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.Properties.cs @@ -50,11 +50,9 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly DirectProperty SelectedDateProperty = - AvaloniaProperty.RegisterDirect( + public static readonly StyledProperty SelectedDateProperty = + AvaloniaProperty.Register( nameof(SelectedDate), - o => o.SelectedDate, - (o, v) => o.SelectedDate = v, enableDataValidation: true, defaultBindingMode:BindingMode.TwoWay); @@ -211,8 +209,8 @@ namespace Avalonia.Controls /// public DateTime? SelectedDate { - get => _selectedDate; - set => SetAndRaise(SelectedDateProperty, ref _selectedDate, value); + get => GetValue(SelectedDateProperty); + set => SetValue(SelectedDateProperty, value); } /// diff --git a/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs index 3f5d355b71..c091d07632 100644 --- a/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs +++ b/src/Avalonia.Controls/CalendarDatePicker/CalendarDatePicker.cs @@ -45,7 +45,6 @@ namespace Avalonia.Controls private DateTime? _onOpenSelectedDate; private bool _settingSelectedDate; - private DateTime? _selectedDate; private bool _suspendTextChangeHandler; private bool _isPopupClosing; private bool _ignoreButtonClick; diff --git a/src/Avalonia.Controls/MenuItem.cs b/src/Avalonia.Controls/MenuItem.cs index 1670e496b4..03e3444d71 100644 --- a/src/Avalonia.Controls/MenuItem.cs +++ b/src/Avalonia.Controls/MenuItem.cs @@ -27,11 +27,8 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly DirectProperty CommandProperty = - Button.CommandProperty.AddOwner( - menuItem => menuItem.Command, - (menuItem, command) => menuItem.Command = command, - enableDataValidation: true); + public static readonly StyledProperty CommandProperty = + Button.CommandProperty.AddOwner(new(enableDataValidation: true)); /// /// Defines the property. @@ -113,7 +110,6 @@ namespace Avalonia.Controls private static readonly ITemplate DefaultPanel = new FuncTemplate(() => new StackPanel()); - private ICommand? _command; private bool _commandCanExecute = true; private bool _commandBindingError; private Popup? _popup; @@ -217,8 +213,8 @@ namespace Avalonia.Controls /// public ICommand? Command { - get { return _command; } - set { SetAndRaise(CommandProperty, ref _command, value); } + get => GetValue(CommandProperty); + set => SetValue(CommandProperty, value); } /// @@ -337,7 +333,7 @@ namespace Avalonia.Controls /// /// This has the same effect as setting to true. /// - public void Open() => IsSubMenuOpen = true; + public void Open() => SetCurrentValue(IsSubMenuOpenProperty, true); /// /// Closes the submenu. @@ -345,7 +341,7 @@ namespace Avalonia.Controls /// /// This has the same effect as setting to false. /// - public void Close() => IsSubMenuOpen = false; + public void Close() => SetCurrentValue(IsSubMenuOpenProperty, false); /// void IMenuItem.RaiseClick() => RaiseEvent(new RoutedEventArgs(ClickEvent)); @@ -369,7 +365,7 @@ namespace Avalonia.Controls { if (_hotkey != null) // Control attached again, set Hotkey to create a hotkey manager for this control { - HotKey = _hotkey; + SetCurrentValue(HotKeyProperty, _hotkey); } base.OnAttachedToLogicalTree(e); @@ -397,7 +393,7 @@ namespace Avalonia.Controls if (HotKey != null) { _hotkey = HotKey; - HotKey = null; + SetCurrentValue(HotKeyProperty, null); } base.OnDetachedFromLogicalTree(e); @@ -663,7 +659,7 @@ namespace Avalonia.Controls } RaiseEvent(new RoutedEventArgs(SubmenuOpenedEvent)); - IsSelected = true; + SetCurrentValue(IsSelectedProperty, true); PseudoClasses.Add(":open"); } else diff --git a/src/Avalonia.Controls/NativeMenuItem.cs b/src/Avalonia.Controls/NativeMenuItem.cs index b7ce928b4b..9b5f756887 100644 --- a/src/Avalonia.Controls/NativeMenuItem.cs +++ b/src/Avalonia.Controls/NativeMenuItem.cs @@ -9,7 +9,6 @@ namespace Avalonia.Controls { public class NativeMenuItem : NativeMenuItemBase, INativeMenuItemExporterEventsImplBridge { - private ICommand? _command; private readonly CanExecuteChangedSubscriber _canExecuteChangedSubscriber; class CanExecuteChangedSubscriber : IWeakEventSubscriber @@ -100,11 +99,8 @@ namespace Avalonia.Controls set => SetValue(ToggleTypeProperty, value); } - public static readonly DirectProperty CommandProperty = - Button.CommandProperty.AddOwner( - menuItem => menuItem.Command, - (menuItem, command) => menuItem.Command = command, - enableDataValidation: true); + public static readonly StyledProperty CommandProperty = + Button.CommandProperty.AddOwner(new(enableDataValidation: true)); /// /// Defines the property. @@ -130,19 +126,8 @@ namespace Avalonia.Controls public ICommand? Command { - get => _command; - set - { - if (_command != null) - WeakEvents.CommandCanExecuteChanged.Unsubscribe(_command, _canExecuteChangedSubscriber); - - SetAndRaise(CommandProperty, ref _command, value); - - if (_command != null) - WeakEvents.CommandCanExecuteChanged.Subscribe(_command, _canExecuteChangedSubscriber); - - CanExecuteChanged(); - } + get => GetValue(CommandProperty); + set => SetValue(CommandProperty, value); } /// @@ -180,6 +165,14 @@ namespace Avalonia.Controls throw new InvalidOperationException("NativeMenu already has a parent"); newMenu.Parent = this; } + else if (change.Property == CommandProperty) + { + if (change.OldValue is ICommand oldCommand) + WeakEvents.CommandCanExecuteChanged.Unsubscribe(oldCommand, _canExecuteChangedSubscriber); + if (change.NewValue is ICommand newCommand) + WeakEvents.CommandCanExecuteChanged.Subscribe(newCommand, _canExecuteChangedSubscriber); + CanExecuteChanged(); + } } } diff --git a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs index e676ec0759..885a8af5d1 100644 --- a/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs +++ b/src/Avalonia.Controls/NumericUpDown/NumericUpDown.cs @@ -91,8 +91,8 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly DirectProperty TextProperty = - AvaloniaProperty.RegisterDirect(nameof(Text), o => o.Text, (o, v) => o.Text = v, + public static readonly StyledProperty TextProperty = + AvaloniaProperty.Register(nameof(Text), defaultBindingMode: BindingMode.TwoWay, enableDataValidation: true); /// @@ -104,9 +104,9 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly DirectProperty ValueProperty = - AvaloniaProperty.RegisterDirect(nameof(Value), updown => updown.Value, - (updown, v) => updown.Value = v, defaultBindingMode: BindingMode.TwoWay, enableDataValidation: true); + public static readonly StyledProperty ValueProperty = + AvaloniaProperty.Register(nameof(Value), coerce: (s,v) => ((NumericUpDown)s).OnCoerceValue(v), + defaultBindingMode: BindingMode.TwoWay, enableDataValidation: true); /// /// Defines the property. @@ -128,8 +128,6 @@ namespace Avalonia.Controls private IDisposable? _textBoxTextChangedSubscription; - private decimal? _value; - private string? _text; private bool _internalValueSet; private bool _isSyncingTextAndValueProperties; private bool _isTextChangedFromUI; @@ -250,8 +248,8 @@ namespace Avalonia.Controls /// public string? Text { - get { return _text; } - set { SetAndRaise(TextProperty, ref _text, value); } + get => GetValue(TextProperty); + set => SetValue(TextProperty, value); } /// @@ -270,12 +268,8 @@ namespace Avalonia.Controls /// public decimal? Value { - get { return _value; } - set - { - value = OnCoerceValue(value); - SetAndRaise(ValueProperty, ref _value, value); - } + get => GetValue(ValueProperty); + set => SetValue(ValueProperty, value); } /// @@ -500,7 +494,7 @@ namespace Avalonia.Controls SyncTextAndValueProperties(true, Text); } } - + /// /// Called when the property value changed. /// @@ -667,7 +661,7 @@ namespace Avalonia.Controls { result = Minimum; } - + SetCurrentValue(ValueProperty, MathUtilities.Clamp(result, Minimum, Maximum)); } @@ -677,7 +671,7 @@ namespace Avalonia.Controls private void OnDecrement() { decimal result; - + if (Value.HasValue) { result = Value.Value - Increment; @@ -686,7 +680,7 @@ namespace Avalonia.Controls { result = Maximum; } - + SetCurrentValue(ValueProperty, MathUtilities.Clamp(result, Minimum, Maximum)); } @@ -704,7 +698,7 @@ namespace Avalonia.Controls { validDirections = ValidSpinDirections.Increase | ValidSpinDirections.Decrease; } - + if (Value < Maximum) { validDirections = validDirections | ValidSpinDirections.Increase; @@ -1058,7 +1052,7 @@ namespace Avalonia.Controls { return null; } - + if (TextConverter != null) { var valueFromText = TextConverter.Convert(text, typeof(decimal?), null, CultureInfo.CurrentCulture); diff --git a/src/Avalonia.Controls/SplitButton/SplitButton.cs b/src/Avalonia.Controls/SplitButton/SplitButton.cs index 31a06d875a..e82fb39a66 100644 --- a/src/Avalonia.Controls/SplitButton/SplitButton.cs +++ b/src/Avalonia.Controls/SplitButton/SplitButton.cs @@ -42,10 +42,8 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly DirectProperty CommandProperty = - Button.CommandProperty.AddOwner( - splitButton => splitButton.Command, - (splitButton, command) => splitButton.Command = command); + public static readonly StyledProperty CommandProperty = + Button.CommandProperty.AddOwner(); /// /// Defines the property. @@ -59,8 +57,6 @@ namespace Avalonia.Controls public static readonly StyledProperty FlyoutProperty = Button.FlyoutProperty.AddOwner(); - private ICommand? _Command; - private Button? _primaryButton = null; private Button? _secondaryButton = null; @@ -83,8 +79,8 @@ namespace Avalonia.Controls /// public ICommand? Command { - get => _Command; - set => SetAndRaise(CommandProperty, ref _Command, value); + get => GetValue(CommandProperty); + set => SetValue(CommandProperty, value); } /// diff --git a/src/Avalonia.Controls/TrayIcon.cs b/src/Avalonia.Controls/TrayIcon.cs index 5713846b35..73bcb84c69 100644 --- a/src/Avalonia.Controls/TrayIcon.cs +++ b/src/Avalonia.Controls/TrayIcon.cs @@ -13,13 +13,10 @@ namespace Avalonia.Controls public sealed class TrayIcons : AvaloniaList { } - - public class TrayIcon : AvaloniaObject, INativeMenuExporterProvider, IDisposable { private readonly ITrayIconImpl? _impl; - private ICommand? _command; private TrayIcon(ITrayIconImpl? impl) { @@ -85,11 +82,8 @@ namespace Avalonia.Controls /// /// Defines the property. /// - public static readonly DirectProperty CommandProperty = - Button.CommandProperty.AddOwner( - trayIcon => trayIcon.Command, - (trayIcon, command) => trayIcon.Command = command, - enableDataValidation: true); + public static readonly StyledProperty CommandProperty = + Button.CommandProperty.AddOwner(new(enableDataValidation: true)); /// /// Defines the property. @@ -136,8 +130,8 @@ namespace Avalonia.Controls /// public ICommand? Command { - get => _command; - set => SetAndRaise(CommandProperty, ref _command, value); + get => GetValue(CommandProperty); + set => SetValue(CommandProperty, value); } /// diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs index be2cae8ec4..09ed78accb 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs @@ -21,17 +21,6 @@ namespace Avalonia.Markup.Xaml.UnitTests { public class XamlIlTests : XamlTestBase { - [Fact] - public void Binding_Button_IsPressed_ShouldWork() - { - var parsed = (Button)AvaloniaRuntimeXamlLoader.Parse(@" -