依赖注入以在 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();
}
它创建了 BookingView
和 BookingViewModel
的实例。我正在尝试使用依赖注入而不是像这样创建实例。
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
取决于 BookingView
和 BookingViewModel
。
我应该将其注入构造函数还是?
希望有人能给点建议。
更新(目前为我工作)
到目前为止我做了什么:
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.DataContext
是ViewModelA
的实例,则显示ViewA
,而Window.DataContext
是ViewModelB
的实例,则[=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 可测试,因为您也可以通过测试注入服务的模拟。
我需要一些关于如何在 ViewModel 中处理依赖项注入的建议。我的 viewModel MenuViewModel
有一个 ICommand
方法,当用户单击视图中的按钮时,该方法将 运行 。此方法将打开一个新的 window。方法如下所示。
public void bookingCommand_DoWork(object obj)
{
BookingView bookingView = new BookingView();
BookingViewModel model = new BookingViewModel();
bookingView.DataContext = model;
bookingView.ShowDialog();
}
它创建了 BookingView
和 BookingViewModel
的实例。我正在尝试使用依赖注入而不是像这样创建实例。
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
取决于 BookingView
和 BookingViewModel
。
我应该将其注入构造函数还是?
希望有人能给点建议。
更新(目前为我工作)
到目前为止我做了什么:
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.DataContext
是ViewModelA
的实例,则显示ViewA
,而Window.DataContext
是ViewModelB
的实例,则[=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 可测试,因为您也可以通过测试注入服务的模拟。