Frage

I'm new on Caliburn Micro and want some advice on which path to take to devolop my app interface and navigation between views. My idea is to have a MainWindow which will contain a menu of buttons, each one related with a specific view. Each view will be stored in a separated WPF UserControl. The mainWindow will also contain a TabControl bound to an ObservableCollection of tabs on viewmodel. Everytime a button on menu is clicked, I want to add a new tab with a ContentPresenter inside that will dynamically load a view and its corresponding viewmodel.

So my questions:

1) Should I use a Screen Collection here?

2) Should the UserControl implement Screen interface?

3) How do I tell MainWindow ViewModel which view to load on the new added tab maintaining viewmodels decoupled?

Thanks to everyone in advance.

UPDATE

After a lot of reading and some help of the community I managed to resolve this. This is the resultant AppViewModel:

class AppViewModel : Conductor<IScreen>.Collection.OneActive
{
    public void OpenTab(Type TipoVista)
    {
        bool bFound = false;
        Screen myScreen = (Screen)Activator.CreateInstance(TipoVista as Type);
        myScreen.DisplayName = myScreen.ToString();
        foreach(Screen miItem in Items)
        {
            if (miItem.ToString() == myScreen.ToString())
            {
                bFound = true;
                ActivateItem(miItem);
            }                
        }
        if (!bFound) ActivateItem(myScreen);        
    }

    public ObservableCollection<MenuItem> myMenu { get; set; }
    public ObservableCollection<LinksItem> myDirectLinks { get; set; }

    public ICommand OpenTabCommand
    {
        get
        {                
            return new RelayCommand(param => this.OpenTab((Type) param), null);
        }
    }       

    public AppViewModel()
    {
        OpenTab(typeof(ClientsViewModel));            
        MenuModel menu = new MenuModel();
        myMenu = menu.getMenu();
        myDirectLinks = menu.getLinks();
    }        

    public void CloseTab(Screen param)
    {            
        DeactivateItem(param, true);
    }    
}

I have to keep the ICommand from OpenTabCommand because the name convention of Caliburn.micro doesn't seems to work inside DataTemplate. Hope it could help someone else. Thanks to all

War es hilfreich?

Lösung

I've done something very similar using Caliburn.Micro, and based it on the SimpleMDI example included with the examples, with a few tweaks to fit my needs.

Much like in the example, I had a main ShellViewModel:

public class ShellViewModel : Conductor<IScreen>.Collection.OneActive
{
}

with a corresponding ShellView containing a TabControl - <TabControl x:Name="Items">, binding it to the Items property of the the Conductor.

In this particular case, I also had a ContextMenu on my ShellView, bound (using the Caliburn.Micro conventions), to a series of commands which instantiated and Activated various other ViewModels (usually with a corresponding UserControl, using the ActivateItem method on the Conductor.

public class YourViewModel: Conductor<IScreen>.Collection.OneActive
{
    // ...

    public void OpenItemBrowser()
    {
        // Create your new ViewModel instance here, or obtain existing instance.
        // ActivateItem(instance)
    }
}

In that case, I didn't require the ViewModels to be created with any particular dependency, or from any other locations in the program.

At other times, when I've needed to trigger ViewModel from elsewhere in the application, I've used the Caliburn.Micro EventAggregator to publish custom events (e.g. OpenNewBrowser), which can be handled by classes implementing the corresponding interface (e.g. IHandle<OpenNewBrowser>), so your main ViewModel could have a simple Handle method responsible for opening the required View:

public class YourViewModel: Conductor<IScreen>.Collection.OneActive, IHandle<OpenNewBrowser>
{
    // ...

    public void Handle(OpenNewBrowser myEvent)
    {
        // Create your new ViewModel instance here, or obtain existing instance.
        // ActivateItem(instance)
    }
}

This section of the documentation will probably be useful, especially the Simple MDI section.

Additional code I mentioned in the comments:

I sometimes use a generic method along these lines ensure that if I have an existing instance of a screen of a particular type, switch to it, or create a new instance if not.

public void ActivateOrOpen<T>() where T : Screen
{
    var currentItem = this.Items.FirstOrDefault(x => x.GetType() == typeof(T));

    if (currentItem != null)
    {
        ActivateItem(currentItem);
    }
    else
    {
        ActivateItem(Activator.CreateInstance<T>());
    }
}

Used like:

public void OpenBrowser()
{
    this.ActivateOrOpen<BrowserViewModel>();
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top