Merge branch 'release/1.1.0'

This commit is contained in:
Antony Male 2015-02-11 12:51:54 +00:00
commit 193591c0b1
37 changed files with 899 additions and 393 deletions

View File

@ -54,7 +54,7 @@ namespace Bootstrappers
return this.container.Resolve(type); return this.container.Resolve(type);
} }
protected override void OnExitInternal(ExitEventArgs e) public override void Dispose()
{ {
this.container.Dispose(); this.container.Dispose();
} }

View File

@ -60,7 +60,7 @@ namespace Bootstrappers
return this.container.Resolve(type); return this.container.Resolve(type);
} }
protected override void OnExitInternal(ExitEventArgs e) public override void Dispose()
{ {
this.container.Dispose(); this.container.Dispose();
} }

View File

@ -51,7 +51,7 @@ namespace Bootstrappers
return this.kernel.Get(type); return this.kernel.Get(type);
} }
protected override void OnExitInternal(ExitEventArgs e) public override void Dispose()
{ {
this.kernel.Dispose(); this.kernel.Dispose();
} }

View File

@ -58,7 +58,7 @@ namespace Bootstrappers
return this.container.GetInstance(type); return this.container.GetInstance(type);
} }
protected override void OnExitInternal(ExitEventArgs e) public override void Dispose()
{ {
this.container.Dispose(); this.container.Dispose();
} }

View File

@ -54,7 +54,7 @@ namespace Bootstrappers
return this.container.Resolve(type); return this.container.Resolve(type);
} }
protected override void OnExitInternal(ExitEventArgs e) public override void Dispose()
{ {
this.container.Dispose(); this.container.Dispose();
} }

View File

@ -1,6 +1,17 @@
Stylet Changelog Stylet Changelog
================ ================
v1.1.0
------
- Backwards-incompatible changes to Bootstrapper: Configure is now called *after* ConfigureIoC, OnStart came before ConfigureIoC, and OnLaunch now happens after Launch
- Screen now uses a property enum-based state machine to manage state. IActivate, IDeactivate and IClose have all been rolled into IScreenState
- Fix incorrect use of WeakEventManager in ScreenExtensions: ConductWith won't have been working properly
- Set WindowStartupLocation to CenterOwner if the user hasn't set it themselves
- WindowManager does not set the Title binding (to DisplayName) if it's been set by the user
- Actions throw on execute if ActionTarget hasn't changed from the default. This catches an edge-case where Actions are used inside something like a ContextMenu which breaks the visual tree
-
v1.0.7 v1.0.7
------ ------

View File

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

View File

@ -31,19 +31,18 @@ namespace Stylet
/// </summary> /// </summary>
protected override sealed void ConfigureBootstrapper() protected override sealed void ConfigureBootstrapper()
{ {
// This needs to be called before the container is set up, as it might affect the assemblies
this.Configure();
var builder = new StyletIoCBuilder(); var builder = new StyletIoCBuilder();
this.DefaultConfigureIoC(builder); this.DefaultConfigureIoC(builder);
this.ConfigureIoC(builder); this.ConfigureIoC(builder);
this.Container = builder.BuildContainer(); this.Container = builder.BuildContainer();
this.Configure();
} }
/// <summary> /// <summary>
/// Override to configure your IoC container, and anything else /// Hook called after the IoC container has been set up
/// </summary> /// </summary>
protected virtual void Configure() { } protected virtual void Configure() { }
@ -80,13 +79,12 @@ namespace Stylet
} }
/// <summary> /// <summary>
/// Hook used internall by the Bootstrapper to do things like dispose the IoC container /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary> /// </summary>
/// <param name="e">The exit event data</param> public override void Dispose()
protected override void OnExitInternal(ExitEventArgs e)
{ {
base.OnExitInternal(e);
this.Container.Dispose(); this.Container.Dispose();
base.Dispose();
} }
} }
} }

View File

@ -12,7 +12,7 @@ namespace Stylet
/// <summary> /// <summary>
/// Bootstrapper to be extended by applications which don't want to use StyletIoC as the IoC container. /// Bootstrapper to be extended by applications which don't want to use StyletIoC as the IoC container.
/// </summary> /// </summary>
public abstract class BootstrapperBase : IBootstrapper, IViewManagerConfig public abstract class BootstrapperBase : IBootstrapper, IViewManagerConfig, IDisposable
{ {
/// <summary> /// <summary>
/// Gets the current application /// Gets the current application
@ -23,7 +23,7 @@ namespace Stylet
/// Gets or sets assemblies which are used for IoC container auto-binding and searching for Views. /// Gets or sets assemblies which are used for IoC container auto-binding and searching for Views.
/// Set this in Configure() if you want to override it /// Set this in Configure() if you want to override it
/// </summary> /// </summary>
public IList<Assembly> Assemblies { get; protected set; } public IReadOnlyList<Assembly> Assemblies { get; protected set; }
/// <summary> /// <summary>
/// Gets the command line arguments that were passed to the application from either the command prompt or the desktop. /// Gets the command line arguments that were passed to the application from either the command prompt or the desktop.
@ -61,13 +61,16 @@ namespace Stylet
// Make life nice for the app - they can handle these by overriding Bootstrapper methods, rather than adding event handlers // Make life nice for the app - they can handle these by overriding Bootstrapper methods, rather than adding event handlers
this.Application.Exit += (o, e) => this.Application.Exit += (o, e) =>
{ {
this.OnExitInternal(e);
this.OnExit(e); this.OnExit(e);
this.Dispose();
}; };
// Fetch this logger when needed. If we fetch it now, then no-one will have been given the change to enable the LogManager, and we'll get a NullLogger // Fetch this logger when needed. If we fetch it now, then no-one will have been given the change to enable the LogManager, and we'll get a NullLogger
this.Application.DispatcherUnhandledException += (o, e) => LogManager.GetLogger(typeof(BootstrapperBase)).Error(e.Exception, "Unhandled exception"); this.Application.DispatcherUnhandledException += (o, e) =>
this.Application.DispatcherUnhandledException += (o, e) => this.OnUnhandledExecption(e); {
LogManager.GetLogger(typeof(BootstrapperBase)).Error(e.Exception, "Unhandled exception");
this.OnUnhandledException(e);
};
} }
/// <summary> /// <summary>
@ -78,12 +81,14 @@ namespace Stylet
{ {
// Set this before anything else, so everything can use it // Set this before anything else, so everything can use it
this.Args = args; this.Args = args;
this.OnStart();
this.ConfigureBootstrapper(); this.ConfigureBootstrapper();
View.ViewManager = (IViewManager)this.GetInstance(typeof(IViewManager)); View.ViewManager = (IViewManager)this.GetInstance(typeof(IViewManager));
this.Launch(); this.Launch();
this.OnLaunch();
} }
/// <summary> /// <summary>
@ -93,7 +98,6 @@ namespace Stylet
{ {
var windowManager = (IWindowManager)this.GetInstance(typeof(IWindowManager)); var windowManager = (IWindowManager)this.GetInstance(typeof(IWindowManager));
windowManager.ShowWindow(this.RootViewModel); windowManager.ShowWindow(this.RootViewModel);
this.OnStartup();
} }
/// <summary> /// <summary>
@ -109,15 +113,14 @@ namespace Stylet
public abstract object GetInstance(Type type); public abstract object GetInstance(Type type);
/// <summary> /// <summary>
/// Hook called on application startup. This occurs once the root view has been displayed /// Called on application startup. This occur after this.Args has been assigned, but before the IoC container has been configured
/// </summary> /// </summary>
protected virtual void OnStartup() { } protected virtual void OnStart() { }
/// <summary> /// <summary>
/// Hook used internall by the Bootstrapper to do things like dispose the IoC container /// Called just after the root View has been displayed
/// </summary> /// </summary>
/// <param name="e">The exit event data</param> protected virtual void OnLaunch() { }
protected virtual void OnExitInternal(ExitEventArgs e) { }
/// <summary> /// <summary>
/// Hook called on application exit /// Hook called on application exit
@ -129,6 +132,11 @@ namespace Stylet
/// Hook called on an unhandled exception /// Hook called on an unhandled exception
/// </summary> /// </summary>
/// <param name="e">The event data</param> /// <param name="e">The event data</param>
protected virtual void OnUnhandledExecption(DispatcherUnhandledExceptionEventArgs e) { } protected virtual void OnUnhandledException(DispatcherUnhandledExceptionEventArgs e) { }
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public virtual void Dispose() { }
} }
} }

View File

@ -86,7 +86,7 @@ namespace Stylet
/// </summary> /// </summary>
protected override void OnActivate() protected override void OnActivate()
{ {
foreach (var item in this.items.OfType<IActivate>()) foreach (var item in this.items.OfType<IScreenState>())
{ {
item.Activate(); item.Activate();
} }
@ -97,7 +97,7 @@ namespace Stylet
/// </summary> /// </summary>
protected override void OnDeactivate() protected override void OnDeactivate()
{ {
foreach (var item in this.items.OfType<IDeactivate>()) foreach (var item in this.items.OfType<IScreenState>())
{ {
item.Deactivate(); item.Deactivate();
} }

View File

@ -23,51 +23,80 @@ namespace Stylet
} }
/// <summary> /// <summary>
/// Can be activated, and raises an event when it is actually activated /// State in which a screen can be
/// </summary> /// </summary>
public interface IActivate public enum ScreenState
{ {
/// <summary> /// <summary>
/// Activate the object. May not actually cause activation (e.g. if it's already active) /// Screen has been created, but has never had any further transitions
/// </summary> /// </summary>
void Activate(); Initial,
/// <summary>
/// Screen is active. It is likely being displayed to the user
/// </summary>
Active,
/// <summary>
/// Screen is deactivated. It has either been hidden in favour of another Screen, or the entire window has been minimised
/// </summary>
Deactivated,
/// <summary>
/// Screen has been closed. It has no associated View, but may yet be displayed again
/// </summary>
Closed,
}
/// <summary>
/// Has a concept of state, which can be manipulated by its Conductor
/// </summary>
public interface IScreenState
{
/// <summary>
/// Gets the current state of the Screen
/// </summary>
ScreenState State { get; }
/// <summary>
/// Gets a value indicating whether the current state is ScreenState.Active
/// </summary>
bool IsActive { get; }
/// <summary>
/// Raised when the Screen's state changed, for any reason
/// </summary>
event EventHandler<ScreenStateChangedEventArgs> StateChanged;
/// <summary> /// <summary>
/// Raised when the object is actually activated /// Raised when the object is actually activated
/// </summary> /// </summary>
event EventHandler<ActivationEventArgs> Activated; event EventHandler<ActivationEventArgs> Activated;
}
/// <summary> /// <summary>
/// Can be deactivated, and raises an event when it is actually deactivated /// Raised when the object is actually deactivated
/// </summary> /// </summary>
public interface IDeactivate event EventHandler<DeactivationEventArgs> Deactivated;
{
/// <summary>
/// Raised when the object is actually closed
/// </summary>
event EventHandler<CloseEventArgs> Closed;
/// <summary>
/// Activate the object. May not actually cause activation (e.g. if it's already active)
/// </summary>
void Activate();
/// <summary> /// <summary>
/// Deactivate the object. May not actually cause deactivation (e.g. if it's already deactive) /// Deactivate the object. May not actually cause deactivation (e.g. if it's already deactive)
/// </summary> /// </summary>
void Deactivate(); void Deactivate();
/// <summary>
/// Raised when the object is actually deactivated
/// </summary>
event EventHandler<DeactivationEventArgs> Deactivated;
}
/// <summary>
/// Can be closed, and raises an event when it is actually closed
/// </summary>
public interface IClose
{
/// <summary> /// <summary>
/// Close the object. May not actually cause closure (e.g. if it's already closed) /// Close the object. May not actually cause closure (e.g. if it's already closed)
/// </summary> /// </summary>
void Close(); void Close();
/// <summary>
/// Raised when the object is actually closed
/// </summary>
event EventHandler<CloseEventArgs> Closed;
} }
/// <summary> /// <summary>
@ -120,28 +149,101 @@ namespace Stylet
/// <summary> /// <summary>
/// Generalised 'screen' composing all the behaviours expected of a screen /// Generalised 'screen' composing all the behaviours expected of a screen
/// </summary> /// </summary>
public interface IScreen : IViewAware, IHaveDisplayName, IActivate, IDeactivate, IChild, IClose, IGuardClose, IRequestClose public interface IScreen : IViewAware, IHaveDisplayName, IScreenState, IChild, IGuardClose, IRequestClose
{ {
} }
/// <summary> /// <summary>
/// EventArgs associated with the IActivate.Activated event /// EventArgs associated with the IScreenState.StateChanged event
/// </summary>
public class ScreenStateChangedEventArgs : EventArgs
{
/// <summary>
/// Gets the state being transitioned to
/// </summary>
public ScreenState NewState { get; private set; }
/// <summary>
/// Gets the state being transitioned away from
/// </summary>
public ScreenState PreviousState { get; private set; }
/// <summary>
/// Initialises a new instance of the <see cref="ScreenStateChangedEventArgs"/> class
/// </summary>
/// <param name="newState">State being transitioned to</param>
/// <param name="previousState">State being transitioned away from</param>
public ScreenStateChangedEventArgs(ScreenState newState, ScreenState previousState)
{
this.NewState = newState;
this.PreviousState = previousState;
}
}
/// <summary>
/// EventArgs associated with the IScreenState.Activated event
/// </summary> /// </summary>
public class ActivationEventArgs : EventArgs public class ActivationEventArgs : EventArgs
{ {
/// <summary>
/// Gets a value indicating whether this is the first time this Screen has been activated, ever
/// </summary>
public bool IsInitialActivate { get; private set; }
/// <summary>
/// Gets the state being transitioned away from
/// </summary>
public ScreenState PreviousState { get; private set; }
/// <summary>
/// Initialises a new instance of the <see cref="ActivationEventArgs"/> class
/// </summary>
/// <param name="previousState">State being transitioned away from</param>
/// <param name="isInitialActivate">True if this is the first time this screen has ever been activated</param>
public ActivationEventArgs(ScreenState previousState, bool isInitialActivate)
{
this.IsInitialActivate = isInitialActivate;
this.PreviousState = previousState;
}
} }
/// <summary> /// <summary>
/// EventArgs associated with the IDeactivate.Deactivated event /// EventArgs associated with the IScreenState.Deactivated event
/// </summary> /// </summary>
public class DeactivationEventArgs : EventArgs public class DeactivationEventArgs : EventArgs
{ {
/// <summary>
/// Gets the state being transitioned away from
/// </summary>
public ScreenState PreviousState { get; private set; }
/// <summary>
/// Initialises a new instance of the <see cref="DeactivationEventArgs"/> class
/// </summary>
/// <param name="previousState">State being transitioned away from</param>
public DeactivationEventArgs(ScreenState previousState)
{
this.PreviousState = previousState;
}
} }
/// <summary> /// <summary>
/// EventArgs associated with the IClose.Closed event /// EventArgs associated with the IScreenState.Closed event
/// </summary> /// </summary>
public class CloseEventArgs : EventArgs public class CloseEventArgs : EventArgs
{ {
/// <summary>
/// Gets the state being transitioned away from
/// </summary>
public ScreenState PreviousState { get; private set; }
/// <summary>
/// Initialises a new instance of the <see cref="CloseEventArgs"/> class
/// </summary>
/// <param name="previousState">State being transitioned away from</param>
public CloseEventArgs(ScreenState previousState)
{
this.PreviousState = previousState;
}
} }
} }

31
Stylet/Logging/ILogger.cs Normal file
View File

@ -0,0 +1,31 @@
using System;
namespace Stylet.Logging
{
/// <summary>
/// Logger used by Stylet for internal logging
/// </summary>
public interface ILogger
{
/// <summary>
/// Log the message as info
/// </summary>
/// <param name="format">A formatted message</param>
/// <param name="args">format parameters</param>
void Info(string format, params object[] args);
/// <summary>
/// Log the message as a warning
/// </summary>
/// <param name="format">A formatted message</param>
/// <param name="args">format parameters</param>
void Warn(string format, params object[] args);
/// <summary>
/// Log an exception as an error
/// </summary>
/// <param name="exception">Exception to log</param>
/// <param name="message">Additional message to add to the exception</param>
void Error(Exception exception, string message = null);
}
}

View File

@ -1,113 +1,7 @@
using System; using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
namespace Stylet.Logging namespace Stylet.Logging
{ {
/// <summary>
/// Logger used by Stylet for internal logging
/// </summary>
public interface ILogger
{
/// <summary>
/// Log the message as info
/// </summary>
/// <param name="format">A formatted message</param>
/// <param name="args">format parameters</param>
void Info(string format, params object[] args);
/// <summary>
/// Log the message as a warning
/// </summary>
/// <param name="format">A formatted message</param>
/// <param name="args">format parameters</param>
void Warn(string format, params object[] args);
/// <summary>
/// Log an exception as an error
/// </summary>
/// <param name="exception">Exception to log</param>
/// <param name="message">Additional message to add to the exception</param>
void Error(Exception exception, string message = null);
}
/// <summary>
/// ILogger implementation which does nothing - used by default
/// </summary>
public class NullLogger : ILogger
{
/// <summary>
/// Log the message as info
/// </summary>
/// <param name="format">A formatted message</param>
/// <param name="args">format parameters</param>
public void Info(string format, params object[] args) { }
/// <summary>
/// Log the message as a warning
/// </summary>
/// <param name="format">A formatted message</param>
/// <param name="args">format parameters</param>
public void Warn(string format, params object[] args) { }
/// <summary>
/// Log an exception as an error
/// </summary>
/// <param name="exception">Exception to log</param>
/// <param name="message">Additional message to add to the exception</param>
public void Error(Exception exception, string message = null) { }
}
/// <summary>
/// ILogger implementation which uses Debug.WriteLine
/// </summary>
public class TraceLogger : ILogger
{
private readonly string name;
/// <summary>
/// Initialises a new instance of the <see cref="TraceLogger"/> class, with the given name
/// </summary>
/// <param name="name">Name of the DebugLogger</param>
public TraceLogger(string name)
{
this.name = name;
}
/// <summary>
/// Log the message as info
/// </summary>
/// <param name="format">A formatted message</param>
/// <param name="args">format parameters</param>
public void Info(string format, params object[] args)
{
Trace.WriteLine(String.Format("INFO [{1}] {0}", String.Format(format, args), this.name), "Stylet");
}
/// <summary>
/// Log the message as a warning
/// </summary>
/// <param name="format">A formatted message</param>
/// <param name="args">format parameters</param>
public void Warn(string format, params object[] args)
{
Trace.WriteLine(String.Format("WARN [{1}] {0}", String.Format(format, args), this.name), "Stylet");
}
/// <summary>
/// Log an exception as an error
/// </summary>
/// <param name="exception">Exception to log</param>
/// <param name="message">Additional message to add to the exception</param>
public void Error(Exception exception, string message = null)
{
if (message == null)
Trace.WriteLine(String.Format("ERROR [{1}] {0}", exception, this.name), "Stylet");
else
Trace.WriteLine(String.Format("ERROR [{2}] {0} {1}", message, exception, this.name), "Stylet");
}
}
/// <summary> /// <summary>
/// Manager for ILoggers. Used to create new ILoggers, and set up how ILoggers are created /// Manager for ILoggers. Used to create new ILoggers, and set up how ILoggers are created
/// </summary> /// </summary>

View File

@ -0,0 +1,31 @@
using System;
namespace Stylet.Logging
{
/// <summary>
/// ILogger implementation which does nothing - used by default
/// </summary>
public class NullLogger : ILogger
{
/// <summary>
/// Log the message as info
/// </summary>
/// <param name="format">A formatted message</param>
/// <param name="args">format parameters</param>
public void Info(string format, params object[] args) { }
/// <summary>
/// Log the message as a warning
/// </summary>
/// <param name="format">A formatted message</param>
/// <param name="args">format parameters</param>
public void Warn(string format, params object[] args) { }
/// <summary>
/// Log an exception as an error
/// </summary>
/// <param name="exception">Exception to log</param>
/// <param name="message">Additional message to add to the exception</param>
public void Error(Exception exception, string message = null) { }
}
}

View File

@ -0,0 +1,55 @@
using System;
using System.Diagnostics;
namespace Stylet.Logging
{
/// <summary>
/// ILogger implementation which uses Debug.WriteLine
/// </summary>
public class TraceLogger : ILogger
{
private readonly string name;
/// <summary>
/// Initialises a new instance of the <see cref="TraceLogger"/> class, with the given name
/// </summary>
/// <param name="name">Name of the DebugLogger</param>
public TraceLogger(string name)
{
this.name = name;
}
/// <summary>
/// Log the message as info
/// </summary>
/// <param name="format">A formatted message</param>
/// <param name="args">format parameters</param>
public void Info(string format, params object[] args)
{
Trace.WriteLine(String.Format("INFO [{1}] {0}", String.Format(format, args), this.name), "Stylet");
}
/// <summary>
/// Log the message as a warning
/// </summary>
/// <param name="format">A formatted message</param>
/// <param name="args">format parameters</param>
public void Warn(string format, params object[] args)
{
Trace.WriteLine(String.Format("WARN [{1}] {0}", String.Format(format, args), this.name), "Stylet");
}
/// <summary>
/// Log an exception as an error
/// </summary>
/// <param name="exception">Exception to log</param>
/// <param name="message">Additional message to add to the exception</param>
public void Error(Exception exception, string message = null)
{
if (message == null)
Trace.WriteLine(String.Format("ERROR [{1}] {0}", exception, this.name), "Stylet");
else
Trace.WriteLine(String.Format("ERROR [{2}] {0} {1}", message, exception, this.name), "Stylet");
}
}
}

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 // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.7.0")] [assembly: AssemblyVersion("1.1.0.0")]
[assembly: AssemblyFileVersion("1.0.7.0")] [assembly: AssemblyFileVersion("1.1.0.0")]

View File

@ -46,47 +46,50 @@ namespace Stylet
#endregion #endregion
#region IActivate #region IScreenState
private ScreenState _state = ScreenState.Initial;
/// <summary>
/// Gets or sets the current state of the Screen
/// </summary>
public virtual ScreenState State
{
get { return this._state; }
protected set
{
this.SetAndNotify(ref this._state, value);
this.NotifyOfPropertyChange(() => this.IsActive);
}
}
/// <summary>
/// Gets a value indicating whether the current state is ScreenState.Active
/// </summary>
public bool IsActive
{
get { return this.State == ScreenState.Active; }
}
/// <summary>
/// Raised when the Screen's state changed, for any reason
/// </summary>
public event EventHandler<ScreenStateChangedEventArgs> StateChanged;
/// <summary> /// <summary>
/// Fired whenever the Screen is activated /// Fired whenever the Screen is activated
/// </summary> /// </summary>
public event EventHandler<ActivationEventArgs> Activated; public event EventHandler<ActivationEventArgs> Activated;
private bool hasBeenActivatedEver; /// <summary>
/// Fired whenever the Screen is deactivated
private bool _isActive; /// </summary>
public event EventHandler<DeactivationEventArgs> Deactivated;
/// <summary> /// <summary>
/// Gets or sets a value indicating whether this Screen is currently active /// Called whenever this Screen is closed
/// </summary> /// </summary>
public bool IsActive public event EventHandler<CloseEventArgs> Closed;
{
get { return this._isActive; }
set { this.SetAndNotify(ref this._isActive, value); }
}
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "As this is a framework type, don't want to make it too easy for users to call this method")]
void IActivate.Activate()
{
if (this.IsActive)
return;
this.IsActive = true;
this.isClosed = false;
this.logger.Info("Activating");
if (!this.hasBeenActivatedEver)
this.OnInitialActivate();
this.hasBeenActivatedEver = true;
this.OnActivate();
var handler = this.Activated;
if (handler != null)
handler(this, new ActivationEventArgs());
}
/// <summary> /// <summary>
/// Called the very first time this Screen is activated, and never again /// Called the very first time this Screen is activated, and never again
@ -98,75 +101,87 @@ namespace Stylet
/// </summary> /// </summary>
protected virtual void OnActivate() { } protected virtual void OnActivate() { }
#endregion
#region IDeactivate
/// <summary>
/// Fired whenever the Screen is deactivated
/// </summary>
public event EventHandler<DeactivationEventArgs> Deactivated;
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "Stylet.Screen.#Stylet.IDeactivate.Deactivate()", Justification = "As this is a framework type, don't want to make it too easy for users to call this method")]
void IDeactivate.Deactivate()
{
if (!this.IsActive)
return;
this.IsActive = false;
this.isClosed = false;
this.logger.Info("Deactivating");
this.OnDeactivate();
var handler = this.Deactivated;
if (handler != null)
handler(this, new DeactivationEventArgs());
}
/// <summary> /// <summary>
/// Called every time this screen is deactivated /// Called every time this screen is deactivated
/// </summary> /// </summary>
protected virtual void OnDeactivate() { } protected virtual void OnDeactivate() { }
#endregion
#region IClose
private bool isClosed;
/// <summary>
/// Called whenever this Screen is closed
/// </summary>
public event EventHandler<CloseEventArgs> Closed;
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "As this is a framework type, don't want to make it too easy for users to call this method")]
void IClose.Close()
{
if (this.isClosed)
return;
// This will early-exit if it's already deactive
((IDeactivate)this).Deactivate();
this.View = null;
this.isClosed = true;
this.logger.Info("Closing");
this.OnClose();
var handler = this.Closed;
if (handler != null)
handler(this, new CloseEventArgs());
}
/// <summary> /// <summary>
/// Called when this screen is closed /// Called when this screen is closed
/// </summary> /// </summary>
protected virtual void OnClose() { } protected virtual void OnClose() { }
/// <summary>
/// Sets the screen's state to the given state, if it differs from the current state
/// </summary>
/// <param name="newState">State to transition to</param>
/// <param name="changedHandler">Called if the transition occurs. Arguments are (newState, previousState)</param>
protected virtual void SetState(ScreenState newState, Action<ScreenState, ScreenState> changedHandler)
{
if (newState == this.State)
return;
var previousState = this.State;
this.State = newState;
this.logger.Info("Setting state from {0} to {1}", previousState, newState);
changedHandler(previousState, newState);
var handler = this.StateChanged;
if (handler != null)
Execute.OnUIThread(() => handler(this, new ScreenStateChangedEventArgs(newState, previousState)));
}
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "As this is a framework type, don't want to make it too easy for users to call this method")]
void IScreenState.Activate()
{
this.SetState(ScreenState.Active, (oldState, newState) =>
{
var isInitialActivate = oldState == ScreenState.Initial;
if (isInitialActivate)
this.OnInitialActivate();
this.OnActivate();
var handler = this.Activated;
if (handler != null)
Execute.OnUIThread(() => handler(this, new ActivationEventArgs(oldState, isInitialActivate)));
});
}
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "As this is a framework type, don't want to make it too easy for users to call this method")]
void IScreenState.Deactivate()
{
this.SetState(ScreenState.Deactivated, (oldState, newState) =>
{
this.OnDeactivate();
var handler = this.Deactivated;
if (handler != null)
Execute.OnUIThread(() => handler(this, new DeactivationEventArgs(oldState)));
});
}
[SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Justification = "As this is a framework type, don't want to make it too easy for users to call this method")]
void IScreenState.Close()
{
// Avoid going from Closed back to Deactivated
if (this.State != ScreenState.Closed)
((IScreenState)this).Deactivate();
this.View = null;
this.SetState(ScreenState.Closed, (oldState, newState) =>
{
this.OnClose();
var handler = this.Closed;
if (handler != null)
Execute.OnUIThread(() => handler(this, new CloseEventArgs(oldState)));
});
}
#endregion #endregion
#region IViewAware #region IViewAware

View File

@ -14,9 +14,9 @@ namespace Stylet
/// <param name="screen">Screen to activate</param> /// <param name="screen">Screen to activate</param>
public static void TryActivate(object screen) public static void TryActivate(object screen)
{ {
var screenAsActivate = screen as IActivate; var screenAsScreenState = screen as IScreenState;
if (screenAsActivate != null) if (screenAsScreenState != null)
screenAsActivate.Activate(); screenAsScreenState.Activate();
} }
/// <summary> /// <summary>
@ -25,9 +25,9 @@ namespace Stylet
/// <param name="screen">Screen to deactivate</param> /// <param name="screen">Screen to deactivate</param>
public static void TryDeactivate(object screen) public static void TryDeactivate(object screen)
{ {
var screenAsDeactivate = screen as IDeactivate; var screenAsScreenState = screen as IScreenState;
if (screenAsDeactivate != null) if (screenAsScreenState != null)
screenAsDeactivate.Deactivate(); screenAsScreenState.Deactivate();
} }
/// <summary> /// <summary>
@ -36,9 +36,9 @@ namespace Stylet
/// <param name="screen">Screen to close</param> /// <param name="screen">Screen to close</param>
public static void TryClose(object screen) public static void TryClose(object screen)
{ {
var screenAsClose = screen as IClose; var screenAsScreenState = screen as IScreenState;
if (screenAsClose != null) if (screenAsScreenState != null)
screenAsClose.Close(); screenAsScreenState.Close();
} }
/// <summary> /// <summary>
@ -58,9 +58,19 @@ namespace Stylet
/// <example>child.ActivateWith(this)</example> /// <example>child.ActivateWith(this)</example>
/// <param name="child">Child to activate whenever the parent is activated</param> /// <param name="child">Child to activate whenever the parent is activated</param>
/// <param name="parent">Parent to observe</param> /// <param name="parent">Parent to observe</param>
public static void ActivateWith(this IActivate child, IActivate parent) public static void ActivateWith(this IScreenState child, IScreenState parent)
{ {
WeakEventManager<IActivate, ActivationEventArgs>.AddHandler(parent, "Activated", (o, e) => child.Activate()); var weakChild = new WeakReference<IScreenState>(child);
EventHandler<ActivationEventArgs> handler = null;
handler = (o, e) =>
{
IScreenState strongChild;
if (weakChild.TryGetTarget(out strongChild))
strongChild.Activate();
else
parent.Activated -= handler;
};
parent.Activated += handler;
} }
/// <summary> /// <summary>
@ -69,9 +79,19 @@ namespace Stylet
/// <example>child.DeactivateWith(this)</example> /// <example>child.DeactivateWith(this)</example>
/// <param name="child">Child to deactivate whenever the parent is deacgtivated</param> /// <param name="child">Child to deactivate whenever the parent is deacgtivated</param>
/// <param name="parent">Parent to observe</param> /// <param name="parent">Parent to observe</param>
public static void DeactivateWith(this IDeactivate child, IDeactivate parent) public static void DeactivateWith(this IScreenState child, IScreenState parent)
{ {
WeakEventManager<IDeactivate, DeactivationEventArgs>.AddHandler(parent, "Deactivated", (o, e) => child.Deactivate()); var weakChild = new WeakReference<IScreenState>(child);
EventHandler<DeactivationEventArgs> handler = null;
handler = (o, e) =>
{
IScreenState strongChild;
if (weakChild.TryGetTarget(out strongChild))
strongChild.Deactivate();
else
parent.Deactivated -= handler;
};
parent.Deactivated += handler;
} }
/// <summary> /// <summary>
@ -80,23 +100,28 @@ namespace Stylet
/// <example>child.CloseWith(this)</example> /// <example>child.CloseWith(this)</example>
/// <param name="child">Child to close when the parent is closed</param> /// <param name="child">Child to close when the parent is closed</param>
/// <param name="parent">Parent to observe</param> /// <param name="parent">Parent to observe</param>
public static void CloseWith(this IClose child, IClose parent) public static void CloseWith(this IScreenState child, IScreenState parent)
{ {
// Using TryCloseAndDispose ensures that Dispose is called if necessary var weakChild = new WeakReference<IScreenState>(child);
WeakEventManager<IClose, CloseEventArgs>.AddHandler(parent, "Closed", (o, e) => TryClose(child)); EventHandler<CloseEventArgs> handler = null;
handler = (o, e) =>
{
IScreenState strongChild;
if (weakChild.TryGetTarget(out strongChild))
TryClose(strongChild);
else
parent.Closed -= handler;
};
parent.Closed += handler;
} }
/// <summary> /// <summary>
/// Activate, Deactivate, or Close the child whenever the parent is Activated, Deactivated, or Closed /// Activate, Deactivate, or Close the child whenever the parent is Activated, Deactivated, or Closed
/// </summary> /// </summary>
/// <example>child.ConductWith(this)</example> /// <example>child.ConductWith(this)</example>
/// <typeparam name="TChild">Type of the child</typeparam>
/// <typeparam name="TParent">Type of the parent</typeparam>
/// <param name="child">Child to conduct with the parent</param> /// <param name="child">Child to conduct with the parent</param>
/// <param name="parent">Parent to observe</param> /// <param name="parent">Parent to observe</param>
public static void ConductWith<TChild, TParent>(this TChild child, TParent parent) public static void ConductWith(this IScreenState child, IScreenState parent)
where TChild : IActivate, IDeactivate, IClose
where TParent : IActivate, IDeactivate, IClose
{ {
child.ActivateWith(parent); child.ActivateWith(parent);
child.DeactivateWith(parent); child.DeactivateWith(parent);

View File

@ -49,6 +49,9 @@
<ItemGroup> <ItemGroup>
<Compile Include="IDispatcher.cs" /> <Compile Include="IDispatcher.cs" />
<Compile Include="INotifyCollectionChanging.cs" /> <Compile Include="INotifyCollectionChanging.cs" />
<Compile Include="Logging\ILogger.cs" />
<Compile Include="Logging\NullLogger.cs" />
<Compile Include="Logging\TraceLogger.cs" />
<Compile Include="StyletIoC\Creation\ICreator.cs" /> <Compile Include="StyletIoC\Creation\ICreator.cs" />
<Compile Include="StyletIoC\Creation\IRegistration.cs" /> <Compile Include="StyletIoC\Creation\IRegistration.cs" />
<Compile Include="StyletIoC\Internal\Builders\BuilderAbstractFactoryBinding.cs" /> <Compile Include="StyletIoC\Internal\Builders\BuilderAbstractFactoryBinding.cs" />

View File

@ -173,6 +173,18 @@ namespace StyletIoC
/// <param name="assemblies">Assembly(s) to search, or leave empty / null to search the current assembly</param> /// <param name="assemblies">Assembly(s) to search, or leave empty / null to search the current assembly</param>
void Autobind(params Assembly[] assemblies); void Autobind(params Assembly[] assemblies);
/// <summary>
/// Add a single module to this builder
/// </summary>
/// <param name="module">Module to add</param>
void AddModule(StyletIoCModule module);
/// <summary>
/// Add many modules to this builder
/// </summary>
/// <param name="modules">Modules to add</param>
void AddModules(params StyletIoCModule[] modules);
/// <summary> /// <summary>
/// Once all bindings have been set, build an IContainer from which instances can be fetches /// Once all bindings have been set, build an IContainer from which instances can be fetches
/// </summary> /// </summary>

View File

@ -53,7 +53,7 @@ namespace Stylet
/// Gets the assemblies which are used for IoC container auto-binding and searching for Views. /// Gets the assemblies which are used for IoC container auto-binding and searching for Views.
/// Set this in Configure() if you want to override it /// Set this in Configure() if you want to override it
/// </summary> /// </summary>
IList<Assembly> Assemblies { get; } IReadOnlyList<Assembly> Assemblies { get; }
/// <summary> /// <summary>
/// Given a type, use the IoC container to fetch an instance of it /// Given a type, use the IoC container to fetch an instance of it
@ -73,7 +73,7 @@ namespace Stylet
/// <summary> /// <summary>
/// Gets or sets the assemblies searched for View types /// Gets or sets the assemblies searched for View types
/// </summary> /// </summary>
protected IList<Assembly> Assemblies { get; set; } protected IReadOnlyList<Assembly> Assemblies { get; set; }
/// <summary> /// <summary>
/// Gets or sets the factory used to create view instances from their type /// Gets or sets the factory used to create view instances from their type

View File

@ -130,7 +130,7 @@ namespace Stylet
} }
var haveDisplayName = viewModel as IHaveDisplayName; var haveDisplayName = viewModel as IHaveDisplayName;
if (haveDisplayName != null && BindingOperations.GetBindingBase(window, Window.TitleProperty) == null) if (haveDisplayName != null && String.IsNullOrEmpty(window.Title) && BindingOperations.GetBindingBase(window, Window.TitleProperty) == null)
{ {
var binding = new Binding("DisplayName") { Mode = BindingMode.TwoWay }; var binding = new Binding("DisplayName") { Mode = BindingMode.TwoWay };
window.SetBinding(Window.TitleProperty, binding); window.SetBinding(Window.TitleProperty, binding);
@ -153,6 +153,7 @@ namespace Stylet
logger.Error(e, "This can occur when the application is closing down"); logger.Error(e, "This can occur when the application is closing down");
} }
} }
logger.Info("Displaying ViewModel {0} with View {1} as a Dialog", viewModel, window); logger.Info("Displaying ViewModel {0} with View {1} as a Dialog", viewModel, window);
} }
else else
@ -160,6 +161,11 @@ namespace Stylet
logger.Info("Displaying ViewModel {0} with View {1} as a Window", viewModel, window); logger.Info("Displaying ViewModel {0} with View {1} as a Window", viewModel, window);
} }
// If and only if they haven't tried to position the window themselves...
// Has to be done after we're attempted to set the owner
if (window.WindowStartupLocation == WindowStartupLocation.Manual && Double.IsNaN(window.Top) && Double.IsNaN(window.Left))
window.WindowStartupLocation = window.Owner == null ? WindowStartupLocation.CenterScreen : WindowStartupLocation.CenterOwner;
// This gets itself retained by the window, by registering events // This gets itself retained by the window, by registering events
// ReSharper disable once ObjectCreationAsStatement // ReSharper disable once ObjectCreationAsStatement
new WindowConductor(window, viewModel); new WindowConductor(window, viewModel);
@ -193,15 +199,15 @@ namespace Stylet
ScreenExtensions.TryActivate(this.viewModel); ScreenExtensions.TryActivate(this.viewModel);
var viewModelAsClose = this.viewModel as IClose; var viewModelAsScreenState = this.viewModel as IScreenState;
if (viewModelAsClose != null) if (viewModelAsScreenState != null)
{
window.StateChanged += this.WindowStateChanged;
window.Closed += this.WindowClosed; window.Closed += this.WindowClosed;
}
if (this.viewModel is IGuardClose) if (this.viewModel is IGuardClose)
window.Closing += this.WindowClosing; window.Closing += this.WindowClosing;
if (this.viewModel is IActivate || this.viewModel is IDeactivate)
window.StateChanged += this.WindowStateChanged;
} }
private void WindowStateChanged(object sender, EventArgs e) private void WindowStateChanged(object sender, EventArgs e)

View File

@ -125,6 +125,15 @@ namespace Stylet.Xaml
} }
} }
/// <summary>
/// The View.ActionTarget was not set. This probably means the item is in a ContextMenu/Popup
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable")]
public class ActionNotSetException : Exception
{
internal ActionNotSetException(string message) : base(message) { }
}
/// <summary> /// <summary>
/// The Action Target was null, and shouldn't have been (NullTarget = Throw) /// The Action Target was null, and shouldn't have been (NullTarget = Throw)
/// </summary> /// </summary>

View File

@ -81,7 +81,10 @@ namespace Stylet.Xaml
// If they've opted to throw if the target is null, then this will cause that exception. // If they've opted to throw if the target is null, then this will cause that exception.
// We'll just wait until the ActionTarget is assigned, and we're called again // We'll just wait until the ActionTarget is assigned, and we're called again
if (newTarget == View.InitialActionTarget) if (newTarget == View.InitialActionTarget)
{
this.target = newTarget;
return; return;
}
this.guardPropertyGetter = null; this.guardPropertyGetter = null;
if (newTarget == null) if (newTarget == null)
@ -95,7 +98,7 @@ namespace Stylet.Xaml
} }
else else
{ {
logger.Info("ActionTarget on element {0} is null (method name is {1}), nut NullTarget is not Throw, so carrying on", this.Subject, this.MethodName); logger.Info("ActionTarget on element {0} is null (method name is {1}), but NullTarget is not Throw, so carrying on", this.Subject, this.MethodName);
} }
} }
else else
@ -147,12 +150,11 @@ namespace Stylet.Xaml
if (oldTarget != null) if (oldTarget != null)
oldTarget.PropertyChanged -= this.PropertyChangedHandler; oldTarget.PropertyChanged -= this.PropertyChangedHandler;
this.target = newTarget;
var inpc = newTarget as INotifyPropertyChanged; var inpc = newTarget as INotifyPropertyChanged;
if (this.guardPropertyGetter != null && inpc != null) if (this.guardPropertyGetter != null && inpc != null)
inpc.PropertyChanged += this.PropertyChangedHandler; inpc.PropertyChanged += this.PropertyChangedHandler;
this.target = newTarget;
this.targetMethodInfo = targetMethodInfo; this.targetMethodInfo = targetMethodInfo;
this.UpdateCanExecute(); this.UpdateCanExecute();
@ -183,6 +185,13 @@ namespace Stylet.Xaml
/// <returns>true if this command can be executed; otherwise, false.</returns> /// <returns>true if this command can be executed; otherwise, false.</returns>
public bool CanExecute(object parameter) public bool CanExecute(object parameter)
{ {
// This can happen if the ActionTarget hasn't been set from its default -
// e.g. if the button/etc in question is in a ContextMenu/Popup, which attached properties
// aren't inherited by.
// Show the control as enabled, but throw if they try and click on it
if (this.target == View.InitialActionTarget)
return true;
// It's enabled only if both the targetNull and actionNonExistent tests pass // It's enabled only if both the targetNull and actionNonExistent tests pass
// Throw is handled when the target is set // Throw is handled when the target is set
@ -215,6 +224,17 @@ namespace Stylet.Xaml
/// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param> /// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
public void Execute(object parameter) public void Execute(object parameter)
{ {
// If we've made it this far and the target is still the default, then something's wrong
// Make sure they know
if (this.target == View.InitialActionTarget)
{
var e = new ActionNotSetException(String.Format("View.ActionTarget not on control {0} (method {1}). " +
"This probably means the control hasn't inherited it from a parent, e.g. because a ContextMenu or Popup sits in the visual tree. " +
"You will need so set 's:View.ActionTarget' explicitly. See the wiki for more details.", this.Subject, this.MethodName));
logger.Error(e);
throw e;
}
// Any throwing would have been handled prior to this // Any throwing would have been handled prior to this
if (this.target == null || this.targetMethodInfo == null) if (this.target == null || this.targetMethodInfo == null)
return; return;

View File

@ -77,7 +77,10 @@ namespace Stylet.Xaml
// If they've opted to throw if the target is null, then this will cause that exception. // If they've opted to throw if the target is null, then this will cause that exception.
// We'll just wait until the ActionTarget is assigned, and we're called again // We'll just wait until the ActionTarget is assigned, and we're called again
if (newTarget == View.InitialActionTarget) if (newTarget == View.InitialActionTarget)
{
this.target = newTarget;
return; return;
}
if (newTarget == null) if (newTarget == null)
{ {
@ -146,6 +149,17 @@ namespace Stylet.Xaml
// ReSharper disable once UnusedMember.Local // ReSharper disable once UnusedMember.Local
private void InvokeCommand(object sender, EventArgs e) private void InvokeCommand(object sender, EventArgs e)
{ {
// If we've made it this far and the target is still the default, then something's wrong
// Make sure they know
if (this.target == View.InitialActionTarget)
{
var ex = new ActionNotSetException(String.Format("View.ActionTarget not on control {0} (method {1}). " +
"This probably means the control hasn't inherited it from a parent, e.g. because a ContextMenu or Popup sits in the visual tree. " +
"You will need so set 's:View.ActionTarget' explicitly. See the wiki for more details.", this.subject, this.methodName));
logger.Error(ex);
throw ex;
}
// Any throwing will have been handled above // Any throwing will have been handled above
if (this.target == null || this.targetMethodInfo == null) if (this.target == null || this.targetMethodInfo == null)
return; return;

View File

@ -17,9 +17,9 @@ namespace StyletIntegrationTests
LogManager.Enabled = true; LogManager.Enabled = true;
} }
protected override void OnUnhandledExecption(System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) protected override void OnUnhandledException(System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{ {
base.OnUnhandledExecption(e); // Calling this just to trigger some code coverage base.OnUnhandledException(e); // Calling this just to trigger some code coverage
var message = e.Exception.Message; var message = e.Exception.Message;
if (e.Exception is TargetInvocationException) if (e.Exception is TargetInvocationException)
message = e.Exception.InnerException.Message; message = e.Exception.InnerException.Message;

View File

@ -51,10 +51,10 @@ namespace StyletUnitTests
return null; return null;
} }
public bool OnStartupCalled; public bool OnStartCalled;
protected override void OnStartup() protected override void OnStart()
{ {
this.OnStartupCalled = true; this.OnStartCalled = true;
} }
public bool OnExitCalled; public bool OnExitCalled;
@ -147,7 +147,7 @@ namespace StyletUnitTests
public void StartCallsOnStartup() public void StartCallsOnStartup()
{ {
this.bootstrapper.Start(new string[0]); this.bootstrapper.Start(new string[0]);
Assert.True(this.bootstrapper.OnStartupCalled); Assert.True(this.bootstrapper.OnStartCalled);
} }
} }
} }

View File

@ -40,11 +40,6 @@ namespace StyletUnitTests
builder.Bind<I1>().To<C1>(); builder.Bind<I1>().To<C1>();
base.ConfigureIoC(builder); base.ConfigureIoC(builder);
} }
public new void OnExitInternal(ExitEventArgs e)
{
base.OnExitInternal(e);
}
} }
private MyBootstrapper<RootViewModel> bootstrapper; private MyBootstrapper<RootViewModel> bootstrapper;
@ -92,11 +87,11 @@ namespace StyletUnitTests
} }
[Test] [Test]
public void OnExitInternalDisposesContainer() public void DisposeDisposesContainer()
{ {
var container = new Mock<IContainer>(); var container = new Mock<IContainer>();
this.bootstrapper.Container = container.Object; this.bootstrapper.Container = container.Object;
this.bootstrapper.OnExitInternal(null); this.bootstrapper.Dispose();
container.Verify(x => x.Dispose()); container.Verify(x => x.Dispose());
} }
} }

View File

@ -51,6 +51,15 @@ namespace StyletUnitTests
{ {
throw new InvalidOperationException("woo"); throw new InvalidOperationException("woo");
} }
public bool CanDoSomethingWithUnsuccessfulGuardMethod
{
get { throw new InvalidOperationException("foo"); }
}
public void DoSomethingWithUnsuccessfulGuardMethod()
{
}
} }
private class Target2 private class Target2
@ -221,5 +230,29 @@ namespace StyletUnitTests
var e = Assert.Throws<InvalidOperationException>(() => cmd.Execute(null)); var e = Assert.Throws<InvalidOperationException>(() => cmd.Execute(null));
Assert.AreEqual("woo", e.Message); Assert.AreEqual("woo", e.Message);
} }
[Test]
public void PropagatesGuardPropertException()
{
var cmd = new CommandAction(this.subject, "DoSomethingWithUnsuccessfulGuardMethod", ActionUnavailableBehaviour.Throw, ActionUnavailableBehaviour.Throw);
var e = Assert.Throws<InvalidOperationException>(() => cmd.CanExecute(null));
Assert.AreEqual("foo", e.Message);
}
[Test]
public void ControlIsEnabledIfTargetIsDefault()
{
View.SetActionTarget(this.subject, View.InitialActionTarget);
var cmd = new CommandAction(this.subject, "DoSomething", ActionUnavailableBehaviour.Throw, ActionUnavailableBehaviour.Throw);
Assert.True(cmd.CanExecute(null));
}
[Test]
public void ExecuteThrowsIfTargetIsDefault()
{
View.SetActionTarget(this.subject, View.InitialActionTarget);
var cmd = new CommandAction(this.subject, "DoSomething", ActionUnavailableBehaviour.Throw, ActionUnavailableBehaviour.Throw);
Assert.Throws<ActionNotSetException>(() => cmd.Execute(null));
}
} }
} }

View File

@ -57,7 +57,7 @@ namespace StyletUnitTests
public void ActivateItemActivatesfConductorIsActive() public void ActivateItemActivatesfConductorIsActive()
{ {
var screen = new Mock<IScreen>(); var screen = new Mock<IScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
screen.Verify(x => x.Activate()); screen.Verify(x => x.Activate());
} }
@ -66,7 +66,7 @@ namespace StyletUnitTests
public void ActivateItemDoesNotDeactivateIfConductorIsActive() public void ActivateItemDoesNotDeactivateIfConductorIsActive()
{ {
var screen = new Mock<IScreen>(); var screen = new Mock<IScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
screen.Verify(x => x.Deactivate(), Times.Never); screen.Verify(x => x.Deactivate(), Times.Never);
} }
@ -75,9 +75,9 @@ namespace StyletUnitTests
public void DeactiveDeactivatesItems() public void DeactiveDeactivatesItems()
{ {
var screen = new Mock<IScreen>(); var screen = new Mock<IScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
((IDeactivate)this.conductor).Deactivate(); ((IScreenState)this.conductor).Deactivate();
screen.Verify(x => x.Deactivate()); screen.Verify(x => x.Deactivate());
} }
@ -85,9 +85,9 @@ namespace StyletUnitTests
public void ClosingClosesAllItems() public void ClosingClosesAllItems()
{ {
var screen = new Mock<IMyScreen>(); var screen = new Mock<IMyScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
((IClose)this.conductor).Close(); ((IScreenState)this.conductor).Close();
screen.Verify(x => x.Close()); screen.Verify(x => x.Close());
screen.Verify(x => x.Dispose()); screen.Verify(x => x.Dispose());
Assert.AreEqual(0, this.conductor.Items.Count); Assert.AreEqual(0, this.conductor.Items.Count);
@ -122,7 +122,7 @@ namespace StyletUnitTests
[Test] [Test]
public void AddingItemActivatesAndSetsParent() public void AddingItemActivatesAndSetsParent()
{ {
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
var screen = new Mock<IScreen>(); var screen = new Mock<IScreen>();
this.conductor.Items.Add(screen.Object); this.conductor.Items.Add(screen.Object);
screen.VerifySet(x => x.Parent = this.conductor); screen.VerifySet(x => x.Parent = this.conductor);
@ -211,7 +211,7 @@ namespace StyletUnitTests
this.conductor.ActivateItem(screen1.Object); this.conductor.ActivateItem(screen1.Object);
this.conductor.ActivateItem(screen2.Object); this.conductor.ActivateItem(screen2.Object);
((IClose)this.conductor).Close(); ((IScreenState)this.conductor).Close();
screen1.Verify(x => x.Close()); screen1.Verify(x => x.Close());
screen1.Verify(x => x.Dispose()); screen1.Verify(x => x.Dispose());
screen1.VerifySet(x => x.Parent = null); screen1.VerifySet(x => x.Parent = null);
@ -237,7 +237,7 @@ namespace StyletUnitTests
public void AddRangeActivatesAddedItems() public void AddRangeActivatesAddedItems()
{ {
var screen = new Mock<IMyScreen>(); var screen = new Mock<IMyScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.Items.AddRange(new[] { screen.Object }); this.conductor.Items.AddRange(new[] { screen.Object });

View File

@ -49,7 +49,7 @@ namespace StyletUnitTests
[Test] [Test]
public void InitialActivateActivatesItemIfConductorIsActive() public void InitialActivateActivatesItemIfConductorIsActive()
{ {
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
var screen = new Mock<IScreen>(); var screen = new Mock<IScreen>();
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
screen.Verify(x => x.Activate()); screen.Verify(x => x.Activate());
@ -70,17 +70,17 @@ namespace StyletUnitTests
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
screen.Verify(x => x.Activate(), Times.Never); screen.Verify(x => x.Activate(), Times.Never);
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
screen.Verify(x => x.Activate()); screen.Verify(x => x.Activate());
} }
[Test] [Test]
public void DeactivatesActiveItemWhenDeactivated() public void DeactivatesActiveItemWhenDeactivated()
{ {
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
var screen = new Mock<IScreen>(); var screen = new Mock<IScreen>();
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
((IDeactivate)this.conductor).Deactivate(); ((IScreenState)this.conductor).Deactivate();
screen.Verify(x => x.Deactivate()); screen.Verify(x => x.Deactivate());
} }
@ -89,7 +89,7 @@ namespace StyletUnitTests
{ {
var screen1 = new Mock<IScreen>(); var screen1 = new Mock<IScreen>();
var screen2 = new Mock<IScreen>(); var screen2 = new Mock<IScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActivateItem(screen1.Object); this.conductor.ActivateItem(screen1.Object);
screen1.Setup(x => x.CanCloseAsync()).Returns(Task.FromResult(true)); screen1.Setup(x => x.CanCloseAsync()).Returns(Task.FromResult(true));
this.conductor.ActivateItem(screen2.Object); this.conductor.ActivateItem(screen2.Object);
@ -100,7 +100,7 @@ namespace StyletUnitTests
public void ActivatingCurrentScreenReactivatesScreen() public void ActivatingCurrentScreenReactivatesScreen()
{ {
var screen = new Mock<IMyScreen>(); var screen = new Mock<IMyScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
screen.Verify(x => x.Activate(), Times.Exactly(2)); screen.Verify(x => x.Activate(), Times.Exactly(2));
@ -113,7 +113,7 @@ namespace StyletUnitTests
{ {
var screen1 = new Mock<IMyScreen>(); var screen1 = new Mock<IMyScreen>();
var screen2 = new Mock<IMyScreen>(); var screen2 = new Mock<IMyScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActivateItem(screen1.Object); this.conductor.ActivateItem(screen1.Object);
this.conductor.CloseItem(screen2.Object); this.conductor.CloseItem(screen2.Object);
@ -126,7 +126,7 @@ namespace StyletUnitTests
public void DeactiveDoesNotChangeActiveItem() public void DeactiveDoesNotChangeActiveItem()
{ {
var screen = new Mock<IScreen>(); var screen = new Mock<IScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
this.conductor.DeactivateItem(screen.Object); this.conductor.DeactivateItem(screen.Object);
@ -146,7 +146,7 @@ namespace StyletUnitTests
public void SettingActiveItemActivatesItem() public void SettingActiveItemActivatesItem()
{ {
var screen = new Mock<IScreen>(); var screen = new Mock<IScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActiveItem =screen.Object; this.conductor.ActiveItem =screen.Object;
screen.Verify(x => x.Activate()); screen.Verify(x => x.Activate());
Assert.AreEqual(this.conductor.ActiveItem, screen.Object); Assert.AreEqual(this.conductor.ActiveItem, screen.Object);
@ -206,7 +206,7 @@ namespace StyletUnitTests
[Test] [Test]
public void DeactivatingActiveItemGoesBack() public void DeactivatingActiveItemGoesBack()
{ {
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
var screen1 = new Mock<IScreen>(); var screen1 = new Mock<IScreen>();
var screen2 = new Mock<IScreen>(); var screen2 = new Mock<IScreen>();
@ -224,7 +224,7 @@ namespace StyletUnitTests
[Test] [Test]
public void ClearClosesAllItemsExceptCurrent() public void ClearClosesAllItemsExceptCurrent()
{ {
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
var screen1 = new Mock<IScreen>(); var screen1 = new Mock<IScreen>();
var screen2 = new Mock<IScreen>(); var screen2 = new Mock<IScreen>();
this.conductor.ActivateItem(screen1.Object); this.conductor.ActivateItem(screen1.Object);
@ -248,7 +248,7 @@ namespace StyletUnitTests
this.conductor.ActivateItem(screen1.Object); this.conductor.ActivateItem(screen1.Object);
this.conductor.ActivateItem(screen2.Object); this.conductor.ActivateItem(screen2.Object);
((IClose)this.conductor).Close(); ((IScreenState)this.conductor).Close();
screen1.Verify(x => x.Close()); screen1.Verify(x => x.Close());
screen1.VerifySet(x => x.Parent = null); screen1.VerifySet(x => x.Parent = null);
screen2.Verify(x => x.Close()); screen2.Verify(x => x.Close());

View File

@ -33,7 +33,7 @@ namespace StyletUnitTests
public void ActivatingItemActivatesAndAddsToItems() public void ActivatingItemActivatesAndAddsToItems()
{ {
var screen = new Mock<IScreen>(); var screen = new Mock<IScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
screen.Verify(x => x.Activate()); screen.Verify(x => x.Activate());
@ -46,7 +46,7 @@ namespace StyletUnitTests
{ {
var screen1 = new Mock<IScreen>(); var screen1 = new Mock<IScreen>();
var screen2 = new Mock<IScreen>(); var screen2 = new Mock<IScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActivateItem(screen1.Object); this.conductor.ActivateItem(screen1.Object);
this.conductor.ActivateItem(screen2.Object); this.conductor.ActivateItem(screen2.Object);
@ -62,7 +62,7 @@ namespace StyletUnitTests
public void SettingActiveItemActivatesItem() public void SettingActiveItemActivatesItem()
{ {
var screen = new Mock<IScreen>(); var screen = new Mock<IScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActiveItem = screen.Object; this.conductor.ActiveItem = screen.Object;
screen.Verify(x => x.Activate()); screen.Verify(x => x.Activate());
Assert.AreEqual(this.conductor.ActiveItem, screen.Object); Assert.AreEqual(this.conductor.ActiveItem, screen.Object);
@ -74,7 +74,7 @@ namespace StyletUnitTests
var screen1 = new Mock<IScreen>(); var screen1 = new Mock<IScreen>();
var screen2 = new Mock<IScreen>(); var screen2 = new Mock<IScreen>();
var screen3 = new Mock<IScreen>(); var screen3 = new Mock<IScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.Items.AddRange(new[] { screen1.Object, screen2.Object, screen3.Object }); this.conductor.Items.AddRange(new[] { screen1.Object, screen2.Object, screen3.Object });
this.conductor.ActivateItem(screen2.Object); this.conductor.ActivateItem(screen2.Object);
@ -92,7 +92,7 @@ namespace StyletUnitTests
var screen1 = new Mock<IScreen>(); var screen1 = new Mock<IScreen>();
var screen2 = new Mock<IScreen>(); var screen2 = new Mock<IScreen>();
var screen3 = new Mock<IScreen>(); var screen3 = new Mock<IScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.Items.AddRange(new[] { screen1.Object, screen2.Object, screen3.Object }); this.conductor.Items.AddRange(new[] { screen1.Object, screen2.Object, screen3.Object });
this.conductor.ActivateItem(screen3.Object); this.conductor.ActivateItem(screen3.Object);
@ -116,7 +116,7 @@ namespace StyletUnitTests
public void ActivateItemDoesActiveIfConductorIsActive() public void ActivateItemDoesActiveIfConductorIsActive()
{ {
var screen = new Mock<IScreen>(); var screen = new Mock<IScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
screen.Verify(x => x.Activate()); screen.Verify(x => x.Activate());
} }
@ -125,9 +125,9 @@ namespace StyletUnitTests
public void DeactiveDeactivatesItems() public void DeactiveDeactivatesItems()
{ {
var screen = new Mock<IScreen>(); var screen = new Mock<IScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
((IDeactivate)this.conductor).Deactivate(); ((IScreenState)this.conductor).Deactivate();
screen.Verify(x => x.Deactivate()); screen.Verify(x => x.Deactivate());
} }
@ -135,9 +135,9 @@ namespace StyletUnitTests
public void ClosingClosesAllItems() public void ClosingClosesAllItems()
{ {
var screen = new Mock<IMyScreen>(); var screen = new Mock<IMyScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
((IClose)this.conductor).Close(); ((IScreenState)this.conductor).Close();
screen.Verify(x => x.Close()); screen.Verify(x => x.Close());
screen.Verify(x => x.Dispose()); screen.Verify(x => x.Dispose());
Assert.AreEqual(0, this.conductor.Items.Count); Assert.AreEqual(0, this.conductor.Items.Count);
@ -182,7 +182,7 @@ namespace StyletUnitTests
{ {
var screen1 = new Mock<IScreen>(); var screen1 = new Mock<IScreen>();
var screen2 = new Mock<IScreen>(); var screen2 = new Mock<IScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActivateItem(screen1.Object); this.conductor.ActivateItem(screen1.Object);
// This is an implementation detail // This is an implementation detail
@ -229,7 +229,7 @@ namespace StyletUnitTests
[Test] [Test]
public void RemovingActiveItemActivatesAnotherItem() public void RemovingActiveItemActivatesAnotherItem()
{ {
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
var screen1 = new Mock<IMyScreen>(); var screen1 = new Mock<IMyScreen>();
var screen2 = new Mock<IMyScreen>(); var screen2 = new Mock<IMyScreen>();
this.conductor.ActivateItem(screen1.Object); this.conductor.ActivateItem(screen1.Object);
@ -310,7 +310,7 @@ namespace StyletUnitTests
var screen1 = new Mock<IMyScreen>(); var screen1 = new Mock<IMyScreen>();
screen1.SetupGet(x => x.Parent).Returns(this.conductor); screen1.SetupGet(x => x.Parent).Returns(this.conductor);
this.conductor.ActivateItem(screen1.Object); this.conductor.ActivateItem(screen1.Object);
((IClose)this.conductor).Close(); ((IScreenState)this.conductor).Close();
screen1.Verify(x => x.Close()); screen1.Verify(x => x.Close());
screen1.Verify(x => x.Dispose()); screen1.Verify(x => x.Dispose());
screen1.VerifySet(x => x.Parent = null); screen1.VerifySet(x => x.Parent = null);

View File

@ -49,7 +49,7 @@ namespace StyletUnitTests
[Test] [Test]
public void InitialActivateActivatesItemIfConductorIsActive() public void InitialActivateActivatesItemIfConductorIsActive()
{ {
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
var screen = new Mock<IScreen>(); var screen = new Mock<IScreen>();
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
screen.Verify(x => x.Activate()); screen.Verify(x => x.Activate());
@ -62,17 +62,17 @@ namespace StyletUnitTests
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
screen.Verify(x => x.Activate(), Times.Never); screen.Verify(x => x.Activate(), Times.Never);
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
screen.Verify(x => x.Activate()); screen.Verify(x => x.Activate());
} }
[Test] [Test]
public void DeactivatesActiveItemWhenDeactivated() public void DeactivatesActiveItemWhenDeactivated()
{ {
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
var screen = new Mock<IScreen>(); var screen = new Mock<IScreen>();
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
((IDeactivate)this.conductor).Deactivate(); ((IScreenState)this.conductor).Deactivate();
screen.Verify(x => x.Deactivate()); screen.Verify(x => x.Deactivate());
} }
@ -81,7 +81,7 @@ namespace StyletUnitTests
{ {
var screen1 = new Mock<IMyScreen>(); var screen1 = new Mock<IMyScreen>();
var screen2 = new Mock<IMyScreen>(); var screen2 = new Mock<IMyScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActivateItem(screen1.Object); this.conductor.ActivateItem(screen1.Object);
screen1.Setup(x => x.CanCloseAsync()).Returns(Task.FromResult(true)); screen1.Setup(x => x.CanCloseAsync()).Returns(Task.FromResult(true));
this.conductor.ActivateItem(screen2.Object); this.conductor.ActivateItem(screen2.Object);
@ -94,7 +94,7 @@ namespace StyletUnitTests
{ {
var screen1 = new Mock<IMyScreen>(); var screen1 = new Mock<IMyScreen>();
var screen2 = new Mock<IMyScreen>(); var screen2 = new Mock<IMyScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActivateItem(screen1.Object); this.conductor.ActivateItem(screen1.Object);
screen1.Setup(x => x.CanCloseAsync()).Returns(Task.FromResult(false)); screen1.Setup(x => x.CanCloseAsync()).Returns(Task.FromResult(false));
this.conductor.ActivateItem(screen2.Object); this.conductor.ActivateItem(screen2.Object);
@ -108,7 +108,7 @@ namespace StyletUnitTests
public void ActivatingCurrentScreenReactivatesScreen() public void ActivatingCurrentScreenReactivatesScreen()
{ {
var screen = new Mock<IMyScreen>(); var screen = new Mock<IMyScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
screen.Verify(x => x.Activate(), Times.Exactly(2)); screen.Verify(x => x.Activate(), Times.Exactly(2));
@ -120,7 +120,7 @@ namespace StyletUnitTests
public void SettingActiveItemActivatesItem() public void SettingActiveItemActivatesItem()
{ {
var screen = new Mock<IScreen>(); var screen = new Mock<IScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActiveItem = screen.Object; this.conductor.ActiveItem = screen.Object;
screen.Verify(x => x.Activate()); screen.Verify(x => x.Activate());
Assert.AreEqual(this.conductor.ActiveItem, screen.Object); Assert.AreEqual(this.conductor.ActiveItem, screen.Object);
@ -131,7 +131,7 @@ namespace StyletUnitTests
{ {
var screen1 = new Mock<IMyScreen>(); var screen1 = new Mock<IMyScreen>();
var screen2 = new Mock<IMyScreen>(); var screen2 = new Mock<IMyScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActivateItem(screen1.Object); this.conductor.ActivateItem(screen1.Object);
this.conductor.CloseItem(screen2.Object); this.conductor.CloseItem(screen2.Object);
@ -144,7 +144,7 @@ namespace StyletUnitTests
public void DeactiveDoesNotChangeActiveItem() public void DeactiveDoesNotChangeActiveItem()
{ {
var screen = new Mock<IScreen>(); var screen = new Mock<IScreen>();
((IActivate)this.conductor).Activate(); ((IScreenState)this.conductor).Activate();
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
this.conductor.DeactivateItem(screen.Object); this.conductor.DeactivateItem(screen.Object);
@ -192,7 +192,7 @@ namespace StyletUnitTests
var screen1 = new Mock<IMyScreen>(); var screen1 = new Mock<IMyScreen>();
screen1.SetupGet(x => x.Parent).Returns(this.conductor); screen1.SetupGet(x => x.Parent).Returns(this.conductor);
this.conductor.ActivateItem(screen1.Object); this.conductor.ActivateItem(screen1.Object);
((IClose)this.conductor).Close(); ((IScreenState)this.conductor).Close();
screen1.Verify(x => x.Close()); screen1.Verify(x => x.Close());
screen1.VerifySet(x => x.Parent = null); screen1.VerifySet(x => x.Parent = null);
} }
@ -202,7 +202,7 @@ namespace StyletUnitTests
{ {
var screen = new Mock<IMyScreen>(); var screen = new Mock<IMyScreen>();
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
((IClose)this.conductor).Close(); ((IScreenState)this.conductor).Close();
screen.Verify(x => x.Dispose()); screen.Verify(x => x.Dispose());
} }
@ -212,7 +212,7 @@ namespace StyletUnitTests
this.conductor.DisposeChildren = false; this.conductor.DisposeChildren = false;
var screen = new Mock<IMyScreen>(); var screen = new Mock<IMyScreen>();
this.conductor.ActivateItem(screen.Object); this.conductor.ActivateItem(screen.Object);
((IClose)this.conductor).Close(); ((IScreenState)this.conductor).Close();
screen.Verify(x => x.Dispose(), Times.Never); screen.Verify(x => x.Dispose(), Times.Never);
} }

View File

@ -190,5 +190,14 @@ namespace StyletUnitTests
Assert.IsInstanceOf<InvalidOperationException>(e.InnerException); Assert.IsInstanceOf<InvalidOperationException>(e.InnerException);
Assert.AreEqual("foo", e.InnerException.Message); Assert.AreEqual("foo", e.InnerException.Message);
} }
[Test]
public void ExecuteThrowsIfActionTargetIsDefault()
{
View.SetActionTarget(this.subject, View.InitialActionTarget);
var cmd = new EventAction(this.subject, this.eventInfo.EventHandlerType, "DoSomething", ActionUnavailableBehaviour.Throw, ActionUnavailableBehaviour.Throw);
var e = Assert.Throws<TargetInvocationException>(() => cmd.GetDelegate().DynamicInvoke(null, null));
Assert.IsInstanceOf<ActionNotSetException>(e.InnerException);
}
} }
} }

View File

@ -26,39 +26,39 @@ namespace StyletUnitTests
} }
[Test] [Test]
public void TryActivateActivatesIActivate() public void TryActivateActivatesIScreenState()
{ {
var screen = new Mock<IActivate>(); var screen = new Mock<IScreenState>();
ScreenExtensions.TryActivate(screen.Object); ScreenExtensions.TryActivate(screen.Object);
screen.Verify(x => x.Activate()); screen.Verify(x => x.Activate());
} }
[Test] [Test]
public void TryActivateDoesNothingToNonIActivate() public void TryActivateDoesNothingToNonIScreenState()
{ {
var screen = new Mock<IDeactivate>(MockBehavior.Strict); var screen = new Mock<IGuardClose>(MockBehavior.Strict);
ScreenExtensions.TryActivate(screen.Object); ScreenExtensions.TryActivate(screen.Object);
} }
[Test] [Test]
public void TryDeactivateDeactivatesIDeactivate() public void TryDeactivateDeactivatesIScreenState()
{ {
var screen = new Mock<IDeactivate>(); var screen = new Mock<IScreenState>();
ScreenExtensions.TryDeactivate(screen.Object); ScreenExtensions.TryDeactivate(screen.Object);
screen.Verify(x => x.Deactivate()); screen.Verify(x => x.Deactivate());
} }
[Test] [Test]
public void TryDeactivateDoesNothingToNonIDeactivate() public void TryDeactivateDoesNothingToNonIScreenState()
{ {
var screen = new Mock<IActivate>(MockBehavior.Strict); var screen = new Mock<IGuardClose>(MockBehavior.Strict);
ScreenExtensions.TryDeactivate(screen.Object); ScreenExtensions.TryDeactivate(screen.Object);
} }
[Test] [Test]
public void TryCloseClosesIClose() public void TryCloseClosesIScreenState()
{ {
var screen = new Mock<IClose>(); var screen = new Mock<IScreenState>();
ScreenExtensions.TryClose(screen.Object); ScreenExtensions.TryClose(screen.Object);
screen.Verify(x => x.Close()); screen.Verify(x => x.Close());
} }
@ -72,35 +72,103 @@ namespace StyletUnitTests
} }
[Test] [Test]
public void TryCloseDoesNothingToNonIClose() public void TryCloseDoesNothingToNonIScreenState()
{ {
var screen = new Mock<IActivate>(MockBehavior.Strict); var screen = new Mock<IGuardClose>(MockBehavior.Strict);
ScreenExtensions.TryClose(screen.Object); ScreenExtensions.TryClose(screen.Object);
} }
[Test]
public void ActivateWithActivates()
{
this.child.Object.ActivateWith(this.parent);
((IScreenState)this.parent).Activate();
this.child.Verify(x => x.Activate());
}
[Test]
public void ActivateWithDoesNotRetainChild()
{
var child = new Screen();
child.ActivateWith(this.parent);
var weakChild = new WeakReference(child);
child = null;
GC.Collect();
((IScreenState)this.parent).Activate();
Assert.Null(weakChild.Target);
}
[Test] [Test]
public void ConductWithActivates() public void ConductWithActivates()
{ {
this.child.Object.ConductWith(this.parent); this.child.Object.ConductWith(this.parent);
((IActivate)this.parent).Activate(); ((IScreenState)this.parent).Activate();
this.child.Verify(x => x.Activate()); this.child.Verify(x => x.Activate());
} }
[Test]
public void DeactivateWithDeactivates()
{
// Needs to be active....
((IScreenState)this.parent).Activate();
this.child.Object.DeactivateWith(this.parent);
((IScreenState)this.parent).Deactivate();
this.child.Verify(x => x.Deactivate());
}
[Test]
public void DeactivateDoesNotRetainChild()
{
var child = new Screen();
child.DeactivateWith(this.parent);
var weakChild = new WeakReference(child);
child = null;
GC.Collect();
((IScreenState)this.parent).Deactivate();
Assert.Null(weakChild.Target);
}
[Test] [Test]
public void ConductWithDeactivates() public void ConductWithDeactivates()
{ {
// Needs to be active.... // Needs to be active....
((IActivate)this.parent).Activate(); ((IScreenState)this.parent).Activate();
this.child.Object.ConductWith(this.parent); this.child.Object.ConductWith(this.parent);
((IDeactivate)this.parent).Deactivate(); ((IScreenState)this.parent).Deactivate();
this.child.Verify(x => x.Deactivate()); this.child.Verify(x => x.Deactivate());
} }
[Test]
public void CloseWithCloses()
{
this.child.Object.CloseWith(this.parent);
((IScreenState)this.parent).Close();
this.child.Verify(x => x.Close());
}
[Test]
public void CloseWithDoesNotRetain()
{
var child = new Screen();
child.CloseWith(this.parent);
var weakChild = new WeakReference(child);
child = null;
GC.Collect();
((IScreenState)this.parent).Close();
Assert.Null(weakChild.Target);
}
[Test] [Test]
public void ConductWithCloses() public void ConductWithCloses()
{ {
this.child.Object.ConductWith(this.parent); this.child.Object.ConductWith(this.parent);
((IClose)this.parent).Close(); ((IScreenState)this.parent).Close();
this.child.Verify(x => x.Close()); this.child.Verify(x => x.Close());
} }

View File

@ -26,6 +26,11 @@ namespace StyletUnitTests
public MyScreen() { } public MyScreen() { }
public MyScreen(IModelValidator validator) : base(validator) { } public MyScreen(IModelValidator validator) : base(validator) { }
public new void SetState(ScreenState newState, Action<ScreenState, ScreenState> changedHandler)
{
base.SetState(newState, changedHandler);
}
public bool OnActivateCalled; public bool OnActivateCalled;
protected override void OnActivate() protected override void OnActivate()
{ {
@ -92,7 +97,7 @@ namespace StyletUnitTests
[Test] [Test]
public void ActivateActivatesIfNotAlreadyActive() public void ActivateActivatesIfNotAlreadyActive()
{ {
((IActivate)this.screen).Activate(); ((IScreenState)this.screen).Activate();
Assert.IsTrue(this.screen.IsActive); Assert.IsTrue(this.screen.IsActive);
} }
@ -101,41 +106,81 @@ namespace StyletUnitTests
{ {
bool fired = false; bool fired = false;
this.screen.Activated += (o, e) => fired = true; this.screen.Activated += (o, e) => fired = true;
((IActivate)this.screen).Activate(); ((IScreenState)this.screen).Activate();
Assert.IsTrue(fired); Assert.IsTrue(fired);
} }
[Test] [Test]
public void ActivateCallsOnActivate() public void ActivateCallsOnActivate()
{ {
((IActivate)this.screen).Activate(); ((IScreenState)this.screen).Activate();
Assert.IsTrue(this.screen.OnActivateCalled); Assert.IsTrue(this.screen.OnActivateCalled);
} }
[Test] [Test]
public void DoubleActivationDoesntActivate() public void DoubleActivationDoesntActivate()
{ {
((IActivate)this.screen).Activate(); ((IScreenState)this.screen).Activate();
this.screen.OnActivateCalled = false; this.screen.OnActivateCalled = false;
((IActivate)this.screen).Activate(); ((IScreenState)this.screen).Activate();
Assert.IsFalse(this.screen.OnActivateCalled); Assert.IsFalse(this.screen.OnActivateCalled);
} }
[Test] [Test]
public void InitialActivationCallsOnInitialActivate() public void InitialActivationCallsOnInitialActivate()
{ {
((IActivate)this.screen).Activate(); ((IScreenState)this.screen).Activate();
this.screen.OnInitialActivateCalled = false; this.screen.OnInitialActivateCalled = false;
((IDeactivate)this.screen).Deactivate(); ((IScreenState)this.screen).Deactivate();
((IActivate)this.screen).Activate(); ((IScreenState)this.screen).Activate();
Assert.IsFalse(this.screen.OnInitialActivateCalled); Assert.IsFalse(this.screen.OnInitialActivateCalled);
} }
[Test]
public void ActivateFiresCorrectEvents()
{
this.screen.SetState(ScreenState.Deactivated, (n, o) => { });
var changedEventArgs = new List<ScreenStateChangedEventArgs>();
this.screen.StateChanged += (o, e) => changedEventArgs.Add(e);
var activatedEventArgs = new List<ActivationEventArgs>();
this.screen.Activated += (o, e) => activatedEventArgs.Add(e);
((IScreenState)this.screen).Activate();
Assert.AreEqual(1, changedEventArgs.Count);
Assert.AreEqual(ScreenState.Active, changedEventArgs[0].NewState);
Assert.AreEqual(ScreenState.Deactivated, changedEventArgs[0].PreviousState);
Assert.AreEqual(1, activatedEventArgs.Count);
Assert.AreEqual(ScreenState.Deactivated, activatedEventArgs[0].PreviousState);
Assert.IsFalse(activatedEventArgs[0].IsInitialActivate);
}
[Test]
public void InitialActivateFiresCorrectEvents()
{
var changedEventArgs = new List<ScreenStateChangedEventArgs>();
this.screen.StateChanged += (o, e) => changedEventArgs.Add(e);
var activatedEventArgs = new List<ActivationEventArgs>();
this.screen.Activated += (o, e) => activatedEventArgs.Add(e);
((IScreenState)this.screen).Activate();
Assert.AreEqual(1, changedEventArgs.Count);
Assert.AreEqual(ScreenState.Active, changedEventArgs[0].NewState);
Assert.AreEqual(ScreenState.Initial, changedEventArgs[0].PreviousState);
Assert.AreEqual(1, activatedEventArgs.Count);
Assert.AreEqual(ScreenState.Initial, activatedEventArgs[0].PreviousState);
Assert.IsTrue(activatedEventArgs[0].IsInitialActivate);
}
[Test] [Test]
public void DeactivateDeactivates() public void DeactivateDeactivates()
{ {
((IActivate)this.screen).Activate(); ; ((IScreenState)this.screen).Activate(); ;
((IDeactivate)this.screen).Deactivate(); ((IScreenState)this.screen).Deactivate();
Assert.IsFalse(this.screen.IsActive); Assert.IsFalse(this.screen.IsActive);
} }
@ -144,34 +189,54 @@ namespace StyletUnitTests
{ {
bool fired = false; bool fired = false;
this.screen.Deactivated += (o, e) => fired = true; this.screen.Deactivated += (o, e) => fired = true;
((IActivate)this.screen).Activate(); ; ((IScreenState)this.screen).Activate(); ;
((IDeactivate)this.screen).Deactivate(); ((IScreenState)this.screen).Deactivate();
Assert.IsTrue(fired); Assert.IsTrue(fired);
} }
[Test] [Test]
public void DeactivateCallsOnDeactivate() public void DeactivateCallsOnDeactivate()
{ {
((IActivate)this.screen).Activate(); ((IScreenState)this.screen).Activate();
((IDeactivate)this.screen).Deactivate(); ((IScreenState)this.screen).Deactivate();
Assert.IsTrue(this.screen.OnDeactivateCalled); Assert.IsTrue(this.screen.OnDeactivateCalled);
} }
[Test] [Test]
public void DoubleDeactivationDoesntDeactivate() public void DoubleDeactivationDoesntDeactivate()
{ {
((IActivate)this.screen).Activate(); ((IScreenState)this.screen).Activate();
((IDeactivate)this.screen).Deactivate(); ((IScreenState)this.screen).Deactivate();
this.screen.OnDeactivateCalled = false; this.screen.OnDeactivateCalled = false;
((IDeactivate)this.screen).Deactivate(); ((IScreenState)this.screen).Deactivate();
Assert.IsFalse(this.screen.OnDeactivateCalled); Assert.IsFalse(this.screen.OnDeactivateCalled);
} }
[Test]
public void DeactivateFiresCorrectEvents()
{
this.screen.SetState(ScreenState.Active, (n, o) => { });
var changedEventArgs = new List<ScreenStateChangedEventArgs>();
this.screen.StateChanged += (o, e) => changedEventArgs.Add(e);
var deactivationEventArgs = new List<DeactivationEventArgs>();
this.screen.Deactivated += (o, e) => deactivationEventArgs.Add(e);
((IScreenState)this.screen).Deactivate();
Assert.AreEqual(1, changedEventArgs.Count);
Assert.AreEqual(ScreenState.Deactivated, changedEventArgs[0].NewState);
Assert.AreEqual(ScreenState.Active, changedEventArgs[0].PreviousState);
Assert.AreEqual(1, deactivationEventArgs.Count);
Assert.AreEqual(ScreenState.Active, deactivationEventArgs[0].PreviousState);
}
[Test] [Test]
public void CloseDeactivates() public void CloseDeactivates()
{ {
((IActivate)this.screen).Activate(); ((IScreenState)this.screen).Activate();
((IClose)this.screen).Close(); ((IScreenState)this.screen).Close();
Assert.IsTrue(this.screen.OnDeactivateCalled); Assert.IsTrue(this.screen.OnDeactivateCalled);
} }
@ -179,7 +244,7 @@ namespace StyletUnitTests
public void CloseClearsView() public void CloseClearsView()
{ {
((IViewAware)this.screen).AttachView(new UIElement()); ((IViewAware)this.screen).AttachView(new UIElement());
((IClose)this.screen).Close(); ((IScreenState)this.screen).Close();
Assert.IsNull(this.screen.View); Assert.IsNull(this.screen.View);
} }
@ -188,33 +253,53 @@ namespace StyletUnitTests
{ {
bool fired = false; bool fired = false;
this.screen.Closed += (o, e) => fired = true; this.screen.Closed += (o, e) => fired = true;
((IClose)this.screen).Close(); ((IScreenState)this.screen).Close();
Assert.IsTrue(fired); Assert.IsTrue(fired);
} }
[Test] [Test]
public void CloseCallsOnClose() public void CloseCallsOnClose()
{ {
((IClose)this.screen).Close(); ((IScreenState)this.screen).Close();
Assert.IsTrue(this.screen.OnCloseCalled); Assert.IsTrue(this.screen.OnCloseCalled);
} }
[Test] [Test]
public void DoubleCloseDoesNotClose() public void DoubleCloseDoesNotClose()
{ {
((IClose)this.screen).Close(); ((IScreenState)this.screen).Close();
this.screen.OnCloseCalled = false; this.screen.OnCloseCalled = false;
((IClose)this.screen).Close(); ((IScreenState)this.screen).Close();
Assert.IsFalse(this.screen.OnCloseCalled); Assert.IsFalse(this.screen.OnCloseCalled);
} }
[Test]
public void CloseFiresCorrectEvents()
{
this.screen.SetState(ScreenState.Deactivated, (n, o) => { });
var changedEventArgs = new List<ScreenStateChangedEventArgs>();
this.screen.StateChanged += (o, e) => changedEventArgs.Add(e);
var closeEventArgs = new List<CloseEventArgs>();
this.screen.Closed += (o, e) => closeEventArgs.Add(e);
((IScreenState)this.screen).Close();
Assert.AreEqual(1, changedEventArgs.Count);
Assert.AreEqual(ScreenState.Closed, changedEventArgs[0].NewState);
Assert.AreEqual(ScreenState.Deactivated, changedEventArgs[0].PreviousState);
Assert.AreEqual(1, closeEventArgs.Count);
Assert.AreEqual(ScreenState.Deactivated, closeEventArgs[0].PreviousState);
}
[Test] [Test]
public void ActivatingAllowsScreenToBeClosedAgain() public void ActivatingAllowsScreenToBeClosedAgain()
{ {
((IClose)this.screen).Close(); ((IScreenState)this.screen).Close();
this.screen.OnCloseCalled = false; this.screen.OnCloseCalled = false;
((IActivate)this.screen).Activate(); ((IScreenState)this.screen).Activate();
((IClose)this.screen).Close(); ((IScreenState)this.screen).Close();
Assert.IsTrue(this.screen.OnCloseCalled); Assert.IsTrue(this.screen.OnCloseCalled);
} }

View File

@ -101,10 +101,41 @@ namespace StyletUnitTests
this.windowManager.CreateWindow(model, false); this.windowManager.CreateWindow(model, false);
var e = window.GetBindingExpression(Window.TitleProperty); var e = window.GetBindingExpression(Window.TitleProperty);
Assert.NotNull(e);
Assert.AreEqual(BindingMode.TwoWay, e.ParentBinding.Mode); Assert.AreEqual(BindingMode.TwoWay, e.ParentBinding.Mode);
Assert.AreEqual("DisplayName", e.ParentBinding.Path.Path); Assert.AreEqual("DisplayName", e.ParentBinding.Path.Path);
} }
[Test]
public void CreateWindowDoesNotSetUpTitleBindingIfTitleHasAValueAlready()
{
var model = new Screen();
var window = new Window();
window.Title = "Foo";
this.viewManager.Setup(x => x.CreateAndBindViewForModel(model)).Returns(window);
this.windowManager.CreateWindow(model, false);
var e = window.GetBindingExpression(Window.TitleProperty);
Assert.IsNull(e);
Assert.AreEqual("Foo", window.Title);
}
[Test]
public void CreateWindowDoesNotSetUpTitleBindingIfTitleHasABindingAlready()
{
var model = new Screen();
var window = new Window();
var binding = new Binding("Test") { Mode = BindingMode.TwoWay };
window.SetBinding(Window.TitleProperty, binding);
this.viewManager.Setup(x => x.CreateAndBindViewForModel(model)).Returns(window);
this.windowManager.CreateWindow(model, false);
var e = window.GetBindingExpression(Window.TitleProperty);
Assert.AreEqual("Test", e.ParentBinding.Path.Path);
}
[Test] [Test]
public void CreateWindowActivatesViewModel() public void CreateWindowActivatesViewModel()
{ {
@ -283,5 +314,56 @@ namespace StyletUnitTests
this.messageBoxViewModel.Verify(x => x.Setup("text", "title", MessageBoxButton.OKCancel, MessageBoxImage.Error, MessageBoxResult.OK, MessageBoxResult.Cancel, MessageBoxOptions.RtlReading, null)); this.messageBoxViewModel.Verify(x => x.Setup("text", "title", MessageBoxButton.OKCancel, MessageBoxImage.Error, MessageBoxResult.OK, MessageBoxResult.Cancel, MessageBoxOptions.RtlReading, null));
} }
[Test]
public void CreateWindowSetsWindowStartupLocationToCenterScreenIfThereIsNoOwnerAndItHasNotBeenSetAlready()
{
var model = new object();
var window = new Window();
this.viewManager.Setup(x => x.CreateAndBindViewForModel(model)).Returns(window);
this.windowManager.CreateWindow(model, false);
Assert.AreEqual(WindowStartupLocation.CenterScreen, window.WindowStartupLocation);
}
[Test]
public void CreateWindowDoesNotSetStartupLocationIfItIsNotManual()
{
var model = new object();
var window = new Window();
window.WindowStartupLocation = WindowStartupLocation.CenterOwner;
this.viewManager.Setup(x => x.CreateAndBindViewForModel(model)).Returns(window);
this.windowManager.CreateWindow(model, false);
Assert.AreEqual(WindowStartupLocation.CenterOwner, window.WindowStartupLocation);
}
[Test]
public void CreateWindowDoesNotSetStartupLocationIfLeftSet()
{
var model = new object();
var window = new Window();
window.Left = 1;
this.viewManager.Setup(x => x.CreateAndBindViewForModel(model)).Returns(window);
this.windowManager.CreateWindow(model, false);
Assert.AreEqual(WindowStartupLocation.Manual, window.WindowStartupLocation);
}
[Test]
public void CreateWindowDoesNotSetStartupLocationIfTopSet()
{
var model = new object();
var window = new Window();
window.Top = 1;
this.viewManager.Setup(x => x.CreateAndBindViewForModel(model)).Returns(window);
this.windowManager.CreateWindow(model, false);
Assert.AreEqual(WindowStartupLocation.Manual, window.WindowStartupLocation);
}
} }
} }