这是在城堡中创建视图的正确方法吗?

时间:2013-11-12 09:42:51

标签: c# mvvm inversion-of-control castle-windsor ioc-container

我在WPF MVVM应用程序中使用Castle Windsor V3.2.1。

这是我的安装程序:

    public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
    {
        container.AddFacility<TypedFactoryFacility>();
        container.Register(Component.For<IAbstructFactory>().AsFactory());

        container


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

            .Register(Types
                          .FromAssemblyInDirectory(new AssemblyFilter(AssemblyDirectory + "\\Map"))
                          .Pick()
                          .If(x => x.IsPublic)
                          .If(x => x.GetInterfaces().Length > 0)
                          .WithService
                          .FirstInterface()
                          .LifestyleTransient())


            .Register(Component.For<MainWindow>().LifestyleTransient());
    }

注意:我正在注册名为Map的FromDirectory。

这是我的地图项目:

MapViewModel

public class MapViewModel : IMapViewModel
{
    #region IMapViewModel Members

    IMapView _theMapView;
    IMapModel _theMapModel;

    /// <summary>
    /// Gets or sets the view.
    /// </summary>
    /// <value>
    /// The view.
    /// </value>
    public IMapView TheView
    {
        get 
        {
            return _theMapView;
        }
        set
        {
            _theMapView = value;
            _theMapView.TheMapViewModel = this;
        }
    }


    /// <summary>
    /// Gets or sets the model.
    /// </summary>
    /// <value>
    /// The model.
    /// </value>
    public IMapModel TheModel
    {
        get
        {
            return _theMapModel;
        }
        set
        {
            _theMapModel = value;
            CreateView();
        }
    }

    /// <summary>
    /// Creates the view.
    /// </summary>
    private void CreateView()
    {
        TheView = new MapView();
    }

    #endregion

    #region IViewModel Members

    /// <summary>
    /// Gets the help.
    /// </summary>
    /// <value>
    /// The help.
    /// </value>
    public IHelpManager Help
    {
        get
        {
            return HelpManager.Instance;
        }
    }

    #endregion
}

MapView类

    public partial class MapView : IMapView
    {
        private IMapViewModel _theMapViewModel;

        /// <summary>
        /// The View Model.
        /// </summary>
        public IMapViewModel TheMapViewModel 
        { 
            get
            {
                return _theMapViewModel;
            }
            set 
            {
                _theMapViewModel = value;
                DataContext = _theMapViewModel.TheModel;
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MapView"/> class.
        /// </summary>
        public MapView()
        {
            InitializeComponent();
        }
   }

这是我的MainWindow类:

    /// <summary>
    /// Initializes a new instance of the <see cref="MainWindow"/> class.
    /// </summary>
    /// <param name="context">The context.</param>
    public MainWindow(IAbstructFactory context)
    {
        InitializeComponent();

        IMapViewModel theMapViewModel = context.Create<IMapViewModel>();
        context.Release(theMapViewModel);
    }

我的问题是:

我认为创建视图的方法比我目前使用的更好。

  1. 我正在MainWindow中创建 MapViewModel
  2. 正如您在 MapViewModel 中看到的那样 - 创建时, MapModel 会被注入 Windsor - 效果很好。
  3. 当我尝试使用 MapView 执行相同操作时 意思是 - 我正在主要添加一个电话:

    IMapView theMapView = context.Create<IMapView>() 
    

    我什么也没得到,应用程序崩溃了。

    为什么视图不像ViewModel那样被注入,尽管它们都在同一个程序集中?

2 个答案:

答案 0 :(得分:0)

我没有给你一个确切的答案。但是,我确实有一个代码示例以及它如何工作的解释,你可以帮助你走上正轨。

当我做MVVM时,它看起来如下:

  1. 我有一个视图,它实现了一个定义视图的界面(很像你有)。视图后面没有任何代码(警告:使用第三方控件并不是真正开发MVVM)。该视图引用了视图模型。因为我喜欢用容器来解决依赖性反转,而我喜欢Castle Windsor,所以最终视图将视图模型作为构造函数参数。

  2. 视图模型实现了一个界面,用于定义视图模型应该执行的操作(也是您所拥有的)。视图模型对其视图完全没有了解(与上图略有不同)。视图模型也引用了它通过构造函数注入操作的模型对象。

  3. 如果某个视图能够生成其他视图,那么它将拥有一个可用于完成此操作的工厂。

  4. 所有视图/视图模型构造都是通过应用程序启动时的初始解析或任何需要生成其他视图的视图工厂完成的。

  5. 这是一个控制台应用程序,它演示了我所做的一些城堡内容。

    示例代码中注意到: IView&amp;城堡主要使用IViewModel进行注册。我有时会得到功能的基本定义,在这种情况下,我通常会创建一个所有view / viewmodel都继承自的抽象基类。

    IViewFactory是城堡类型工厂的定义。一个重要的注意事项,我相信通过查看您的代码可以理解,需要释放由城堡跟踪的所有瞬态生活方式对象(跟踪是默认行为和建议)。所以我的IViewFactory定义了一个destroy以及实现IDisposable(当你的工厂被处置时,它创建的所有组件都被释放)。主要模式是当一个可以创建视图的窗口消失时,你可以处理视图工厂并释放它的所有组件。

    我的WpfInstaller将注册应用程序目录中找到的所有IView和IViewModel。

    WpfViewCreaterFacility为任何IView设置自定义组件激活器。激活器将自动将IViewModel分配给视图的DataContext。

    该应用程序代表一个具有一个主窗口的系统,该主窗口可以生成子窗口,我已经定义了两个窗口。

    如果您有任何问题请告诉我,这是代码:

    using Castle.Core;
    using Castle.Facilities.TypedFactory;
    using Castle.MicroKernel;
    using Castle.MicroKernel.ComponentActivator;
    using Castle.MicroKernel.Context;
    using Castle.MicroKernel.Facilities;
    using Castle.MicroKernel.Registration;
    using Castle.MicroKernel.SubSystems.Configuration;
    using Castle.Windsor;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using ViewFun.Castle;
    using ViewFun.Common;
    using ViewFun.View;
    using ViewFun.ViewModels;
    
    namespace ViewFun
    {
        namespace Common
        {
            public interface IView
            {
                void PrintViewModelName();
            }
    
            public interface IViewModel
            {
            }
    
            public interface IViewFactory : IDisposable
            {
                TView CreateView<TView>();
                void DestroyView<TView>(TView view);
            }
        }
    
        namespace ViewModels
        {
            public interface IMainViewModel : IViewModel
            {
            }
    
            public interface ISecondaryViewModel : IViewModel
            {
            }
    
            public interface ISecondary2ViewModel : IViewModel
            {
            }
    
            public class MainViewModel : IMainViewModel
            {
            }
            public class SecondaryViewModel : ISecondaryViewModel
            {
            }
            public class Secondary2ViewModel : ISecondary2ViewModel
            {
            }
        }
    
        namespace View
        {
            public interface IMainView : IView, IDisposable
            {
                void ShowView<TView>()
                    where TView : IView;
            }
    
            public interface ISecondaryView : IView
            {
            }
    
            public interface ISecondaryView2 : IView
            {
            }
        }
    
        namespace WPFImplementation
        {
            public class MainWindow : Window, IMainView, IDisposable
            {
                private readonly IViewFactory viewFactory;
    
                public MainWindow(IMainViewModel viewModel, IViewFactory viewFactory)
                {
                    this.viewFactory = viewFactory;
                }
    
                public void PrintViewModelName()
                {
                    Console.WriteLine(string.Format("The main window is of type {0} with a view model type of {1}", this.GetType(), this.DataContext.GetType().Name));
                    Console.WriteLine();
                    Console.WriteLine();
                }
    
                public void ShowView<TView>()
                    where TView : IView
                {
                    IView view = this.viewFactory.CreateView<TView>();
                    Console.WriteLine(view.GetType().Name);
                    view.PrintViewModelName();
                }
    
                public void Dispose()
                {
                    this.viewFactory.Dispose();
                }
            }
    
            public class SecondaryWPFView1 : Window, ISecondaryView
            {
                public SecondaryWPFView1(ISecondaryViewModel viewModel)
                {
                }
    
                public void PrintViewModelName()
                {
                    Console.WriteLine(string.Format("One of the secondary windows is of type {0} with a view model type of {1}", this.GetType(), this.DataContext.GetType().Name));
                    Console.WriteLine();
                    Console.WriteLine();
                }
            }
    
            public class SecondaryWPFView2 : Window, ISecondaryView2
            {
                public SecondaryWPFView2(ISecondary2ViewModel viewModel)
                {
                }
    
                public void PrintViewModelName()
                {
                    Console.WriteLine(string.Format("The other secondary window is of type {0} with a view model type of {1}", this.GetType(), this.DataContext.GetType().Name));
                    Console.WriteLine();
                    Console.WriteLine();
                }
            }
        }
    
        namespace Castle
        {
            public class WpfInstaller : IWindsorInstaller
            {
                    private static string AssemblyDirectory
                    {
                        get
                        {
                            string codeBase = Assembly.GetExecutingAssembly().CodeBase;
                            var uri = new UriBuilder(codeBase);
                            string path = Uri.UnescapeDataString(uri.Path);
                            return Path.GetDirectoryName(path);
                        }
                    }
    
                    public void Install(IWindsorContainer container, IConfigurationStore store)
                    {
                        container.AddFacility<WpfViewCreaterFacility>()
                            .Register(
                                Classes.FromAssemblyInDirectory(new AssemblyFilter(AssemblyDirectory))
                                    .BasedOn<IView>()
                                    .Configure(c => c.LifestyleTransient().Named(c.Implementation.Name))
                                    .WithService.Base()
                                    .WithService.FromInterface(typeof(IView)),
                                Classes.FromAssemblyInDirectory(new AssemblyFilter(AssemblyDirectory))
                                    .BasedOn<IViewModel>()
                                    .Configure(c => c.LifestyleTransient().Named(c.Implementation.Name))
                                    .WithService.Base()
                                    .WithService.FromInterface(typeof(IViewModel)),
                                Component.For<IViewFactory>()
                                    .AsFactory()
                                    .LifestyleTransient());
                    }
            }
    
            public class WpfViewCreaterFacility : AbstractFacility
            {
                protected override void Init()
                {
                    this.Kernel.ComponentModelCreated += this.RegisterComponentActivator;
                }
    
                private void RegisterComponentActivator(ComponentModel model)
                {
                    bool isView = typeof(IView).IsAssignableFrom(model.Services.First());
                    if (!isView)
                    {
                        return;
                    }
    
                    if (model.CustomComponentActivator == null)
                    {
                        model.CustomComponentActivator = typeof(WpfViewCreater);
                    }
                }
            }
    
            public class WpfViewCreater : DefaultComponentActivator
            {
                public WpfViewCreater(
                    ComponentModel model,
                    IKernel kernel,
                    ComponentInstanceDelegate onCreation,
                    ComponentInstanceDelegate onDestruction)
                    : base(model, kernel, onCreation, onDestruction)
                {
                }
    
                protected override object CreateInstance(
                    CreationContext context, ConstructorCandidate constructor, object[] arguments)
                {
                    object component = base.CreateInstance(context, constructor, arguments);
    
                    var frameworkElement = component as FrameworkElement;
    
                    if (frameworkElement != null && arguments != null)
                    {
                        object viewModel = arguments.FirstOrDefault(vm => vm is IViewModel);
                        if (viewModel != null)
                        {
                            frameworkElement.DataContext = viewModel;
                        }
                    }
    
                    return component;
                }
            }
        }
    
        class Program
        {
            [STAThread]
            static void Main(string[] args)
            {
                IWindsorContainer container = new WindsorContainer();
                container.AddFacility<TypedFactoryFacility>();
                container.Install(new WpfInstaller());
    
                IMainView mainView = container.Resolve<IMainView>();
                mainView.PrintViewModelName();
                mainView.ShowView<ISecondaryView>();
                mainView.ShowView<ISecondaryView2>();
    
                mainView.Dispose();
    
                Console.ReadLine();
            }
        }
    }
    

答案 1 :(得分:0)

我的发现:

我发现(这要归功于@ greyalien007的回答)我的问题包括两个问题:

  1. 我以错误的方式注册我的Map程序集,导致IMapView接口无法注册。所以我将注册更改为:container.Register(Types.FromAssemblyInDirectory(new AssemblyFilter(AssemblyDirectory + "\\Map")) .Where(type => type.Name.StartsWith("Map")) .WithService.AllInterfaces());
  2. 所以现在我要做的就是在我的MapView中添加一个属性,将MapViewModel注入:/// <summary> /// The View Model. /// (Automatically Injected by the Castle Windsor Framework) /// </summary> public IMapViewModel TheMapViewModel { get { return _theMapViewModel; } set { _theMapViewModel = value; DataContext = _theMapViewModel.TheModel; } }
  3. 完成这项工作后,我所需要的就是在我的Main中创建它们:IMapViewModel theMapViewModel = context.Create<IMapViewModel>(); IMapView theMapView = context.Create<IMapView>();
  4. 现在我有一个基于Castle的主项目 - 谁只引用接口程序集并使用Castle框架来访问和创建所需的程序集。

    感谢。