How to use MainWindow as ShellViewModel View? - wpf

I understand that by default CM will look for ShellView in Views folder to use as ShellViewModel View but I want to use the MainWindow instead... can this be done and how?

How it Works
CM uses a set of View/ViewModel Naming Conventions, generally speaking, if you have a ViewModel named FooViewModel CM will attempt to locate a type with a similar name of FooView or FooPage.
What if you really want "MainWindow" and "ShellViewModel"?
If you just wanted to use an existing "MainWindow" with an existing 'root viewmodel' then consider subclassing Bootstrapper<TRootModel> and override OnStartUp. This is a prescribed method, but can seem daunting.
(I have not tested this code.)
protected override void OnStartup(object sender, StartupEventArgs e)
{
var rootModel = IoC.Get<TRootModel>();
var rootView = new MainWindow();
ViewModelBinder.Bind(rootModel, rootView, this);
rootView.Show();
}
The above method, of course, would only apply to the initial view for the root view model shown during start-up. Future attempts to display a view for ShellViewModel may work, or they may result in errors, I am not certain.
Extending Conventions
There are a few ways to customize the convention itself. The most flexible and direct method is to intercept/hook Caliburn.Micro.ViewLocator.LocateForModelType, this allows you to modify the behavior/strategy applied during view location.
private static void CustomViewLocatorStrategy()
{
// store original implementation so we can fall back to it as necessary
var originalLocatorStrategy = Caliburn.Micro.ViewLocator.LocateForModelType;
// intercept ViewLocator.LocateForModelType requests and apply custom mappings
Caliburn.Micro.ViewLocator.LocateForModelType = (modelType, displayLocation, context) =>
{
// implement your custom logic
if (modelType == typeof(ShellViewModel))
{
return new MainWindow();
}
// fall back on original locator
return originalLocatorStrategy(modelType, displayLocation, context);
};
}
The above can be called from inside a Bootstrapper<TRootModel>.Configure override:
protected override void Configure()
{
CustomViewLocatorStrategy();
base.Configure();
}
This method is more likely to play well with CM (in terms of any view caching, namely.) However, it still breaks conventions, and it's still a fair amount of code.
Registering Additional Suffixes?
One thing I want to point out, but have not had a chance to play with, is ViewLocator.RegisterViewSuffix implementation. I believe if you executed ViewLocator.RegisterViewSuffix(#"Window") then you could rely on CM to map MainViewModel to MainWindow.
This would allow for more expressive suffixes (such as Window, Dialog, Form, or others you may want to use.) Personally I dislike the use of 'View' as a suffix, I believe it's too generic (after all, they are all Views.)

Caliburn.Micro doesn't look for ShellView by default, this is how things work. Let's say you have a bootstrapper defined like this:
class MyBootsrtapper : Bootstrapper<MyViewModel> { }
Then CM (Caliburn.Micro) will look for a view named MyView. So yes you can use MainWindow instead as long as your view model name is MainWindowViewModel.
I have answered the other question you have asked and it seems you don't fully comprehend CM so i really really advise you to Start Here and you can always check the projects Documentation on codeplex because it contains all updated information and documentation.
Edit:
Caliburn.Micro uses a simple naming convention to locate Views for
ViewModels. Essentially, it takes the FullName and removes “Model”
from it. So, given MyApp.ViewModels.MyViewModel, it would look for
MyApp.Views.MyView.
Taken from official documentation here.

Related

Correct way to handle commands that rely on multiple view models

I'm relatively new to WPF and MVVM and i am trying to understand how to use commands correctly when they have dependencies in more than 1 view model.
A couple of examples:
In my current application i have a RelayCommand which causes a save action to occur in a couple of different view models (they write a couple of different files). Currently i am handling this using a the mvvmlight messenger to send a message to those view models to get them to do the save which i think is the correct way to do it as it avoids having to provide some kind of delegate or event to/on those view models.
I have a RelayCommand in a view model that has a CanExecute method which relies on the state of 2 other view models. I've currently handled this via the mvvmlight messenger as well by having changes in the view models the CanExecute method depends on message that their state is now valid for the operation. This seems messy but the only alternative i could think of was to use a delegate or event effectively weaving the view models together which i believe i should be avoiding.
Is there some generally accepted way to deal with this which i am missing?
In general your view model layer should have a 1:1 relationship with your view, there should be no good reason for a "Save" function to exist in a view model which is then called by another view model.
What it sounds like you should be doing is putting that logic into a service i.e. something like this:
public interface ISerializationService
{
void Save(SomeData data);
}
Then you need an implementation for this service that does the actual work:
public class SerializationService : ISerializationService
{
void Save(SomeData data)
{
// actual save happens here
}
}
Your view models should then contain properties that point to instances of these services:
public class MyViewModel : ViewModelBase
{
[Inject]
public ISerializationService SerializationService { get; set; }
// called when the user clicks a button or something
private void ButtonClickCommand()
{
this.SerializationService.Save(this.SomeData);
}
}
The only question remaining is "What sets the value of SerializationService?", and for that you need a dependency injection framework. There are plenty out there, MVVMLight installs one itself, but Ninject is the de-facto standard. When implemented properly the injection framework will create all view models for you and then "inject" the dependencies, i.e. your SerializationService property, of type ISerializationService, will be initialized with an instance of your SerializationService class (which in a case like this will also be configured to be a singleton).
Dependency Injection takes a bit of work to get your head around but once you start using it you'll never look back. It facilitates complete separation-of-concerns whilst alleviating the need to pass pointers to everything all up and down your architectural hierarchy.

Avoid Prism AutoWireViewModel Creating the ViewModel Twice

Prism can create an unneeded ViewModel if you have a one-arg constructor on your view. I am trying to understand how this can be avoided, or, if I can design something to work differently. Here is what happens.
The XAML view declares ViewModelLocator.AutoWireViewModel:
mvvm:ViewModelLocator.AutoWireViewModel="True"
And the class declares two constructors:
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
public MainWindow(MainWindowViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
}
There is a reason that I declare the one-arg constructor: it is because the ViewModel is serializable; and when deserialized, the view is constructed by explicitly invoking that constructor with the restored ViewModel. But the issue can happen in two ways.
First, when you invoke the one-arg constructor:
MainWindowViewModel viewModel = new MainWindowViewModel();
MainWindow window = new MainWindow(viewModel);
Then Prism constructs the view, and that invokes the ViewModelLocator from the XAML, which creates and sets a ViewModel ... And then your explicit argument is set; replacing the auto-created instance (or if you reverse the lines in the constructor, than your explicit argument is actually wiped off).
AND, perhaps unexpectedly, or perhaps by some folly in my understanding, or some other unknown design aspect, it ALSO will happen if you resolve the view from the Container --- you might do this expecting to invoke the default constructor for the view; but that in fact does not happen; and, once again you will create two ViewModels:
MainWindow window = Container.Resolve<MainWindow>();
This line of code actually begins by discovering the one-arg constructor on the View, and then RESOLVING a ViewModel and invoking that constructor ... which again triggers the XAML auto-ViewModel; and then your one-arg constructor continues to wipe off the auto-ViewModel ...
It's consuming resources; and in fact, I tripped an exception where the view was binding based on some other state that was inconsistent with the ViewModel that I was expecting to explicitly be set.
I can't see a way to defeat the auto-created instance, and so I am not seeing how to invoke a one-arg constructor around the AutoWireViewModel behavior; or, how to resolve the view from the Container and avoid two ViewModels being created.
Perhaps resolving the View from the Container is abuse if it hasn't been registered, but the one-arg constructor seems to be reasonable, and it creates two instances ...
Is there some way? [Perhaps you can customize that behavior to check for an existing DataContext and then not set it if present ... or something along those lines?]
I created a simple example on GitHub:
https://github.com/steevcoco/PrismAutoCreatesViewModelTwice
To be clear here, Prism isn't creating the ViewModel twice. You are. You do it once in the code behind with the ctor, and you do it again with the ViewModelLocator. Pick one approach and use it. There is no need to have two different ways of setting the VM on the same View.
You should read up on containers and how they work. That will help your understanding of what is going on here. Every thing is working exactly as it should.
Well, for what it's worth, I have actually implemented a custom ViewModelLocator that works around this behavior. It is somewhat crude: it simply first checks if the current DataContext is non-null; and if so, the AutoWire will not create or set a Viewmodel.
I have updated the code in the repository for anyone interested.

A Controller for MVVM

I'm working on a WPF project that's a mishmash of code-behind xaml/xaml.cs and a few not-quite ViewModels as well.
(Disclaimer: Until recently I've had very little in the way of WPF experience. I can design and lay-out a Window or UserControl fairly proficiently, and I think I get the hang of separating an MVVM ViewModel from the View and doing binding wire-ups, but that's the limit of my experience with WPF at present.)
I've been tasked with adding some new features to the program, such that it looks like converting it to use MVVM properly first is going to be necessary.
I'll demonstrate a specific problem I'm facing:
There is a View called SettingsWindow.xaml that I'm working with. It's a set of textboxes, labels and whatnot. I've stripped-out all of the View data into a ViewModel class which resembles something like this:
class SettingsViewModel : ViewModelBase {
private String _outputDirectory;
public String OutputDirectory {
get { return _outputDirectory; }
set { SetValue( () => this.OutputDirectory, ref _outputDirectory, value) ); }
}
// `SetValue` calls `PropertyChanged` and does other common-tasks.
// Repeat for other properties, like "Int32 Timeout" and "Color FontColor"
}
In the original ViewModel class there were 2 methods: ReadFromRegistry and SaveToRegistry. The ReadFromRegistry method was called by the ViewModel's constructor, and the SaveToRegistry method was called by MainWindow.xaml.cs's code-behind like so:
private void Settings_Click(Object sender, RoutedEventArgs e) {
SettingsViewModel model = new SettingsViewModel(); // loads from registry via constructor
SettingsWindow window = new SettingsWindow();
window.Owner = this;
window.DataContext = model;
if( dialog.ShowDialog() == true ) {
model.SaveToRegistry();
}
}
...but this seems wrong to me. I thought a ViewModel should consist only of an observable data bag for binding purposes, it should not be responsible for self-population or persistence, which is the responsibility of the controller or some other orchestrator.
I've done a few days' worth of reading about MVVM, and none of the articles I've read mention a controller or where the logic for opening child-windows or saving state should go. I've seen some articles that do put that code in the ViewModels, others continue to use code-behind for this, others abstract away everything and use IService-based solutions, which is OTT for me.
Given this is a conversion project where I'll convert each Window/View individually over-time I can't really overhaul it, but where can I go from here? What does a Controller in MVVM look-like, exactly? (My apologies for the vague terminology, it's 3am :) ).
My aim with the refactoring is to separate concerns; testability is not an objective nor would it be implemented.
I personally disagree with putting much in my ViewModels beyond the stuff that is pertinent to the View (it is, after all, a model of a View!)
So I use a Controller paradigm whereby when the View tells the ViewModel to perform some action (via a Command usually) and the ViewModel uses a Command class to perfrom actions, such as saving the data, instantiating new View/Viewmodel pairs etc.
I also actually separate my ViewModel and ViewData (the ViewModel 'contains' the ViewData) so the ViewData is puirely dealing with the data, the ViewModel with some logic and command handling etc.
I wrote about it here
What you need is called Commanding in WPF.
Basically you bind Button.Command to a ICommand property in your ViewModel and when Button is clicked you get a notification in ViewModel without using code behind and casing DataContext or whathever hacks you tried.
http://msdn.microsoft.com/en-us/library/ms752308.aspx

Custom property injection in Castle Windsor for Windows Forms

I have a Windows Forms application which makes use of an MVP pattern. Each view is a WinForms user control and is backed by a presenter which handles non-UI concerns. The application makes use of Castle Windsor, and all views presenters and many other components are resolved via the Windsor Container.
What I would like to be able to do is customise property injection for the user control views. My views don't make a whole lot of use of property injection, but it is occasionally very useful and works well. The problem is, my user controls often contain nested controls, which in turn can contain other nested controls, and property injection is not going to work for these nested controls, because they were not directly resolved via the container.
What I would like to do is to configure property injection for components that inherit from the SWF Control class. In addition to finding properties on the component, I would like to also find properties on nested controls (in the Controls) collection and inject into these nested properties as well.
I know that Castle Windsor is extremely flexible and configurable so this may be possible. I need a bit of a nudge in the right direction though. Is this possible? Has anyone tried to do something similar?
If I have understood your question correctly I think that the only way to achieve what you want is by some sort of poor man's dependency injection because the way the winforms designer generates a method that constructs the various sub-controls you speak of makes it decidedly uncondusive to IoC.
I am not sure you will be able to do property injection but you can utilise the constructor, here is a hair-brained scheme I have just concocted ...
Firstly, create some way to access your windsor container - something like this would probably do the trick:
public static class MyContainer
{
private static readonly IWindsorContainer _container = Bootstrap();
// I did it like this so that there is no explicit dependency
// on Windsor - this would be the only place you need to change
// if you want an alternate container (how much you care about
// that is up to you)
public static T Resolve<T>()
{
return _container.Resolve<T>();
}
private static IWindsorContainer Bootstrap()
{
var container = new WindsorContainer();
container.Install(FromAssembly.This());
// ... whatever else you need to do here
return container;
}
}
Secondly, in the inner controls, where you want some properties injected do something like this (I went for the good ol' ILogger as an example of something you may want injected):
public partial class MyFancyControl : UserControl
{
// Constructor to keep the winforms designer happy
public MyFancyControl()
: this (MyContainer.Resolve<ILogger>())
{
// Intentionally always blank as this
// is an un-unit-testable method
}
// Constructor that does the actual work
public MyFancyControl(ILogger logger)
{
InitializeComponent();
Logger = logger;
}
public ILogger Logger { get; private set; }
}
Note: using the logger raises one of the couple of obvious smells in this - sometimes you don't register such a component with the container at all (usually you have a null logger) so you may need to hook up some sort of mechanism for that but I leave that up to you if you need it or not.

WPF unit testing, UIElement.RaiseEvent, ArgumentException

I am trying to build out a unit test and would like to fake out a DropEvent.
I get an error as it is attempting to raise the event. I can't create a DragEventArgs is it protection level is internal.
System.ArgumentException : Object of type 'System.Windows.RoutedEventArgs' cannot be converted to type 'System.Windows.DragEventArgs'
I have the following...
[Test]
public void Test()
{
DesignerMock view = new DesignerMock();
_viewModel = (PanelDesignViewModel)view.DataContext;
view.Show();
Assert.IsNotNull(_viewModel);
RoutedEventArgs args = new RoutedEventArgs(DesignerCanvas.DropEvent,view.DesignerCanvas) ;
view.DesignerCanvas.RaiseEvent(args);
view.Close();
}
As ascalonx says, you shouldn't test your View, only your ViewModel. This means your ViewModel needs to be decoupled from the View. Instead of an event handler, expose a simple public method on the ViewModel that will do the work you need, and simply call this in your test. To make your View call this method, use an attached behavior. Caliburn has a nice "Actions" attached behavior to do just this.
You mention you also have some VisualTreeHelper stuff "going on". This is a violation of the M-V-VM pattern, as it couples your VM to a specific V implementation. What ever your doing there, abstract it out to a "service" interface and use IoC or Service Locator to provide this service to your VM. I might suggest looking at Onyx for this (disclaimer: I'm the author of this library). It provides a flexible and easy to use mechanism for providing services to your VM.
If you're adamant that you want to test your code in this way, you could use reflections to create a DragEventArgs. However, the real question, I think, is if you're going in the right direction with your unit testing. I usually avoid doing any testing directly on my Views and stick with testing the more easily testable viewmodels (if indeed you're using MVVM) because of exactly the issue you're having right now.

Resources