diff --git a/samples/ControlCatalog/SideBar.xaml b/samples/ControlCatalog/SideBar.xaml index e5baf9e942..7d72d1821b 100644 --- a/samples/ControlCatalog/SideBar.xaml +++ b/samples/ControlCatalog/SideBar.xaml @@ -8,7 +8,7 @@ + SelectedIndex="{TemplateBinding SelectedIndex, Mode=TwoWay}"> @@ -20,7 +20,7 @@ Margin="8 0 0 0" MemberSelector="{x:Static TabControl.ContentSelector}" Items="{TemplateBinding Items}" - SelectedIndex="{TemplateBinding Path=SelectedIndex}" + SelectedIndex="{TemplateBinding SelectedIndex}" PageTransition="{TemplateBinding PageTransition}" Grid.Row="1"/> diff --git a/samples/RenderDemo/SideBar.xaml b/samples/RenderDemo/SideBar.xaml index 29e5d854f9..b5f8ccaf01 100644 --- a/samples/RenderDemo/SideBar.xaml +++ b/samples/RenderDemo/SideBar.xaml @@ -9,7 +9,7 @@ + SelectedIndex="{TemplateBinding SelectedIndex, Mode=TwoWay}"> @@ -21,7 +21,7 @@ Margin="8 0 0 0" MemberSelector="{x:Static TabControl.ContentSelector}" Items="{TemplateBinding Items}" - SelectedIndex="{TemplateBinding Path=SelectedIndex}" + SelectedIndex="{TemplateBinding SelectedIndex}" PageTransition="{TemplateBinding PageTransition}" Grid.Row="1"/> diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index baca1494be..761c0618da 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -29,12 +29,6 @@ namespace Avalonia /// private IAvaloniaObject _inheritanceParent; - /// - /// The set values/bindings on this object. - /// - private readonly Dictionary _values = - new Dictionary(); - /// /// Maintains a list of direct property binding subscriptions so that the binding source /// doesn't get collected. @@ -52,6 +46,7 @@ namespace Avalonia private EventHandler _propertyChanged; private DeferredSetter _directDeferredSetter; + private ValueStore _values; /// /// Delayed setter helper for direct properties. Used to fix #855. @@ -228,9 +223,20 @@ namespace Avalonia { return ((IDirectPropertyAccessor)GetRegistered(property)).GetValue(this); } + else if (_values != null) + { + var result = _values.GetValue(property); + + if (result == AvaloniaProperty.UnsetValue) + { + result = GetDefaultValue(property); + } + + return result; + } else { - return GetValueInternal(property); + return GetDefaultValue(property); } } @@ -257,7 +263,7 @@ namespace Avalonia Contract.Requires(property != null); VerifyAccess(); - return _values.TryGetValue(property, out PriorityValue value) ? value.IsAnimating : false; + return _values?.IsAnimating(property) ?? false; } /// @@ -274,14 +280,7 @@ namespace Avalonia Contract.Requires(property != null); VerifyAccess(); - PriorityValue value; - - if (_values.TryGetValue(property, out value)) - { - return value.Value != AvaloniaProperty.UnsetValue; - } - - return false; + return _values?.IsSet(property) ?? false; } /// @@ -369,14 +368,6 @@ namespace Avalonia } else { - PriorityValue v; - - if (!_values.TryGetValue(property, out v)) - { - v = CreatePriorityValue(property); - _values.Add(property, v); - } - Logger.Verbose( LogArea.Property, this, @@ -385,7 +376,12 @@ namespace Avalonia description, priority); - return v.Add(source, (int)priority); + if (_values == null) + { + _values = new ValueStore(this); + } + + return _values.AddBinding(property, source, priority); } } @@ -416,20 +412,12 @@ namespace Avalonia public void Revalidate(AvaloniaProperty property) { VerifyAccess(); - PriorityValue value; - - if (_values.TryGetValue(property, out value)) - { - value.Revalidate(); - } + _values?.Revalidate(property); } /// - void IPriorityValueOwner.Changed(PriorityValue sender, object oldValue, object newValue) + void IPriorityValueOwner.Changed(AvaloniaProperty property, int priority, object oldValue, object newValue) { - var property = sender.Property; - var priority = (BindingPriority)sender.ValuePriority; - oldValue = (oldValue == AvaloniaProperty.UnsetValue) ? GetDefaultValue(property) : oldValue; @@ -439,7 +427,7 @@ namespace Avalonia if (!Equals(oldValue, newValue)) { - RaisePropertyChanged(property, oldValue, newValue, priority); + RaisePropertyChanged(property, oldValue, newValue, (BindingPriority)priority); Logger.Verbose( LogArea.Property, @@ -448,14 +436,14 @@ namespace Avalonia property, oldValue, newValue, - priority); + (BindingPriority)priority); } } /// - void IPriorityValueOwner.BindingNotificationReceived(PriorityValue sender, BindingNotification notification) + void IPriorityValueOwner.BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification) { - UpdateDataValidation(sender.Property, notification); + UpdateDataValidation(property, notification); } /// @@ -468,10 +456,7 @@ namespace Avalonia /// Gets all priority values set on the object. /// /// A collection of property/value tuples. - internal IDictionary GetSetValues() - { - return _values; - } + internal IDictionary GetSetValues() => _values?.GetSetValues(); /// /// Forces revalidation of properties when a property value changes. @@ -660,68 +645,18 @@ namespace Avalonia } } - /// - /// Creates a for a . - /// - /// The property. - /// The . - private PriorityValue CreatePriorityValue(AvaloniaProperty property) - { - var validate = ((IStyledPropertyAccessor)property).GetValidationFunc(GetType()); - Func validate2 = null; - - if (validate != null) - { - validate2 = v => validate(this, v); - } - - PriorityValue result = new PriorityValue( - this, - property, - property.PropertyType, - validate2); - - return result; - } - /// /// Gets the default value for a property. /// /// The property. /// The default value. - private object GetDefaultValue(AvaloniaProperty property) + internal object GetDefaultValue(AvaloniaProperty property) { if (property.Inherits && InheritanceParent is AvaloniaObject aobj) - return aobj.GetValueInternal(property); + return aobj.GetValue(property); return ((IStyledPropertyAccessor) property).GetDefaultValue(GetType()); } - /// - /// Gets a value - /// without check for registered as this can slow getting the value - /// this method is intended for internal usage in AvaloniaObject only - /// it's called only after check the property is registered - /// - /// The property. - /// The value. - private object GetValueInternal(AvaloniaProperty property) - { - object result = AvaloniaProperty.UnsetValue; - PriorityValue value; - - if (_values.TryGetValue(property, out value)) - { - result = value.Value; - } - - if (result == AvaloniaProperty.UnsetValue) - { - result = GetDefaultValue(property); - } - - return result; - } - /// /// Sets the value of a direct property. /// @@ -802,21 +737,13 @@ namespace Avalonia originalValue?.GetType().FullName ?? "(null)")); } - PriorityValue v; - - if (!_values.TryGetValue(property, out v)) + if (_values == null) { - if (value == AvaloniaProperty.UnsetValue) - { - return; - } - - v = CreatePriorityValue(property); - _values.Add(property, v); + _values = new ValueStore(this); } LogPropertySet(property, value, priority); - v.SetValue(value, (int)priority); + _values.AddValue(property, value, (int)priority); } /// diff --git a/src/Avalonia.Base/IPriorityValueOwner.cs b/src/Avalonia.Base/IPriorityValueOwner.cs index aeec720920..5f63f6ef91 100644 --- a/src/Avalonia.Base/IPriorityValueOwner.cs +++ b/src/Avalonia.Base/IPriorityValueOwner.cs @@ -13,18 +13,19 @@ namespace Avalonia /// /// Called when a 's value changes. /// - /// The source of the change. + /// The the property that has changed. + /// The priority of the value. /// The old value. /// The new value. - void Changed(PriorityValue sender, object oldValue, object newValue); + void Changed(AvaloniaProperty property, int priority, object oldValue, object newValue); /// /// Called when a is received by a /// . /// - /// The source of the change. + /// The the property that has changed. /// The notification. - void BindingNotificationReceived(PriorityValue sender, BindingNotification notification); + void BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification); /// /// Ensures that the current thread is the UI thread. diff --git a/src/Avalonia.Base/PriorityValue.cs b/src/Avalonia.Base/PriorityValue.cs index 9b5318083a..c474f9098e 100644 --- a/src/Avalonia.Base/PriorityValue.cs +++ b/src/Avalonia.Base/PriorityValue.cs @@ -281,12 +281,12 @@ namespace Avalonia if (notification == null || notification.HasValue) { - notify(() => Owner?.Changed(this, old, Value)); + notify(() => Owner?.Changed(Property, ValuePriority, old, Value)); } if (notification != null) { - Owner?.BindingNotificationReceived(this, notification); + Owner?.BindingNotificationReceived(Property, notification); } } else diff --git a/src/Avalonia.Base/ValueStore.cs b/src/Avalonia.Base/ValueStore.cs new file mode 100644 index 0000000000..8283edab80 --- /dev/null +++ b/src/Avalonia.Base/ValueStore.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using Avalonia.Data; + +namespace Avalonia +{ + internal class ValueStore : IPriorityValueOwner + { + private readonly AvaloniaObject _owner; + private readonly Dictionary _values = + new Dictionary(); + + public ValueStore(AvaloniaObject owner) + { + _owner = owner; + } + + public IDisposable AddBinding( + AvaloniaProperty property, + IObservable source, + BindingPriority priority) + { + PriorityValue priorityValue; + + if (_values.TryGetValue(property, out var v)) + { + priorityValue = v as PriorityValue; + + if (priorityValue == null) + { + priorityValue = CreatePriorityValue(property); + priorityValue.SetValue(v, (int)BindingPriority.LocalValue); + _values[property] = priorityValue; + } + } + else + { + priorityValue = CreatePriorityValue(property); + _values.Add(property, priorityValue); + } + + return priorityValue.Add(source, (int)priority); + } + + public void AddValue(AvaloniaProperty property, object value, int priority) + { + PriorityValue priorityValue; + + if (_values.TryGetValue(property, out var v)) + { + priorityValue = v as PriorityValue; + + if (priorityValue == null) + { + if (priority == (int)BindingPriority.LocalValue) + { + _values[property] = Validate(property, value); + Changed(property, priority, v, value); + return; + } + else + { + priorityValue = CreatePriorityValue(property); + priorityValue.SetValue(v, (int)BindingPriority.LocalValue); + _values[property] = priorityValue; + } + } + } + else + { + if (value == AvaloniaProperty.UnsetValue) + { + return; + } + + if (priority == (int)BindingPriority.LocalValue) + { + _values.Add(property, Validate(property, value)); + Changed(property, priority, AvaloniaProperty.UnsetValue, value); + return; + } + else + { + priorityValue = CreatePriorityValue(property); + _values.Add(property, priorityValue); + } + } + + priorityValue.SetValue(value, priority); + } + + public void BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification) + { + ((IPriorityValueOwner)_owner).BindingNotificationReceived(property, notification); + } + + public void Changed(AvaloniaProperty property, int priority, object oldValue, object newValue) + { + ((IPriorityValueOwner)_owner).Changed(property, priority, oldValue, newValue); + } + + public IDictionary GetSetValues() => throw new NotImplementedException(); + + public object GetValue(AvaloniaProperty property) + { + var result = AvaloniaProperty.UnsetValue; + + if (_values.TryGetValue(property, out var value)) + { + result = (value is PriorityValue priorityValue) ? priorityValue.Value : value; + } + + return result; + } + + public bool IsAnimating(AvaloniaProperty property) + { + return _values.TryGetValue(property, out var value) ? (value as PriorityValue)?.IsAnimating ?? false : false; + } + + public bool IsSet(AvaloniaProperty property) + { + if (_values.TryGetValue(property, out var value)) + { + return ((value as PriorityValue)?.Value ?? value) != AvaloniaProperty.UnsetValue; + } + + return false; + } + + public void Revalidate(AvaloniaProperty property) + { + if (_values.TryGetValue(property, out var value)) + { + (value as PriorityValue)?.Revalidate(); + } + } + + public void VerifyAccess() => _owner.VerifyAccess(); + + private PriorityValue CreatePriorityValue(AvaloniaProperty property) + { + var validate = ((IStyledPropertyAccessor)property).GetValidationFunc(_owner.GetType()); + Func validate2 = null; + + if (validate != null) + { + validate2 = v => validate(_owner, v); + } + + PriorityValue result = new PriorityValue( + this, + property, + property.PropertyType, + validate2); + + return result; + } + + private object Validate(AvaloniaProperty property, object value) + { + var validate = ((IStyledPropertyAccessor)property).GetValidationFunc(_owner.GetType()); + + if (validate != null && value != AvaloniaProperty.UnsetValue) + { + return validate(_owner, value); + } + + return value; + } + } +} diff --git a/src/Avalonia.Controls/Primitives/AdornerLayer.cs b/src/Avalonia.Controls/Primitives/AdornerLayer.cs index 51c22c88e7..676cdc456a 100644 --- a/src/Avalonia.Controls/Primitives/AdornerLayer.cs +++ b/src/Avalonia.Controls/Primitives/AdornerLayer.cs @@ -21,7 +21,6 @@ namespace Avalonia.Controls.Primitives static AdornerLayer() { AdornedElementProperty.Changed.Subscribe(AdornedElementChanged); - IsHitTestVisibleProperty.OverrideDefaultValue(typeof(AdornerLayer), false); } public AdornerLayer() diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index e0fefef0b7..bfb9973ee6 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -302,18 +302,17 @@ namespace Avalonia.Controls internal void Close(bool ignoreCancel) { - var cancelClosing = false; try { - cancelClosing = HandleClosing(); + if (!ignoreCancel && HandleClosing()) + { + return; + } } finally { - if (ignoreCancel || !cancelClosing) - { - PlatformImpl?.Dispose(); - HandleClosed(); - } + PlatformImpl?.Dispose(); + HandleClosed(); } } @@ -324,6 +323,7 @@ namespace Avalonia.Controls { var args = new CancelEventArgs(); Closing?.Invoke(this, args); + return args.Cancel; } diff --git a/src/Avalonia.Themes.Default/AutoCompleteBox.xaml b/src/Avalonia.Themes.Default/AutoCompleteBox.xaml index 82dbf6064b..6a9af487cb 100644 --- a/src/Avalonia.Themes.Default/AutoCompleteBox.xaml +++ b/src/Avalonia.Themes.Default/AutoCompleteBox.xaml @@ -16,7 +16,7 @@ DataValidationErrors.Errors="{TemplateBinding (DataValidationErrors.Errors)}" /> diff --git a/src/Avalonia.Themes.Default/DropDown.xaml b/src/Avalonia.Themes.Default/DropDown.xaml index 139b2b3687..c57d961f4b 100644 --- a/src/Avalonia.Themes.Default/DropDown.xaml +++ b/src/Avalonia.Themes.Default/DropDown.xaml @@ -32,7 +32,7 @@ diff --git a/src/Avalonia.Themes.Default/MenuItem.xaml b/src/Avalonia.Themes.Default/MenuItem.xaml index efb31175fa..53965db016 100644 --- a/src/Avalonia.Themes.Default/MenuItem.xaml +++ b/src/Avalonia.Themes.Default/MenuItem.xaml @@ -45,7 +45,7 @@ @@ -67,7 +67,7 @@ Grid.Column="1" Minimum="{TemplateBinding Minimum}" Maximum="{TemplateBinding Maximum}" - Value="{TemplateBinding Path=Value, Mode=TwoWay}" + Value="{TemplateBinding Value, Mode=TwoWay}" ViewportSize="{TemplateBinding ViewportSize}" Orientation="{TemplateBinding Orientation}"> diff --git a/src/Avalonia.Themes.Default/ScrollViewer.xaml b/src/Avalonia.Themes.Default/ScrollViewer.xaml index 5355d5c7c3..c493fb32e3 100644 --- a/src/Avalonia.Themes.Default/ScrollViewer.xaml +++ b/src/Avalonia.Themes.Default/ScrollViewer.xaml @@ -9,21 +9,21 @@ CanHorizontallyScroll="{TemplateBinding CanHorizontallyScroll}" CanVerticallyScroll="{TemplateBinding CanVerticallyScroll}" Content="{TemplateBinding Content}" - Extent="{TemplateBinding Path=Extent, Mode=TwoWay}" + Extent="{TemplateBinding Extent, Mode=TwoWay}" Margin="{TemplateBinding Padding}" - Offset="{TemplateBinding Path=Offset, Mode=TwoWay}" - Viewport="{TemplateBinding Path=Viewport, Mode=TwoWay}"/> + Offset="{TemplateBinding Offset, Mode=TwoWay}" + Viewport="{TemplateBinding Viewport, Mode=TwoWay}"/> diff --git a/src/Avalonia.Themes.Default/Slider.xaml b/src/Avalonia.Themes.Default/Slider.xaml index 0944612417..58fd67b2f6 100644 --- a/src/Avalonia.Themes.Default/Slider.xaml +++ b/src/Avalonia.Themes.Default/Slider.xaml @@ -11,7 +11,7 @@ - + @@ -46,7 +46,7 @@ - + @@ -72,8 +72,7 @@