简体   繁体   中英

Caliburn Micro MVVM Multiple Active Items AND IoC Dependency Injection

I stumbled around an issue in which I did not find a solution in the official documentation of CM. Our Application looks similar to this image:

结构

While searching StackOverflow, I found solutions in doing something like this with the Conductor<object>.Collection.AllActive class ( more active screens / views in shell caliburn micro or another MVVM framework ). The biggest problem I ran into is, that I can't find a way to make the dependency injection, using a factory. The constructor of the ShellViewModel currently looks like this.

public ShellViewModel(
            Func<FooterViewModel> footerViewModelFactory,
            Func<LoginViewModel> loginViewModelFactory)
{
   this.ActivateItemAsync(loginViewModelFactory);
   this.ActivateItemAsync(footerViewModelFactory);
}

Obviously this does not work, because the ContentControl can not display the factory and needs a Screen. But how do I manage to bind the objects to the shell in the first place, while still maintaining the features of dependency injection? Otherwise an easy workaround would just be to create a new instance for the ViewModels and pass down all of the parameters, which is just super dirty in my eyes.

I finally figured it out. While I hate the factory pattern in Java, it seems necessary in Caliburn Micro and makes sense.

Anyways, I created a factory similar to this:

public class FooterFactory
{
    // Add all the needed interfaces you get injected from the IoC container here.
    private readonly IMyInterface myImplementation;

    public FooterFactory(IMyInterface myImplementation, ...)
    {
        this.myImplementation = myImplementation;
    }

    //Creates the ViewModel. You can also pass some parameters here.
    public FooterViewModel Create(...)
    {
        return new FooterViewModel (..., this.myImplementation);
    }
}

After the factory is then added to the bootstrapper it can then easily be added like followed. I had some issues with my initial approach though. Therefore, I changed my code a little bit.

public ShellViewModel(
            FooterFactory footerFactory,
            Func<LoginViewModel> loginViewModelFactory)
{
   this.ActivateItemAsync(loginViewModelFactory);
   this.FooterViewModel = footerFactory.Create(...);
}

And my XAML looks something like this.

<Window x:Class="My.Client.ShellView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        WindowState="Maximized"
        WindowStartupLocation="CenterScreen"
        Title="Dummy" Icon="/Resources/favicon.ico">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="120" />
        </Grid.RowDefinitions>

        <ContentControl Name="ActiveItem" Grid.Row="0"/>
        <ContentControl Name="FooterViewModel" Grid.Row="1" />
    </Grid>
</Window>

Note: As I said, this is a simplified version of the code I use. Anyway, it should highlight the approach on how to solve this issue. The good thing about this approach is, that you do not need the Conductor<>.Collection with multiple item controls. Therefore, you do not need to mess around with the order of the objects, if for example one object takes longer to load than the other, or you have to deactivate something. + you can create multiple ContentControl s in the XAML if you want more stuff on your screen (eg Header, Menubar, etc.).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM