依赖注入以在 MVVM 中以对话框形式启动视图

Dependency Injection to launch view as Dialog in MVVM

我需要一些关于如何在 ViewModel 中处理依赖项注入的建议。我的 viewModel MenuViewModel 有一个 ICommand 方法,当用户单击视图中的按钮时,该方法将 运行 。此方法将打开一个新的 window。方法如下所示。

public void bookingCommand_DoWork(object obj)
{
    BookingView bookingView = new BookingView();
    BookingViewModel model = new BookingViewModel();
    bookingView.DataContext = model;

    bookingView.ShowDialog();
}

它创建了 BookingViewBookingViewModel 的实例。我正在尝试使用依赖注入而不是像这样创建实例。

MenuViewModel

public class MenuViewModel : IViewMainWindowViewModel
{
    //commands
    public ICommand bookingCommand { get; set; }

    public MenuViewModel()
    {
        bookingCommand = new RelayCommand(bookingCommand_DoWork, () => true);
    }

    public void bookingCommand_DoWork(object obj)
    {
        BookingView bookingView = new BookingView();
        BookingViewModel model = new BookingViewModel();
        bookingView.DataContext = model;

        bookingView.ShowDialog();
    }
}

IViewMainWindowViewModel 是一个空接口,它在我的 MainWindow 和 MenuViewModel 之间建立契约。

我用的unity启动方式

protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        IUnityContainer container = new UnityContainer();
        container.RegisterType<IViewMainWindowViewModel, MainWindow>();
        container.RegisterType<IViewMainWindowViewModel, MenuViewModel>();
        container.RegisterType<IViewBookingViewModel, BookingViewModel>();
        container.RegisterType<IViewBookingViewModel, BookingView>();

        container.Resolve<MainWindow>().Show();
        //Do the same actions for  all views and their viewmodels
    }

好吧,我的 MenuViewModel 取决于 BookingViewBookingViewModel

我应该将其注入构造函数还是?

希望有人能给点建议。

更新(目前为我工作)

到目前为止我做了什么:

1.应用程序 Class

public partial class App : Application
{
    public IUnityContainer _container;

    public IUnityContainer UnityContainer
    {
        get
        {
            if (_container == null)
            {
                _container = new UnityContainer();

            }
            return _container;
        }
    }

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        _container = new UnityContainer();
        _container.RegisterType<IViewMainWindowViewModel, MainWindow>();
        _container.RegisterType<IViewMainWindowViewModel, MenuViewModel>();
        _container.RegisterType<IViewBookingViewModel, BookingView>();
        _container.RegisterType<IViewBookingViewModel, BookingViewModel>();

        _container.RegisterType(typeof(IDialogService<>), typeof(DialogService<>));

        _container.Resolve<MainWindow>().Show();
    }

}

2。创建 IDialogService 并从 App class.

导入容器
public interface IDialogService<T>
{
    void Show();
    void ShowDialog();
}

public class DialogService<T> : IDialogService<T> where T : Window
{

    public void Show()
    {
        var container = ((App)Application.Current).UnityContainer;
        container.Resolve<T>().Show();
    }

    public void ShowDialog()
    {
        var container = ((App)Application.Current).UnityContainer;
        container.Resolve<T>().ShowDialog();
    }
}

我现在在 Show 和 ShowDialog 方法中遇到 错误

"T" 不包含 "Show" / "ShowDialog" 的定义并且没有扩展方法 "Show" / "ShowDialog" 接受第一个T.

类型的参数

3。在 MenuViewModel

中注入服务
public class MenuViewModel : IViewMainWindowViewModel
{
    //commands
    public ICommand bookingCommand { get; set; }

    //entities
    private IDialogService<BookingView> _dialogService;


    public MenuViewModel(IDialogService<BookingView> dialogService)
    {
        // Injecting
        _dialogService = dialogService;

        bookingCommand = new RelayCommand(bookingCommand_DoWork, () => true);
    }

    public void bookingCommand_DoWork(object obj)
    {
        _dialogService.ShowDialog();

    }
}

当您编写以下行代码时:

BookingView bookingView = new BookingView();
BookingViewModel model = new BookingViewModel();
bookingView.DataContext = model;
bookingView.ShowDialog();

您违反了 MVVM 规则,因为 viewmodel 应该什么都不知道 Views。我建议你使用 DataTemplates

如果要根据ViewModel动态切换Views,用DataTemplates比较合适:

<Window>
   <Window.Resources>
      <DataTemplate DataType="{x:Type ViewModelA}">
         <localControls:ViewAUserControl/>
      </DataTemplate>
      <DataTemplate DataType="{x:Type ViewModelB}">
         <localControls:ViewBUserControl/>
      </DataTemplate>
   <Window.Resources>
  <ContentPresenter Content="{Binding CurrentView}"/>
</Window>

如果Window.DataContextViewModelA的实例,则显示ViewA,而Window.DataContextViewModelB的实例,则[=23] =] 将显示。

我见过和读过的最好的例子是 Rachel Lim 做的。 See the example.

在某些情况下,我们必须将视图作为模型或无模式对话框启动。为了保持 MVVM 的边界,我宁愿创建一个单独的服务来将视图作为对话框启动,以便它可以在整个应用程序中以通用方式使用。并将通过想要启动任何对话框的构造函数将此服务注入 ViewModel。

public interface IDialogService<T>
{
    void Show();
    void ShowDialog();
}

public class DialogService<T> : IDialogService<T> where T : Window
{
    public void Show()
    {
        container.Resolve<T>().Show();
    }

    public void ShowDialog()
    {
        container.Resolve<T>().ShowDialog();
    }
}

现在我将把这个服务注入到各自的视图模型中。

public class MenuViewModel : IViewMainWindowViewModel
{
    //commands
    public ICommand bookingCommand { get; set; }

    private IDialogService<BookingView> _dialogService;
    public MenuViewModel(IDialogService<BookingView > dialogService)
    {
        _dialogService = dialogService
        bookingCommand = new RelayCommand(bookingCommand_DoWork, () => true);
    }

    public void bookingCommand_DoWork(object obj)
    {
        //Since you want to launch this view as dialog you can set its datacontext in its own constructor.

        _dialogService.ShowDialog();
    }
}

在App.xaml.cs中你可以像下面这样定义属性。然后你可以使用 属性 UnityContainer 然后在服务中你可以获得像 var container = ((App) Application.Current).UnityContainer;

这样的容器
    public IUnityContainer _container;
    public IUnityContainer UnityContainer
    {
        get
        {
            if (_container == null)
            {
              _container = new UnityContainer();

            }
            return _container;
        }
    }

这样做将有助于保持您的 VM 可测试,因为您也可以通过测试注入服务的模拟。