0
votes

Suppose I have two ViewModels and a Screen Conductor in my application.

public class ShellViewModel : Conductor<IScreen>, IShell
{
    public ShellViewModel()
    {
        ShowConnectionScreen();
    }
    public void ShowConnectionScreen()
    {
        ActivateItem(new ConnectionViewModel());
    }

    public void ShowSetupScreen()
    {
        ActivateItem(new SetupViewModel());
    }
}

The first ViewModel is displayed on start up, and contains some setup information and a Connect button which initializes a connect to somewhere.

If the connection is established successfully, then I would like the first ViewModel to close and the second ViewModel to display some information about the connection. If it fails, the first ViewModel should simply display that, and allow the user to attempt connection once again.

Thus I need the actual connection object to be passed from the first ViewModel to the second ViewModel and the Screen Conductor to change viewmodels on success.

How can this be achieved in Caliburn.Micro?

1
use a common type between the 3 class objects, obviously create this type in question and pass it either in constructor or make it a property of all 3 classes...? Or you could use EventAggregator to publish an event that the other ViewModels would accept and process or ignore. - mvermef

1 Answers

0
votes

To illustrate @mvermef's comment:

use a common type between the 3 class objects, obviously create this type in question

This would be the connection object that the first view model populates and is used by the second view model.

public class Connection {
    // props, methods, etc...
}

and pass it either in constructor or make it a property of all 3 classes

public class ShellViewModel : Conductor<IScreen>, IShell
{
    public Connection Connection { get; set; }
    public ShellViewModel()
    {
        Connection = new Connection();
        ShowConnectionScreen();
    }
    public void ShowConnectionScreen()
    {
        ActivateItem(new ConnectionViewModel(Connection));
    }

    public void ShowSetupScreen()
    {
        ActivateItem(new SetupViewModel(Connection));
    }
}

Do what you want with the Connection object inside ConnectionViewModel

public class ConnectionViewModel : Screen
{
    public Connection Connection { get; set; }
    // establish connection
    // can call (Parent as IConductor).DeactivateItem(this)
    // after connection is established
}

You can notify the parent conductor if a connection is established by (1) registering through ConnectionViewModel's Deactivated event (assuming you subclass Screen). Or (2) you can use EventAggregator to fire an event if the connection is established and having ShellViewModel implement IHandle. You can then call ShowSetupScreen() inside the Deactivated event handler or the Handle method.

Option 1:

// ShellViewModel
public void ShowConnectionScreen()
{
    var connectionVM = new ConnectionViewModel();
    connectionVM.Deactivated += ConnectionViewModel_Deactivated;
    ActivateItem();
}

private void Scheduler_Deactivated1(object sender, DeactivationEventArgs e)
{
    ShowSetupScreen();
}

Option 2:

public class ShellViewModel : Conductor<IScreen>,
    IShell, IHandle<string>
{
    private readonly IEventAggregator _eventAggregator;

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

    // from IHandle<string>. you can create a custom object to represent this event
    public void Handle(string message)
    {
        if (message.Equals("connection.successful"))
        {
            ShowSetupScreen();
        }
    }
}

public class ConnectionViewModel : Screen
{
    private readonly IEventAggregator _eventAggregator;
    public ConnectionViewModel(IEventAggregator eventAggregator)
    {
        _eventAggregator = eventAggregator;
    }

    // call _eventAggregator.PublishOnUIThread("connection.successful");
}