Cancelable Navigation Events

Mar 22, 2012 at 2:24 PM
Edited Mar 22, 2012 at 2:37 PM

A fairly common scenario for LOB apps is that the user tries to initiate some form of navigation while they've got unsaved data on a screen.  At this point I'd like to present the user with an "Are you sure you want to leave?" prompt.  

Any recommendations for handling this with Jounce?   I love that navigation is just handled with events but I'm not (so far) seeing a way I can handle one and mark it as cancelled so that the region logic never fires.   It looks like I'd need something in the ViewRouter.   Unfortunately, if I'm correct, I don't know that I could even write my own version and export it because then MEF would choke at having two IFluentViewXapRouter exports...   Would need to build Jounce entirely from source which I prefer not to do if possible.   Could be wrong there though...

I'm thinking something like the ViewRouter raising a BeforeNavigate event and then checking a Cancelled property on the event args at which point it can just return if Cancelled is true.   I'd want this BeforeNavigate event to either directly use the ViewNavigationArgs or contain it so that where I choose to handle this event I could save the original navigation request and re-issue that event after the user responds to the prompt dialog.

Mar 22, 2012 at 2:50 PM

I always implement this with a simple dirty flag - for example, you can latch into the commited using the entity model and track that flag. Any commands for navigation first go through a generic dialog that asks the active view model if it's dirty and displays a dialog. If the dialog cancels, nothing happens, otherwise the dialog fires the navigation event. I don't think the click on a button drives navigation. That's just a command. Instead of firing and canceling, instead review and only fire it if it is a valid request.

Mar 22, 2012 at 3:03 PM

That makes sense if we're only talking about navigation initiated on the page that's dirty.  Totally agree; the view model can handle button clicks (and enabled state) and make the call about when to issue a navigation event.

However, what I'm talking about is the user clicking somewhere else (e.g. some global navigation element backed by a different view model than the one with a dirty entity like moving to a different module, etc.).   Assuming there's a NavigtionTrigger there it's just going to raise a ViewNavigation event directly without consulting any view model.   Are you saying in that case I wouldn't want to use a XAML Navigation Trigger but route ALL user navigation interactions through some kind of NavigationController that finds the active view model (how can I do that btw?) and checks for a dirty flag?

I guess that could work as well if that's what you're suggesting but I may have misunderstood.


BTW, really appreciate all the work on Jounce!  

Mar 22, 2012 at 3:08 PM

Yeah - I'll get this application up. It's just that I developed it while I was building Jounce 2 so waiting to carve some time to get it in parity, but it has the example. Basically I create an IApplicationContext object that can be imported anywhere and view models can set the dirty flag there. In the implementation I automatically intercept the browser in JavaScript to prevent users hitting the physical back button or navigating to another URL, while in OOB it does a dialog instead. The view models simply set that. Any generic navigation component can then query that property without knowledge of the view models (the view models automatically clear/set it as they go about their business) and prompt as needed. It's usually the navigation view model that imports the interface and queries the property.

Mar 22, 2012 at 3:28 PM

OK...   I think I'm following you now...   Let me know if not so this thread doesn't end with misinformation :-)

Any view model can import a shared context object and mark it with a dirty flag.   Any navigation object can reference that shared dirty flag to prevent navigation.  

If you want to still use a simple XAML navigation trigger you'd probably bind IsEnabled to this global dirty flag.  If you wanted to raise a prompt dialog, you'd ditch the simple XAML navigation trigger (because it's ignorant of the dirty flag) and bind that button to a navigation view model that can check the dirty flag and prompt if necessary, otherwise it raises the navigation event.

Nice touch intercepting the JS back button as well!

Mar 26, 2012 at 9:44 PM

No worries. The link to the reference example explained in a separate discussion in case you missed it.