4

I'm using Caliburn.Micro and Modern-UI in a WPF application. On a "page" inside the modern-ui framework (which is a UserControl), I am trying to use a Conductor to switch the current view. Here is what I've got so far:

NOTE: Namespaces removed from source for brevity

XAML of "page" inside modern-ui window

<UserControl x:Class="ShellView">
    <ContentControl x:Name="ActiveItem" />
</UserControl>

Source for ShellViewModel (the conductor)

[Export]
public class ShellViewModel : Conductor<IScreen>.Collection.OneActive
{
    private readonly Test1ViewModel m_TestView1;
    private readonly Test2ViewModel m_TestView2;

    public ShellViewModel()
    {
        this.m_TestView1 = new Test1ViewModel();
        this.m_TestView2 = new Test2ViewModel();

        this.ActivateItem(this.m_TestView1);
    }
}

The XAML for Test1View doesn't have anything in it right now, just normal UserControl stuff.

Source for Test1ViewModel

public class Test1ViewModel : Screen
{
    protected override void OnActivate()
    {
        //This DOES NOT show or fire, I even put a breakpoint to double check
        Debug.Print("This should show in output");
    }
}

when ActivateItem is called, OnActivate does not fire at all. I even tried calling ConductWith(this) on the view model Test1ViewModel in the conductor but that didn't work. I am using Modern-UI which might be important because this same thing works in a different project that is not using Modern-UI. Oh and when ActivateItem is called, the appropriate view does show on the screen (I added some buttons for verification that the view does change).

Any ideas as to why the UserControl will show in the ContentControl after calling ActivateItem but OnActivate does not fire at all?

One more thing... This might also have something to do with it, but if it does I don't know why or how to fix it. I'm using this class to make the view first Modern-UI work well with Caliburn.Micro's model first approach.

internal class ModernContentLoader : DefaultContentLoader
{
    protected override object LoadContent(Uri uri)
    {
        object content = base.LoadContent(uri);
        if (content == null)
            return null;

        // Locate the right viewmodel for this view
        object vm = ViewModelLocator.LocateForView(content);
        if (vm == null)
            return content;

        // Bind it up with CM magic
        if (content is DependencyObject)
            ViewModelBinder.Bind(vm, content as DependencyObject, null);

        return content;
    }
}
vane
  • 2,125
  • 1
  • 21
  • 40

1 Answers1

9

I went and downloaded the source for Caliburn.Micro and debugged the whole thing like I should have done from the start.

Turns out, because of the way Modern-UI handles navigation the Conductor (unless it's the main shell view attached to the main window) doesn't get activated. In other words, it never knows that it's being shown and the source for Caliburn checks to make sure the Conductor is active before it will allow activating a new view. For some reason the view is displayed just fine but the View Model (Screen) never gets activated or instantiated. In my case it is instantiated because of the Modern-UI+Caliburn.Micro view binding hack.

I did get it to finally work, so if anyone is interested, this is how to get ActivateItem with a Conductor to work inside Modern-UI.

Add the following line of code to your Conductor's constructor or the Modern-UI method OnNavigatedTo

ScreenExtensions.TryActivate(this);

This is part of Caliburn-Micro and will allow your Conductor to activate items properly. If you're using it inside the OnNavigatedTo you might want to add this line to your OnNavigatedFrom method:

ScreenExtensions.TryDeactivate(this, true);
vane
  • 2,125
  • 1
  • 21
  • 40
  • I'm having a similar problem. I need to switch the view by calling ActivateItem from an object reference. Example: lets assume a tabbed interface where viewmodel aaa is currently active and viewmodel ccc is deactivated. Following code is written inside a method in viewmodel aaa: `ViewModelB bbb = new ViewModelB();` `ccc.ActivateItem(bbb);` But if i switch back to ccc where bbb is not loadedd. How can I overcome this issue? – Rahul Oct 30 '15 at 07:17
  • @Rahul Although it seems similar I think your issue is completely different than what my original problem was. I'd suggest creating a new question so you can better describe your issue and provide more in depth code examples. – vane Apr 01 '16 at 23:41
  • many thanks @vane - and thanks for debugging this for me - your solution is absolutely spot on, I've had the same issue, for no apparent reason my Items were not activating (OnActivated event), it turned out that exactly the parent, conductor was not activated itself, as soon as I placed that one liner `ScreenExtensions.TryActivate(this);` all was just working (as it normally would). I'm not sure if that would need to be 'deactivated' (like you've mentioned) as I dont' really need that. Again thanks man! – NSGaga-mostly-inactive Jul 29 '16 at 20:45