Better error handling for DispatcherHelper when not initialized

Added a way to reset the dispatcher in DispatcherHelper
This commit is contained in:
Laurent Bugnion 2013-09-23 13:52:51 +02:00
Родитель 30dc6dca24
Коммит d2651f1aff
2 изменённых файлов: 159 добавлений и 54 удалений

Просмотреть файл

@ -15,6 +15,7 @@
// ****************************************************************************
using System;
using System.Text;
#if NETFX_CORE
using Windows.UI.Core;
@ -36,8 +37,8 @@ namespace GalaSoft.MvvmLight.Threading
/// Helper class for dispatcher operations on the UI thread.
/// </summary>
//// [ClassInfo(typeof(DispatcherHelper),
//// VersionString = "4.0.4",
//// DateString = "201206191330",
//// VersionString = "4.1.6",
//// DateString = "201305190047",
//// Description = "Helper class for dispatcher operations on the UI thread.",
//// UrlContacts = "http://www.galasoft.ch/contact_en.html",
//// Email = "laurent@galasoft.ch")]
@ -70,6 +71,13 @@ namespace GalaSoft.MvvmLight.Threading
/// thread.</param>
public static void CheckBeginInvokeOnUI(Action action)
{
if (action == null)
{
return;
}
CheckDispatcher();
#if NETFX_CORE
if (UIDispatcher.HasThreadAccess)
#else
@ -88,6 +96,30 @@ namespace GalaSoft.MvvmLight.Threading
}
}
private static void CheckDispatcher()
{
if (UIDispatcher == null)
{
var error = new StringBuilder("The DispatcherHelper is not initialized.");
error.AppendLine();
#if SILVERLIGHT
#if WINDOWS_PHONE
error.Append("Call DispatcherHelper.Initialize() at the end of App.InitializePhoneApplication.");
#else
error.Append("Call DispatcherHelper.Initialize() in Application_Startup (App.xaml.cs).");
#endif
#elif NETFX_CORE
error.Append("Call DispatcherHelper.Initialize() at the end of App.OnLaunched.");
#else
error.Append("Call DispatcherHelper.Initialize() in the static App constructor.");
#endif
throw new InvalidOperationException(error.ToString());
}
}
#if NETFX_CORE
/// <summary>
/// Invokes an action asynchronously on the UI thread.
/// </summary>
@ -104,6 +136,8 @@ namespace GalaSoft.MvvmLight.Threading
public static DispatcherOperation RunAsync(Action action)
#endif
{
CheckDispatcher();
#if NETFX_CORE
return UIDispatcher.RunAsync(CoreDispatcherPriority.Normal, () => action());
#else
@ -144,5 +178,13 @@ namespace GalaSoft.MvvmLight.Threading
#endif
#endif
}
/// <summary>
/// Resets the class by deleting the <see cref="UIDispatcher"/>
/// </summary>
public static void Reset()
{
UIDispatcher = null;
}
}
}

Просмотреть файл

@ -1,60 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Windows.Controls;
using System.Threading;
using System.Windows.Controls;
using GalaSoft.MvvmLight.Threading;
using System.Windows;
using System.Windows.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace GalaSoft.MvvmLight.Test.Threading
{
[TestClass]
public class TestDispatcherHelper
public class DispatcherHelperTest
{
private const string NewContent = "New content";
private Button _button;
[TestMethod]
public void TestDispatchingToUiThread()
{
_button = new Button
{
Content = "Content1"
};
var manualEvent = new ManualResetEvent(false);
var thread = new Thread(() =>
{
Thread.Sleep(500);
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
AccessMethodOnUiThread(NewContent);
manualEvent.Set();
});
});
DispatcherHelper.Initialize();
thread.Start();
manualEvent.WaitOne(1000);
#if SILVERLIGHT
// No way to verify that the button is correctly set
// in WPF, however the mere fact that we didn't get an exception
// is an indication of success.
DispatcherHelper.UIDispatcher.BeginInvoke(VerifyButtonNewContent);
#endif
}
private void AccessMethodOnUiThread(string newContent)
{
_button.Content = newContent;
}
[TestMethod]
public void TestDirectAccessToUiThread()
{
@ -67,18 +24,124 @@ namespace GalaSoft.MvvmLight.Test.Threading
DispatcherHelper.CheckBeginInvokeOnUI(() => _button.Content = NewContent);
#if SILVERLIGHT
// No way to verify that the button is correctly set
// in WPF, however the mere fact that we didn't get an exception
// is an indication of success.
// No way to verify that the button is correctly set
// in WPF, however the mere fact that we didn't get an exception
// is an indication of success.
Assert.AreEqual(NewContent, _button.Content);
#endif
}
private const string NewContent = "New content";
[TestMethod]
public void TestDispatchingToUiThread()
{
_button = new Button
{
Content = "Content1"
};
var manualEvent = new ManualResetEvent(false);
var thread = new Thread(
() =>
{
Thread.Sleep(500);
DispatcherHelper.CheckBeginInvokeOnUI(
() =>
{
AccessMethodOnUiThread(NewContent);
manualEvent.Set();
});
});
DispatcherHelper.Initialize();
thread.Start();
manualEvent.WaitOne(1000);
#if SILVERLIGHT
// No way to verify that the button is correctly set
// in WPF, however the mere fact that we didn't get an exception
// is an indication of success.
DispatcherHelper.UIDispatcher.BeginInvoke(VerifyButtonNewContent);
#endif
}
[TestMethod]
public void TestFailingInitializationInCheckBeginInvokeOnUi()
{
DispatcherHelper.Reset();
var done = false;
Exception receivedException = null;
try
{
DispatcherHelper.CheckBeginInvokeOnUI(
() =>
{
done = true;
});
}
catch (Exception ex)
{
receivedException = ex;
}
CheckException(receivedException);
Assert.IsFalse(done);
}
[TestMethod]
public void TestFailingInitializationInRunAsync()
{
DispatcherHelper.Reset();
var done = false;
Exception receivedException = null;
try
{
DispatcherHelper.RunAsync(
() =>
{
done = true;
});
}
catch (Exception ex)
{
receivedException = ex;
}
CheckException(receivedException);
Assert.IsFalse(done);
}
private void AccessMethodOnUiThread(string newContent)
{
_button.Content = newContent;
}
private void CheckException(Exception receivedException)
{
Assert.IsNotNull(receivedException);
Assert.AreEqual(typeof (InvalidOperationException), receivedException.GetType());
#if SILVERLIGHT
#if WINDOWS_PHONE
Assert.IsTrue(receivedException.Message.Contains("Call DispatcherHelper.Initialize() at the end of App.InitializePhoneApplication."));
#else
Assert.IsTrue(receivedException.Message.Contains("Call DispatcherHelper.Initialize() in Application_Startup (App.xaml.cs)."));
#endif
#elif NETFX_CORE
Assert.IsTrue(receivedException.Message.Contains("Call DispatcherHelper.Initialize() at the end of App.OnLaunched."));
#else
Assert.IsTrue(
receivedException.Message.Contains("Call DispatcherHelper.Initialize() in the static App constructor."));
#endif
}
private void VerifyButtonNewContent()
{
Assert.AreEqual(NewContent, _button.Content);
}
}
}
}