Prism 6 с Unity - разрешение моделей представления для представлений без соглашения об именовании

Я пытаюсь разрешить модели просмотра с использованием DI с Prism 6 и Unity в моем приложении WPF, и это работает. Однако я не знаю, как сказать платформе, какое представление должно быть объединено с какой моделью представления.

Если я использую соглашение, т. Е. Имею пространства имен ViewModels и Views, а также классы ViewA и ViewAViewModel, все работает, однако я хотел бы иметь больше гибкости для именования и организации своих классов, и поэтому я хочу как-то явно сообщить платформе, какое представление идет с какой моделью представления. Я перепробовал много вещей, но на самом деле ничего не работает. Текущее "решение" запускает приложение, но модель представления не установлена.

Вот код:

ViewA.xaml

<UserControl x:Class="WPFDITest.Views.ViewA"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <StackPanel>
        <TextBlock Text="{Binding ViewAMessage}"/>
        <TextBox Text="{Binding ViewAMessage, UpdateSourceTrigger=PropertyChanged}"/>
    </StackPanel>
</UserControl>

MainWindow.xaml

<UserControl x:Class="WPFDITest.Views.ViewA"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:prism="http://prismlibrary.com/"
             prism:ViewModelLocator.AutoWireViewModel="True"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <StackPanel>
        <TextBlock Text="{Binding ViewAMessage}"/>
        <TextBox Text="{Binding ViewAMessage, UpdateSourceTrigger=PropertyChanged}"/>
    </StackPanel>
</UserControl>

ViewAVM.cs

public class ViewAVM : BindableBase
{
    private string viewAMessage;

    public ViewAVM(IModelA model)
    {
        viewAMessage = model.HelloMsgA();
    }

    public string ViewAMessage
    {
        get { return viewAMessage; }
        set { SetProperty(ref viewAMessage, value); }
    }
}

Model.cs

public interface IModelA
{
    string HelloMsgA();
}

public class ModelA : IModelA
{
    public string HelloMsgA()
    {
        return "Hello from A!";
    }
}

App.xaml.cs

public partial class App
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        var bootstraper = new Bootstrapper();
        bootstraper.Run();
    }
}

Bootstrapper.cs

public class Bootstrapper : UnityBootstrapper
{
    protected override DependencyObject CreateShell()
    {
        return Container.Resolve<MainWindow>();
    }

    protected override void InitializeShell()
    {
        Application.Current.MainWindow.Show();
    }

    protected override void ConfigureContainer()
    {
        base.ConfigureContainer();
        Container.RegisterType<IModelA, ModelA>(new ContainerControlledLifetimeManager());
        Container.RegisterType<object, ViewAVM>("ViewA");
    }

    protected override void ConfigureViewModelLocator()
    {
        ViewModelLocationProvider.SetDefaultViewModelFactory(type => Container.Resolve(type));
    }
}

2 ответа

Решение

После некоторых поисков источников Prism я узнал, как делать то, что я хочу. Я могу зарегистрировать каждый вид с ViewModelLocationProvider.Register передавая фабричный метод для представления модели. Я создал метод, который делает это с удобным синтаксисом и использует контейнер для разрешения модели представления для данного типа:

public void BindViewModelToView<TViewModel, TView>()
{
    ViewModelLocationProvider.Register(typeof(TView).ToString(), () => Container.Resolve<TViewModel>());
}

И есть, как я использую это, чтобы связать ViewAVM в ViewA а также ViewB оба используют один и тот же экземпляр.

public class Bootstrapper : UnityBootstrapper
{
    protected override DependencyObject CreateShell()
    {
        return Container.Resolve<MainWindow>();
    }

    protected override void InitializeShell()
    {
        Application.Current.MainWindow.Show();
    }

    protected override void ConfigureContainer()
    {
        base.ConfigureContainer();
        Container.RegisterType<IModelA, ModelA>(new ContainerControlledLifetimeManager());
        Container.RegisterType<ViewAVM>(new ContainerControlledLifetimeManager());
    }

    protected override void ConfigureViewModelLocator()
    {
        BindViewModelToView<ViewAVM, ViewA>();
        BindViewModelToView<ViewAVM, ViewB>();
    }
}

Между прочим, насколько я могу судить по источникам, связать представление для просмотра модели можно только с помощью ViewModelLocator, зарегистрировав фабрики или используя их или пользовательское соглашение, не ищите никакой магии DI.

Вот ссылка на блог Брайана на ViewModelLocator, и он включает в себя раздел (Изменить эти неприятные соглашения) о том, как переопределить соглашения, если вы хотите.

Начало работы с новым ViewModelLocator от Prism

Лично я установил свой DataContext в коде позади UserControl, в конструкторе, после того как представление будет зарегистрировано с контейнером в модуле. Соглашения будь прокляты!!:)

public ProductView(ProductViewModel view_model)
{
    InitializeComponent();
    DataContext = view_model;
}
Другие вопросы по тегам