mirror of https://github.com/AMT-Cheif/Stylet.git
Refactor the ViewManager, splitting out a few methods to make it easier to customize
This commit is contained in:
parent
2f4424a85d
commit
a914d58668
|
@ -18,14 +18,14 @@ namespace Stylet
|
|||
{
|
||||
/// <summary>
|
||||
/// Called by the View.Model attached property when the ViewModel its bound to changes
|
||||
void OnModelChanged(DependencyObject targetLocation, DependencyPropertyChangedEventArgs e);
|
||||
void OnModelChanged(DependencyObject targetLocation, object oldValue, object newValue);
|
||||
|
||||
/// <summary>
|
||||
/// Given an instance of a ViewModel, locate the correct view for it, and instantiate it
|
||||
/// </summary>
|
||||
/// <param name="model">ViewModel to locate the view for</param>
|
||||
/// <returns>An instance of the correct view</returns>
|
||||
UIElement CreateViewForModel(object model);
|
||||
UIElement CreateAndSetupViewForModel(object model);
|
||||
|
||||
/// <summary>
|
||||
/// Given an instance of a ViewModel and an instance of its View, bind the two together
|
||||
|
@ -37,23 +37,23 @@ namespace Stylet
|
|||
|
||||
public class ViewManager : IViewManager
|
||||
{
|
||||
public virtual void OnModelChanged(DependencyObject targetLocation, DependencyPropertyChangedEventArgs e)
|
||||
public virtual void OnModelChanged(DependencyObject targetLocation, object oldValue, object newValue)
|
||||
{
|
||||
if (e.OldValue == e.NewValue)
|
||||
if (oldValue == newValue)
|
||||
return;
|
||||
|
||||
if (e.NewValue != null)
|
||||
if (newValue != null)
|
||||
{
|
||||
UIElement view;
|
||||
var viewModelAsViewAware = e.NewValue as IViewAware;
|
||||
var viewModelAsViewAware = newValue as IViewAware;
|
||||
if (viewModelAsViewAware != null && viewModelAsViewAware.View != null)
|
||||
{
|
||||
view = viewModelAsViewAware.View;
|
||||
}
|
||||
else
|
||||
{
|
||||
view = this.CreateViewForModel(e.NewValue);
|
||||
this.BindViewToModel(view, e.NewValue);
|
||||
view = this.CreateAndSetupViewForModel(newValue);
|
||||
this.BindViewToModel(view, newValue);
|
||||
}
|
||||
|
||||
View.SetContentProperty(targetLocation, view);
|
||||
|
@ -67,7 +67,11 @@ namespace Stylet
|
|||
public virtual Type ViewTypeForViewName(string viewName)
|
||||
{
|
||||
// TODO: This might need some more thinking
|
||||
return AssemblySource.Assemblies.SelectMany(x => x.GetExportedTypes()).FirstOrDefault(x => x.FullName == viewName);
|
||||
var viewType = AssemblySource.Assemblies.SelectMany(x => x.GetExportedTypes()).FirstOrDefault(x => x.FullName == viewName);
|
||||
if (viewType == null)
|
||||
throw new Exception(String.Format("Unable to find a View with type {0}", viewName));
|
||||
|
||||
return viewType;
|
||||
}
|
||||
|
||||
public virtual Type LocateViewForModel(Type modelType)
|
||||
|
@ -75,13 +79,22 @@ namespace Stylet
|
|||
var viewName = Regex.Replace(modelType.FullName, @"ViewModel", "View");
|
||||
var viewType = this.ViewTypeForViewName(viewName);
|
||||
|
||||
if (viewType == null)
|
||||
throw new Exception(String.Format("Unable to find a View with type {0}", viewName));
|
||||
|
||||
return viewType;
|
||||
}
|
||||
|
||||
public virtual UIElement CreateViewForModel(object model)
|
||||
public virtual void BindViewToModel(UIElement view, object viewModel)
|
||||
{
|
||||
View.SetActionTarget(view, viewModel);
|
||||
|
||||
var viewAsFrameworkElement = view as FrameworkElement;
|
||||
if (viewAsFrameworkElement != null)
|
||||
viewAsFrameworkElement.DataContext = viewModel;
|
||||
|
||||
var viewModelAsViewAware = viewModel as IViewAware;
|
||||
if (viewModelAsViewAware != null)
|
||||
viewModelAsViewAware.AttachView(view);
|
||||
}
|
||||
public virtual UIElement CreateAndSetupViewForModel(object model)
|
||||
{
|
||||
var viewType = this.LocateViewForModel(model.GetType());
|
||||
|
||||
|
@ -97,18 +110,5 @@ namespace Stylet
|
|||
|
||||
return view;
|
||||
}
|
||||
|
||||
public virtual void BindViewToModel(UIElement view, object viewModel)
|
||||
{
|
||||
View.SetActionTarget(view, viewModel);
|
||||
|
||||
var viewAsFrameworkElement = view as FrameworkElement;
|
||||
if (viewAsFrameworkElement != null)
|
||||
viewAsFrameworkElement.DataContext = viewModel;
|
||||
|
||||
var viewModelAsViewAware = viewModel as IViewAware;
|
||||
if (viewModelAsViewAware != null)
|
||||
viewModelAsViewAware.AttachView(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace Stylet.Xaml
|
|||
}
|
||||
|
||||
public static readonly DependencyProperty ModelProperty =
|
||||
DependencyProperty.RegisterAttached("Model", typeof(object), typeof(View), new PropertyMetadata(null, (d, e) => ViewManager.OnModelChanged(d, e) ));
|
||||
DependencyProperty.RegisterAttached("Model", typeof(object), typeof(View), new PropertyMetadata(null, (d, e) => ViewManager.OnModelChanged(d, e.OldValue, e.NewValue) ));
|
||||
|
||||
|
||||
public static void SetContentProperty(DependencyObject targetLocation, UIElement view)
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace StyletUnitTests
|
|||
{
|
||||
public UIElement View;
|
||||
public object RequestedModel;
|
||||
public override UIElement CreateViewForModel(object model)
|
||||
public override UIElement CreateAndSetupViewForModel(object model)
|
||||
{
|
||||
this.RequestedModel = model;
|
||||
return this.View;
|
||||
|
@ -84,14 +84,14 @@ namespace StyletUnitTests
|
|||
public void OnModelChangedDoesNothingIfNoChange()
|
||||
{
|
||||
var val = new object();
|
||||
this.viewManager.OnModelChanged(null, new DependencyPropertyChangedEventArgs(View.ModelProperty, val, val));
|
||||
this.viewManager.OnModelChanged(null, val, val);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OnModelChangedSetsNullIfNewValueNull()
|
||||
{
|
||||
var target = new ContentControl();
|
||||
this.viewManager.OnModelChanged(target, new DependencyPropertyChangedEventArgs(View.ModelProperty, 5, null));
|
||||
this.viewManager.OnModelChanged(target, 5, null);
|
||||
Assert.Null(target.Content);
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ namespace StyletUnitTests
|
|||
var view = new UIElement();
|
||||
|
||||
model.Setup(x => x.View).Returns(view);
|
||||
this.viewManager.OnModelChanged(target, new DependencyPropertyChangedEventArgs(View.ModelProperty, null, model.Object));
|
||||
this.viewManager.OnModelChanged(target, null, model.Object);
|
||||
|
||||
Assert.AreEqual(view, target.Content);
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ namespace StyletUnitTests
|
|||
|
||||
viewManager.View = view;
|
||||
|
||||
viewManager.OnModelChanged(target, new DependencyPropertyChangedEventArgs(View.ModelProperty, null, model));
|
||||
viewManager.OnModelChanged(target, null, model);
|
||||
|
||||
Assert.AreEqual(viewManager.RequestedModel, model);
|
||||
Assert.AreEqual(viewManager.BindViewToModelView, view);
|
||||
|
@ -147,13 +147,13 @@ namespace StyletUnitTests
|
|||
var viewManager = new LocatingViewManager();
|
||||
|
||||
viewManager.LocatedViewType = typeof(I1);
|
||||
Assert.Throws<Exception>(() => viewManager.CreateViewForModel(new object()));
|
||||
Assert.Throws<Exception>(() => viewManager.CreateAndSetupViewForModel(new object()));
|
||||
|
||||
viewManager.LocatedViewType = typeof(AC1);
|
||||
Assert.Throws<Exception>(() => viewManager.CreateViewForModel(new object()));
|
||||
Assert.Throws<Exception>(() => viewManager.CreateAndSetupViewForModel(new object()));
|
||||
|
||||
viewManager.LocatedViewType = typeof(C1);
|
||||
Assert.Throws<Exception>(() => viewManager.CreateViewForModel(new object()));
|
||||
Assert.Throws<Exception>(() => viewManager.CreateAndSetupViewForModel(new object()));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -169,7 +169,7 @@ namespace StyletUnitTests
|
|||
var viewManager = new LocatingViewManager();
|
||||
viewManager.LocatedViewType = typeof(TestView);
|
||||
|
||||
var returnedView = viewManager.CreateViewForModel(new object());
|
||||
var returnedView = viewManager.CreateAndSetupViewForModel(new object());
|
||||
|
||||
Assert.True(view.InitializeComponentCalled);
|
||||
Assert.AreEqual(view, returnedView);
|
||||
|
@ -188,7 +188,7 @@ namespace StyletUnitTests
|
|||
var viewManager = new LocatingViewManager();
|
||||
viewManager.LocatedViewType = typeof(UIElement);
|
||||
|
||||
var returnedView = viewManager.CreateViewForModel(new object());
|
||||
var returnedView = viewManager.CreateAndSetupViewForModel(new object());
|
||||
|
||||
Assert.AreEqual(view, returnedView);
|
||||
}
|
||||
|
|
|
@ -47,15 +47,19 @@ namespace StyletUnitTests
|
|||
var model = new object();
|
||||
View.SetModel(obj, null);
|
||||
|
||||
DependencyPropertyChangedEventArgs ea = default(DependencyPropertyChangedEventArgs);
|
||||
this.viewManager.Setup(x => x.OnModelChanged(obj, It.IsAny<DependencyPropertyChangedEventArgs>()))
|
||||
.Callback<DependencyObject, DependencyPropertyChangedEventArgs>((d, e) => ea = e).Verifiable();
|
||||
object oldValue = null;
|
||||
object newValue = null;
|
||||
this.viewManager.Setup(x => x.OnModelChanged(obj, It.IsAny<object>(), It.IsAny<object>()))
|
||||
.Callback<DependencyObject, object, object>((d, eOldValue, eNewValue) =>
|
||||
{
|
||||
oldValue = eOldValue;
|
||||
newValue = eNewValue;
|
||||
}).Verifiable();
|
||||
View.SetModel(obj, model);
|
||||
|
||||
this.viewManager.Verify();
|
||||
Assert.Null(ea.OldValue);
|
||||
Assert.AreEqual(model, ea.NewValue);
|
||||
Assert.AreEqual(View.ModelProperty, ea.Property);
|
||||
Assert.Null(oldValue);
|
||||
Assert.AreEqual(model, newValue);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
|
@ -61,7 +61,7 @@ namespace StyletUnitTests
|
|||
public void CreateWindowAsksViewManagerForView()
|
||||
{
|
||||
var model = new object();
|
||||
this.viewManager.Setup(x => x.CreateViewForModel(model)).Verifiable();
|
||||
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model)).Verifiable();
|
||||
// Don't care if this throws - that's OK
|
||||
try { this.windowManager.CreateWindow(model, false); }
|
||||
catch (Exception) { }
|
||||
|
@ -72,7 +72,7 @@ namespace StyletUnitTests
|
|||
public void CreateWindowThrowsIfViewIsntAWindow()
|
||||
{
|
||||
var model = new object();
|
||||
this.viewManager.Setup(x => x.CreateViewForModel(model)).Returns(new UIElement());
|
||||
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model)).Returns(new UIElement());
|
||||
Assert.Throws<ArgumentException>(() => this.windowManager.CreateWindow(model, false));
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ namespace StyletUnitTests
|
|||
{
|
||||
var model = new object();
|
||||
var window = new Window();
|
||||
this.viewManager.Setup(x => x.CreateViewForModel(model)).Returns(window);
|
||||
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model)).Returns(window);
|
||||
|
||||
this.windowManager.CreateWindow(model, false);
|
||||
|
||||
|
@ -93,7 +93,7 @@ namespace StyletUnitTests
|
|||
{
|
||||
var model = new Screen();
|
||||
var window = new Window();
|
||||
this.viewManager.Setup(x => x.CreateViewForModel(model)).Returns(window);
|
||||
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model)).Returns(window);
|
||||
|
||||
this.windowManager.CreateWindow(model, false);
|
||||
|
||||
|
@ -106,7 +106,7 @@ namespace StyletUnitTests
|
|||
public void CreateWindowActivatesViewModel()
|
||||
{
|
||||
var model = new Mock<IScreen>();
|
||||
this.viewManager.Setup(x => x.CreateViewForModel(model.Object)).Returns(new Window());
|
||||
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(new Window());
|
||||
this.windowManager.CreateWindow(model.Object, false);
|
||||
model.Verify(x => x.Activate());
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ namespace StyletUnitTests
|
|||
{
|
||||
var model = new Mock<IScreen>();
|
||||
var window = new MyWindow();
|
||||
this.viewManager.Setup(x => x.CreateViewForModel(model.Object)).Returns(window);
|
||||
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
|
||||
this.windowManager.CreateWindow(model.Object, false);
|
||||
window.WindowState = WindowState.Maximized;
|
||||
window.OnStateChanged(EventArgs.Empty);
|
||||
|
@ -128,7 +128,7 @@ namespace StyletUnitTests
|
|||
{
|
||||
var model = new Mock<IScreen>();
|
||||
var window = new MyWindow();
|
||||
this.viewManager.Setup(x => x.CreateViewForModel(model.Object)).Returns(window);
|
||||
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
|
||||
this.windowManager.CreateWindow(model.Object, false);
|
||||
window.WindowState = WindowState.Normal;
|
||||
window.OnStateChanged(EventArgs.Empty);
|
||||
|
@ -140,7 +140,7 @@ namespace StyletUnitTests
|
|||
{
|
||||
var model = new Mock<IScreen>();
|
||||
var window = new MyWindow();
|
||||
this.viewManager.Setup(x => x.CreateViewForModel(model.Object)).Returns(window);
|
||||
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
|
||||
this.windowManager.CreateWindow(model.Object, false);
|
||||
window.WindowState = WindowState.Minimized;
|
||||
window.OnStateChanged(EventArgs.Empty);
|
||||
|
@ -152,7 +152,7 @@ namespace StyletUnitTests
|
|||
{
|
||||
var model = new Screen();
|
||||
var window = new MyWindow();
|
||||
this.viewManager.Setup(x => x.CreateViewForModel(model)).Returns(window);
|
||||
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model)).Returns(window);
|
||||
this.windowManager.CreateWindow(model, false);
|
||||
window.OnClosing(new CancelEventArgs(true));
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ namespace StyletUnitTests
|
|||
{
|
||||
var model = new Mock<IScreen>();
|
||||
var window = new MyWindow();
|
||||
this.viewManager.Setup(x => x.CreateViewForModel(model.Object)).Returns(window);
|
||||
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
|
||||
this.windowManager.CreateWindow(model.Object, false);
|
||||
model.Setup(x => x.CanCloseAsync()).Returns(Task.FromResult(false));
|
||||
var ea = new CancelEventArgs();
|
||||
|
@ -175,7 +175,7 @@ namespace StyletUnitTests
|
|||
{
|
||||
var model = new Mock<IScreen>();
|
||||
var window = new MyWindow();
|
||||
this.viewManager.Setup(x => x.CreateViewForModel(model.Object)).Returns(window);
|
||||
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
|
||||
this.windowManager.CreateWindow(model.Object, false);
|
||||
model.Setup(x => x.CanCloseAsync()).Returns(Task.FromResult(true));
|
||||
var ea = new CancelEventArgs();
|
||||
|
@ -188,7 +188,7 @@ namespace StyletUnitTests
|
|||
{
|
||||
var model = new Mock<IScreen>();
|
||||
var window = new MyWindow();
|
||||
this.viewManager.Setup(x => x.CreateViewForModel(model.Object)).Returns(window);
|
||||
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
|
||||
this.windowManager.CreateWindow(model.Object, false);
|
||||
model.Setup(x => x.CanCloseAsync()).Returns(Task.Delay(1).ContinueWith(t => false));
|
||||
var ea = new CancelEventArgs();
|
||||
|
@ -201,7 +201,7 @@ namespace StyletUnitTests
|
|||
{
|
||||
var model = new Mock<IScreen>();
|
||||
var window = new MyWindow();
|
||||
this.viewManager.Setup(x => x.CreateViewForModel(model.Object)).Returns(window);
|
||||
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
|
||||
this.windowManager.CreateWindow(model.Object, false);
|
||||
model.Setup(x => x.CanCloseAsync()).Returns(Task.Delay(1).ContinueWith(t => true));
|
||||
var ea = new CancelEventArgs();
|
||||
|
@ -214,7 +214,7 @@ namespace StyletUnitTests
|
|||
{
|
||||
var model = new Mock<IScreen>();
|
||||
var window = new MyWindow();
|
||||
this.viewManager.Setup(x => x.CreateViewForModel(model.Object)).Returns(window);
|
||||
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
|
||||
this.windowManager.CreateWindow(model.Object, false);
|
||||
var tcs = new TaskCompletionSource<bool>();
|
||||
model.Setup(x => x.CanCloseAsync()).Returns(tcs.Task);
|
||||
|
@ -234,7 +234,7 @@ namespace StyletUnitTests
|
|||
{
|
||||
var model = new Screen();
|
||||
var window = new MyWindow();
|
||||
this.viewManager.Setup(x => x.CreateViewForModel(model)).Returns(window);
|
||||
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model)).Returns(window);
|
||||
this.windowManager.CreateWindow(model, false);
|
||||
((IChildDelegate)model.Parent).CloseItem(new object());
|
||||
}
|
||||
|
@ -244,7 +244,7 @@ namespace StyletUnitTests
|
|||
{
|
||||
var model = new Mock<IScreen>();
|
||||
var window = new MyWindow();
|
||||
this.viewManager.Setup(x => x.CreateViewForModel(model.Object)).Returns(window);
|
||||
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
|
||||
object parent = null;
|
||||
model.SetupSet(x => x.Parent = It.IsAny<object>()).Callback((object x) => parent = x);
|
||||
this.windowManager.CreateWindow(model.Object, false);
|
||||
|
@ -258,7 +258,7 @@ namespace StyletUnitTests
|
|||
{
|
||||
var model = new Mock<IScreen>();
|
||||
var window = new MyWindow();
|
||||
this.viewManager.Setup(x => x.CreateViewForModel(model.Object)).Returns(window);
|
||||
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
|
||||
object parent = null;
|
||||
model.SetupSet(x => x.Parent = It.IsAny<object>()).Callback((object x) => parent = x);
|
||||
this.windowManager.CreateWindow(model.Object, true);
|
||||
|
|
Loading…
Reference in New Issue