Simplify IViewManager, by aligning it to what someone actually wants the ViewManager to do

This commit is contained in:
Antony Male 2014-07-24 16:39:26 +01:00
parent b53d0f5195
commit 60aca05dd2
5 changed files with 67 additions and 59 deletions

View File

@ -21,18 +21,11 @@ namespace Stylet
void OnModelChanged(DependencyObject targetLocation, object oldValue, object newValue);
/// <summary>
/// Given a ViewModel instance, locate its View type (using LocateViewForModel), instantiates and initializes it
/// Create a View for the given ViewModel, and bind the two together
/// </summary>
/// <param name="model">ViewModel to locate and instantiate the View for</param>
/// <returns>Instantiated and setup view</returns>
UIElement CreateAndSetupViewForModel(object model);
/// <summary>
/// Given an instance of a ViewModel and an instance of its View, bind the two together
/// </summary>
/// <param name="view">View to bind to the ViewModel</param>
/// <param name="viewModel">ViewModel to bind the View to</param>
void BindViewToModel(UIElement view, object viewModel);
/// <param name="model">ViewModel to create a Veiw for</param>
/// <returns>Newly created View, bound to the given ViewModel</returns>
UIElement CreateAndBindViewForModel(object model);
}
/// <summary>
@ -61,8 +54,7 @@ namespace Stylet
}
else
{
view = this.CreateAndSetupViewForModel(newValue);
this.BindViewToModel(view, newValue);
view = this.CreateAndBindViewForModel(newValue);
}
View.SetContentProperty(targetLocation, view);
@ -73,6 +65,20 @@ namespace Stylet
}
}
/// <summary>
/// Create a View for the given ViewModel, and bind the two together
/// </summary>
/// <param name="model">ViewModel to create a Veiw for</param>
/// <returns>Newly created View, bound to the given ViewModel</returns>
public virtual UIElement CreateAndBindViewForModel(object model)
{
// Need to bind before we initialize the view
// Otherwise e.g. the Command bindings get evaluated (by InitializeComponent) but the ActionTarget hasn't been set yet
var view = this.CreateViewForModel(model);
this.BindViewToModel(view, model);
return view;
}
/// <summary>
/// Given the expected name for a view, locate its type (or throw an exception if a suitable type couldn't be found)
/// </summary>
@ -102,11 +108,11 @@ namespace Stylet
}
/// <summary>
/// Given a ViewModel instance, locate its View type (using LocateViewForModel), instantiates and initializes it, and binds it to the ViewModel (using BindViewToModel)
/// Given a ViewModel instance, locate its View type (using LocateViewForModel), and instantiates it
/// </summary>
/// <param name="model">ViewModel to locate and instantiate the View for</param>
/// <returns>Instantiated and setup view</returns>
public virtual UIElement CreateAndSetupViewForModel(object model)
protected virtual UIElement CreateViewForModel(object model)
{
var viewType = this.LocateViewForModel(model.GetType());
@ -123,12 +129,12 @@ namespace Stylet
return view;
}
/// <summary>
/// <summary>
/// Given an instance of a ViewModel and an instance of its View, bind the two together
/// </summary>
/// <param name="view">View to bind to the ViewModel</param>
/// <param name="viewModel">ViewModel to bind the View to</param>
public virtual void BindViewToModel(UIElement view, object viewModel)
protected virtual void BindViewToModel(UIElement view, object viewModel)
{
View.SetActionTarget(view, viewModel);

View File

@ -68,13 +68,11 @@ namespace Stylet
/// <returns>Window which was created and set up</returns>
protected virtual Window CreateWindow(object viewModel, bool isDialog)
{
var view = this.viewManager.CreateAndSetupViewForModel(viewModel);
var view = this.viewManager.CreateAndBindViewForModel(viewModel);
var window = view as Window;
if (window == null)
throw new ArgumentException(String.Format("Tried to show {0} as a window, but it isn't a Window", view == null ? "(null)" : view.GetType().Name));
this.viewManager.BindViewToModel(window, viewModel);
var haveDisplayName = viewModel as IHaveDisplayName;
if (haveDisplayName != null && BindingOperations.GetBindingBase(window, Window.TitleProperty) == null)
{

View File

@ -6,7 +6,7 @@
<DockPanel LastChildFill="False">
<GroupBox DockPanel.Dock="Top" Header="ShowDialog and DialogResult" Padding="10">
<StackPanel Orientation="Horizontal">
<Button Command="{s:Action ShowDialogAndDialogResult}">Show Dialog</Button>
<Button Command="{s:Action ShowDialogAndDialogResult, NullTarget=Throw}">Show Dialog</Button>
<TextBlock Margin="50,0,0,0">Result: </TextBlock>
<TextBlock Margin="10,0,0,0" Text="{Binding ShowDialogAndDialogResultDialogResult}"/>
</StackPanel>

View File

@ -28,11 +28,24 @@ namespace StyletUnitTests
private abstract class AC1 { }
private class C1 { }
private class AccessibleViewManager : ViewManager
{
public new UIElement CreateViewForModel(object model)
{
return base.CreateViewForModel(model);
}
public new void BindViewToModel(UIElement view, object viewModel)
{
base.BindViewToModel(view, viewModel);
}
}
private class CreatingAndBindingViewManager : ViewManager
{
public UIElement View;
public object RequestedModel;
public override UIElement CreateAndSetupViewForModel(object model)
protected override UIElement CreateViewForModel(object model)
{
this.RequestedModel = model;
return this.View;
@ -40,7 +53,7 @@ namespace StyletUnitTests
public UIElement BindViewToModelView;
public object BindViewtoModelViewModel;
public override void BindViewToModel(UIElement view, object viewModel)
protected override void BindViewToModel(UIElement view, object viewModel)
{
this.BindViewToModelView = view;
this.BindViewtoModelViewModel = viewModel;
@ -156,13 +169,13 @@ namespace StyletUnitTests
var viewManager = new LocatingViewManager();
viewManager.LocatedViewType = typeof(I1);
Assert.Throws<StyletViewLocationException>(() => viewManager.CreateAndSetupViewForModel(new object()));
Assert.Throws<StyletViewLocationException>(() => viewManager.CreateAndBindViewForModel(new object()));
viewManager.LocatedViewType = typeof(AC1);
Assert.Throws<StyletViewLocationException>(() => viewManager.CreateAndSetupViewForModel(new object()));
Assert.Throws<StyletViewLocationException>(() => viewManager.CreateAndBindViewForModel(new object()));
viewManager.LocatedViewType = typeof(C1);
Assert.Throws<StyletViewLocationException>(() => viewManager.CreateAndSetupViewForModel(new object()));
Assert.Throws<StyletViewLocationException>(() => viewManager.CreateAndBindViewForModel(new object()));
}
[Test]
@ -178,7 +191,7 @@ namespace StyletUnitTests
var viewManager = new LocatingViewManager();
viewManager.LocatedViewType = typeof(TestView);
var returnedView = viewManager.CreateAndSetupViewForModel(new object());
var returnedView = viewManager.CreateAndBindViewForModel(new object());
Assert.True(view.InitializeComponentCalled);
Assert.AreEqual(view, returnedView);
@ -197,7 +210,7 @@ namespace StyletUnitTests
var viewManager = new LocatingViewManager();
viewManager.LocatedViewType = typeof(UIElement);
var returnedView = viewManager.CreateAndSetupViewForModel(new object());
var returnedView = viewManager.CreateAndBindViewForModel(new object());
Assert.AreEqual(view, returnedView);
}
@ -207,7 +220,8 @@ namespace StyletUnitTests
{
var view = new UIElement();
var model = new object();
this.viewManager.BindViewToModel(view, model);
var viewManager = new AccessibleViewManager();
viewManager.BindViewToModel(view, model);
Assert.AreEqual(model, View.GetActionTarget(view));
}
@ -217,7 +231,8 @@ namespace StyletUnitTests
{
var view = new FrameworkElement();
var model = new object();
this.viewManager.BindViewToModel(view, model);
var viewManager = new AccessibleViewManager();
viewManager.BindViewToModel(view, model);
Assert.AreEqual(model, view.DataContext);
}
@ -227,7 +242,8 @@ namespace StyletUnitTests
{
var view = new UIElement();
var model = new Mock<IViewAware>();
this.viewManager.BindViewToModel(view, model.Object);
var viewManager = new AccessibleViewManager();
viewManager.BindViewToModel(view, model.Object);
model.Verify(x => x.AttachView(view));
}

View File

@ -61,7 +61,7 @@ namespace StyletUnitTests
public void CreateWindowAsksViewManagerForView()
{
var model = new object();
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model)).Verifiable();
this.viewManager.Setup(x => x.CreateAndBindViewForModel(model)).Verifiable();
// Don't care if this throws - that's OK
try { this.windowManager.CreateWindow(model, false); }
catch (Exception) { }
@ -72,28 +72,16 @@ namespace StyletUnitTests
public void CreateWindowThrowsIfViewIsntAWindow()
{
var model = new object();
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model)).Returns(new UIElement());
this.viewManager.Setup(x => x.CreateAndBindViewForModel(model)).Returns(new UIElement());
Assert.Throws<ArgumentException>(() => this.windowManager.CreateWindow(model, false));
}
[Test]
public void CreateWindowBindsViewToModel()
{
var model = new object();
var window = new Window();
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model)).Returns(window);
this.windowManager.CreateWindow(model, false);
this.viewManager.Verify(x => x.BindViewToModel(window, model));
}
[Test]
public void CreateWindowSetsUpTitleBindingIfViewModelIsIHaveDisplayName()
{
var model = new Screen();
var window = new Window();
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model)).Returns(window);
this.viewManager.Setup(x => x.CreateAndBindViewForModel(model)).Returns(window);
this.windowManager.CreateWindow(model, false);
@ -106,7 +94,7 @@ namespace StyletUnitTests
public void CreateWindowActivatesViewModel()
{
var model = new Mock<IScreen>();
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(new Window());
this.viewManager.Setup(x => x.CreateAndBindViewForModel(model.Object)).Returns(new Window());
this.windowManager.CreateWindow(model.Object, false);
model.Verify(x => x.Activate());
}
@ -116,7 +104,7 @@ namespace StyletUnitTests
{
var model = new Mock<IScreen>();
var window = new MyWindow();
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
this.viewManager.Setup(x => x.CreateAndBindViewForModel(model.Object)).Returns(window);
this.windowManager.CreateWindow(model.Object, false);
window.WindowState = WindowState.Maximized;
window.OnStateChanged(EventArgs.Empty);
@ -128,7 +116,7 @@ namespace StyletUnitTests
{
var model = new Mock<IScreen>();
var window = new MyWindow();
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
this.viewManager.Setup(x => x.CreateAndBindViewForModel(model.Object)).Returns(window);
this.windowManager.CreateWindow(model.Object, false);
window.WindowState = WindowState.Normal;
window.OnStateChanged(EventArgs.Empty);
@ -140,7 +128,7 @@ namespace StyletUnitTests
{
var model = new Mock<IScreen>();
var window = new MyWindow();
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
this.viewManager.Setup(x => x.CreateAndBindViewForModel(model.Object)).Returns(window);
this.windowManager.CreateWindow(model.Object, false);
window.WindowState = WindowState.Minimized;
window.OnStateChanged(EventArgs.Empty);
@ -152,7 +140,7 @@ namespace StyletUnitTests
{
var model = new Screen();
var window = new MyWindow();
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model)).Returns(window);
this.viewManager.Setup(x => x.CreateAndBindViewForModel(model)).Returns(window);
this.windowManager.CreateWindow(model, false);
window.OnClosing(new CancelEventArgs(true));
}
@ -162,7 +150,7 @@ namespace StyletUnitTests
{
var model = new Mock<IScreen>();
var window = new MyWindow();
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
this.viewManager.Setup(x => x.CreateAndBindViewForModel(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 +163,7 @@ namespace StyletUnitTests
{
var model = new Mock<IScreen>();
var window = new MyWindow();
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
this.viewManager.Setup(x => x.CreateAndBindViewForModel(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 +176,7 @@ namespace StyletUnitTests
{
var model = new Mock<IScreen>();
var window = new MyWindow();
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
this.viewManager.Setup(x => x.CreateAndBindViewForModel(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 +189,7 @@ namespace StyletUnitTests
{
var model = new Mock<IScreen>();
var window = new MyWindow();
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
this.viewManager.Setup(x => x.CreateAndBindViewForModel(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 +202,7 @@ namespace StyletUnitTests
{
var model = new Mock<IScreen>();
var window = new MyWindow();
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
this.viewManager.Setup(x => x.CreateAndBindViewForModel(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 +222,7 @@ namespace StyletUnitTests
{
var model = new Screen();
var window = new MyWindow();
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model)).Returns(window);
this.viewManager.Setup(x => x.CreateAndBindViewForModel(model)).Returns(window);
this.windowManager.CreateWindow(model, false);
((IChildDelegate)model.Parent).CloseItem(new object());
}
@ -244,7 +232,7 @@ namespace StyletUnitTests
{
var model = new Mock<IScreen>();
var window = new MyWindow();
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
this.viewManager.Setup(x => x.CreateAndBindViewForModel(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 +246,7 @@ namespace StyletUnitTests
{
var model = new Mock<IScreen>();
var window = new MyWindow();
this.viewManager.Setup(x => x.CreateAndSetupViewForModel(model.Object)).Returns(window);
this.viewManager.Setup(x => x.CreateAndBindViewForModel(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);