diff --git a/.ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject b/.ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject
index a8c3abe8f2..04ab17c4e1 100644
--- a/.ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject
+++ b/.ncrunch/Avalonia.Direct2D1.RenderTests.v3.ncrunchproject
@@ -1,7 +1,6 @@
- 1000
- True
+ 3000
True
\ No newline at end of file
diff --git a/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs b/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs
index 7973ad72e5..ad2cec2ae3 100644
--- a/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs
+++ b/src/Android/Avalonia.AndroidTestApplication/MainActivity.cs
@@ -56,7 +56,7 @@ namespace Avalonia.AndroidTestApplication
{
Margin = new Thickness(30),
Background = Brushes.Yellow,
- Children = new Avalonia.Controls.Controls
+ Children =
{
new TextBlock
{
diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs
index efcbb57244..17e6ea8f0f 100644
--- a/src/Avalonia.Base/AvaloniaObject.cs
+++ b/src/Avalonia.Base/AvaloniaObject.cs
@@ -51,6 +51,21 @@ namespace Avalonia
///
private EventHandler _propertyChanged;
+ private DeferredSetter _directDeferredSetter;
+
+ ///
+ /// Delayed setter helper for direct properties. Used to fix #855.
+ ///
+ private DeferredSetter DirectPropertyDeferredSetter
+ {
+ get
+ {
+ return _directDeferredSetter ??
+ (_directDeferredSetter = new DeferredSetter());
+ }
+ }
+
+
///
/// Initializes a new instance of the class.
///
@@ -225,6 +240,19 @@ namespace Avalonia
return (T)GetValue((AvaloniaProperty)property);
}
+ ///
+ /// Checks whether a is animating.
+ ///
+ /// The property.
+ /// True if the property is animating, otherwise false.
+ public bool IsAnimating(AvaloniaProperty property)
+ {
+ Contract.Requires(property != null);
+ VerifyAccess();
+
+ return _values.TryGetValue(property, out PriorityValue value) ? value.IsAnimating : false;
+ }
+
///
/// Checks whether a is set on this object.
///
@@ -311,9 +339,6 @@ namespace Avalonia
var description = GetDescription(source);
- var scheduler = AvaloniaLocator.Current.GetService() ?? ImmediateScheduler.Instance;
- source = source.ObserveOn(scheduler);
-
if (property.IsDirect)
{
if (property.IsReadOnly)
@@ -539,6 +564,45 @@ 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 DirectPropertyDeferredSetter.SetAndNotify(
+ property,
+ ref field,
+ (object val, ref T backing, Action notify) =>
+ {
+ setterCallback((T)val, ref backing, notify);
+ return true;
+ },
+ value);
+ }
+
///
/// Sets the backing field for a direct avalonia property, raising the
/// event if the value has changed.
@@ -553,17 +617,32 @@ namespace Avalonia
protected bool SetAndRaise(AvaloniaProperty property, ref T field, T value)
{
VerifyAccess();
- if (!object.Equals(field, value))
- {
- var old = field;
- field = value;
- RaisePropertyChanged(property, old, value, BindingPriority.LocalValue);
- return true;
- }
- else
- {
- return false;
- }
+ return SetAndRaise(
+ property,
+ ref field,
+ (T val, ref T backing, Action notifyWrapper)
+ => SetAndRaiseCore(property, ref backing, val, notifyWrapper),
+ 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;
}
///
@@ -661,29 +740,41 @@ namespace Avalonia
/// The value.
private void SetDirectValue(AvaloniaProperty property, object value)
{
- var notification = value as BindingNotification;
-
- if (notification != null)
+ void Set()
{
- notification.LogIfError(this, property);
- value = notification.Value;
- }
+ var notification = value as BindingNotification;
- if (notification == null || notification.ErrorType == BindingErrorType.Error || notification.HasValue)
- {
- var metadata = (IDirectPropertyMetadata)property.GetMetadata(GetType());
- var accessor = (IDirectPropertyAccessor)GetRegistered(property);
- var finalValue = value == AvaloniaProperty.UnsetValue ?
- metadata.UnsetValue : value;
+ if (notification != null)
+ {
+ notification.LogIfError(this, property);
+ value = notification.Value;
+ }
- LogPropertySet(property, value, BindingPriority.LocalValue);
+ if (notification == null || notification.ErrorType == BindingErrorType.Error || notification.HasValue)
+ {
+ var metadata = (IDirectPropertyMetadata)property.GetMetadata(GetType());
+ var accessor = (IDirectPropertyAccessor)GetRegistered(property);
+ var finalValue = value == AvaloniaProperty.UnsetValue ?
+ metadata.UnsetValue : value;
- accessor.SetValue(this, finalValue);
+ LogPropertySet(property, value, BindingPriority.LocalValue);
+
+ accessor.SetValue(this, finalValue);
+ }
+
+ if (notification != null)
+ {
+ UpdateDataValidation(property, notification);
+ }
}
- if (notification != null)
+ if (Dispatcher.UIThread.CheckAccess())
+ {
+ Set();
+ }
+ else
{
- UpdateDataValidation(property, notification);
+ Dispatcher.UIThread.InvokeAsync(Set);
}
}
diff --git a/src/Avalonia.Base/AvaloniaObjectExtensions.cs b/src/Avalonia.Base/AvaloniaObjectExtensions.cs
index e96679e643..1da2ecb942 100644
--- a/src/Avalonia.Base/AvaloniaObjectExtensions.cs
+++ b/src/Avalonia.Base/AvaloniaObjectExtensions.cs
@@ -138,17 +138,9 @@ namespace Avalonia
AvaloniaProperty property,
BindingPriority priority = BindingPriority.LocalValue)
{
- // TODO: Subject.Create is not yet in stable Rx : once it is, remove the
- // AnonymousSubject classes and use Subject.Create.
- var output = new Subject