Merge branch 'release/0.9.5'

This commit is contained in:
Antony Male 2014-04-24 13:00:38 +01:00
commit f32e83cbb5
20 changed files with 245 additions and 121 deletions

View File

@ -1,6 +1,13 @@
Stylet Changelog
================
v0.9.5
------
- Add StyletConductorItemsControl style, for using an ItemsControl with a Conductor<T>.Collection.AllActive
- Make Execute.OnUIThread synchronous, and add BeginOnUIThread (asynchronous)
- Rename conductors. It's now Conductor<T>.Collection.xxx not Conductor<T>.Collections.xxx
v0.9.4
------

View File

@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2011/10/nuspec.xsd">
<metadata>
<id>Stylet</id>
<version>0.9.4</version>
<version>0.9.5</version>
<title>Stylet</title>
<authors>Antony Male</authors>
<owners>Antony Male</owners>

View File

@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace Stylet.Samples.RedditBrowser.Pages
{
public class ShellViewModel : Conductor<IScreen>.Collections.OneActive, IHandle<OpenSubredditEvent>
public class ShellViewModel : Conductor<IScreen>.Collection.OneActive, IHandle<OpenSubredditEvent>
{
private ISubredditViewModelFactory subredditViewModelFactory;

View File

@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace Stylet.Samples.RedditBrowser.Pages
{
public class SubredditViewModel : Conductor<IScreen>.Collections.Navigation
public class SubredditViewModel : Conductor<IScreen>.StackNavigation
{
private IPostCommentsViewModelFactory postCommentsViewModelFactory;

View File

@ -6,7 +6,7 @@ using System.Threading.Tasks;
namespace Stylet.Samples.TabNavigation
{
class ShellViewModel : Conductor<IScreen>.Collections.OneActive
class ShellViewModel : Conductor<IScreen>.Collection.OneActive
{
public ShellViewModel(Page1ViewModel page1, Page2ViewModel page2)
{

View File

@ -10,7 +10,7 @@ namespace Stylet
{
public partial class Conductor<T>
{
public partial class Collections
public partial class Collection
{
/// <summary>
/// Conductor which has many items, all of which active at the same time

View File

@ -7,13 +7,11 @@ using System.Threading.Tasks;
namespace Stylet
{
public partial class Conductor<T>
{
public partial class Collections
{
/// <summary>
/// Stack-based navigation. A Conductor which has one active item, and a stack of previous items
/// </summary>
public class Navigation : ConductorBaseWithActiveItem<T>
public class StackNavigation : ConductorBaseWithActiveItem<T>
{
// We need to remove arbitrary items, so no Stack<T> here!
private List<T> history = new List<T>();
@ -79,7 +77,7 @@ namespace Stylet
if (this.history.Count > 0)
{
newItem = this.history.Last();
this.history.RemoveAt(this.history.Count-1);
this.history.RemoveAt(this.history.Count - 1);
}
this.ChangeActiveItem(newItem, true);
}
@ -110,5 +108,4 @@ namespace Stylet
}
}
}
}
}

View File

@ -10,7 +10,7 @@ namespace Stylet
{
public partial class Conductor<T>
{
public partial class Collections
public partial class Collection
{
/// <summary>
/// Conductor with many items, only one of which is active

View File

@ -12,24 +12,90 @@ namespace Stylet
{
public static class Execute
{
/// <summary>
/// Should be set to the UI thread's SynchronizationContext. This is normally done by the Bootstrapper.
/// </summary>
public static SynchronizationContext SynchronizationContext;
/// <summary>
/// FOR TESTING ONLY. Causes everything to execute synchronously
/// </summary>
public static bool TestExecuteSynchronously = false;
private static bool? inDesignMode;
public static Action<Action> DefaultPropertyChangedDispatcher = Execute.OnUIThread;
/// <summary>
/// Default dispatcher used by PropertyChangedBase instances. Defaults to BeginOnUIThreadOrSynchronous
/// </summary>
public static Action<Action> DefaultPropertyChangedDispatcher = Execute.BeginOnUIThreadOrSynchronous;
public static void OnUIThread(Action action)
private static void EnsureSynchronizationContext()
{
// If we're already on the given SynchronizationContext, or it hasn't been set, run synchronously
if (SynchronizationContext != null && SynchronizationContext != SynchronizationContext.Current)
if (SynchronizationContext == null && !TestExecuteSynchronously)
throw new Exception("Execute.SynchronizationContext must be set before this method can be called. This should normally have been done by the Bootstrapper");
}
/// <summary>
/// Dispatches the given action to be run on the UI thread, even if the current thread is the UI thread
/// </summary>
public static void BeginOnUIThread(Action action)
{
EnsureSynchronizationContext();
if (!TestExecuteSynchronously)
SynchronizationContext.Post(_ => action(), null);
else
action();
}
/// <summary>
/// Dispatches the given action to be run on the UI thread, or runs it synchronously if the current thread is the UI thread
/// </summary>
public static void BeginOnUIThreadOrSynchronous(Action action)
{
EnsureSynchronizationContext();
if (SynchronizationContext != SynchronizationContext.Current && !TestExecuteSynchronously)
SynchronizationContext.Post(_ => action(), null);
else
action();
}
/// <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
/// </summary>
public static void OnUIThread(Action action)
{
EnsureSynchronizationContext();
Exception exception = null;
if (SynchronizationContext != SynchronizationContext.Current && !TestExecuteSynchronously)
{
SynchronizationContext.Send(_ =>
{
try
{
action();
}
catch (Exception e)
{
exception = e;
}
}, null);
if (exception != null)
throw new System.Reflection.TargetInvocationException("An error occurred while dispatching a call to the UI Thread", exception);
}
else
{
action();
}
}
/// <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
/// </summary>
public static Task OnUIThreadAsync(Action action)
{
// If we're already on the given SynchronizationContext, or it hasn't been set, run synchronously
if (SynchronizationContext != null && SynchronizationContext != SynchronizationContext.Current)
EnsureSynchronizationContext();
if (SynchronizationContext != SynchronizationContext.Current && !TestExecuteSynchronously)
{
var tcs = new TaskCompletionSource<object>();
SynchronizationContext.Post(_ =>

View File

@ -35,5 +35,5 @@ using System.Windows.Markup;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.9.4.0")]
[assembly: AssemblyFileVersion("0.9.4.0")]
[assembly: AssemblyVersion("0.9.5.0")]
[assembly: AssemblyFileVersion("0.9.5.0")]

View File

@ -64,7 +64,7 @@ namespace Stylet
var propertyAsEventInfo = valueService.TargetProperty as EventInfo;
if (propertyAsEventInfo != null)
{
var ec = new EventAction((DependencyObject)valueService.TargetObject, propertyAsEventInfo, this.Method, this.NullTarget.GetValueOrDefault(ActionUnavailableBehaviour.Throw), this.ActionNotFound.GetValueOrDefault(ActionUnavailableBehaviour.Throw));
var ec = new EventAction((DependencyObject)valueService.TargetObject, propertyAsEventInfo, this.Method, this.NullTarget.GetValueOrDefault(ActionUnavailableBehaviour.Enable), this.ActionNotFound.GetValueOrDefault(ActionUnavailableBehaviour.Throw));
return ec.GetDelegate();
}

View File

@ -13,4 +13,15 @@
</Setter.Value>
</Setter>
</Style>
<Style x:Key="StyletConductorItemsControl" TargetType="ItemsControl">
<Setter Property="ItemsSource" Value="{Binding Items}"/>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<ContentControl s:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@ -16,6 +16,12 @@ namespace StyletUnitTests
{
private class Element { }
[TestFixtureSetUp]
public void SetUpFixture()
{
Execute.TestExecuteSynchronously = true;
}
[Test]
public void AddRangeAddsElements()
{

View File

@ -12,12 +12,18 @@ namespace StyletUnitTests
[TestFixture]
public class ConductorAllActiveTests
{
private Conductor<IScreen>.Collections.AllActive conductor;
private Conductor<IScreen>.Collection.AllActive conductor;
[TestFixtureSetUp]
public void SetUpFixture()
{
Execute.TestExecuteSynchronously = true;
}
[SetUp]
public void SetUp()
{
this.conductor = new Conductor<IScreen>.Collections.AllActive();
this.conductor = new Conductor<IScreen>.Collection.AllActive();
}
[Test]

View File

@ -12,12 +12,12 @@ namespace StyletUnitTests
[TestFixture]
public class ConductorNavigatingTests
{
private Conductor<IScreen>.Collections.Navigation conductor;
private Conductor<IScreen>.StackNavigation conductor;
[SetUp]
public void SetUp()
{
this.conductor = new Conductor<IScreen>.Collections.Navigation();
this.conductor = new Conductor<IScreen>.StackNavigation();
}
[Test]

View File

@ -12,12 +12,18 @@ namespace StyletUnitTests
[TestFixture]
public class ConductorOneActiveTests
{
private Conductor<IScreen>.Collections.OneActive conductor;
private Conductor<IScreen>.Collection.OneActive conductor;
[TestFixtureSetUp]
public void SetUpFixture()
{
Execute.TestExecuteSynchronously = true;
}
[SetUp]
public void SetUp()
{
this.conductor = new Conductor<IScreen>.Collections.OneActive();
this.conductor = new Conductor<IScreen>.Collection.OneActive();
}
[Test]

View File

@ -13,23 +13,21 @@ namespace StyletUnitTests
[TestFixture]
public class ExecuteTests
{
[Test]
public void OnUIThreadExecutesSynchronouslyIfNoSynchronizationContext()
[TestFixtureSetUp]
public void SetUpFixture()
{
Execute.SynchronizationContext = null;
bool blockWasRun = false;
Execute.OnUIThread(() => blockWasRun = true);
Assert.IsTrue(blockWasRun);
// Dont want this being previously set by anything and messing us around
Execute.TestExecuteSynchronously = false;
}
[Test]
public void OnUIThreadExecutesAsynchronouslyIfSynchronizationContextIsNotNull()
public void OnUIThreadExecutesUsingSynchronizationContext()
{
var sync = new Mock<SynchronizationContext>();
Execute.SynchronizationContext = sync.Object;
SendOrPostCallback passedAction = null;
sync.Setup(x => x.Post(It.IsAny<SendOrPostCallback>(), null)).Callback((SendOrPostCallback a, object o) => passedAction = a);
sync.Setup(x => x.Send(It.IsAny<SendOrPostCallback>(), null)).Callback((SendOrPostCallback a, object o) => passedAction = a);
bool actionCalled = false;
Execute.OnUIThread(() => actionCalled = true);
@ -40,12 +38,20 @@ namespace StyletUnitTests
}
[Test]
public async Task OnUIThreadAsyncExecutesSynchronouslyIfNoSynchronizationContext()
public void BeginOnUIThreadExecutesUsingSynchronizationContext()
{
Execute.SynchronizationContext = null;
bool blockWasRun = false;
await Execute.OnUIThreadAsync(() => blockWasRun = true);
Assert.IsTrue(blockWasRun);
var sync = new Mock<SynchronizationContext>();
Execute.SynchronizationContext = sync.Object;
SendOrPostCallback passedAction = null;
sync.Setup(x => x.Post(It.IsAny<SendOrPostCallback>(), null)).Callback((SendOrPostCallback a, object o) => passedAction = a);
bool actionCalled = false;
Execute.BeginOnUIThread(() => actionCalled = true);
Assert.IsFalse(actionCalled);
passedAction(null);
Assert.IsTrue(actionCalled);
}
[Test]

View File

@ -34,6 +34,12 @@ namespace StyletUnitTests
}
}
[TestFixtureSetUp]
public void SetUpFixture()
{
Execute.TestExecuteSynchronously = true;
}
[Test]
public void RefreshRaisesPropertyChangedWithEmptyString()
{

View File

@ -50,6 +50,12 @@ namespace StyletUnitTests
}
}
[TestFixtureSetUp]
public void SetUpFixture()
{
Execute.TestExecuteSynchronously = true;
}
[Test]
public void StrongBindingBinds()
{

View File

@ -48,6 +48,13 @@ namespace StyletUnitTests
private MyScreen screen;
[TestFixtureSetUp]
public void SetUpFixture()
{
Execute.TestExecuteSynchronously = true;
}
[SetUp]
public void SetUp()
{