diff --git a/src/Perspex.Application/Application.cs b/src/Perspex.Application/Application.cs
index 5bdf187f71..ac93f82fec 100644
--- a/src/Perspex.Application/Application.cs
+++ b/src/Perspex.Application/Application.cs
@@ -144,7 +144,7 @@ namespace Perspex
{
var source = new CancellationTokenSource();
closable.Closed += (s, e) => source.Cancel();
- Dispatcher.MainLoop(source.Token);
+ Dispatcher.UIThread.MainLoop(source.Token);
}
///
diff --git a/src/Perspex.Base/Perspex.Base.csproj b/src/Perspex.Base/Perspex.Base.csproj
index 51e3d0663d..258afdc1b4 100644
--- a/src/Perspex.Base/Perspex.Base.csproj
+++ b/src/Perspex.Base/Perspex.Base.csproj
@@ -75,6 +75,7 @@
+
diff --git a/src/Perspex.Base/Platform/IPclPlatformWrapper.cs b/src/Perspex.Base/Platform/IPclPlatformWrapper.cs
index c57b87a738..20c836bdaf 100644
--- a/src/Perspex.Base/Platform/IPclPlatformWrapper.cs
+++ b/src/Perspex.Base/Platform/IPclPlatformWrapper.cs
@@ -10,5 +10,7 @@ namespace Perspex.Platform
public interface IPclPlatformWrapper
{
Assembly[] GetLoadedAssemblies();
+ void PostThreadPoolItem(Action cb);
+ IDisposable StartSystemTimer(TimeSpan interval, Action tick);
}
}
diff --git a/src/Perspex.Base/Threading/Dispatcher.cs b/src/Perspex.Base/Threading/Dispatcher.cs
index 29e59bb98e..ee9555552c 100644
--- a/src/Perspex.Base/Threading/Dispatcher.cs
+++ b/src/Perspex.Base/Threading/Dispatcher.cs
@@ -15,26 +15,29 @@ namespace Perspex.Threading
/// In Perspex, there is usually only a single in the application -
/// the one for the UI thread, retrieved via the property.
///
- public static class Dispatcher
+ public class Dispatcher
{
- private static readonly JobRunner _jobRunner =
- new JobRunner(PerspexLocator.Current.GetService());
+ private readonly IPlatformThreadingInterface _platform;
+ private readonly JobRunner _jobRunner;
- ///
- /// Initializes a new instance of the class.
- ///
- static Dispatcher()
+ public static Dispatcher UIThread { get; } =
+ new Dispatcher(PerspexLocator.Current.GetService());
+
+ public Dispatcher(IPlatformThreadingInterface platform)
{
- PerspexLocator.Current.GetService().Signaled += _jobRunner.RunJobs;
+ _platform = platform;
+ _jobRunner = new JobRunner(platform);
+ _platform.Signaled += _jobRunner.RunJobs;
}
+
///
/// Runs the dispatcher's main loop.
///
///
/// A cancellation token used to exit the main loop.
///
- public static void MainLoop(CancellationToken cancellationToken)
+ public void MainLoop(CancellationToken cancellationToken)
{
var platform = PerspexLocator.Current.GetService();
cancellationToken.Register(platform.Signal);
@@ -44,7 +47,7 @@ namespace Perspex.Threading
///
/// Runs continuations pushed on the loop.
///
- public static void RunJobs()
+ public void RunJobs()
{
_jobRunner.RunJobs();
}
@@ -55,7 +58,7 @@ namespace Perspex.Threading
/// The method.
/// The priority with which to invoke the method.
/// A task that can be used to track the method's execution.
- public static Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
+ public Task InvokeAsync(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
return _jobRunner.InvokeAsync(action, priority);
}
@@ -65,7 +68,7 @@ namespace Perspex.Threading
///
/// The method.
/// The priority with which to invoke the method.
- internal static void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
+ internal void Post(Action action, DispatcherPriority priority = DispatcherPriority.Normal)
{
_jobRunner.Post(action, priority);
}
diff --git a/src/Perspex.Base/Threading/DispatcherTimer.cs b/src/Perspex.Base/Threading/DispatcherTimer.cs
index bcd228d0c6..688cf57605 100644
--- a/src/Perspex.Base/Threading/DispatcherTimer.cs
+++ b/src/Perspex.Base/Threading/DispatcherTimer.cs
@@ -177,7 +177,7 @@ namespace Perspex.Threading
///
private void InternalTick()
{
- Dispatcher.Post(RaiseTick, _priority);
+ Dispatcher.UIThread.Post(RaiseTick, _priority);
}
///
diff --git a/src/Perspex.Base/Threading/PerspexSynchronizationContext.cs b/src/Perspex.Base/Threading/PerspexSynchronizationContext.cs
index 8ee323e423..ac7b509740 100644
--- a/src/Perspex.Base/Threading/PerspexSynchronizationContext.cs
+++ b/src/Perspex.Base/Threading/PerspexSynchronizationContext.cs
@@ -36,14 +36,14 @@ namespace Perspex.Threading
///
public override void Post(SendOrPostCallback d, object state)
{
- Dispatcher.Post(() => d(state));
+ Dispatcher.UIThread.Post(() => d(state));
}
///
public override void Send(SendOrPostCallback d, object state)
{
// TODO: Add check for being on the main thread, we should invoke the method immediately in this case
- Dispatcher.InvokeAsync(() => d(state)).Wait();
+ Dispatcher.UIThread.InvokeAsync(() => d(state)).Wait();
}
}
}
\ No newline at end of file
diff --git a/src/Perspex.Base/Threading/SingleThreadDispatcher.cs b/src/Perspex.Base/Threading/SingleThreadDispatcher.cs
new file mode 100644
index 0000000000..7f1ca48593
--- /dev/null
+++ b/src/Perspex.Base/Threading/SingleThreadDispatcher.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Perspex.Platform;
+
+namespace Perspex.Threading
+{
+ public class SingleThreadDispatcher : Dispatcher
+ {
+ class ThreadingInterface : IPlatformThreadingInterface
+ {
+ private AutoResetEvent _evnt = new AutoResetEvent(false);
+ private JobRunner _timerJobRunner;
+
+ public ThreadingInterface()
+ {
+ _timerJobRunner = new JobRunner(this);
+ }
+
+ public void RunLoop(CancellationToken cancellationToken)
+ {
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ _evnt.WaitOne();
+ if (cancellationToken.IsCancellationRequested)
+ return;
+ Signaled?.Invoke();
+ _timerJobRunner.RunJobs();
+ }
+ }
+
+ public IDisposable StartTimer(TimeSpan interval, Action tick)
+ => PerspexLocator.Current.GetService().StartSystemTimer(interval,
+ () => _timerJobRunner.Post(tick, DispatcherPriority.Normal));
+
+ public void Signal() => _evnt.Set();
+
+ public event Action Signaled;
+ }
+
+ public SingleThreadDispatcher() : base(new ThreadingInterface())
+ {
+ }
+
+ public static Dispatcher StartNew(CancellationToken token)
+ {
+ var dispatcher = new SingleThreadDispatcher();
+ PerspexLocator.Current.GetService().PostThreadPoolItem(() =>
+ {
+ dispatcher.MainLoop(token);
+ });
+ return dispatcher;
+ }
+ }
+}
diff --git a/src/Perspex.Controls/TopLevel.cs b/src/Perspex.Controls/TopLevel.cs
index da59235dd6..d6e3c37a75 100644
--- a/src/Perspex.Controls/TopLevel.cs
+++ b/src/Perspex.Controls/TopLevel.cs
@@ -397,7 +397,7 @@ namespace Perspex.Controls
///
private void HandleLayoutNeeded()
{
- Dispatcher.InvokeAsync(LayoutManager.ExecuteLayoutPass, DispatcherPriority.Render);
+ Dispatcher.UIThread.InvokeAsync(LayoutManager.ExecuteLayoutPass, DispatcherPriority.Render);
}
///
@@ -413,7 +413,7 @@ namespace Perspex.Controls
///
private void HandleRenderNeeded()
{
- Dispatcher.InvokeAsync(
+ Dispatcher.UIThread.InvokeAsync(
() => PlatformImpl.Invalidate(new Rect(ClientSize)),
DispatcherPriority.Render);
}
diff --git a/src/Perspex.HtmlRenderer/HtmlControl.cs b/src/Perspex.HtmlRenderer/HtmlControl.cs
index f5e52a5fed..55037f9a2f 100644
--- a/src/Perspex.HtmlRenderer/HtmlControl.cs
+++ b/src/Perspex.HtmlRenderer/HtmlControl.cs
@@ -563,7 +563,7 @@ namespace Perspex.Controls.Html
if (CheckAccess())
OnLoadComplete(e);
else
- Dispatcher.Invoke(new Action(OnLinkClicked), e);
+ Dispatcher.UIThread.Invoke(new Action(OnLinkClicked), e);
}
@@ -572,7 +572,7 @@ namespace Perspex.Controls.Html
if (CheckAccess())
OnLinkClicked(e);
else
- Dispatcher.Invoke(new Action(OnLinkClicked), e);
+ Dispatcher.UIThread.Invoke(new Action(OnLinkClicked), e);
}
private void OnRenderError(object sender, HtmlRenderErrorEventArgs e)
@@ -580,7 +580,7 @@ namespace Perspex.Controls.Html
if (CheckAccess())
OnRenderError(e);
else
- Dispatcher.Invoke(new Action(OnRenderError), e);
+ Dispatcher.UIThread.Invoke(new Action(OnRenderError), e);
}
private void OnStylesheetLoad(object sender, HtmlStylesheetLoadEventArgs e)
@@ -588,7 +588,7 @@ namespace Perspex.Controls.Html
if (CheckAccess())
OnStylesheetLoad(e);
else
- Dispatcher.Invoke(new Action(OnStylesheetLoad), e);
+ Dispatcher.UIThread.Invoke(new Action(OnStylesheetLoad), e);
}
private void OnImageLoad(object sender, HtmlImageLoadEventArgs e)
@@ -596,7 +596,7 @@ namespace Perspex.Controls.Html
if (CheckAccess())
OnImageLoad(e);
else
- Dispatcher.Invoke(new Action(OnImageLoad), e);
+ Dispatcher.UIThread.Invoke(new Action(OnImageLoad), e);
}
private void OnRefresh(object sender, HtmlRefreshEventArgs e)
@@ -604,7 +604,7 @@ namespace Perspex.Controls.Html
if (CheckAccess())
OnRefresh(e);
else
- Dispatcher.Invoke(new Action(OnRefresh), e);
+ Dispatcher.UIThread.Invoke(new Action(OnRefresh), e);
}
*/
}
diff --git a/src/Shared/PlatformSupport/PclPlatformWrapper.cs b/src/Shared/PlatformSupport/PclPlatformWrapper.cs
index c5a4b3a320..7ef2839354 100644
Binary files a/src/Shared/PlatformSupport/PclPlatformWrapper.cs and b/src/Shared/PlatformSupport/PclPlatformWrapper.cs differ