diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs
index 99e27e7daf..85dccd53cb 100644
--- a/src/Avalonia.Base/AvaloniaObject.cs
+++ b/src/Avalonia.Base/AvaloniaObject.cs
@@ -508,45 +508,6 @@ namespace Avalonia
}
}
- ///
- /// A callback type for encapsulating complex logic for setting direct properties.
- ///
- /// The type of the property.
- /// The value to which to set the property.
- /// The backing field for the property.
- /// A wrapper for the property-changed notification.
- protected delegate void SetAndRaiseCallback(T value, ref T field, Action notifyWrapper);
-
- ///
- /// Sets the backing field for a direct avalonia property, raising the
- /// event if the value has changed.
- ///
- /// The type of the property.
- /// The property.
- /// The backing field.
- /// A callback called to actually set the value to the backing field.
- /// The value.
- ///
- /// True if the value changed, otherwise false.
- ///
- protected bool SetAndRaise(
- AvaloniaProperty property,
- ref T field,
- SetAndRaiseCallback setterCallback,
- T value)
- {
- Contract.Requires(setterCallback != null);
- return Values.Setter.SetAndNotify(
- property,
- ref field,
- (object update, ref T backing, Action notify) =>
- {
- setterCallback((T)update, ref backing, notify);
- return true;
- },
- value);
- }
-
///
/// Sets the backing field for a direct avalonia property, raising the
/// event if the value has changed.
@@ -562,48 +523,16 @@ namespace Avalonia
{
VerifyAccess();
- return SetAndRaise(
- property,
- ref field,
- (T val, ref T backing, Action notifyWrapper)
- => SetAndRaiseCore(property, ref backing, val, notifyWrapper),
- value);
- }
-
- protected bool SetAndRaiseOptimized(AvaloniaProperty property, ref T field, T value)
- {
- VerifyAccess();
-
if (EqualityComparer.Default.Equals(field, value))
{
return false;
}
- DeferredSetterOptimized setter = Values.GetDeferredSetter(property);
+ DeferredSetter setter = Values.GetDeferredSetter(property);
return setter.SetAndNotify(this, property, ref field, value);
}
- ///
- /// Default assignment logic for SetAndRaise.
- ///
- /// The type of the property.
- /// The property.
- /// The backing field.
- /// The value.
- /// A wrapper for the property-changed notification.
- ///
- /// True if the value changed, otherwise false.
- ///
- private bool SetAndRaiseCore(AvaloniaProperty property, ref T field, T value, Action notifyWrapper)
- {
- var old = field;
- field = value;
-
- notifyWrapper(() => RaisePropertyChanged(property, old, value, BindingPriority.LocalValue));
- return true;
- }
-
///
/// Tries to cast a value to a type, taking into account that the value may be a
/// .
diff --git a/src/Avalonia.Base/Utilities/AvaloniaPropertyCollection.cs b/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs
similarity index 91%
rename from src/Avalonia.Base/Utilities/AvaloniaPropertyCollection.cs
rename to src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs
index 9b64342814..ac128d83de 100644
--- a/src/Avalonia.Base/Utilities/AvaloniaPropertyCollection.cs
+++ b/src/Avalonia.Base/Utilities/AvaloniaPropertyValueStore.cs
@@ -1,13 +1,20 @@
-using System;
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
using System.Collections.Generic;
namespace Avalonia.Utilities
{
- internal sealed class AvaloniaPropertyCollection
+ ///
+ /// Stores values with as key.
+ ///
+ /// Stored value type.
+ internal sealed class AvaloniaPropertyValueStore
{
private Entry[] _entries;
- public AvaloniaPropertyCollection()
+ public AvaloniaPropertyValueStore()
{
// The last item in the list is always int.MaxValue
_entries = new[] { new Entry { PropertyId = int.MaxValue, Value = default } };
diff --git a/src/Avalonia.Base/Utilities/DeferredSetter.cs b/src/Avalonia.Base/Utilities/DeferredSetter.cs
index 1b1324b1c5..54458d6e6a 100644
--- a/src/Avalonia.Base/Utilities/DeferredSetter.cs
+++ b/src/Avalonia.Base/Utilities/DeferredSetter.cs
@@ -1,5 +1,7 @@
-using System;
-using System.Collections.Generic;
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
namespace Avalonia.Utilities
{
@@ -8,161 +10,75 @@ namespace Avalonia.Utilities
/// Used to fix #855.
///
/// The type of value with which to track the delayed assignment.
- class DeferredSetter
+ internal sealed class DeferredSetter
{
- private struct NotifyDisposable : IDisposable
+ private readonly SingleOrQueue _pendingValues;
+ private bool _isNotifying;
+
+ public DeferredSetter()
{
- private readonly SettingStatus status;
+ _pendingValues = new SingleOrQueue();
+ }
- internal NotifyDisposable(SettingStatus status)
- {
- this.status = status;
- status.Notifying = true;
- }
+ private static void SetAndRaisePropertyChanged(AvaloniaObject source, AvaloniaProperty property, ref TSetRecord backing, TSetRecord value)
+ {
+ var old = backing;
- public void Dispose()
- {
- status.Notifying = false;
- }
+ backing = value;
+
+ source.RaisePropertyChanged(property, old, value);
}
- ///
- /// Information on current setting/notification status of a property.
- ///
- private class SettingStatus
+ public bool SetAndNotify(
+ AvaloniaObject source,
+ AvaloniaProperty property,
+ ref TSetRecord backing,
+ TSetRecord value)
{
- public bool Notifying { get; set; }
-
- private SingleOrQueue pendingValues;
-
- public SingleOrQueue PendingValues
+ if (!_isNotifying)
{
- get
+ using (new NotifyDisposable(this))
{
- return pendingValues ?? (pendingValues = new SingleOrQueue());
+ SetAndRaisePropertyChanged(source, property, ref backing, value);
}
- }
- }
- private Dictionary _setRecords;
- private Dictionary SetRecords
- => _setRecords ?? (_setRecords = new Dictionary());
+ if (!_pendingValues.Empty)
+ {
+ using (new NotifyDisposable(this))
+ {
+ while (!_pendingValues.Empty)
+ {
+ SetAndRaisePropertyChanged(source, property, ref backing, _pendingValues.Dequeue());
+ }
+ }
+ }
- private SettingStatus GetOrCreateStatus(AvaloniaProperty property)
- {
- if (!SetRecords.TryGetValue(property, out var status))
- {
- status = new SettingStatus();
- SetRecords.Add(property, status);
+ return true;
}
- return status;
- }
-
- ///
- /// Mark the property as currently notifying.
- ///
- /// The property to mark as notifying.
- /// Returns a disposable that when disposed, marks the property as done notifying.
- private NotifyDisposable MarkNotifying(AvaloniaProperty property)
- {
- Contract.Requires(!IsNotifying(property));
-
- SettingStatus status = GetOrCreateStatus(property);
-
- return new NotifyDisposable(status);
- }
-
- ///
- /// Check if the property is currently notifying listeners.
- ///
- /// The property.
- /// If the property is currently notifying listeners.
- private bool IsNotifying(AvaloniaProperty property)
- => SetRecords.TryGetValue(property, out var value) && value.Notifying;
-
- ///
- /// Add a pending assignment for the property.
- ///
- /// The property.
- /// The value to assign.
- private void AddPendingSet(AvaloniaProperty property, TSetRecord value)
- {
- Contract.Requires(IsNotifying(property));
-
- GetOrCreateStatus(property).PendingValues.Enqueue(value);
- }
+ _pendingValues.Enqueue(value);
- ///
- /// Checks if there are any pending assignments for the property.
- ///
- /// The property to check.
- /// If the property has any pending assignments.
- private bool HasPendingSet(AvaloniaProperty property)
- {
- return SetRecords.TryGetValue(property, out var status) && !status.PendingValues.Empty;
+ return false;
}
///
- /// Gets the first pending assignment for the property.
+ /// Disposable that marks the property as currently notifying.
+ /// When disposed, marks the property as done notifying.
///
- /// The property to check.
- /// The first pending assignment for the property.
- private TSetRecord GetFirstPendingSet(AvaloniaProperty property)
+ private readonly struct NotifyDisposable : IDisposable
{
- return GetOrCreateStatus(property).PendingValues.Dequeue();
- }
-
- public delegate bool SetterDelegate(TSetRecord record, ref TValue backing, Action notifyCallback);
+ private readonly DeferredSetter _setter;
- ///
- /// Set the property and notify listeners while ensuring we don't get into a stack overflow as happens with #855 and #824
- ///
- /// The property to set.
- /// The backing field for the property
- ///
- /// A callback that actually sets the property.
- /// The first parameter is the value to set, and the second is a wrapper that takes a callback that sends the property-changed notification.
- ///
- /// The value to try to set.
- public bool SetAndNotify(
- AvaloniaProperty property,
- ref TValue backing,
- SetterDelegate setterCallback,
- TSetRecord value)
- {
- Contract.Requires(setterCallback != null);
- if (!IsNotifying(property))
+ internal NotifyDisposable(DeferredSetter setter)
{
- bool updated = false;
- if (!object.Equals(value, backing))
- {
- updated = setterCallback(value, ref backing, notification =>
- {
- using (MarkNotifying(property))
- {
- notification();
- }
- });
- }
- while (HasPendingSet(property))
- {
- updated |= setterCallback(GetFirstPendingSet(property), ref backing, notification =>
- {
- using (MarkNotifying(property))
- {
- notification();
- }
- });
- }
-
- return updated;
+ _setter = setter;
+ _setter._isNotifying = true;
}
- else if(!object.Equals(value, backing))
+
+ public void Dispose()
{
- AddPendingSet(property, value);
+ _setter._isNotifying = false;
}
- return false;
}
}
}
diff --git a/src/Avalonia.Base/Utilities/DeferredSetterOptimized.cs b/src/Avalonia.Base/Utilities/DeferredSetterOptimized.cs
deleted file mode 100644
index 4858372828..0000000000
--- a/src/Avalonia.Base/Utilities/DeferredSetterOptimized.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-using System;
-
-namespace Avalonia.Utilities
-{
- ///
- /// A utility class to enable deferring assignment until after property-changed notifications are sent.
- /// Used to fix #855.
- ///
- /// The type of value with which to track the delayed assignment.
- internal sealed class DeferredSetterOptimized
- {
- private bool _isNotifying;
- private readonly SingleOrQueue _pendingValues;
-
- public DeferredSetterOptimized()
- {
- _pendingValues = new SingleOrQueue();
- }
-
- private static void SetAndRaisePropertyChanged(AvaloniaObject source, AvaloniaProperty property, ref TSetRecord backing, TSetRecord value)
- {
- var old = backing;
-
- backing = value;
-
- source.RaisePropertyChanged(property, old, value);
- }
-
- public bool SetAndNotify(
- AvaloniaObject source,
- AvaloniaProperty property,
- ref TSetRecord backing,
- TSetRecord value)
- {
- if (!_isNotifying)
- {
- using (new NotifyDisposable(this))
- {
- SetAndRaisePropertyChanged(source, property, ref backing, value);
- }
-
- if (!_pendingValues.Empty)
- {
- using (new NotifyDisposable(this))
- {
- while (!_pendingValues.Empty)
- {
- SetAndRaisePropertyChanged(source, property, ref backing, _pendingValues.Dequeue());
- }
- }
- }
-
- return true;
- }
-
- _pendingValues.Enqueue(value);
-
- return false;
- }
-
- ///
- /// Disposable that marks the property as currently notifying.
- /// When disposed, marks the property as done notifying.
- ///
- private readonly struct NotifyDisposable : IDisposable
- {
- private readonly DeferredSetterOptimized _setter;
-
- internal NotifyDisposable(DeferredSetterOptimized setter)
- {
- _setter = setter;
- _setter._isNotifying = true;
- }
-
- public void Dispose()
- {
- _setter._isNotifying = false;
- }
- }
- }
-}
diff --git a/src/Avalonia.Base/ValueStore.cs b/src/Avalonia.Base/ValueStore.cs
index b7f5c26801..8dfabc71a2 100644
--- a/src/Avalonia.Base/ValueStore.cs
+++ b/src/Avalonia.Base/ValueStore.cs
@@ -7,15 +7,15 @@ namespace Avalonia
{
internal class ValueStore : IPriorityValueOwner
{
- private readonly AvaloniaPropertyCollection