mirror of https://github.com/AMT-Cheif/Stylet.git
Work towards conductors
This commit is contained in:
parent
23a361df6e
commit
11a58f3af2
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Threading;
|
||||
|
@ -20,6 +21,7 @@ namespace Stylet
|
|||
public void Start()
|
||||
{
|
||||
this.Application = Application.Current;
|
||||
Execute.SynchronizationContext = SynchronizationContext.Current;
|
||||
|
||||
this.Application.Startup += OnStartup;
|
||||
this.Application.Exit += OnExit;
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
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 override async void ActivateItem(T item)
|
||||
{
|
||||
if (item != null && item.Equals(this.ActiveItem))
|
||||
this.ActivateItemIfActive(item);
|
||||
else if (await this.CanCloseItem(this.ActiveItem))
|
||||
this.ChangeActiveItem(item, true);
|
||||
}
|
||||
|
||||
public override async void DeactivateItem(T item, bool close)
|
||||
{
|
||||
if (item == null || !item.Equals(this.ActiveItem))
|
||||
return;
|
||||
|
||||
if (await this.CanCloseItem(item))
|
||||
this.ChangeActiveItem(default(T), close);
|
||||
}
|
||||
|
||||
public 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Stylet
|
||||
{
|
||||
public static class Execute
|
||||
{
|
||||
public static SynchronizationContext SynchronizationContext;
|
||||
|
||||
public static void OnUIThread(Action action)
|
||||
{
|
||||
SynchronizationContext.Post(_ => action(), null);
|
||||
}
|
||||
|
||||
public static Task OnUIThreadAsync(Action action)
|
||||
{
|
||||
var tcs = new TaskCompletionSource<object>();
|
||||
SynchronizationContext.Post(_ =>
|
||||
{
|
||||
try
|
||||
{
|
||||
action();
|
||||
tcs.SetResult(null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
tcs.SetException(e);
|
||||
}
|
||||
}, null);
|
||||
return tcs.Task;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Stylet
|
||||
{
|
||||
public interface IParent<out T>
|
||||
{
|
||||
IEnumerable<T> GetChildren();
|
||||
}
|
||||
|
||||
public interface IHaveActiveItem<T>
|
||||
{
|
||||
T ActiveItem { get; set; }
|
||||
}
|
||||
|
||||
public interface IConductor<T> : IParent<T>
|
||||
{
|
||||
void ActivateItem(T item);
|
||||
|
||||
void DeactivateItem(T item, bool close);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Stylet
|
||||
{
|
||||
public interface IViewAware
|
||||
{
|
||||
void AttachView(object view);
|
||||
}
|
||||
|
||||
public interface IActivate
|
||||
{
|
||||
bool IsActive { get; }
|
||||
void Activate();
|
||||
event EventHandler<EventArgs> Activated;
|
||||
}
|
||||
|
||||
public interface IDeactivate
|
||||
{
|
||||
void Deactivate(bool close);
|
||||
event EventHandler<DeactivationEventArgs> Deactivated;
|
||||
}
|
||||
|
||||
public interface IHaveDisplayName
|
||||
{
|
||||
string DisplayName { get; set; }
|
||||
}
|
||||
|
||||
public interface IChild
|
||||
{
|
||||
object Parent { get; set; }
|
||||
}
|
||||
|
||||
public interface IClose
|
||||
{
|
||||
void TryClose();
|
||||
}
|
||||
|
||||
public interface IGuardClose : IClose
|
||||
{
|
||||
Task<bool> CanCloseAsync();
|
||||
}
|
||||
|
||||
public interface IScreen : IViewAware, IHaveDisplayName, IActivate, IDeactivate, IChild
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class DeactivationEventArgs : EventArgs
|
||||
{
|
||||
public bool WasClosed;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Stylet
|
||||
{
|
||||
public class PropertyChangedBase : INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
this.NotifyOfPropertyChange(String.Empty);
|
||||
}
|
||||
|
||||
protected void NotifyOfPropertyChange<TProperty>(Expression<Func<TProperty>> property)
|
||||
{
|
||||
string propertyName;
|
||||
if (property.Body is UnaryExpression)
|
||||
propertyName = ((MemberExpression)((UnaryExpression)property.Body).Operand).Member.Name;
|
||||
else
|
||||
propertyName = ((MemberExpression)property.Body).Member.Name;
|
||||
this.NotifyOfPropertyChange(propertyName);
|
||||
}
|
||||
|
||||
protected virtual void NotifyOfPropertyChange([CallerMemberName] string propertyName = "")
|
||||
{
|
||||
Execute.OnUIThread(() =>
|
||||
{
|
||||
var handler = this.PropertyChanged;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
116
Stylet/Screen.cs
116
Stylet/Screen.cs
|
@ -4,25 +4,16 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls.Primitives;
|
||||
|
||||
namespace Stylet
|
||||
{
|
||||
public interface IViewAware
|
||||
{
|
||||
object View { get; }
|
||||
void AttachView(object view);
|
||||
}
|
||||
|
||||
public interface IScreen : IViewAware
|
||||
{
|
||||
}
|
||||
|
||||
public class Screen : IScreen
|
||||
public class Screen : PropertyChangedBase, IScreen
|
||||
{
|
||||
public virtual void TryClose(bool? dialogResult = null)
|
||||
{
|
||||
// TODO: Check for parent conductor
|
||||
var viewWindow = this.View as Window;
|
||||
var viewWindow = this.view as Window;
|
||||
if (viewWindow != null)
|
||||
{
|
||||
if (dialogResult != null)
|
||||
|
@ -31,19 +22,99 @@ namespace Stylet
|
|||
return;
|
||||
}
|
||||
|
||||
var viewPopover = this.view as Popup;
|
||||
if (viewPopover != null)
|
||||
{
|
||||
viewPopover.IsOpen = false;
|
||||
return;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(String.Format("Unable to close ViewModel {0} as it must have a parent, or its view must be a Window", this.GetType().Name));
|
||||
}
|
||||
|
||||
#region IHaveDisplayName
|
||||
|
||||
private string _displayName;
|
||||
public string DisplayName
|
||||
{
|
||||
get { return this._displayName; }
|
||||
set
|
||||
{
|
||||
this._displayName = value;
|
||||
this.NotifyOfPropertyChange();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IActivate
|
||||
|
||||
public event EventHandler<EventArgs> Activated;
|
||||
|
||||
private bool _isActive;
|
||||
public bool IsActive
|
||||
{
|
||||
get { return this._isActive; }
|
||||
set
|
||||
{
|
||||
this._isActive = value;
|
||||
this.NotifyOfPropertyChange();
|
||||
}
|
||||
}
|
||||
|
||||
void IActivate.Activate()
|
||||
{
|
||||
if (this.IsActive)
|
||||
return;
|
||||
|
||||
this.IsActive = true;
|
||||
|
||||
this.OnActivate();
|
||||
|
||||
var handler = this.Activated;
|
||||
if (handler != null)
|
||||
handler(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
protected virtual void OnActivate() { }
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDeactivate
|
||||
|
||||
public event EventHandler<DeactivationEventArgs> Deactivated;
|
||||
|
||||
void IDeactivate.Deactivate(bool close)
|
||||
{
|
||||
if (!this.IsActive)
|
||||
return;
|
||||
|
||||
this.IsActive = false;
|
||||
|
||||
this.OnDeactivate(close);
|
||||
|
||||
var handler = this.Deactivated;
|
||||
if (handler != null)
|
||||
handler(this, new DeactivationEventArgs() { WasClosed = close });
|
||||
|
||||
if (close)
|
||||
this.view = null;
|
||||
}
|
||||
|
||||
protected virtual void OnDeactivate(bool close) { }
|
||||
|
||||
#endregion
|
||||
|
||||
#region IViewAware
|
||||
|
||||
public object View { get; private set; }
|
||||
private object view;
|
||||
|
||||
void IViewAware.AttachView(object view)
|
||||
{
|
||||
if (this.View != null)
|
||||
if (this.view != null)
|
||||
throw new Exception(String.Format("Tried to attach View {0} to ViewModel {1}, but it already has a view attached", view.GetType().Name, this.GetType().Name));
|
||||
|
||||
this.View = view;
|
||||
this.view = view;
|
||||
|
||||
var viewAsFrameworkElement = view as FrameworkElement;
|
||||
if (viewAsFrameworkElement != null)
|
||||
|
@ -58,5 +129,20 @@ namespace Stylet
|
|||
protected virtual void OnViewLoaded() { }
|
||||
|
||||
#endregion
|
||||
|
||||
#region IChild
|
||||
|
||||
private object _parent;
|
||||
public object Parent
|
||||
{
|
||||
get { return this._parent; }
|
||||
set
|
||||
{
|
||||
this._parent = value;
|
||||
this.NotifyOfPropertyChange();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,7 +54,12 @@
|
|||
<SubType>Designer</SubType>
|
||||
</ApplicationDefinition>
|
||||
<Compile Include="Bootstrapper.cs" />
|
||||
<Compile Include="Conductor.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="SubView.xaml.cs">
|
||||
<DependentUpon>SubView.xaml</DependentUpon>
|
||||
|
|
|
@ -4,23 +4,24 @@ using System.Linq;
|
|||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Navigation;
|
||||
|
||||
namespace Stylet
|
||||
{
|
||||
public interface IWindowManager
|
||||
{
|
||||
void ShowWindow(object viewModel, IDictionary<string, object> settings = null);
|
||||
void ShowWindow(object viewModel);
|
||||
}
|
||||
|
||||
public class WindowManager : IWindowManager
|
||||
{
|
||||
public void ShowWindow(object viewModel, IDictionary<string, object> settings = null)
|
||||
public void ShowWindow(object viewModel)
|
||||
{
|
||||
this.CreateWindow(viewModel, false, settings).Show();
|
||||
this.CreateWindow(viewModel, false).Show();
|
||||
}
|
||||
|
||||
private Window CreateWindow(object viewModel, bool isDialog, IDictionary<string, object> settings)
|
||||
private Window CreateWindow(object viewModel, bool isDialog)
|
||||
{
|
||||
var view = ViewLocator.LocateForModel(viewModel) as Window;
|
||||
if (view == null)
|
||||
|
@ -28,6 +29,13 @@ namespace Stylet
|
|||
|
||||
ViewModelBinder.Bind(view, viewModel);
|
||||
|
||||
var haveDisplayName = viewModel as IHaveDisplayName;
|
||||
if (haveDisplayName != null)
|
||||
{
|
||||
var binding = new Binding("DisplayName") { Mode = BindingMode.TwoWay };
|
||||
view.SetBinding(Window.TitleProperty, binding);
|
||||
}
|
||||
|
||||
if (isDialog)
|
||||
{
|
||||
var owner = this.InferOwnerOf(view);
|
||||
|
|
Loading…
Reference in New Issue