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.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
|
@ -20,6 +21,7 @@ namespace Stylet
|
||||||
public void Start()
|
public void Start()
|
||||||
{
|
{
|
||||||
this.Application = Application.Current;
|
this.Application = Application.Current;
|
||||||
|
Execute.SynchronizationContext = SynchronizationContext.Current;
|
||||||
|
|
||||||
this.Application.Startup += OnStartup;
|
this.Application.Startup += OnStartup;
|
||||||
this.Application.Exit += OnExit;
|
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.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Controls.Primitives;
|
||||||
|
|
||||||
namespace Stylet
|
namespace Stylet
|
||||||
{
|
{
|
||||||
public interface IViewAware
|
public class Screen : PropertyChangedBase, IScreen
|
||||||
{
|
|
||||||
object View { get; }
|
|
||||||
void AttachView(object view);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IScreen : IViewAware
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Screen : IScreen
|
|
||||||
{
|
{
|
||||||
public virtual void TryClose(bool? dialogResult = null)
|
public virtual void TryClose(bool? dialogResult = null)
|
||||||
{
|
{
|
||||||
// TODO: Check for parent conductor
|
// TODO: Check for parent conductor
|
||||||
var viewWindow = this.View as Window;
|
var viewWindow = this.view as Window;
|
||||||
if (viewWindow != null)
|
if (viewWindow != null)
|
||||||
{
|
{
|
||||||
if (dialogResult != null)
|
if (dialogResult != null)
|
||||||
|
@ -31,19 +22,99 @@ namespace Stylet
|
||||||
return;
|
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));
|
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
|
#region IViewAware
|
||||||
|
|
||||||
public object View { get; private set; }
|
private object view;
|
||||||
|
|
||||||
void IViewAware.AttachView(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));
|
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;
|
var viewAsFrameworkElement = view as FrameworkElement;
|
||||||
if (viewAsFrameworkElement != null)
|
if (viewAsFrameworkElement != null)
|
||||||
|
@ -58,5 +129,20 @@ namespace Stylet
|
||||||
protected virtual void OnViewLoaded() { }
|
protected virtual void OnViewLoaded() { }
|
||||||
|
|
||||||
#endregion
|
#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>
|
<SubType>Designer</SubType>
|
||||||
</ApplicationDefinition>
|
</ApplicationDefinition>
|
||||||
<Compile Include="Bootstrapper.cs" />
|
<Compile Include="Bootstrapper.cs" />
|
||||||
|
<Compile Include="Conductor.cs" />
|
||||||
|
<Compile Include="Execute.cs" />
|
||||||
|
<Compile Include="IConductor.cs" />
|
||||||
<Compile Include="IoC.cs" />
|
<Compile Include="IoC.cs" />
|
||||||
|
<Compile Include="IScreen.cs" />
|
||||||
|
<Compile Include="PropertyChangedBase.cs" />
|
||||||
<Compile Include="Screen.cs" />
|
<Compile Include="Screen.cs" />
|
||||||
<Compile Include="SubView.xaml.cs">
|
<Compile Include="SubView.xaml.cs">
|
||||||
<DependentUpon>SubView.xaml</DependentUpon>
|
<DependentUpon>SubView.xaml</DependentUpon>
|
||||||
|
|
|
@ -4,23 +4,24 @@ using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Data;
|
||||||
using System.Windows.Navigation;
|
using System.Windows.Navigation;
|
||||||
|
|
||||||
namespace Stylet
|
namespace Stylet
|
||||||
{
|
{
|
||||||
public interface IWindowManager
|
public interface IWindowManager
|
||||||
{
|
{
|
||||||
void ShowWindow(object viewModel, IDictionary<string, object> settings = null);
|
void ShowWindow(object viewModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class WindowManager : IWindowManager
|
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;
|
var view = ViewLocator.LocateForModel(viewModel) as Window;
|
||||||
if (view == null)
|
if (view == null)
|
||||||
|
@ -28,6 +29,13 @@ namespace Stylet
|
||||||
|
|
||||||
ViewModelBinder.Bind(view, viewModel);
|
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)
|
if (isDialog)
|
||||||
{
|
{
|
||||||
var owner = this.InferOwnerOf(view);
|
var owner = this.InferOwnerOf(view);
|
||||||
|
|
Loading…
Reference in New Issue