Friday, January 07, 2011 2:17 PM
I'm having an issue whereby my ViewModels need to spinup new views, and instead of having ViewModels know explicitly about View classes, I'm using an interfaced helper/service class to handle this creation.
I posted this over on MvvmLight on CodePlex & StackOverflow as well.
I'm using MvvmLight's EventToCommand & base classes for views and viewmodels to handle the Loaded event, and PassEventArgsToCommand, but it appears that what I'm trying to do is impossible: RoutedEventArgs.OriginalSource is null even when wiring up a code-behind EventHandler, and I see no way to get the sender, unless I wire it manually. (I had assumed that OriginalSource was going to be the sender. fail)
It may be that I should be going about this a different way -- I'm just not sure what I should be doing instead.
My application is a windowed environment. I'm using a modeless ChildWindow control and a special WindowHost into which all windows are added (the WindowHost also provides a taskbar).
When a user performs an action that should open a window, the ViewModel Command handling that action uses a helper class through which all window creations are funneled. (i.e. - IWindowCommander.Show())
The problem with this is that the helper class needs to know about the WindowHost control on the MainPage, so it can add the ChildWindow to it.
How can I solve this problem without hard references between view & viewmodel, and without one-off code in the codebehind?
My current architecture uses MEF: MainPage imports MainPageViewModel, which imports(ImportingConstructor) my DataProviderService (helper class) and IWindowCommander. My interim solution (sloppy) is to tightly-couple the View and ViewModel (the import on MainPage, for instance, sets DataContext as MainPageViewModel, instead of object, and then explicitly sets IWindowCommander.WindowHost = ((MainPage)this).WindowHostElement). Another option is to remove this tight coupling and have IWindowCommander register to recieve an MvvmLight message, and have MainPage send it in the loaded eventhandler, but I'm trying to avoid having to rely on weak-referenced messages for something that HAS to be there for the app to work.
Friday, January 07, 2011 2:31 PM
Not to steer you away from MVVM Light as it is a fantastic framework, but have you looked at Caliburn.Micro? This is a viewmodel-first framework explicitly designed around the concept of the VMs triggering the views. It's a small code base, easy to go through, and might give you some ideas about how the problem has been solved.
Friday, January 07, 2011 2:51 PM
I've not looked at the Caliburn framework very thoroughly.
I was actually going to take another look at Jounce to see how you dealt with notifying the viewmodels of view 'activation' (i think that's what you called it). Though I feel like I did this before, and I seem to remember it was hooked pretty heavily into the Navigation framework, which might not work very well with this implementation.
Thanks for the tip, though -- and I'll check back after taking a look at Micro.
Friday, January 07, 2011 3:05 PM
Have you heard of Catel? I just stumbled across it yesterday.
SO MANY FRAMEWORKS! arrgh lol.
Friday, January 07, 2011 3:26 PM
So Catel is WPF-only. Jounce is Silverlight-only (should be easy enough to port to WPF, I just haven't had need nor demand).
Jounce does not require the navigation framework and in fact discourages it's use unless you really need the URL routing. The Jounce navigation is similar to the "view service" Laurent Bugnion recently posted about - you simply raise an event for a view to become active.
The only reason I didn't suggest Jounce is because Jounce isn't view model first. Here's how it happens in Jounce:
1. A view navigation event is fired, indicating a view is needed
2. The routing tables are inspected and if a view model is routed for the view, they are bound
3. If this is the first time the view model is being bound, a delegate on the view model is set to enable the view model to set view states on the view (i.e. GoToVisualState("SomeState",true)) - this keeps the view completely decoupled but allows the view model to fire transitions/etc by understand states. The Initialize method is also called on the View Model to let it know it is being used for the first time.
4. Next, regardless of step 3, the Activate method on the view model is called so it is aware that the view has been requested
5. On the flipside, a deactivation event can be raised to indicate the view is going away, in this case Deactivate is called on the view model
So, let's say I wanted to cause a view to fade in/out. On my view I can create a visual state for "ShowState" and "HideState." On my view model, in the Activate() method I can call GoToVisualState with ShowState and in the Deactivate() method I can call GoToVisualState with HideState. I can still unit test by replacing the delegate on the view model with a mock that checks that the right state transition was requested. The view model doesn't understand the view, only that it needs to transition to particular states at particulare times, and then the VSM handles that however you want it to.
Friday, January 07, 2011 3:37 PM
ah. all clear now.
(also, Catel says it's WPF & Silverlight, but I don't know how deep that compatibility runs: just that it says so in the title)
Friday, January 14, 2011 9:08 AM
As developer of Catel, I can say that it also supports Silverlight. There is a SL demo app (it's not much, still working on it) inside the solution. As soon as we have finished writing the 5th and last article about Catel for WPF, we will start writing articles for SL as well.
The good thing about Catel is that it's not a "one-man-show", but there is a development team of 4 developers behind it.
Friday, January 14, 2011 9:22 AM
Thanks for the clarification. Looks quite interesting and will be watching it develop!