结合MVVM Light Toolkit和Unity 2.0

7

这更像是一篇评论而非问题,但反馈将是很好的。我被指派为我们正在进行的一个新项目创建用户界面。我们想要使用WPF,并且我想了解所有现代UI设计技术。由于我对WPF还比较陌生,所以一直在研究可以使用的技术。我认为我已经基本确定使用MVVM Light Toolkit(主要因为它的"可混合性"和EventToCommand行为!),但我也想结合IoC一起使用。所以,这就是我想到的。我修改了MVVM Light项目中默认的ViewModelLocator类,使用UnityContainer来处理依赖注入。考虑到3个月前我连90%的这些术语都不知道,我想我正在正确的道路上。

// Example of MVVM Light Toolkit ViewModelLocator class that implements Microsoft 
// Unity 2.0 Inversion of Control container to resolve ViewModel dependencies.

using Microsoft.Practices.Unity;

namespace MVVMLightUnityExample
{
    public class ViewModelLocator
    {
        public static UnityContainer Container { get; set; }

        #region Constructors
        static ViewModelLocator() 
        {
            if (Container == null)
            {
                Container = new UnityContainer();

                // register all dependencies required by view models
                Container
                    .RegisterType<IDialogService, ModalDialogService>(new ContainerControlledLifetimeManager())
                    .RegisterType<ILoggerService, LogFileService>(new ContainerControlledLifetimeManager())
                    ;
            }
        }

        /// <summary>
        /// Initializes a new instance of the ViewModelLocator class.
        /// </summary>
        public ViewModelLocator()
        {
            ////if (ViewModelBase.IsInDesignModeStatic)
            ////{
            ////    // Create design time view models
            ////}
            ////else
            ////{
            ////    // Create run time view models
            ////}

            CreateMain();
        }

        #endregion

        #region MainViewModel

        private static MainViewModel _main;

        /// <summary>
        /// Gets the Main property.
        /// </summary>
        public static MainViewModel MainStatic
        {
            get
            {
                if (_main == null)
                {
                    CreateMain();
                }
                return _main;
            }
        }

        /// <summary>
        /// Gets the Main property.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
            "CA1822:MarkMembersAsStatic",
            Justification = "This non-static member is needed for data binding purposes.")]
        public MainViewModel Main
        {
            get
            {
                return MainStatic;
            }
        }

        /// <summary>
        /// Provides a deterministic way to delete the Main property.
        /// </summary>
        public static void ClearMain()
        {
            _main.Cleanup();
            _main = null;
        }

        /// <summary>
        /// Provides a deterministic way to create the Main property.
        /// </summary>
        public static void CreateMain()
        {
            if (_main == null)
            {
                // allow Unity to resolve the view model and hold onto reference
                _main = Container.Resolve<MainViewModel>();
            }
        }

        #endregion

        #region OrderViewModel

        // property to hold the order number (injected into OrderViewModel() constructor when resolved)
    public static string OrderToView { get; set; }

        /// <summary>
        /// Gets the OrderViewModel property.
        /// </summary>
        public static OrderViewModel OrderViewModelStatic
        {
            get 
            {
                // allow Unity to resolve the view model
                // do not keep local reference to the instance resolved because we need a new instance 
                // each time - the corresponding View is a UserControl that can be used multiple times 
                // within a single window/view
                // pass current value of OrderToView parameter to constructor!
                return Container.Resolve<OrderViewModel>(new ParameterOverride("orderNumber", OrderToView));
            }
        }

        /// <summary>
        /// Gets the OrderViewModel property.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
            "CA1822:MarkMembersAsStatic",
            Justification = "This non-static member is needed for data binding purposes.")]
        public OrderViewModel Order
        {
            get
            {
                return OrderViewModelStatic;
            }
        }
        #endregion

        /// <summary>
        /// Cleans up all the resources.
        /// </summary>
        public static void Cleanup()
        {
            ClearMain();
            Container = null;
        }
    }
}

以下是展示依赖注入使用的MainViewModel类:

using GalaSoft.MvvmLight;
using Microsoft.Practices.Unity;

namespace MVVMLightUnityExample
{
    public class MainViewModel : ViewModelBase
    {
        private IDialogService _dialogs;
        private ILoggerService _logger;

        /// <summary>
        /// Initializes a new instance of the MainViewModel class. This default constructor calls the 
        /// non-default constructor resolving the interfaces used by this view model.
        /// </summary>
        public MainViewModel() 
            : this(ViewModelLocator.Container.Resolve<IDialogService>(), ViewModelLocator.Container.Resolve<ILoggerService>())
        {
            if (IsInDesignMode)
            {
                // Code runs in Blend --> create design time data.
            }
            else
            {
                // Code runs "for real"
            }
        }

        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// Interfaces are automatically resolved by the IoC container.
        /// </summary>
        /// <param name="dialogs">Interface to dialog service</param>
        /// <param name="logger">Interface to logger service</param>
        public MainViewModel(IDialogService dialogs, ILoggerService logger)
        {
            _dialogs = dialogs;
            _logger = logger;

            if (IsInDesignMode)
            {
                // Code runs in Blend --> create design time data.
                _dialogs.ShowMessage("Running in design-time mode!", "Injection Constructor", DialogButton.OK, DialogImage.Information);
                _logger.WriteLine("Running in design-time mode!");
            }
            else
            {
                // Code runs "for real"
                _dialogs.ShowMessage("Running in run-time mode!", "Injection Constructor", DialogButton.OK, DialogImage.Information);
                _logger.WriteLine("Running in run-time mode!");
            }
        }

        public override void Cleanup()
        {
            // Clean up if needed
            _dialogs = null;
            _logger = null;

            base.Cleanup();
        }
    }
}

还有OrderViewModel类:

using GalaSoft.MvvmLight;
using Microsoft.Practices.Unity;

namespace MVVMLightUnityExample
{
    /// <summary>
    /// This class contains properties that a View can data bind to.
    /// <para>
    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
    /// </para>
    /// <para>
    /// You can also use Blend to data bind with the tool's support.
    /// </para>
    /// <para>
    /// See http://www.galasoft.ch/mvvm/getstarted
    /// </para>
    /// </summary>
    public class OrderViewModel : ViewModelBase
    {

        private const string testOrderNumber = "123456";
        private Order _order;

        /// <summary>
        /// Initializes a new instance of the OrderViewModel class.
        /// </summary>
        public OrderViewModel()
            : this(testOrderNumber)
        {

        }

        /// <summary>
        /// Initializes a new instance of the OrderViewModel class.
        /// </summary>
        public OrderViewModel(string orderNumber)
        {
            if (IsInDesignMode)
            {
                // Code runs in Blend --> create design time data.
                _order = new Order(orderNumber, "My Company", "Our Address");
            }
            else
            {
                _order = GetOrder(orderNumber);
            }
        }

        public override void Cleanup()
        {
            // Clean own resources if needed
            _order = null;

            base.Cleanup();
        }
    }
}

以下是用于显示特定订单视图的代码:

public void ShowOrder(string orderNumber)
{
    // pass the order number to show to ViewModelLocator to be injected
    //into the constructor of the OrderViewModel instance
    ViewModelLocator.OrderToShow = orderNumber;

    View.OrderView orderView = new View.OrderView();
}

这些示例已经被简化,只展示了IoC的想法。为了得出这个解决方案,需要进行大量的试验和错误,搜索互联网上的示例,并发现Unity 2.0文档缺乏(扩展示例代码),请让我知道您是否认为它可以改进。


我很想知道您认为Unity文档中存在哪些漏洞。还需要哪些东西? - Chris Tavares
Unity的文档相当不错,但我在寻找2.0版本的示例代码片段时遇到了一些困难,特别是在使用扩展方面。虽然有很多1.x版本的示例代码,但其中许多已经过时(我一直收到消息提示不要使用那种语法,因为有新的语法可用于使用扩展)。 - acordner
距离您首次发布这篇文章已经快一年了,acordner。我开始感到困难,因为我正在思考您当时提出的想法。我想知道现在您对MVVM Light/Unity的看法如何。 - Rod
1个回答

1

首先,您应该避免使用服务定位器反模式。

其次,每次想要获取一个OrderView时都要设置一个静态变量吗?这是非常丑陋的设计。


正如我所说的,我对这方面的东西真的很陌生,只是试图找到感觉。我不仅愿意接受批评,还愿意听取有关如何改进的建议。我从未使用MVVM模式组合过完整的工作应用程序,因此我希望在走得太远之前找到最佳解决方案。我认为让其他经验更丰富的开发人员各抒己见是发现改进机会的好方法。我会研究一下“服务定位器反模式”(因为我目前不知道那是什么) :) - acordner
我同意在获取OrderView之前设置一个静态变量并不是一个好的解决方案,但在我弄清楚所有参数覆盖和依赖注入技术之前,这就是我想出来的办法。 - acordner
更好的解决方案是使用可注入的抽象工厂模式。 - onof

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接