Caliburn.Micro rebind ContentControl on navigation GoBack
Asked Answered
S

2

9

I'm using Caliburn.Micro within WinRT application

Here is my main VM:

public class MainViewModel : Conductor<Screen>
{
    protected override void OnActivate()
    {
        if (ActiveItem == null)
        {
           ActivateItem(
               ViewModelLocator.LocateForViewType(typeof(NewsFeedView)) as Screen);
        }

        base.OnActivate();
    }
}

here I use conductor because I want to load different controls in ContentControl, but now I have only this code. Here is my content control in main view:

<ContentControl x:Name="ActiveItem" Grid.Column="1" Grid.Row="1" />

When I running the application everything work fine, MainViewModel.Activate gets called and ActiveItem set to NewsFeedViewModel and ContentControl loads NewsFeedView.

The problem:

When I navigate in NewsFeedView control to another view using NavigationService.NavigateToViewModel method and then in that view use NavigationService.GoBack, i'm returning to MainView and in that moment when MainViewModel.Activate gets called ActiveItem is not null, but ContentControl.Content is null. I've tried use View.Model attached property for ContentControl but no luck, how to make it rebind?

EDIT: Finally i'm setup logger in Caliburn to see what happens and I found an error - when MainView loaded after navigationg back this events occuring:

Attaching ***.Views.MainView to ***.ViewModels.MainViewModel.
ViewModel bound on ActiveItem.
Using cached view for ***.ViewModels.NewsFeedViewModel.
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Exception: Unspecified error
at Windows.UI.Xaml.Controls.ContentControl.put_Content(Object value)
... some winRT stack
at Caliburn.Micro.View.SetContentPropertyCore(...

Though it was not so informative I've used InteliTrace to get more info and got this message: "Element is already child of another element". I suppose NewsFeedView stored somewhere and when time comes to put it in ContentControl this exception thrown. How to solve this?

Stereograph answered 21/5, 2013 at 16:38 Comment(3)
Have you checked whether the MainViewModel object is still the same instance as before? "Element is already child of another element" sounds like you're navigating to a new MainViewModel instance, but the old one is still around hanging on to the NewsFeedViewModel. Can you share your bootstrapper configuration?Conium
Please share your bootstrapper. This will tell what possibilities that you have. Have solved such a problem in a .net 4.0 / wpf scenario.Metonym
@MareInfinitus there is no bootstrapper in WinRT app, MainViewModel registered as Singleton in Caliburn IoC containerStereograph
R
3

You should adopt the view model first approach. In other words, activate an instance of a view model, and Caliburn.Micro will do the view location and binding for you.

It also looks like you want to just instantiate the view model once in the constructor for example, or OnInitialise:

public MainViewModel()
{
    this.ActivateItem(new NewsFeedViewModel());
}
Roxy answered 21/5, 2013 at 19:1 Comment(4)
thanks for an answer, but I really don't understand what you mean by "adopt view model first approach", i'm using view model when calling ActivateItem and when calling NavigateToViewModel when navigating between views, so I'm not using view first approach hereStereograph
Yes you are, but from the MainViewModel code you've included above, you're going about it in a very roundabout way. You're getting the view model by using the ViewModelLocator and the view type. Just use the view model instance in the first place.Roxy
ok, understood, but in the end this has no effect on databinding, still when navigating back from view, ContentControl is empty despite that ActiveItem is not null, I've tried call NotifyOfPropertyChange(()=>ActiveItem) but still the same resultStereograph
Sorry I'm confused, are you doing Windows Phone development or WinRT?Roxy
L
1

Initialize the news feed view model only once as @devdigital said, probably in the constructor, and why not use Conductor.Collection.OneActive since you only have one active item at any given time, it is used for this situations, this could solve your issue.

Lambdacism answered 30/5, 2013 at 17:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.