diff --git a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
index 5639963e9e..a5fa41d534 100644
--- a/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
+++ b/src/Avalonia.Base/Threading/AvaloniaSynchronizationContext.cs
@@ -1,12 +1,11 @@
using System;
-using System.Runtime.ConstrainedExecution;
using System.Threading;
using Avalonia.Utilities;
namespace Avalonia.Threading
{
///
- /// SynchronizationContext to be used on main thread
+ /// A that uses a to post messages.
///
public class AvaloniaSynchronizationContext : SynchronizationContext
{
@@ -16,7 +15,7 @@ namespace Avalonia.Threading
private readonly Dispatcher _dispatcher;
// This constructor is here to enforce STA behavior for unit tests
- internal AvaloniaSynchronizationContext(Dispatcher dispatcher, DispatcherPriority priority, bool isStaThread = false)
+ internal AvaloniaSynchronizationContext(Dispatcher dispatcher, DispatcherPriority priority, bool isStaThread)
{
_dispatcher = dispatcher;
Priority = priority;
@@ -26,17 +25,17 @@ namespace Avalonia.Threading
}
public AvaloniaSynchronizationContext()
- : this(Dispatcher.UIThread, DispatcherPriority.Default, Thread.CurrentThread.GetApartmentState() == ApartmentState.STA)
+ : this(Dispatcher.CurrentDispatcher, DispatcherPriority.Default)
{
}
public AvaloniaSynchronizationContext(DispatcherPriority priority)
- : this(Dispatcher.UIThread, priority, false)
+ : this(Dispatcher.CurrentDispatcher, priority)
{
}
public AvaloniaSynchronizationContext(Dispatcher dispatcher, DispatcherPriority priority)
- : this(dispatcher, priority, false)
+ : this(dispatcher, priority, dispatcher.IsSta)
{
}
@@ -55,7 +54,7 @@ namespace Avalonia.Threading
return;
}
- SetSynchronizationContext(Dispatcher.UIThread.GetContextWithPriority(DispatcherPriority.Normal));
+ SetSynchronizationContext(Dispatcher.CurrentDispatcher.GetContextWithPriority(DispatcherPriority.Normal));
}
///
@@ -105,7 +104,7 @@ namespace Avalonia.Threading
}
}
- public static RestoreContext Ensure(DispatcherPriority priority) => Ensure(Dispatcher.UIThread, priority);
+ public static RestoreContext Ensure(DispatcherPriority priority) => Ensure(Dispatcher.CurrentDispatcher, priority);
public static RestoreContext Ensure(Dispatcher dispatcher, DispatcherPriority priority)
{
if (Current is AvaloniaSynchronizationContext avaloniaContext
diff --git a/src/Avalonia.Base/Threading/Dispatcher.ThreadStorage.cs b/src/Avalonia.Base/Threading/Dispatcher.ThreadStorage.cs
index 7d9d0b39cf..eb0fbbcdf0 100644
--- a/src/Avalonia.Base/Threading/Dispatcher.ThreadStorage.cs
+++ b/src/Avalonia.Base/Threading/Dispatcher.ThreadStorage.cs
@@ -44,7 +44,14 @@ public partial class Dispatcher
return null;
}
}
-
+
+ ///
+ /// Gets the dispatcher for the UI thread.
+ ///
+ ///
+ /// Control and libraries author are encouraged to use and
+ /// instead.
+ ///
public static Dispatcher UIThread
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/Avalonia.Base/Threading/Dispatcher.cs b/src/Avalonia.Base/Threading/Dispatcher.cs
index 07582ac3f4..100df0acbc 100644
--- a/src/Avalonia.Base/Threading/Dispatcher.cs
+++ b/src/Avalonia.Base/Threading/Dispatcher.cs
@@ -13,10 +13,6 @@ namespace Avalonia.Threading;
///
/// Provides services for managing work items on a thread.
///
-///
-/// In Avalonia, there is usually only a single in the application -
-/// the one for the UI thread, retrieved via the property.
-///
public partial class Dispatcher : IDispatcher
{
private IDispatcherImpl _impl;
@@ -52,6 +48,8 @@ public partial class Dispatcher : IDispatcher
s_currentThreadDispatcher = new() { Reference = new WeakReference(this) });
}
+ IsSta = _thread.GetApartmentState() == ApartmentState.STA;
+
if (impl is null)
{
var st = Stopwatch.StartNew();
@@ -70,6 +68,8 @@ public partial class Dispatcher : IDispatcher
public bool SupportsRunLoops => _controlledImpl != null;
+ internal bool IsSta { get; }
+
///
/// Checks that the current thread is the UI thread.
///
diff --git a/src/Avalonia.Base/Threading/DispatcherTimer.cs b/src/Avalonia.Base/Threading/DispatcherTimer.cs
index ea88109bdf..443b9ae89f 100644
--- a/src/Avalonia.Base/Threading/DispatcherTimer.cs
+++ b/src/Avalonia.Base/Threading/DispatcherTimer.cs
@@ -7,65 +7,102 @@ namespace Avalonia.Threading;
/// A timer that is integrated into the Dispatcher queues, and will
/// be processed after a given amount of time at a specified priority.
///
-public partial class DispatcherTimer
+public class DispatcherTimer
{
internal static int ActiveTimersCount { get; private set; }
///
- /// Creates a timer that uses theUI thread's Dispatcher2 to
- /// process the timer event at background priority.
+ /// Creates a timer that uses to
+ /// process the timer event at background priority.
///
- public DispatcherTimer() : this(DispatcherPriority.Background)
+ public DispatcherTimer()
+ : this(TimeSpan.Zero, DispatcherPriority.Background, Dispatcher.CurrentDispatcher)
{
}
///
- /// Creates a timer that uses the UI thread's Dispatcher2 to
- /// process the timer event at the specified priority.
+ /// Creates a timer that uses to
+ /// process the timer event at the specified priority.
///
- ///
- /// The priority to process the timer at.
- ///
- public DispatcherTimer(DispatcherPriority priority) : this(Threading.Dispatcher.UIThread, priority,
- TimeSpan.FromMilliseconds(0))
+ /// The priority to process the timer at.
+ public DispatcherTimer(DispatcherPriority priority)
+ : this(TimeSpan.Zero, priority, Dispatcher.CurrentDispatcher)
{
}
///
- /// Creates a timer that uses the specified Dispatcher2 to
- /// process the timer event at the specified priority.
+ /// Creates a timer that uses the specified to
+ /// process the timer event at the specified priority.
///
- ///
- /// The priority to process the timer at.
- ///
- ///
- /// The dispatcher to use to process the timer.
- ///
- internal DispatcherTimer(DispatcherPriority priority, Dispatcher dispatcher) : this(dispatcher, priority,
- TimeSpan.FromMilliseconds(0))
+ /// The priority to process the timer at.
+ /// The dispatcher to use to process the timer.
+ public DispatcherTimer(DispatcherPriority priority, Dispatcher dispatcher)
+ : this(TimeSpan.Zero, priority, dispatcher)
{
}
///
- /// Creates a timer that uses the UI thread's Dispatcher2 to
- /// process the timer event at the specified priority after the specified timeout.
+ /// Creates a timer that uses the specified to
+ /// process the timer event at the specified priority after the specified timeout.
///
- ///
- /// The interval to tick the timer after.
- ///
- ///
- /// The priority to process the timer at.
- ///
- ///
- /// The callback to call when the timer ticks.
- ///
- public DispatcherTimer(TimeSpan interval, DispatcherPriority priority, EventHandler callback)
- : this(Threading.Dispatcher.UIThread, priority, interval)
+ /// The interval to tick the timer after.
+ /// The priority to process the timer at.
+ /// The dispatcher to use to process the timer.
+ public DispatcherTimer(TimeSpan interval, DispatcherPriority priority, Dispatcher dispatcher)
{
- if (callback == null)
+ ArgumentNullException.ThrowIfNull(dispatcher);
+
+ DispatcherPriority.Validate(priority, "priority");
+ if (priority == DispatcherPriority.Inactive)
+ {
+ throw new ArgumentException("Specified priority is not valid.", nameof(priority));
+ }
+
+ var ms = interval.TotalMilliseconds;
+ if (ms < 0)
{
- throw new ArgumentNullException(nameof(callback));
+ throw new ArgumentOutOfRangeException(nameof(interval),
+ "TimeSpan period must be greater than or equal to zero.");
}
+ if (ms > int.MaxValue)
+ {
+ throw new ArgumentOutOfRangeException(nameof(interval),
+ "TimeSpan period must be less than or equal to Int32.MaxValue.");
+ }
+
+ _dispatcher = dispatcher;
+ _priority = priority;
+ _interval = interval;
+ }
+
+ ///
+ /// Creates a timer that uses to
+ /// process the timer event at the specified priority after the specified timeout and with
+ /// the specified handler.
+ ///
+ /// The interval to tick the timer after.
+ /// The priority to process the timer at.
+ /// The callback to call when the timer ticks.
+ /// This constructor immediately starts the timer.
+ public DispatcherTimer(TimeSpan interval, DispatcherPriority priority, EventHandler callback)
+ : this(interval, priority, Dispatcher.CurrentDispatcher, callback)
+ {
+ }
+
+ ///
+ /// Creates a timer that uses the specified to
+ /// process the timer event at the specified priority after the specified timeout and with
+ /// the specified handler.
+ ///
+ /// The interval to tick the timer after.
+ /// The priority to process the timer at.
+ /// The dispatcher to use to process the timer.
+ /// The callback to call when the timer ticks.
+ /// This constructor immediately starts the timer.
+ public DispatcherTimer(TimeSpan interval, DispatcherPriority priority, Dispatcher dispatcher, EventHandler callback)
+ : this(interval, priority, dispatcher)
+ {
+ ArgumentNullException.ThrowIfNull(callback);
Tick += callback;
Start();
@@ -252,33 +289,6 @@ public partial class DispatcherTimer
///
public object? Tag { get; set; }
-
- internal DispatcherTimer(Dispatcher dispatcher, DispatcherPriority priority, TimeSpan interval)
- {
- if (dispatcher == null)
- {
- throw new ArgumentNullException(nameof(dispatcher));
- }
-
- DispatcherPriority.Validate(priority, "priority");
- if (priority == DispatcherPriority.Inactive)
- {
- throw new ArgumentException("Specified priority is not valid.", nameof(priority));
- }
-
- if (interval.TotalMilliseconds < 0)
- throw new ArgumentOutOfRangeException(nameof(interval), "TimeSpan period must be greater than or equal to zero.");
-
- if (interval.TotalMilliseconds > Int32.MaxValue)
- throw new ArgumentOutOfRangeException(nameof(interval),
- "TimeSpan period must be less than or equal to Int32.MaxValue.");
-
-
- _dispatcher = dispatcher;
- _priority = priority;
- _interval = interval;
- }
-
private void Restart()
{
lock (_instanceLock)