Browse Source

Make DeferredSetter tied to AvaloniaProperty and use a regular Dictionary. Remove SettingStatus entries for bindings from the dictionary for bindings that don't create a queue of pending values.

pull/1732/head
Jeremy Koritzinsky 8 years ago
parent
commit
1ad7f27cf9
  1. 2
      src/Avalonia.Base/IPriorityValueOwner.cs
  2. 58
      src/Avalonia.Base/Utilities/DeferredSetter.cs
  3. 2
      src/Avalonia.Base/Utilities/SingleOrQueue.cs
  4. 6
      src/Avalonia.Base/ValueStore.cs

2
src/Avalonia.Base/IPriorityValueOwner.cs

@ -33,6 +33,6 @@ namespace Avalonia
/// </summary>
void VerifyAccess();
DeferredSetter<AvaloniaProperty, object> Setter { get; }
DeferredSetter<object> Setter { get; }
}
}

58
src/Avalonia.Base/Utilities/DeferredSetter.cs

@ -10,10 +10,8 @@ namespace Avalonia.Utilities
/// A utility class to enable deferring assignment until after property-changed notifications are sent.
/// Used to fix #855.
/// </summary>
/// <typeparam name="TProperty">The type of the object that represents the property.</typeparam>
/// <typeparam name="TSetRecord">The type of value with which to track the delayed assignment.</typeparam>
class DeferredSetter<TProperty, TSetRecord>
where TProperty: class
class DeferredSetter<TSetRecord>
{
private struct NotifyDisposable : IDisposable
{
@ -47,20 +45,37 @@ namespace Avalonia.Utilities
return pendingValues ?? (pendingValues = new SingleOrQueue<TSetRecord>());
}
}
public bool IsSimpleSet => pendingValues?.HasTail != true;
}
private readonly ConditionalWeakTable<TProperty, SettingStatus> setRecords = new ConditionalWeakTable<TProperty, SettingStatus>();
private Dictionary<AvaloniaProperty, SettingStatus> _setRecords;
private Dictionary<AvaloniaProperty, SettingStatus> SetRecords
=> _setRecords ?? (_setRecords = new Dictionary<AvaloniaProperty, SettingStatus>());
private SettingStatus GetOrCreateStatus(AvaloniaProperty property)
{
if (!SetRecords.TryGetValue(property, out var status))
{
status = new SettingStatus();
SetRecords.Add(property, status);
}
return status;
}
/// <summary>
/// Mark the property as currently notifying.
/// </summary>
/// <param name="property">The property to mark as notifying.</param>
/// <returns>Returns a disposable that when disposed, marks the property as done notifying.</returns>
private NotifyDisposable MarkNotifying(TProperty property)
private NotifyDisposable MarkNotifying(AvaloniaProperty property)
{
Contract.Requires<InvalidOperationException>(!IsNotifying(property));
return new NotifyDisposable(setRecords.GetOrCreateValue(property));
SettingStatus status = GetOrCreateStatus(property);
return new NotifyDisposable(status);
}
/// <summary>
@ -68,19 +83,19 @@ namespace Avalonia.Utilities
/// </summary>
/// <param name="property">The property.</param>
/// <returns>If the property is currently notifying listeners.</returns>
private bool IsNotifying(TProperty property)
=> setRecords.TryGetValue(property, out var value) && value.Notifying;
private bool IsNotifying(AvaloniaProperty property)
=> SetRecords.TryGetValue(property, out var value) && value.Notifying;
/// <summary>
/// Add a pending assignment for the property.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="value">The value to assign.</param>
private void AddPendingSet(TProperty property, TSetRecord value)
private void AddPendingSet(AvaloniaProperty property, TSetRecord value)
{
Contract.Requires<InvalidOperationException>(IsNotifying(property));
setRecords.GetOrCreateValue(property).PendingValues.Enqueue(value);
GetOrCreateStatus(property).PendingValues.Enqueue(value);
}
/// <summary>
@ -88,9 +103,9 @@ namespace Avalonia.Utilities
/// </summary>
/// <param name="property">The property to check.</param>
/// <returns>If the property has any pending assignments.</returns>
private bool HasPendingSet(TProperty property)
private bool HasPendingSet(AvaloniaProperty property)
{
return setRecords.TryGetValue(property, out var status) && !status.PendingValues.Empty;
return SetRecords.TryGetValue(property, out var status) && !status.PendingValues.Empty;
}
/// <summary>
@ -98,9 +113,17 @@ namespace Avalonia.Utilities
/// </summary>
/// <param name="property">The property to check.</param>
/// <returns>The first pending assignment for the property.</returns>
private TSetRecord GetFirstPendingSet(TProperty property)
private TSetRecord GetFirstPendingSet(AvaloniaProperty property)
{
return GetOrCreateStatus(property).PendingValues.Dequeue();
}
private void CleanupSetStatus(AvaloniaProperty property)
{
return setRecords.GetOrCreateValue(property).PendingValues.Dequeue();
if (SetRecords.TryGetValue(property, out var status) && status.IsSimpleSet)
{
SetRecords.Remove(property);
}
}
public delegate bool SetterDelegate<TValue>(TSetRecord record, ref TValue backing, Action<Action> notifyCallback);
@ -116,7 +139,7 @@ namespace Avalonia.Utilities
/// </param>
/// <param name="value">The value to try to set.</param>
public bool SetAndNotify<TValue>(
TProperty property,
AvaloniaProperty property,
ref TValue backing,
SetterDelegate<TValue> setterCallback,
TSetRecord value)
@ -145,6 +168,9 @@ namespace Avalonia.Utilities
}
});
}
CleanupSetStatus(property);
return updated;
}
else if(!object.Equals(value, backing))

2
src/Avalonia.Base/Utilities/SingleOrQueue.cs

@ -11,7 +11,7 @@ namespace Avalonia.Utilities
private Queue<T> Tail => _tail ?? (_tail = new Queue<T>());
private bool HasTail => _tail != null;
public bool HasTail => _tail != null;
public bool Empty { get; private set; } = true;

6
src/Avalonia.Base/ValueStore.cs

@ -168,14 +168,14 @@ namespace Avalonia
return value;
}
private DeferredSetter<AvaloniaProperty, object> _defferedSetter;
private DeferredSetter<object> _defferedSetter;
public DeferredSetter<AvaloniaProperty, object> Setter
public DeferredSetter<object> Setter
{
get
{
return _defferedSetter ??
(_defferedSetter = new DeferredSetter<AvaloniaProperty, object>());
(_defferedSetter = new DeferredSetter<object>());
}
}
}

Loading…
Cancel
Save