Confused about navigation

Aug 27, 2010 at 10:36 PM

I've been browsing and snooping on the forum and blog updates while I wait to find time to update my current framework to CM but I have to admit I'm quite confused now on what to do about navigation. 

I'm currently using some navigation via Silverlight.fX but it currently amounts to nothing more than a ViewLocator/Controller setup and then I maintain a visual breadcrumb trail and no ability to dive in deep or to use the back button.  I've pushed/encouraged my users to run Out of Browser so it is less of an issue.  I am passing parameters to my navigation controller and that is a maintenance pain.  I would be much happier creating VM's first and then pushing them around.

Maybe the next blog post on screens and conductors will clear all that up but there are comments in discussions about navigation in WP7 but not migrated to SL etc.  Can someone please clarify what is / isn't supported?

I'm not happy with my application at the moment and while it would be nice to be able to send someone a URL that drilled down directly to a screen I'm sure that really helps with an OOB implementation anyways.

Thanks

jack

Aug 27, 2010 at 10:56 PM

Hi Jack,

The way I have done it with CM is to use the event aggregator to tell my shell what viewmodel to activate. From the samples you have probably seen that CM has two built-in conductor classes to aid with managing the screens. I expose a Screens property in my shell that

gets bound to a custom menu control that creates a hierarchical list that groups the screens based on functionality. From anywhere in the application I can publish my activate screen message to load whatever I want. My main shell looks something like this:

 

    public class ShellViewModel : Conductor<IScreen>.Collection.OneActive, IHandle<ActivateScreenMessage>, IHandle<CloseScreenMessage>
    {
	...

        public IObservableCollection<IScreen> Screens
        {
            get { return this.Items; }
        }
	

        private void Activate(ActivateScreenMessage activateScreenMessage)
        {
            string contractName = GetContractName(activateScreenMessage);
            if (contractName != null)
            {
                IScreen screen = null;
	//Try to find the screen inside currently conducted items 
                screen = GetLocalScreen(activateScreenMessage);
                if (screen == null)
                {
                    screen = IoC.Get<IScreen>(contractName);
                    if (screen is IHaveUniqueKey)
                    {
                        (screen as IHaveUniqueKey).UniqueKey = GetUniqueKey(activateScreenMessage);
                    }
                }

                ChangeActiveItem(screen, true);
            }
        }

Hope that helps. Let me know if you have any questions about this.

Sep 3, 2010 at 7:44 PM

Thanks for the reply, I got dragged into some production work and couldn't get back to this.

So basically you are plugging everything you need (like a URI) into the activateScreenMessage or do you plug your view model into it?  Would you share that implementation? 

Maybe I need to change my way of thinking so that I expose more viewModels or childViewModels instead of objects and have lazyloaded data where needed.

jack

Sep 3, 2010 at 8:19 PM
Edited Sep 3, 2010 at 8:55 PM

My ActivateScreenMessage class has properties to tell my Conductor what xap to dynamically load (using a CatalogService that uses the CatalogLoadResult from the "Recipes" section), which viemodel I want to instantiate, an Id for the record that I want and an enum for the conductor type that should handle the request (I have nested conductors).

    public class ActivateScreenMessage
    {
        public string Id { get; set; }
        public string ViewModelName { get; set; }
        public string Module { get; set; }
        public ConductorType ConductorType { get; set; }
    }

My handle method for the ActivateScreenMessage checks first if the view model type I am asking for is already in memory otherwise it calls the CatalogService to retrieve the module xap file and then calls Activate.

        public void Handle(ActivateScreenMessage activateScreenMessage)
        {
            if (MessageIsForThisConductor(activateScreenMessage))
            {
                var viewType = (from assembly in AssemblySource.Instance
                                from type in assembly.GetExportedTypes()
                                where type.FullName.Equals(GetContractName(activateScreenMessage))
                                select type).FirstOrDefault();

                if (viewType != null)
                {
                    Activate(activateScreenMessage);
                    return;
                }

                if (!string.IsNullOrEmpty(activateScreenMessage.Module))
                {
                    CatalogService catalogService = new CatalogService();
                    catalogService.Completed += (s, e) => Activate(activateScreenMessage);
                    catalogService.Load(activateScreenMessage.Module);
                }
            }
        }
So the view model is instantiated inside of the Activate method I had posted in my previous reply. Don't know if this is the best way to do it 
(maybe because of coming from PRISM this makes more sense to me) but this allows me to load any form from anywhere in the application.

 

Coordinator
Sep 3, 2010 at 8:45 PM

Cool. I haven't actually done it that way myself. However, it looks clean and extensible. It makes sense and it works.