Navigate to another screen on WP8?

May 16, 2013 at 4:11 PM
Can I use navigationService.UriFor to navigate to another screen in panorama control - navigation is rather for pages but I would like it to be transparent and navigate to any viewmodel I put in navigationService. Is this impelmenten in some way?
May 16, 2013 at 9:55 PM
No,

I think it is helpful to remember that ViewModel's are not always pages or screens. As you start to decompose your app you will see that a ViewModel can also be a Conductor and manage a list of sub ViewModels. Have a look Screens, Conductors and Composition it discusses the type of composition you need.
May 17, 2013 at 6:45 AM
Edited May 17, 2013 at 6:46 AM
I have read all documentation but CM have only articles and there is no full documentation of the library and I was wondering if in some way I can navigate to any ViewModel I want regardless how it is implemented (screen, conductor etc.)
May 17, 2013 at 8:51 AM
If you mean MSDN style documentation, no, we do not have anything like that, however I would think our documentation is pretty comprehensive.

I'm struggling to understand what you are looking for we support plenty of activation options for ViewModels, for "Page" or screen level you have the NavigationService (or WindowManager in WPF/SL) you can use conductors which maintain a collection of items plus an active item, finally we allow you two swap out ViewModel properties by using content controls to make "Templates"

We have a few samples in the source repository that show all these things fitting together.

Perhaps you can give me a quick run down of how you are laying out your screens and navigation and I could respond with how caliburn.micro would handle these situations?
May 17, 2013 at 9:12 AM
Edited May 17, 2013 at 11:00 AM
I have conductor with couple screens (Page with panorama control), beside this I will have other subpages in application.
When I’m in main page and call navigationService.UriFor<StoreViewModel>().Navigate();
than it works when StoreViewModel is other page but when it is just another item in panorama control it fails. I know that for this I can simply activate item of panorama control but I’m thinking about something universal – call navigate on any viewmodel. Other more complicated situation will be when StoreViewModel will be item of other panorama on other page – navigation should load this page and navigate me to specified item inside panorama.
Are You understand me now? Maybe it is available now but I’m doing something wrong.

Edit:
Meanwhile I was trying to navigate inside one conductor (Conductor<object>.Collection.OneActive – Page with panorama control where items are implemented as screen). I have really strange behavior:
  1. When I’m invoking below code in conductor constructor it activates screen but OnActivate method of the screen is invoked twice
    this.ActivateItem(this.Items[2]);
  2. When I’m trying to invoke this code from ApplicationBar then activation is not working – fanny thing is that breakpoint in OnActivate is hitting but item is not activated
  3. When I have invoked below code from ApplicationBar behavior is the same as in point one – OnActive is invoked twice:
var test = ViewLocator.LocateForModel(this, null, null) as MainPage;
test.Items.DefaultItem = this.Items[2];


For now I don’t know how to activate screen and invoke OnActivate only once ?
May 17, 2013 at 11:39 AM
Edited May 17, 2013 at 11:43 AM
Ok, so you are on the right track using activate item.

I can appreciate what you are looking for, a sort of universal navigation manager. You are correct in that this doesn't exist. The navigation framework was built specifically because windows phone demands a page is loaded into a frame for navigational purposes. In general Caliburn.Micro favours a ViewModel first approach were navigation becomes unimportant. The reason for this is it makes composition easier, and makes ViewModels polymorphic in their use.

By way of example. Assume I have a shell ViewModel. It has a conductor and a property that holds a contextual ViewModel. The code below shows how I can have my active ViewModel swap out the contextual ViewModel by sending a message when it activates. The shell will check the message and if it came from the ActiveItem it knows to put it into the ContextArea, if it did not come from the ActiveItem it adds it to the Conductor itself.
    public class ShellViewModel : 
        PropertyChangedBase,
        IHandle<DisplayViewModelCommand>

    {
        private readonly IEventAggregator _eventAggregator;

        public ShellViewModel(IEventAggregator eventAggregator)
        {
            _eventAggregator = eventAggregator;
            _eventAggregator.Subscribe(this);
        }

        public object ContextArea { get; set; }

        public IConductActiveItem ConductorArea { get; set; }

        public void Handle(DisplayViewModelCommand message)
        {
            if (message.Requestor == ConductorArea.ActiveItem)
            {
                ContextArea = message.ViewModel;

                var activatableViewModel = ContextArea as IActivate;
                if (activatableViewModel != null)
                    activatableViewModel.Activate();

                return;
            }

            ConductorArea.ActivateItem(message.ViewModel);
        }
    }
public class SomeViewModel : Screen
    {
        private readonly IEventAggregator _eventAggregator;

        public SomeViewModel(IEventAggregator eventAggregator)
        {
            _eventAggregator = eventAggregator;
        }

        protected override void OnActivate()
        {
            _eventAggregator.Publish(new DisplayViewModelCommand
                {
                    Requestor = this, 
                    ViewModel = new SomeOtherViewModel()
                });

            base.OnActivate();
        }
    }
Of course this is just an example, but it gets the point across, where and when ViewModels need to activate changes from application to application.

In terms of activation not activating. You need to ensure that your conductor itself is activated otherwise the screens will not activate, from the docs:

If you activate an item in a conductor that is itself not active, that item won’t actually be activated until the conductor gets activated. This makes sense when you think about it, but can occasionally cause hair pulling.

In general my approach is to have my ShellViewModel contain a single conductor as a property. I pull in the main conductor via DI, assign it to this property and then call TryActivate on it. This will allow other screens to activate, this Link and the comment from Rob explains this in better detail.

I am not sure why your activation is called twice. Perhaps if I could see your ViewModel were activate is located and the places you are calling it I may be able to work out what is wrong.
May 17, 2013 at 12:25 PM
Edited May 17, 2013 at 12:26 PM
Tnx for clarification – I will think about this and read suggested article. As far as double activation I finally found what was the cause. WP8 have a bug in Panorama control (http://stackoverflow.com/questions/14260701/windows-phone-8-panorama-selectionchanged-databinding) and I fix this bug by adding this code to Screen class in CM sources:
public override bool Equals(object obj)
        {
            if ((obj != null) && (obj.GetType() == typeof(Microsoft.Phone.Controls.PanoramaItem)))
            {
                Microsoft.Phone.Controls.PanoramaItem thePanoItem = (Microsoft.Phone.Controls.PanoramaItem)obj;
 
                return base.Equals(thePanoItem.Header);
            }
            else
            {
                return base.Equals(obj);
            }
        }
 
       
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
Unfortunately this gives me side effect with double activation ;(
May 17, 2013 at 2:03 PM
Edited May 17, 2013 at 4:22 PM
I’m reading article about activation but I’m still don’t know how to do this right.

Maybe it will be better to talk with some example. I found some article http://wp.qmatteoq.com/first-steps-with-caliburn-micro-in-windows-phone-8-pivot/
Attached project contains mainpage and pivot with two screens – I have tried to put button in screen one and navigate to screen two with this code but it failed.
 public void Test()
 {
    var viewmodel = ViewModelLocator.LocateForViewType(typeof(PivotItem2View));
    ScreenExtensions.TryActivate(viewmodel);
 }

Pivot1 View:
<Button Content="Button" cal:Message.Attach="Test"/>
Maybe when I understand this I will be able to fix my app.

Second question - how Can I check if condactor is activated?
May 19, 2013 at 4:11 AM
I want to say check ActiveItem to see if it has an object assigned (aka active).
May 20, 2013 at 11:01 AM
Don't bother question about how to check if conductor is active – I was tired. All mess with this is again because of bug in panorama ;(