Conductor optionally disposes children, and WindowConductor never does

This commit is contained in:
Antony Male 2015-01-15 09:28:24 +00:00
parent dca24b4d5e
commit 20c708ddbe
9 changed files with 54 additions and 29 deletions

View File

@ -55,17 +55,17 @@ namespace Stylet
break;
case NotifyCollectionChangedAction.Remove:
this.CloseAndCleanUp(e.OldItems);
this.CloseAndCleanUp(e.OldItems, this.DisposeChildren);
break;
case NotifyCollectionChangedAction.Replace:
this.ActivateAndSetParent(e.NewItems);
this.CloseAndCleanUp(e.OldItems);
this.CloseAndCleanUp(e.OldItems, this.DisposeChildren);
break;
case NotifyCollectionChangedAction.Reset:
this.ActivateAndSetParent(this.items.Except(this.itemsBeforeReset));
this.CloseAndCleanUp(this.itemsBeforeReset.Except(this.items));
this.CloseAndCleanUp(this.itemsBeforeReset.Except(this.items), this.DisposeChildren);
this.itemsBeforeReset = null;
break;
}
@ -117,7 +117,7 @@ namespace Stylet
{
// We've already been deactivated by this point
foreach (var item in this.items)
this.CloseAndCleanUp(item);
this.CloseAndCleanUp(item, this.DisposeChildren);
this.items.Clear();
}
@ -166,7 +166,7 @@ namespace Stylet
if (await this.CanCloseItem(item))
{
this.CloseAndCleanUp(item);
this.CloseAndCleanUp(item, this.DisposeChildren);
this.items.Remove(item);
}
}

View File

@ -12,6 +12,11 @@ namespace Stylet
/// <typeparam name="T">Type of item to be conducted</typeparam>
public abstract class ConductorBase<T> : Screen, IConductor<T>, IParent<T>, IChildDelegate where T : class
{
/// <summary>
/// Gets or sets a value indicating whether to dispose a child when it's closed. True by default
/// </summary>
public virtual bool DisposeChildren { get; set; }
/// <summary>
/// Retrieves the Item or Items associated with this Conductor
/// </summary>
@ -85,5 +90,13 @@ namespace Stylet
if (typedItem != null)
this.CloseItem(typedItem);
}
/// <summary>
/// Initialises a new instance of the <see cref="ConductorBase{T}"/> class
/// </summary>
public ConductorBase()
{
this.DisposeChildren = true;
}
}
}

View File

@ -38,7 +38,7 @@ namespace Stylet
{
ScreenExtensions.TryDeactivate(this.ActiveItem);
if (closePrevious)
this.CloseAndCleanUp(this.ActiveItem);
this.CloseAndCleanUp(this.ActiveItem, this.DisposeChildren);
if (newItem != null)
{
@ -73,7 +73,7 @@ namespace Stylet
/// </summary>
protected override void OnClose()
{
this.CloseAndCleanUp(this.ActiveItem);
this.CloseAndCleanUp(this.ActiveItem, this.DisposeChildren);
}
}
}

View File

@ -57,7 +57,7 @@ namespace Stylet
public void Clear()
{
foreach (var item in this.history)
this.CloseAndCleanUp(item);
this.CloseAndCleanUp(item, this.DisposeChildren);
this.history.Clear();
}
@ -82,7 +82,7 @@ namespace Stylet
}
else if (this.history.Contains(item))
{
this.CloseAndCleanUp(item);
this.CloseAndCleanUp(item, this.DisposeChildren);
this.history.Remove(item);
}
}
@ -103,10 +103,10 @@ namespace Stylet
{
// We've already been deactivated by this point
foreach (var item in this.history)
this.CloseAndCleanUp(item);
this.CloseAndCleanUp(item, this.DisposeChildren);
this.history.Clear();
this.CloseAndCleanUp(this.ActiveItem);
this.CloseAndCleanUp(this.ActiveItem, this.DisposeChildren);
}
}
}

View File

@ -39,13 +39,13 @@ namespace Stylet
break;
case NotifyCollectionChangedAction.Remove:
this.CloseAndCleanUp(e.OldItems);
this.CloseAndCleanUp(e.OldItems, this.DisposeChildren);
this.ActiveItemMayHaveBeenRemovedFromItems();
break;
case NotifyCollectionChangedAction.Replace:
this.SetParent(e.NewItems);
this.CloseAndCleanUp(e.OldItems);
this.CloseAndCleanUp(e.OldItems, this.DisposeChildren);
this.ActiveItemMayHaveBeenRemovedFromItems();
break;
@ -130,7 +130,7 @@ namespace Stylet
}
else
{
this.CloseAndCleanUp(item);
this.CloseAndCleanUp(item, this.DisposeChildren);
}
this.items.Remove(item);
@ -181,7 +181,7 @@ namespace Stylet
{
// We've already been deactivated by this point
foreach (var item in this.items)
this.CloseAndCleanUp(item);
this.CloseAndCleanUp(item, this.DisposeChildren);
this.items.Clear();
}

View File

@ -34,12 +34,19 @@ namespace Stylet
/// Try to close the screen, if it implements IClose
/// </summary>
/// <param name="screen">Screen to close</param>
public static void TryCloseAndDispose(object screen)
public static void TryClose(object screen)
{
var screenAsClose = screen as IClose;
if (screenAsClose != null)
screenAsClose.Close();
}
/// <summary>
/// Try to dispose a screen, if it implements IDisposable
/// </summary>
/// <param name="screen">Screen to dispose</param>
public static void TryDispose(object screen)
{
var screenAsDispose = screen as IDisposable;
if (screenAsDispose != null)
screenAsDispose.Dispose();
@ -76,7 +83,7 @@ namespace Stylet
public static void CloseWith(this IClose child, IClose parent)
{
// Using TryCloseAndDispose ensures that Dispose is called if necessary
WeakEventManager<IClose, CloseEventArgs>.AddHandler(parent, "Closed", (o, e) => TryCloseAndDispose(child));
WeakEventManager<IClose, CloseEventArgs>.AddHandler(parent, "Closed", (o, e) => TryClose(child));
}
/// <summary>

View File

@ -31,13 +31,17 @@ namespace Stylet
/// <typeparam name="T">Type of conductor</typeparam>
/// <param name="parent">Parent</param>
/// <param name="item">Item to close and clean up</param>
public static void CloseAndCleanUp<T>(this IConductor<T> parent, T item)
/// <param name="dispose">True to dispose children as well as close them</param>
public static void CloseAndCleanUp<T>(this IConductor<T> parent, T item, bool dispose)
{
ScreenExtensions.TryCloseAndDispose(item);
ScreenExtensions.TryClose(item);
var itemAsChild = item as IChild;
if (itemAsChild != null && itemAsChild.Parent == parent)
itemAsChild.Parent = null;
if (dispose)
ScreenExtensions.TryDispose(item);
}
/// <summary>
@ -46,11 +50,12 @@ namespace Stylet
/// <typeparam name="T">Type of conductor</typeparam>
/// <param name="parent">Parent</param>
/// <param name="items">List of items to close and clean up</param>
public static void CloseAndCleanUp<T>(this IConductor<T> parent, IEnumerable items)
/// <param name="dispose">True to dispose children as well as close them</param>
public static void CloseAndCleanUp<T>(this IConductor<T> parent, IEnumerable items, bool dispose)
{
foreach (var item in items.OfType<T>())
{
parent.CloseAndCleanUp(item);
parent.CloseAndCleanUp(item, dispose);
}
}
}

View File

@ -229,7 +229,7 @@ namespace Stylet
this.window.Closed -= this.WindowClosed;
this.window.Closing -= this.WindowClosing; // Not sure this is required
ScreenExtensions.TryCloseAndDispose(this.viewModel);
ScreenExtensions.TryClose(this.viewModel);
}
private async void WindowClosing(object sender, CancelEventArgs e)
@ -296,7 +296,7 @@ namespace Stylet
if (dialogResult != null)
this.window.DialogResult = dialogResult;
ScreenExtensions.TryCloseAndDispose(this.viewModel);
ScreenExtensions.TryClose(this.viewModel);
this.window.Close();
}

View File

@ -56,26 +56,26 @@ namespace StyletUnitTests
}
[Test]
public void TryCloseAndDisposeClosesIClose()
public void TryCloseClosesIClose()
{
var screen = new Mock<IClose>();
ScreenExtensions.TryCloseAndDispose(screen.Object);
ScreenExtensions.TryClose(screen.Object);
screen.Verify(x => x.Close());
}
[Test]
public void TryCloseAndDisposeDisposesIDisposable()
public void TryDisposeDisposesIDisposable()
{
var screen = new Mock<IDisposable>();
ScreenExtensions.TryCloseAndDispose(screen.Object);
ScreenExtensions.TryDispose(screen.Object);
screen.Verify(x => x.Dispose());
}
[Test]
public void TryCloseAndDisposeDoesNothingToNonIClose()
public void TryCloseDoesNothingToNonIClose()
{
var screen = new Mock<IActivate>(MockBehavior.Strict);
ScreenExtensions.TryCloseAndDispose(screen.Object);
ScreenExtensions.TryClose(screen.Object);
}
[Test]