(WPF) DevExpress mvvm + 温莎城堡。如何构建 POCO 视图模型?

(WPF) DevExpress mvvm + Castle Windsor. How to build POCO viewmodels?

在DevExpress MVVM框架中,您可以定义两个标准的ViewModels,继承自ViewModelBase。或者,您可以定义更好、更复杂的 POCO ViewModels (see this link)。

要构建这样的“POCO”视图模型,您必须使用ViewModelSource 实用程序。这将使标准虚拟模型 class 变成 POCO ViewModel。

namespace DataAbstractWPF.ViewModels
{
    [POCOViewModel]
    public class EntityKindViewModel : Interfaces.ICreateEntityKindViewModel
    {
    }
}

然后,在XAML中,要实例化或定义这样一个 POCO 视图模型,您必须:

         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm" 
         xmlns:ViewModels="clr-namespace:DataAbstractWPF.ViewModels"
         d:DataContext="{dxmvvm:ViewModelSource ViewModels:CreateEntityWizardViewModel}"

如果您将 ViewModel 动态或简单地传递给 View

         xmlns:dxmvvm="http://schemas.devexpress.com/winfx/2008/xaml/mvvm" 
         xmlns:ViewModels="clr-namespace:DataAbstractWPF.ViewModels"
         DataContext="{dxmvvm:ViewModelSource ViewModels:CreateEntityWizardViewModel}"

如果你想让ViewModel直接被XAML实例化。

您也可以通过代码创建

ViewModelSource.Create<LoginViewModel>();

或者由框架创建的工厂。

var factory = ViewModelSource.Factory((string caption) => new LoginViewModel(caption));
factory("Login");

现在,我使用 Castle Windsor 进行注册:

container.Register(Component.For<Interfaces.ICreateEntityKindViewModel>().ImplementedBy<ViewModels.EntityKindViewModel>().LifestyleTransient());
container.Register(Component.For<Interfaces.ICreateEntityWizardViewModel>().ImplementedBy<ViewModels.CreateEntityWizardViewModel>().LifestyleTransient());
container.Register(Component.For<Interfaces.IMainWindowViewModel>().ImplementedBy<ViewModels.MainWindowViewModel>().LifestyleTransient());

container.Register(Component.For<Interfaces.ICreateEntityWizard>().ImplementedBy<Views.CreateEntityWizard>().LifestyleTransient());
container.Register(Component.For<Interfaces.IMainWindow>().ImplementedBy<Views.MainWindow>().LifestyleTransient());
container.Register(Component.For<Interfaces.ICreateEntityKind>().ImplementedBy<Views.EntityKind>().LifestyleTransient());

container.Register(Component.For<Interfaces.IShell>().ImplementedBy<Shell>().LifestyleTransient());

但是现在,当然,ViewModelSource 被完全绕过了,曾经漂亮的 POCO viewmodel 现在只是一个无用的虚拟对象。

现在,我的问题是,如何使用 ViewModelSource 来创建 POCO viewModel,并允许它们由 Castle Windsor 注入?

非常感谢。

我找到了解决方案:

创建这个助手class

using System;
using System.Collections.Generic;

namespace DataAbstractWPF.Helpers
{
    public class AttributeHelper
    {

        public static bool HasAttribute(Type implementation, Type attr)
        {
            object[] arr = implementation.GetCustomAttributes(true);
            List<object> list = new List<object>(arr);
            object attrib = list.Find(delegate (object o) { return ( attr.IsAssignableFrom(o.GetType()) ); });
            return attrib != null;
        }

    }
}

创建组件激活器

using System;
using Castle.MicroKernel.ComponentActivator;
using DevExpress.Mvvm.DataAnnotations;
using DevExpress.Mvvm.Native;
using DataAbstractWPF.Helpers;

namespace DataAbstractWPF.Activators
{
    class DXViewModelActivator : DefaultComponentActivator
    {
        public DXViewModelActivator(Castle.Core.ComponentModel model, Castle.MicroKernel.IKernelInternal kernel, Castle.MicroKernel.ComponentInstanceDelegate onCreation, Castle.MicroKernel.ComponentInstanceDelegate onDestruction)
            : base(model, kernel, onCreation, onDestruction)
        {
            Model.Implementation = TryGetPOCOType(Model.Implementation);
        }

        Type TryGetPOCOType(Type implementation)
        {
            if (AttributeHelper.HasAttribute(implementation, typeof(POCOViewModelAttribute)))
                implementation = ViewModelSourceHelper.GetProxyType(implementation);

            return implementation;
        }

    }
}

像这样创建一个安装程序

using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
using DataAbstractWPF.Helpers;
using DevExpress.Mvvm.DataAnnotations;
using DataAbstractWPF.Activators;

namespace DataAbstractWPF.Bootstrapping
{
    public class Installers : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {

            container.Register(Classes.FromThisAssembly()
                .Where(type => AttributeHelper.HasAttribute(type, typeof(POCOViewModelAttribute)))
                .Configure(r => r.Activator<DXViewModelActivator>())
                .LifestyleTransient()
                .WithServiceDefaultInterfaces()
                );

            container.Register(Component.For<Interfaces.ICreateEntityWizard>().ImplementedBy<Views.CreateEntityWizard>().LifestyleTransient());
            container.Register(Component.For<Interfaces.IMainWindow>().ImplementedBy<Views.MainWindow>().LifestyleTransient());
            container.Register(Component.For<Interfaces.ICreateEntityKind>().ImplementedBy<Views.CreateEntityKind>().LifestyleTransient());

            container.Register(Component.For<Interfaces.IShell>().ImplementedBy<Shell>().LifestyleTransient());
        }

    }
}

现在在您的构造函数中,依赖关系将得到解决。

public partial class MainWindow : Window, Interfaces.IMainWindow
{ 

    public MainWindow()
    {
        InitializeComponent();
    }

    public MainWindow(Interfaces.IMainWindowViewModel context)
    {
        InitializeComponent();
        this.DataContext = context;
    }
}