Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Caliburn.Micro's (used with modern-ui) OnActivate not being called after ActivateItem?

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;
    }
}
like image 955
vane Avatar asked Jan 09 '23 16:01

vane


1 Answers

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);
like image 186
vane Avatar answered May 08 '23 16:05

vane