Add Execute.PostToUIThreadAsync

This commit is contained in:
Antony Male 2014-07-26 11:51:46 +01:00
parent a7a63d5ae1
commit 642a038488
2 changed files with 122 additions and 23 deletions

View File

@ -84,6 +84,7 @@ namespace Stylet
/// <summary> /// <summary>
/// Dispatches the given action to be run on the UI thread asynchronously, even if the current thread is the UI thread /// Dispatches the given action to be run on the UI thread asynchronously, even if the current thread is the UI thread
/// </summary> /// </summary>
/// <param name="action">Action to run on the UI thread</param>
public static void PostToUIThread(Action action) public static void PostToUIThread(Action action)
{ {
EnsureDispatcher(); EnsureDispatcher();
@ -93,9 +94,29 @@ namespace Stylet
action(); action();
} }
/// <summary>
/// Dispatches the given action to be run on the UI thread asynchronously, and returns a task which completes when the action completes, even if the current thread is the UI thread
/// </summary>
/// <param name="action">Action to run on the UI thread</param>
/// <returns>Task which completes when the action has been run</returns>
public static Task PostToUIThreadAsync(Action action)
{
EnsureDispatcher();
if (!TestExecuteSynchronously)
{
return PostOnUIThreadInternalAsync(action);
}
else
{
action();
return Task.FromResult(false);
}
}
/// <summary> /// <summary>
/// Dispatches the given action to be run on the UI thread asynchronously, or runs it synchronously if the current thread is the UI thread /// Dispatches the given action to be run on the UI thread asynchronously, or runs it synchronously if the current thread is the UI thread
/// </summary> /// </summary>
/// <param name="action">Action to run on the UI thread</param>
public static void OnUIThread(Action action) public static void OnUIThread(Action action)
{ {
EnsureDispatcher(); EnsureDispatcher();
@ -108,6 +129,7 @@ namespace Stylet
/// <summary> /// <summary>
/// Dispatches the given action to be run on the UI thread and blocks until it completes, or runs it synchronously if the current thread is the UI thread /// Dispatches the given action to be run on the UI thread and blocks until it completes, or runs it synchronously if the current thread is the UI thread
/// </summary> /// </summary>
/// <param name="action">Action to run on the UI thread</param>
public static void OnUIThreadSync(Action action) public static void OnUIThreadSync(Action action)
{ {
EnsureDispatcher(); EnsureDispatcher();
@ -138,10 +160,23 @@ namespace Stylet
/// <summary> /// <summary>
/// Dispatches the given action to be run on the UI thread and returns a task that completes when the action completes, or runs it synchronously if the current thread is the UI thread /// Dispatches the given action to be run on the UI thread and returns a task that completes when the action completes, or runs it synchronously if the current thread is the UI thread
/// </summary> /// </summary>
/// <param name="action">Action to run on the UI thread</param>
/// <returns>Task which completes when the action has been run</returns>
public static Task OnUIThreadAsync(Action action) public static Task OnUIThreadAsync(Action action)
{ {
EnsureDispatcher(); EnsureDispatcher();
if (!TestExecuteSynchronously && !Dispatcher.IsCurrent) if (!TestExecuteSynchronously && !Dispatcher.IsCurrent)
{
return PostOnUIThreadInternalAsync(action);
}
else
{
action();
return Task.FromResult(false);
}
}
private static Task PostOnUIThreadInternalAsync(Action action)
{ {
var tcs = new TaskCompletionSource<object>(); var tcs = new TaskCompletionSource<object>();
Dispatcher.Post(() => Dispatcher.Post(() =>
@ -158,12 +193,6 @@ namespace Stylet
}); });
return tcs.Task; return tcs.Task;
} }
else
{
action();
return Task.FromResult(false);
}
}
/// <summary> /// <summary>
/// Determing if we're currently running in design mode /// Determing if we're currently running in design mode

View File

@ -21,7 +21,7 @@ namespace StyletUnitTests
} }
[Test] [Test]
public void OnUIThreadExecutesUsingDispatcher() public void OnUIThreadSyncExecutesUsingDispatcher()
{ {
var sync = new Mock<IDispatcher>(); var sync = new Mock<IDispatcher>();
Execute.Dispatcher = sync.Object; Execute.Dispatcher = sync.Object;
@ -38,7 +38,22 @@ namespace StyletUnitTests
} }
[Test] [Test]
public void BeginOnUIThreadExecutesUsingDispatcher() public void OnUIThreadSyncExecutesSynchronouslyIfDispatcherIsCurrent()
{
var sync = new Mock<IDispatcher>();
Execute.Dispatcher = sync.Object;
sync.SetupGet(x => x.IsCurrent).Returns(true);
bool actionCalled = false;
Execute.OnUIThreadSync(() => actionCalled = true);
Assert.IsTrue(actionCalled);
sync.Verify(x => x.Send(It.IsAny<Action>()), Times.Never);
}
[Test]
public void PostToUIThreadExecutesUsingDispatcher()
{ {
var sync = new Mock<IDispatcher>(); var sync = new Mock<IDispatcher>();
Execute.Dispatcher = sync.Object; Execute.Dispatcher = sync.Object;
@ -55,7 +70,26 @@ namespace StyletUnitTests
} }
[Test] [Test]
public void BeginOnUIThreadOrSynchronousExecutesUsingDispatcherIfNotCurrent() public void PostToUIThreadAsyncExecutesUsingDispatcher()
{
var sync = new Mock<IDispatcher>();
Execute.Dispatcher = sync.Object;
Action passedAction = null;
sync.Setup(x => x.Post(It.IsAny<Action>())).Callback((Action a) => passedAction = a);
bool actionCalled = false;
var task = Execute.PostToUIThreadAsync(() => actionCalled = true);
Assert.IsFalse(task.IsCompleted);
Assert.IsFalse(actionCalled);
passedAction();
Assert.IsTrue(actionCalled);
Assert.IsTrue(task.IsCompleted);
}
[Test]
public void OnUIThreadExecutesUsingDispatcherIfNotCurrent()
{ {
var sync = new Mock<IDispatcher>(); var sync = new Mock<IDispatcher>();
Execute.Dispatcher = sync.Object; Execute.Dispatcher = sync.Object;
@ -91,7 +125,7 @@ namespace StyletUnitTests
} }
[Test] [Test]
public void OnUIThreadPropagatesException() public void OnUIThreadSyncPropagatesException()
{ {
var sync = new Mock<IDispatcher>(); var sync = new Mock<IDispatcher>();
Execute.Dispatcher = sync.Object; Execute.Dispatcher = sync.Object;
@ -125,21 +159,45 @@ namespace StyletUnitTests
} }
[Test] [Test]
public void ThrowsIfBeginOnUIThreadCalledWithNoDispatcher() public void PostToUIThreadAsyncPrepagatesException()
{
var sync = new Mock<IDispatcher>();
Execute.Dispatcher = sync.Object;
Action passedAction = null;
sync.Setup(x => x.Post(It.IsAny<Action>())).Callback((Action a) => passedAction = a);
var ex = new Exception("test");
var task = Execute.PostToUIThreadAsync(() => { throw ex; });
passedAction();
Assert.IsTrue(task.IsFaulted);
Assert.AreEqual(ex, task.Exception.InnerExceptions[0]);
}
[Test]
public void ThrowsIfPostToUIThreadCalledWithNoDispatcher()
{ {
Execute.Dispatcher = null; Execute.Dispatcher = null;
Assert.Throws<InvalidOperationException>(() => Execute.PostToUIThread(() => { })); Assert.Throws<InvalidOperationException>(() => Execute.PostToUIThread(() => { }));
} }
[Test] [Test]
public void ThrowsIfBeginOnUIThreadOrSynchronousCalledWithNoDispatcher() public void ThrowsIfPostToUIThreadAsyncCalledWithNoDispatcher()
{
Execute.Dispatcher = null;
Assert.Throws<InvalidOperationException>(() => Execute.PostToUIThreadAsync(() => { }));
}
[Test]
public void ThrowsIfOnUIThreadCalledWithNoDispatcher()
{ {
Execute.Dispatcher = null; Execute.Dispatcher = null;
Assert.Throws<InvalidOperationException>(() => Execute.OnUIThread(() => { })); Assert.Throws<InvalidOperationException>(() => Execute.OnUIThread(() => { }));
} }
[Test] [Test]
public void ThrowsIfOnUIThreadCalledWithNoDispatcher() public void ThrowsIfOnUIThreadSyncCalledWithNoDispatcher()
{ {
Execute.Dispatcher = null; Execute.Dispatcher = null;
Assert.Throws<InvalidOperationException>(() => Execute.OnUIThreadSync(() => { })); Assert.Throws<InvalidOperationException>(() => Execute.OnUIThreadSync(() => { }));
@ -153,7 +211,7 @@ namespace StyletUnitTests
} }
[Test] [Test]
public void BeginOnUIThreadExecutesSynchronouslyIfTestExecuteSynchronouslySet() public void PostToUIThreadExecutesSynchronouslyIfTestExecuteSynchronouslySet()
{ {
Execute.TestExecuteSynchronously = true; Execute.TestExecuteSynchronously = true;
@ -164,7 +222,19 @@ namespace StyletUnitTests
} }
[Test] [Test]
public void OnUIThreadExecutesSynchronouslyIfTestExecuteSynchronouslySet() public void PostToUIThreadAsyncExecutesSynchronouslyIfTestExecuteSynchronouslySet()
{
Execute.TestExecuteSynchronously = true;
Execute.Dispatcher = null;
bool called = false;
var task = Execute.PostToUIThreadAsync(() => called = true);
Assert.True(called);
Assert.True(task.IsCompleted);
}
[Test]
public void OnUIThreadSyncExecutesSynchronouslyIfTestExecuteSynchronouslySet()
{ {
Execute.TestExecuteSynchronously = true; Execute.TestExecuteSynchronously = true;