我想在我必须将资源注入GUI控件的场景中使用依赖注入。由于这可能是错误的地方,我有一些理由在这里做而不是在视图模型中(例如,我需要Window句柄等)。
构造函数参数注入似乎是首选方法。正如大多数人所知,WPF控件必须具有无参数构造函数,否则XAML不起作用,对于当前场景,我喜欢保留我的XAML,因为它包含一些名称注册和绑定。
那么:我如何在WPF + XAML场景中使用构造函数-DI(如果可能的话,在Simple Injector的情况下)?
是否存在标记扩展,或者XAML解析器是否可以成为容器感知并接受具有参数的构造函数作为控件?
方案示例:
<Grid>
<gg:WhateverResourceNeedingViewer ItemSource={Binding Items}/>
</Grid>
和
public class WhateverResourceNeedingViewer : ItemsControl
{
public WhateverResourceNeedingViewer(Dep1 d, DepResource d2)
{
...
}
...
}
答案 0 :(得分:5)
优良作法不仅是使用SOLID设计原则构建视图模型,还要在视图中执行此操作。 usercontrols的使用可以帮助您解决这个问题。
如果技术上可行的话,您建议的方法的缺点是此设计将违反SRP和OCP。
SRP,因为必须在使用窗口/视图中注入usercontrol所需的所有依赖项,而此视图可能不需要(全部)这些依赖项。
和OCP因为你从usercontrol中添加或删除了一个依赖项,你还需要在使用窗口/视图中添加或删除它。
使用usercontrols,您可以像编写其他类(如服务,命令和查询处理程序等)一样编写视图。当涉及到依赖项注入时,编写应用程序的位置是composition root < / p> WPF中的
ContentControls都是关于从应用程序中的其他“内容”“组合”您的视图。
像Caliburn Micro这样的MVVM工具通常使用内容控件来注入一个usercontrol视图(读取:xaml,没有代码隐藏)和它自己的viewmodel。事实上,在使用MVVM时,您将从usercontrols类构建应用程序中的所有视图,这是最佳实践。
这看起来像这样:
public interface IViewModel<T> { }
public class MainViewModel : IViewModel<Someclass>
{
public MainViewModel(IViewModel<SomeOtherClass> userControlViewModel)
{
this.UserControlViewModel = userControlViewModel;
}
public IViewModel<SomeOtherClass> UserControlViewModel { get; private set; }
}
public class UserControlViewModel : IViewModel<SomeOtherClass>
{
private readonly ISomeService someDependency;
public UserControlViewModel(ISomeService someDependency)
{
this.someDependency = someDependency;
}
}
MainView的XAML:
// MainView
<UserControl x:Class="WpfUserControlTest.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<ContentControl Name="UserControlViewModel" />
</Grid>
</UserControl>
// UserControl View
<UserControl x:Class="WpfUserControlTest.UserControlView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<TextBlock Text="SomeInformation"/>
</Grid>
</UserControl>
结果将是MainView
显示在该窗口的DataContext
设置为MainViewModel
的窗口中。内容控件将填充UserControlView
,其DataContext
设置为UserControlViewModel
类。这是自动发生的,因为MVVM工具将使用Convention over configuration将视图模型绑定到相应的视图。
如果您不使用MVVM工具,而是直接在窗口类的代码中注入依赖项,则只需遵循相同的模式即可。在视图中使用ContentControl,就像上面的示例一样,在窗口的构造函数中注入UserControl
(带有包含参数的构造函数)。然后只需将ContentControl的Content
属性设置为UserControl的注入实例。
这看起来像是:
public partial class MainWindow : Window
{
public MainWindow(YourUserControl userControl)
{
InitializeComponent();
// assuming you have a contentcontrol named 'UserControlViewModel'
this.UserControlViewModel.Content = userControl;
}
// other code...
}
答案 1 :(得分:0)
这可能被认为是一种反模式-从多个层面上来说(请参阅Ric的答案以获取详细信息),但是如果您只是想使它起作用,希望务实并有一个简单的用例,我建议静态DI解析器。这对于遗留组件或诸如此类受基础架构约束的情况非常有用。
/// <summary>
/// Provides static resolution of Simple Injector instances.
/// </summary>
public class ServiceResolver
{
private Container Container { get; }
private static ServiceResolver Resolver { get; set; }
public ServiceResolver(Container container)
{
Container = container;
Resolver = this;
}
public static T GetInstance<T>()
{
if (Resolver == null) throw new InvalidOperationException($"{nameof(ServiceResolver)} must be constructed prior to use.");
return (T) Resolver.Container.GetInstance(typeof(T));
}
}
用法,根据您的示例:
public WhateverResourceNeedingViewer()
{
InitializeComponent();
// Resolve view model statically to fulfill no-arg constructor for controls
DataContext = ServiceResolver.GetInstance<DepResource>();
}