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;
}