WPF,MVVM IoC:服务定位器模式的替代方案。在后面的View代码中需要依赖

时间:2018-04-25 08:34:47

标签: wpf inversion-of-control mvvm-light

按照几个指南,我使用WPF .NET 4.7.1和M​​VVM-Light进行如下应用程序布局。我对WPF btw来说是全新的。

的App.xaml:

<Application x:Class="My.App" 
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
         xmlns:viewmodel="clr-namespace:My.ViewModel" 
         StartupUri="View\MainView.xaml">
<Application.Resources>
    <ResourceDictionary>
        <viewmodel:ViewModelLocator x:Key="Locator" />
    </ResourceDictionary>
</Application.Resources>

注册&#34; ViewModelLocator&#34; class作为资源并将WPF启动设置为&#34; View / MainView.xaml&#34;。

MainView.xaml:

<Window x:Class="My.View.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<Window.DataContext>
    <Binding  Path="Main" Source="{StaticResource Locator}"/>
</Window.DataContext>

使用ViewModelLocator就像服务定位器模式一样。这里将DataContext设置为我的&#34; MainViewModel&#34; (未示出)。尽管我不喜欢这个,但我可以在WPF XAML环境中使用它。但是现在事实证明我需要在视图的代码隐藏中使用依赖项(而不是ViewModel)。

MainView.cs:

public partial class MainView : INotifyPropertyChanged
{
   public MainView()
   {
       // Need to access dependency here.          
   }
}

现在我可以直接在该构造函数中调用ViewModelLocator并从我的IoC容器中获得该解析 - 然后我完全放弃并接受该模式。

我宁愿在ctor中注入依赖项,如果可行的话,我也会完全离开ViewModelLocator并在这里注入ViewModel。

所以问题是,是否有一些标准方法指示WPF应用程序使用我的容器?如果是的话,沿着这条路走下去并且不使用ViewModelLocator是否可行?

1 个答案:

答案 0 :(得分:2)

你绝对不必使用ViewModelLocator(旁注,服务定位器模式最近有一些批评的反对模式,但我会让你形成自己的观点)。 MVVM Light和其他库基本上可以让您访问工具包。您不需要使用所有工具,只应使用特定域所需的工具。

ViewModelLocator之外,有两种模式称为ViewModel FirstView First,它们都有自己的优点和缺点。但是两者都提供了一种解耦代码的方法,这意味着以后切换并不困难。

至于使用没有服务定位器的MVVM Light构建应用程序,我对View First方法的实现看起来像这样。

我听说ViewModel First是首选,但我发现View First对测试驱动开发(TDD)更为简单

App.xaml.cs(应用程序代码背后)

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        var bootStrapper = new BootStrapper();
        //Container Builder
        var container = bootStrapper.BootStrap();
        var mainWindow = container.Resolve<MainWindow>();
        mainWindow.Show();
    }
}

BootStrapper.cs(我在这种情况下使用AutoFac,但您可以轻松替换。)

public class BootStrapper
{
    public IContainer BootStrap()
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<MainWindow>().AsSelf();
        builder.RegisterType<MainWindowViewModel>().AsSelf();
        return builder.Build();
    }
}

MainWindowViewModel.cs

//I rolled my own ViewModelBase, but you can use MVVM Light's ViewModelBase
public class MainWindowViewModel : ViewModelBase
{
    public string DisplayProgram
    {
        get { return _displayProgram; }
        //MVVM Light's ViewModelBase uses RaisePropertyChanged();
        set { _displayProgram = value; OnPropertyChanged(); }
    }

    public void Initialize()
    {
        //Called from view code behind.
    }
}

MainWindow.xaml.cs(MainWindow Code Behind)

//When MainWindow.Show()..
public partial class MainWindow : Window
{
    private readonly MainWindowViewModel _viewModel;
    //Container resolves dependencies
    public MainWindow(MainWindowViewModel viewModel)
    {
        //Let base Window class do its thing.
        InitializeComponent();

        //Handle loaded event
        Loaded += MainWindowLoaded;

        //Hold on to the MainWindowViewModel, and set it as the windows DataContext            
        _viewModel = viewModel;
        DataContext = _viewModel;
    }

    private void MainWindowLoaded(object sender, RoutedEventArgs e)
    {
        _viewModel.Initialize();
    }
}