diff --git a/Avalonia.v3.ncrunchsolution b/Avalonia.v3.ncrunchsolution
index c2c454eae1..1b5b0c8930 100644
--- a/Avalonia.v3.ncrunchsolution
+++ b/Avalonia.v3.ncrunchsolution
@@ -3,7 +3,7 @@
tests\TestFiles\**.*
- False
+ True
.ncrunch
True
diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs
index 409abfe8fa..c258070505 100644
--- a/src/Avalonia.Base/AvaloniaObject.cs
+++ b/src/Avalonia.Base/AvaloniaObject.cs
@@ -181,6 +181,7 @@ namespace Avalonia
public void ClearValue(AvaloniaProperty property)
{
Contract.Requires(property != null);
+ VerifyAccess();
SetValue(property, AvaloniaProperty.UnsetValue);
}
@@ -193,6 +194,7 @@ namespace Avalonia
public object GetValue(AvaloniaProperty property)
{
Contract.Requires(property != null);
+ VerifyAccess();
if (property.IsDirect)
{
@@ -234,7 +236,8 @@ namespace Avalonia
public bool IsSet(AvaloniaProperty property)
{
Contract.Requires(property != null);
-
+ VerifyAccess();
+
PriorityValue value;
if (_values.TryGetValue(property, out value))
@@ -332,6 +335,7 @@ namespace Avalonia
}
subscription = source
+ .Do(_ => VerifyAccess())
.Select(x => CastOrDefault(x, property.PropertyType))
.Do(_ => { }, () => _directBindings.Remove(subscription))
.Subscribe(x => SetDirectValue(property, x));
diff --git a/src/Avalonia.Base/IPriorityValueOwner.cs b/src/Avalonia.Base/IPriorityValueOwner.cs
index 57f98c0717..aeec720920 100644
--- a/src/Avalonia.Base/IPriorityValueOwner.cs
+++ b/src/Avalonia.Base/IPriorityValueOwner.cs
@@ -25,5 +25,10 @@ namespace Avalonia
/// The source of the change.
/// The notification.
void BindingNotificationReceived(PriorityValue sender, BindingNotification notification);
+
+ ///
+ /// Ensures that the current thread is the UI thread.
+ ///
+ void VerifyAccess();
}
}
diff --git a/src/Avalonia.Base/PriorityBindingEntry.cs b/src/Avalonia.Base/PriorityBindingEntry.cs
index 25b7bede7e..eb7cf5414c 100644
--- a/src/Avalonia.Base/PriorityBindingEntry.cs
+++ b/src/Avalonia.Base/PriorityBindingEntry.cs
@@ -92,6 +92,8 @@ namespace Avalonia
private void ValueChanged(object value)
{
+ _owner.Owner.Owner?.VerifyAccess();
+
var notification = value as BindingNotification;
if (notification != null)
diff --git a/src/Avalonia.Base/PriorityLevel.cs b/src/Avalonia.Base/PriorityLevel.cs
index 122a6df821..b25247deaf 100644
--- a/src/Avalonia.Base/PriorityLevel.cs
+++ b/src/Avalonia.Base/PriorityLevel.cs
@@ -33,7 +33,6 @@ namespace Avalonia
///
internal class PriorityLevel
{
- private PriorityValue _owner;
private object _directValue;
private int _nextIndex;
@@ -48,13 +47,18 @@ namespace Avalonia
{
Contract.Requires(owner != null);
- _owner = owner;
+ Owner = owner;
Priority = priority;
Value = _directValue = AvaloniaProperty.UnsetValue;
ActiveBindingIndex = -1;
Bindings = new LinkedList();
}
+ ///
+ /// Gets the owner of the level.
+ ///
+ public PriorityValue Owner { get; }
+
///
/// Gets the priority of this level.
///
@@ -73,7 +77,7 @@ namespace Avalonia
set
{
Value = _directValue = value;
- _owner.LevelValueChanged(this);
+ Owner.LevelValueChanged(this);
}
}
@@ -131,7 +135,7 @@ namespace Avalonia
{
Value = entry.Value;
ActiveBindingIndex = entry.Index;
- _owner.LevelValueChanged(this);
+ Owner.LevelValueChanged(this);
}
else
{
@@ -161,7 +165,7 @@ namespace Avalonia
/// The error.
public void Error(PriorityBindingEntry entry, BindingNotification error)
{
- _owner.LevelError(this, error);
+ Owner.LevelError(this, error);
}
///
@@ -175,14 +179,14 @@ namespace Avalonia
{
Value = binding.Value;
ActiveBindingIndex = binding.Index;
- _owner.LevelValueChanged(this);
+ Owner.LevelValueChanged(this);
return;
}
}
Value = DirectValue;
ActiveBindingIndex = -1;
- _owner.LevelValueChanged(this);
+ Owner.LevelValueChanged(this);
}
}
}
diff --git a/src/Avalonia.Base/PriorityValue.cs b/src/Avalonia.Base/PriorityValue.cs
index 3f4b405de9..57e2854014 100644
--- a/src/Avalonia.Base/PriorityValue.cs
+++ b/src/Avalonia.Base/PriorityValue.cs
@@ -26,7 +26,6 @@ namespace Avalonia
///
internal class PriorityValue
{
- private readonly IPriorityValueOwner _owner;
private readonly Type _valueType;
private readonly SingleOrDictionary _levels = new SingleOrDictionary();
private object _value;
@@ -45,7 +44,7 @@ namespace Avalonia
Type valueType,
Func
internal class JobRunner
{
- private readonly IPlatformThreadingInterface _platform;
private readonly Queue _queue = new Queue();
+ private IPlatformThreadingInterface _platform;
public JobRunner(IPlatformThreadingInterface platform)
{
@@ -82,6 +81,14 @@ namespace Avalonia.Threading
AddJob(new Job(action, priority, true));
}
+ ///
+ /// Allows unit tests to change the platform threading interface.
+ ///
+ internal void UpdateServices()
+ {
+ _platform = AvaloniaLocator.Current.GetService();
+ }
+
private void AddJob(Job job)
{
var needWake = false;
@@ -91,7 +98,7 @@ namespace Avalonia.Threading
_queue.Enqueue(job);
}
if (needWake)
- _platform.Signal();
+ _platform?.Signal();
}
///
diff --git a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj
index 07ed7f14ca..d4cf22ed63 100644
--- a/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj
+++ b/tests/Avalonia.Base.UnitTests/Avalonia.Base.UnitTests.csproj
@@ -90,6 +90,7 @@
+
@@ -124,6 +125,26 @@
{B09B78D8-9B26-48B0-9149-D64A2F120F3F}
Avalonia.Base
+
+ {d2221c82-4a25-4583-9b43-d791e3f6820c}
+ Avalonia.Controls
+
+
+ {62024b2d-53eb-4638-b26b-85eeaa54866e}
+ Avalonia.Input
+
+
+ {42472427-4774-4c81-8aff-9f27b8e31721}
+ Avalonia.Layout
+
+
+ {f1baa01a-f176-4c6a-b39d-5b40bb1b148f}
+ Avalonia.Styling
+
+
+ {eb582467-6abb-43a1-b052-e981ba910e3a}
+ Avalonia.Visuals
+
{88060192-33d5-4932-b0f9-8bd2763e857d}
Avalonia.UnitTests
diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs
index 5e286305d2..cd8184b140 100644
--- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs
+++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs
@@ -365,7 +365,7 @@ namespace Avalonia.Base.UnitTests
}
[Fact]
- public async void Bind_With_Scheduler_Executes_On_Scheduler()
+ public async Task Bind_With_Scheduler_Executes_On_Scheduler()
{
var target = new Class1();
var source = new Subject