WIP on Conductors

This commit is contained in:
Antony Male 2014-02-05 17:03:18 +00:00
parent 11a58f3af2
commit db2c9bfeee
9 changed files with 362 additions and 86 deletions

View File

@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace Stylet
{
public interface IObservableCollection<T> : IList<T>, INotifyPropertyChanged, INotifyCollectionChanged
{
void AddRange(IEnumerable<T> items);
void RemoveRange(IEnumerable<T> items);
}
public class BindableCollection<T> : ObservableCollection<T>, IObservableCollection<T>
{
private bool isNotifying = true;
public event PropertyChangedEventHandler PropertyChanged;
public BindableCollection() : base() { }
public BindableCollection(IEnumerable<T> collection) : base(collection) { }
protected void NotifyOfPropertyChange([CallerMemberName] string propertyName = "")
{
if (this.isNotifying)
Execute.OnUIThread(() =>
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
});
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (this.isNotifying)
base.OnPropertyChanged(e);
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (this.isNotifying)
base.OnCollectionChanged(e);
}
public virtual void AddRange(IEnumerable<T> items)
{
Execute.OnUIThread(() =>
{
var previousNotificationSetting = this.isNotifying;
this.isNotifying = false;
var index = Count;
foreach (var item in items)
{
this.InsertItem(index, item);
index++;
}
this.isNotifying = previousNotificationSetting;
this.Refresh();
});
}
public virtual void RemoveRange(IEnumerable<T> items)
{
Execute.OnUIThread(() =>
{
var previousNotificationSetting = this.isNotifying;
this.isNotifying = false;
foreach (var item in items)
{
var index = IndexOf(item);
if (index >= 0)
{
this.RemoveItem(index);
}
}
this.isNotifying = previousNotificationSetting;
this.Refresh();
});
}
public void Refresh()
{
Execute.OnUIThread(() =>
{
OnPropertyChanged(new PropertyChangedEventArgs("Count"));
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
});
}
}
}

View File

@ -6,79 +6,19 @@ using System.Threading.Tasks;
namespace Stylet
{
public abstract class ConductorWithActiveItem<T> : Screen, IConductor<T>, IParent<T> where T : class
{
protected bool CloseConductedItemsWhenConductorCannotClose = false;
private T _activeItem;
public T ActiveItem
{
get { return this._activeItem; }
set { this.ActivateItem(value); }
}
public abstract void ActivateItem(T item);
public abstract void DeactivateItem(T item, bool close);
public IEnumerable<T> GetChildren()
{
return new[] { ActiveItem };
}
protected virtual void ChangeActiveItem(T newItem, bool closePrevious)
{
var activeItem = this.ActiveItem as IDeactivate;
if (activeItem != null)
activeItem.Deactivate(closePrevious);
var newItemAsChild = newItem as IChild;
if (newItemAsChild != null && newItemAsChild.Parent != this)
newItemAsChild.Parent = this;
if (this.IsActive)
{
this.ActivateItemIfActive(newItem);
this._activeItem = newItem;
this.NotifyOfPropertyChange(() => this.ActiveItem);
}
}
protected void ActivateItemIfActive(T item)
{
var itemAsIActivate = item as IActivate;
if (this.IsActive && itemAsIActivate != null)
itemAsIActivate.Activate();
}
protected virtual async Task<IEnumerable<T>> ItemsThatCanCloseAsync(IEnumerable<T> toClose)
{
var results = await Task.WhenAll(toClose.Select(x => this.CanCloseItem(x).ContinueWith(t => new { Item = x, Result = t.Result })));
if (this.CloseConductedItemsWhenConductorCannotClose)
return results.Where(x => x.Result).Select(x => x.Item);
else
return results.All(x => x.Result) ? results.Select(x => x.Item) : Enumerable.Empty<T>();
}
protected virtual Task<bool> CanCloseItem(T item)
{
var itemAsGuardClose = item as IGuardClose;
if (itemAsGuardClose != null)
return itemAsGuardClose.CanCloseAsync();
else
return Task.FromResult(true);
}
}
public class Conductor<T> : ConductorWithActiveItem<T> where T : class
public partial class Conductor<T> : ConductorBaseWithActiveItem<T> where T : class
{
public override async void ActivateItem(T item)
{
if (item != null && item.Equals(this.ActiveItem))
this.ActivateItemIfActive(item);
{
if (this.IsActive)
ScreenExtensions.TryActivate(item);
}
else if (await this.CanCloseItem(this.ActiveItem))
{
this.ChangeActiveItem(item, true);
}
}
public override async void DeactivateItem(T item, bool close)
@ -90,21 +30,9 @@ namespace Stylet
this.ChangeActiveItem(default(T), close);
}
public Task<bool> CanClose()
public override Task<bool> CanClose()
{
return this.CanCloseItem(this.ActiveItem);
}
protected override void OnActivate()
{
this.ActivateItemIfActive(this.ActiveItem);
}
protected override void OnDeactivate(bool close)
{
var activeItemAsDeactivate = this.ActiveItem as IDeactivate;
if (activeItemAsDeactivate != null)
activeItemAsDeactivate.Deactivate(close);
}
}
}

View File

@ -0,0 +1,144 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Stylet
{
public partial class Conductor<T>
{
public class Collections
{
public class OneActive : ConductorBaseWithActiveItem<T>
{
private BindableCollection<T> items = new BindableCollection<T>();
public IObservableCollection<T> Items
{
get { return this.items; }
}
public OneActive()
{
this.items.CollectionChanged += (o, e) =>
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
this.SetParent(e.NewItems, this);
break;
case NotifyCollectionChangedAction.Remove:
this.SetParent(e.OldItems, null);
break;
case NotifyCollectionChangedAction.Replace:
this.SetParent(e.NewItems, this);
this.SetParent(e.OldItems, null);
break;
case NotifyCollectionChangedAction.Reset:
this.SetParent(this.items, this);
break;
}
};
}
private void SetParent(IEnumerable items, object parent)
{
foreach (var child in items.OfType<IChild>())
{
child.Parent = parent;
}
}
public override IEnumerable<T> GetChildren()
{
return this.items;
}
public override void ActivateItem(T item)
{
if (item != null && item.Equals(this.ActiveItem))
{
if (this.IsActive)
ScreenExtensions.TryActivate(this.ActiveItem);
}
else
{
this.ChangeActiveItem(item, false);
}
}
public override async void DeactivateItem(T item, bool close)
{
if (item == null)
return;
if (close)
{
if (await this.CanCloseItem(item))
this.CloseItem(item);
}
else
{
ScreenExtensions.TryDeactivate(item, false);
}
}
private void CloseItem(T item)
{
if (item.Equals(this.ActiveItem))
{
var index = this.items.IndexOf(item);
var nextItem = this.DetermineNextItemToActivate(this.items, index);
this.ChangeActiveItem(nextItem, true);
}
else
{
ScreenExtensions.TryDeactivate(item, true);
}
this.items.Remove(item);
}
protected virtual T DetermineNextItemToActivate(IList<T> list, int indexOfItemBeingRemoved)
{
if (list.Count > 1)
{
if (indexOfItemBeingRemoved == 0)
return list[1];
else
return list[indexOfItemBeingRemoved - 1];
}
else
{
return default(T);
}
}
public override async Task<bool> CanClose()
{
return (await this.ItemsThatCanCloseAsync(this.items)).Any();
}
protected override void OnDeactivate(bool close)
{
if (close)
{
foreach (var item in this.items.OfType<IDeactivate>())
item.Deactivate(true);
}
else
{
base.OnDeactivate(false);
}
}
}
}
}
}

View File

@ -0,0 +1,75 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Stylet
{
public abstract class ConductorBaseWithActiveItem<T> : Screen, IConductor<T>, IParent<T> where T : class
{
protected bool CloseConductedItemsWhenConductorCannotClose = false;
private T _activeItem;
public T ActiveItem
{
get { return this._activeItem; }
set { this.ActivateItem(value); }
}
public abstract void ActivateItem(T item);
public abstract void DeactivateItem(T item, bool close);
public virtual IEnumerable<T> GetChildren()
{
return new[] { ActiveItem };
}
protected virtual void ChangeActiveItem(T newItem, bool closePrevious)
{
ScreenExtensions.TryDeactivate(this.ActiveItem, closePrevious);
var newItemAsChild = newItem as IChild;
if (newItemAsChild != null && newItemAsChild.Parent != this)
newItemAsChild.Parent = this;
if (this.IsActive)
{
ScreenExtensions.TryActivate(newItem);
this._activeItem = newItem;
this.NotifyOfPropertyChange(() => this.ActiveItem);
}
}
protected virtual async Task<IEnumerable<T>> ItemsThatCanCloseAsync(IEnumerable<T> toClose)
{
var results = await Task.WhenAll(toClose.Select(x => this.CanCloseItem(x).ContinueWith(t => new { Item = x, Result = t.Result })));
if (this.CloseConductedItemsWhenConductorCannotClose)
return results.Where(x => x.Result).Select(x => x.Item);
else
return results.All(x => x.Result) ? results.Select(x => x.Item) : Enumerable.Empty<T>();
}
protected virtual Task<bool> CanCloseItem(T item)
{
var itemAsGuardClose = item as IGuardClose;
if (itemAsGuardClose != null)
return itemAsGuardClose.CanCloseAsync();
else
return Task.FromResult(true);
}
protected override void OnActivate()
{
ScreenExtensions.TryActivate(this.ActiveItem);
}
protected override void OnDeactivate(bool close)
{
ScreenExtensions.TryDeactivate(this.ActiveItem, close);
}
}
}

View File

@ -44,7 +44,7 @@ namespace Stylet
Task<bool> CanCloseAsync();
}
public interface IScreen : IViewAware, IHaveDisplayName, IActivate, IDeactivate, IChild
public interface IScreen : IViewAware, IHaveDisplayName, IActivate, IDeactivate, IChild, IGuardClose
{
}

View File

@ -12,11 +12,6 @@ namespace Stylet
{
get { return new SubViewModel(); }
}
protected override void OnViewLoaded()
{
this.TryClose();
}
}
class SubViewModel

View File

@ -144,5 +144,14 @@ namespace Stylet
}
#endregion
#region IGuardClose
public virtual Task<bool> CanClose()
{
return Task.FromResult(true);
}
#endregion
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Stylet
{
public static class ScreenExtensions
{
public static void TryActivate(object screen)
{
var screenAsActivate = screen as IActivate;
if (screenAsActivate != null)
screenAsActivate.Activate();
}
public static void TryDeactivate(object screen, bool close)
{
var screenAsDeactivate = screen as IDeactivate;
if (screenAsDeactivate != null)
screenAsDeactivate.Deactivate(close);
}
}
}

View File

@ -53,14 +53,18 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="BindableCollection.cs" />
<Compile Include="Bootstrapper.cs" />
<Compile Include="Conductor.cs" />
<Compile Include="ConductorOneActive.cs" />
<Compile Include="ConductorWithActiveItem.cs" />
<Compile Include="Execute.cs" />
<Compile Include="IConductor.cs" />
<Compile Include="IoC.cs" />
<Compile Include="IScreen.cs" />
<Compile Include="PropertyChangedBase.cs" />
<Compile Include="Screen.cs" />
<Compile Include="ScreenExtensions.cs" />
<Compile Include="SubView.xaml.cs">
<DependentUpon>SubView.xaml</DependentUpon>
</Compile>