Posts Tagged ‘model-first

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.

Advertisements



October 2017
S M T W T F S
« Oct    
1234567
891011121314
15161718192021
22232425262728
293031  

%d bloggers like this: