带有 Unity 的 Prism 6 - 在没有命名约定的情况下解析视图的视图模型

Prism 6 with Unity - resolving view models for views without naming convention

我正在尝试在我的 WPF 应用程序中使用 DI 与 Prism 6 和 Unity 解析视图模型,这很有效。但是我不知道如何告诉框架应该将哪个视图与哪个视图模型合并。

如果我使用约定,即有 ViewModels 和 Views 命名空间,并且 类 ViewA 和 ViewAViewModel 一切正常,但是我想更灵活地命名和组织我的 类 和这就是为什么我想以某种方式明确地告诉框架哪个视图与哪个视图模型匹配。我尝试了很多东西,但没有任何效果。当前 "solution" 制作应用程序 运行 但未设置视图模型。

代码如下:

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

这里是 Brian 关于 ViewModelLocator 的博客 link,其中包含一个部分(更改那些讨厌的约定),如果您愿意,可以介绍如何覆盖约定。

Getting Started with Prism’s new ViewModelLocator

就我个人而言,在视图注册到模块中的容器后,我在构造函数中的 UserControl 后面的代码中设置了我的 DataContext。公约该死! :)

public ProductView(ProductViewModel view_model)
{
    InitializeComponent();
    DataContext = view_model;
}

在对 Prism 资源进行一些挖掘之后,我发现了如何做我想做的事情。我可以通过 ViewModelLocationProvider.Register 为视图模型传入工厂方法来注册每个视图。我创建了使用方便的语法并使用容器来解析给定类型的视图模型的方法:

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

还有我如何使用它将 ViewAVM 绑定到 ViewAViewB,两者都使用相同的单例实例。

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魔法.