This project is read-only.

Closing the current view when using different regions.

Developer
Sep 27, 2010 at 2:25 AM

I'm building a CSLA+Silverlight solution using BXF and I'm a bit stuck on how to close the current view when I have a solution that utilises different regions.

I know that the samples talk about the following code which is basically sending in NULL for the view parameter:

Shell.Instance.ShowView( nullRegions.MainContent );

and that works pretty well in most simple scenarios.

My scenario is a bit larger and has more than one "Content Regions" (in addition to also using a ChildWindow for "Model Popup" user interactions). I have the above logic in a method on my ViewModel (and I'm using a TriggerAction to connect a "close" / "ok" / "cancel" button on the View, to this method on the ViewModel).

The trouble I run into is that when the method that calls the above code, it will always and only apply to the "MainContent" (I guess that in itself is the issue - the ViewModel has knowledge of the Regions available to the presenter and in this case is tightly coupled to it). This causes me to "empty" the same region which may or may not have had the relevant view in it.

How could the design be altered to enable a scenario where in the ViewModel I can close the "current" view - or at least the view from which the method call is being made because I might just be that I have muliple views in different regions connected to the same viewmodel in which case the "close/ok/cancel/accept" method to close the view should only do it to one?

Is this perhaps something that might form part of the BXF framework itself?

- Jaans

Developer
Sep 27, 2010 at 3:02 AM

I realised from my question, I'm mixing presentation/navigation logic into the view and/or viewmodel.

I figured that if I instead move this kind of logic to MainView/MainPage and the corresponding view model and presenter I'm able to achieve what I need.
Basically the view/viewmodel won't have any knowledge of the region, and most importantly the ViewModel should not need to know anything about the View instance consuming it, nor get involved in with the Shell's implementation.

Am I going down the right track?

An aside question, has anyone thought about implementing a navigation history / view nesting concept with BXF?

Nov 10, 2010 at 9:22 PM

This is right where I am, too.

I am using tab controls and I want to have a singleton approach on business objects.  That is, if you have already displayed a specific existing employee on Tab 3, then when you try to open him again from a list on another tab, the view doesn't create another tab, it simply switches to Tab 3 (or expands a collapsed docking window).  If he's not in a view model, a new tab item is opened up with his view model in it.

From the looks of it, that logic has to go in the main page presenter, seeing how the request to create - or switch to - that tab could come from anywhere, right?  It just seems like I wouldn't want to create a view model just to throw it out before I presented it.

And this is where the "close" issue comes in, probably.  You close a tab and that instance of that view model (and the page it's on) have to go away and that has to be kept track of.  Is this correct?

Are you suggesting that you moved the decision on which region the control goes in to the presenter, and you just don't pass the "region" argument?

I hadn't even thought of different parts of the same model in different regions.  I did consider that a single instance of business object might also appear in different view models in different regions  This could be a expander editable view model that occurs in a list while a more traditional entry form would also be available for a different entry mode in a docked window.  Since the instance lives in the view model, and there are multiple view models, there's no sharing an instance.  I've seen apps where an instance is shown in two windows and as you edit a field in one, it updates in the other.

It seems like this last example would just have to be controlled and prevent the users from doing it.  Otherwise, you'd have to keep a global collection of all your dirty objects and tie all that to data binding on top.  Yuk.

 

Nov 10, 2010 at 10:27 PM

OK, well that turned out to be harder than I thought.

Developer
Nov 11, 2010 at 12:07 AM

I must admit I never really got my original scnario resolved cleanly. My main issue was to be able to close a "modal dialog" and I cheated a bit by using a code-behind to the view (not viewmodel) to set the diaglog result. This is obviously a solution limited to ChildWindow scenarios and not suitable for the same kind of dialog that is based on a user control (like the standard view implementation).

It would appear that you need to have the instance of the IView available (and/or likely the region also), in order to "close" it from the ViewModel as per the suggested implementation. Perhaps something like adding string Region to the IView interface and have the ViewFactory set the region it's been opened in. I just don't know if that's the best way and if it should be done from a completely different angle altogether - just feels like I'm breaking the goals of separating View from ViewLogic if I do that.

I suspect that the sample implementation for BXF is perhaps over-simplistic for that particular usage scenario. Unfortunately I'm not that strong with M-V-VM and BXF was my first foray into it. BXF's goal is to be the simplist example of an M-V-VM implemantation possible to make the concepts of it clear, and for me anyways, it has done exactly that - I've learned so much from it and have difficulty to think of a world without it. I love it and want to go further with it.

Having said that, I still feel there are a few (hopefully) small shortcomings / wishlist:

  • Guidance on how to implement multiple regions in line with the design pattern, such that managing view remain practical (e.g. How a view can have a button and trigger action to close itself, no matter which region it's been opened in.).
  • The current implementation doesn't work with ChildWindow controls, only UserControls for views. This isn't nessecarily an issue as you can wrap your user control in a ChildWindow, but it's a bit cumbersome to make work (this is the bits where the View gets the ViewModel instance applied).
  • Guidance on how to implement navigation support to enable forward / back / browser url integration.
  • Not sure how I can get the OnNewUser event from the Presenter to notify my view - thus allowing the view to respond accordingly. (Not a biggie for me though)
  • I personally (your mileage may vary) don't like that the ShowError / OnShowError implemention uses two strings (Title & Message) because I need more informaiton about the exception to make the handler for the event more intelligible.

    I've created a separate Bxf.Extensions project (internally) that inherits and extends BXF to create a ShowException / OnShowException variation that uses a string and an exception class (Title, Exception) and I rather use that implementation instead. Would've been nice if it was in BXF instead. Now I can show a much more intelligible message to the user and still have a button for technical information available where the "root cause" isn't clear.
  • Extensibility to allow a developer to hook into the existing view creation and initialisation - for example to set additional information on a view during the view initialisation and the setting of the viewmodel onto the view. Perhaps this is extensible already (I'm thinking ViewFactory), but some guidance on it would be very helpful.

It might be worth looking at some other implementations of M-V-VM so garner some understanding at how to look at "current"/"active" view management and the like. I do fear dirtying the clean and precise waters that is BXF with it.

Rocky, I know you are a busy man, do you have some insights to offer here. I just feel I'm out of my depth and some good guidance would be useful.

Nov 11, 2010 at 12:55 AM

Wow.  How incredibly generous of you to author such an articulate response to my post.  Thank you very much.

I took a quick look at Prism and some other frameworks and felt like I was drowning.  Maybe this is a progressive effort and I should look at it like that.  Unfortunately, the customers won't see it that way. :)

Have you found another framework instead or have you just forced your solutions into BXF and wish it was a better fit?

Rocky typically makes more sense than any given 1/2 dozen "experts" and I have never gone wrong following his lead.  I really want to use BXF, but the world's expectation of high production values means the user interface has to shine, and BXF will be the glue.  I was even planning on investing in some Telerik controls to make it look more polished and take the slack out of the time frame.

I'm going to continue studying it to see where it leads for now.

Thanks again.

Developer
Nov 11, 2010 at 1:38 AM
Edited Nov 11, 2010 at 1:40 AM

Not sure I helped much :-/

I agree, I too find Rocky's reasoning to resonate with me, especially at the larger scale of architecture and design goals.

I've been meaning to glance at the "MVVM Light Toolkit" to see what works well there also.
http://mvvmlight.codeplex.com/
http://www.galasoft.ch/mvvm/getstarted/
Ps: One bit that I don't like about this one is the dependency on Blend's Trigger Interactions and so forth. Rocky really got it right by creating a standalone TriggerAction the way he did.

Sorry... the above is proably very un-BXF of me and I truely feel guilty... just dont jump ship OK ;-)

There are some nice shows on Channel9's Silverlight TV hosted by John Papa: http://channel9.msdn.com/Shows/SilverlightTV
Have a look if you like - there are shows about M-V-VM specifically the "MVVM Light Tookit" from Laurent Bugnion (Show 13) and also Prism (Show 37)

Jaans

Coordinator
Nov 11, 2010 at 2:17 AM

I think you guys are onto something - and it has been a problem for me as well.

It seems reasonable to think that the viewmodel calling ShowView shouldn't know where the new content should show up. In other words it shouldn't provide the target region.

Of course this means that something else must exist that knows what views should appear in what regions.

So what is this "something else"? It seems like we have a responsibility here, but no type to implement the responsibility.

This isn't something a view should know, nor a viewmodel. You could code some view<->region mapping code into the main presenter, but that's not right either.

So I suspect there really is the need for something else here.

Also, a potentially related idea came from a recent project - the idea being dynamic discovery and description of views. Like a ViewInfo type or something.

Basically, suppose you want to create a menu based on the views available to you. How do you do that? And where do you get things like the menu text, the type of the view, the type of the viewmodel, etc?

Our train of thought was that it would be beneficial to have a ViewInfo type that could be discovered by MEF (or similar). This type would include the label of the view (menu text), the menu/submenu name where it should appear, a description (tooltip text), the type of the view and the type of the viewmodel.

Potentially this ViewInfo type could also include the region where the view should be displayed when shown.

Going down this road, Shell.ShowView would probably act on a ViewInfo, since the ViewInfo would have all the necessary information (except perhaps the ability to correctly initialize a viewmodel).

We didn't implement this, just thought through the concept, so it may or may not work. I wonder if it would help address the issue you guys brought up in this thread?

Developer
Nov 11, 2010 at 3:38 AM

Thanks for your input Rocky - much appreciated. As always, very insightful.

I like the idea of a ViewInfo, and on the face of it, helps to increase the separation of area's of concern (ie. the View and the View Metadata).

Here are some intial thoughs...

  • One aspect I like about the ViewInfo is that it could be derived / override to add/change behavoir - more flexible.
  • +1 for metadata like menu/title text, menu position, viewtype, modeltype, etc.
  • If we for example define the "region" with the ViewInfo, then consider the scenario where the same view is potentially shown in another "region" (not sure how realistic this is, because my scenarios actualy have a different view (using the same viewmodel) in the second region (kind of like a summary / overview / dashboard section) with the "main region" showing some detail aspect. We also offer the user the ability to change the layout of the regions (bit edge case for M-V-VM?)
    • Did you imagine that the "Region" for a ViewInfo is static (ie. read-only) or would we want to be able to set/change the "Region" in the ViewInfo. Thinking about it as I write this, to me it feels then that it is technically a different view info? Is ViewInfo more about the View or is it more about how/where to present the View (if there's a difference)?
  • Along the lines of the original discussion, lets say you are showing a view (populated by it's ViewModel) and on that view you would like to "close" it.
    • Current BXF sample/pattern is to have the trigger call a method on the ViewModel that in turn calls Shell.ShowView(null, "MainRegion")
      This is problematic in the sense that it assumes that the view was actually shown on "MainRegion", but this is not known. It may potentially be shown elsewhere.
    • Not sure about this, but would the View / ViewModel somehow know about the ViewInfo? If the View does know, it could pass it as a parameter to the VM, but am I wrong in feeling this isn't the right place for it.
    • Perhaps this is altogether something irrelevant to the View/ViewModel in that there is some sort of "View Container" that knows where it is (Region), and what it's showing (View/ViewInfo). Is the closing of a view really a concern for that of a view/viewmodel?

      Maybe it's the "View Container" responsibility to close it's content, but exposes some event to let the view know (if it wanted) that this has happened so that it could "trigger action" to a VM method to for example Save(), perhaps event provide a "Cancel" to prevent the View closing incase there are unsaved changes?

      In lieu of a "View Container" which currently is currently a ContentControl on the Shell, could take a different tack and consider having a ViewBase that helps define this information and behaviour. The Shell.ShowView still accepts a UserControl but I might also accept a class deriving from ViewBase to provide added behaviour. Or in the case where we pass a ViewInfo to Shell.ShowView, the ViewType would point to a class deriving from ViewBase?

One scenario of what I'm trying to do?
The UI usage scenario I'm thinking about is one where I could have a "List" items of say customers, and then clicking on "Update" for one of those customers I would like to be able to:

  • Show a modal dialog window in some cases (ChildWindow)
  • Show a modeless window in other cases (User Control) in another region maybe

Eitherway, that dialog (what I like to call a "Card") typically has a OK / Cancel / Apply interface that when OK is selected it needs to trigger the Saving on the ViewModel but also dismiss the "card" (aka close the view).

With code-behind-the-view I could just set the DialogResult for the ChildWindow. In the case of a user control being shown in a region (typical scenario), you would Shell.ShowView(), but would need to know which region it's been in.

More questions than answers I guess - I'm a bit out of my depth I think.

Nov 12, 2010 at 12:36 AM

OK guys, I am *really* out of my league.  But sometimes, while the vastly uninformed have no answers, they ask pretty good questions.  At least I hope.

As I see it now, in the SLDemo app in the video, the MainMenuViewModel is the guy who makes the association between the view (EditEmployee.xaml) and view model (EmployeeViewModel.cs) and the region to show it in.

So already there is already some "other thing" managing what goes where, right?

It seems to me the intelligence to create the right ViewModel in the right region has to be centralized and has to be shared (static).  And a menu is perfect for the example code, but a more general approach might be useful in practice.

In that case, couldn't any control (menu or whatever) that has the need to launch a view simply ask the "ViewInfo" to handle it?

The requests to create ViewModels would all end up at the same place and the singleton (or not) issue would be solved there.

And with respect to the question of a user moving different viewModels to different regions using drag and drop; you bet.  That's exactly the behavior they'll want.  And doesn't that make sense?  Take work and park it in a docking window or a tab in a notebook.  I'd want that - and I'd want it to remember the next time I used the app.

And so long as I'm dreaming in public...

If this ViewInfo object somehow had DependencyObject behavior, would that make it possible to bind controls in, say, a RibbonControl, to whatever is happening in any of the views?

I may be off on this one, but I can see a RibbonControl with buttons that are enabled/disabled based on the attributes of the selected row of a datagrid.  Maybe that's already possible and I just haven't figured it out yet.

I don't think there's any way for me to build my app using BXF unless I address at least a couple of these issues.  So I'm going to have to work this somehow.

I looked briefly at MVVMLight.  I noticed they had a ViewModelLocator which sounds (I haven't looked, yet) a lot like what we're talking about.