26
May
11

Model-first navigation in WP7 with Caliburn.Micro

I just saw Rob’s recent UriBuilder addition in Caliburn.Micro source; it really simplifies the configuration of the View Model of the page to open through the use of a fluent-style API:

navigationService.UriFor<PageTwoViewModel>()
 .WithParam(x => x.NumberOfTabs, 5)
 .Navigate();

Also, it brings WP7 navigation stuff (which is strongly page oriented) a step closer to a model-first approach.

I was recently struggling with an attempt to improve this very area.

My goal, however, was to navigate to a real View Model instance, with the aim to get exactly that instance bound to the new page.

The idea I had is very simple (apparently, even too simple, which makes me think that it could eventually have some problems on the long run):

  • in the calling VM, create and set up an instance of the destination VM;
  • put the VM instance into a slot of the Phone Service state;
  • navigate to a generic “hosting” page (HostPage);
  • HostPage has a corresponding HostPageViewModel based on Conductor<> class; CM already takes care of binding it to the new page when the navigation is completed.On initialization, the HostPageViewModel just activates whatever it finds in the Phone Service state slot used before;
  • the View corresponding to the destination ViewModel is resolved and composed as usual inside the HostPage.

Actually, this is very similar to the old-school-web-application trick of using the http session to pass objects between different pages.

Not very elegant, yet effective.

I built a simple proof of concept showing the idea and the trick seems to work (I wonder why I didn’t try it before…).

Here the key points:

//the hosting VM
public class HostPageViewModel : Conductor<object>
{
	public const string TARGET_VM_KEY = "cm-navigation-target-vm";

	IPhoneService phoneService;
	public HostPageViewModel(IPhoneService phoneService) {
		this.phoneService = phoneService;
	}

	protected override void OnInitialize()
	{
		base.OnInitialize();
		if (!phoneService.State.ContainsKey(TARGET_VM_KEY)) return;

		var targetVM = phoneService.State[TARGET_VM_KEY];
		phoneService.State.Remove(TARGET_VM_KEY);

		this.ActivateItem(targetVM);
	}
}
<!-- the hosting View -->
<phone:PhoneApplicationPage x:Class="Caliburn.Micro.HelloWP7.HostPage" ...> 
    <ContentControl Name="ActiveItem" 
        HorizontalContentAlignment="Stretch" 
        VerticalContentAlignment="Stretch" /> 
</phone:PhoneApplicationPage> 
//a convenience extension method
public static class NavigationExtension
{
	public static void NavigateTo(this INavigationService navigationService, object targetModel)
	{ 
		IoC.Get<IPhoneService>().State[HostPageViewModel.TARGET_VM_KEY] = targetModel;
		navigationService.UriFor<HostPageViewModel>().Navigate();
	}
}
//the usage
PageTwoViewModel model = ... //obtain a new instance of the destination VM
model.NumberOfTabs = 5; //sets the VM up
navigationService.NavigateTo(model);

It has the great advantage of being very similar to the usual way we deal with dialogs in Caliburn.Micro.

Also, it allows to inject into the destination VM any data already available in the context of the calling VM, thus saving an hit to remote services or the use of a global data cache.

About these ads

2 Responses to “Model-first navigation in WP7 with Caliburn.Micro”


  1. May 26, 2011 at 3:14 am

    I’ve pondered adding something like this. I’ve gone back and forth. I think you can handle normal usage and tombstoning fairly well. Yeah, there may be some gotchas, but those could be worked through. However, in Mango, the platform now allows “deep linking” and I’m not sure how we would address that given that we always have one page. You could extend the HostVM to search the query string as well, and if a special parameter is present, use that to load a vm. However, you still have the problem of things like this:

    HostPage.xaml?viewModel=CustomerEditViewModel&customerId=5

    So, you’d have to have an additional mechanism to handle the “real” qs parameters. It’s still worth thinking about, because I like this style of development much better. It’s like the difference between MVC and WebForms…

  2. 2 Marco Amendola
    May 26, 2011 at 3:48 am

    I didn’t think of deep linking, so I discarded the extended query string solution: my primary goal was to show a previously initialized VM instance.
    Perhaps, using the new VM storage mechanism, we could persist the state of the destination VM so that it is restored in the Phone Service state slot *before* the HostVM tries to activate it (basically recreating the same conditions of the regular application flow).

    Anyway, I should definitely get more details about how deep linking is implemented in Mango and what control we’ll have on that.

    In the worst case, I think we could deal with it just like we did in web applications which used the session trick: checking the app state to determine if the expected preconditions are met, and falling back into a default behavior if needed.
    Maybe we can accept (in the NavigateTo method) an additional class aimed to rebuild the state of the destination VM from query string using the required service calls…

    Also, we could keep using the existing approach (which should not have any issues with deep linking) when a bullet-proof authonomous page is required.


Comments are currently closed.

May 2011
S M T W T F S
« Oct   Oct »
1234567
891011121314
15161718192021
22232425262728
293031  

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: