如何使用具有简单注入器依赖的WPF控件
本文关键字:依赖 WPF 控件 注入器 简单 何使用 | 更新日期: 2023-09-27 18:10:26
我想在我必须向gui控件注入资源的场景中使用依赖注入。因为这可能是错误的地方,我有一些理由在这里做,而不是在视图模型(例如。我需要窗口句柄之类的)。
构造函数参数注入似乎是首选的方式。正如你们大多数人所知道的,WPF控件必须有一个无参数的构造函数,否则XAML无法工作,对于当前的场景,我喜欢保留我的XAML,因为它包含一些名称注册和绑定。
那么:我如何在WPF+XAML场景中使用构造器di和(如果可能的话,在简单注入器的情况下)?
是否存在标记扩展,或者XAML解析器是否可以实现容器感知并接受具有参数的构造函数作为控件?方案的例子:
<Grid>
<gg:WhateverResourceNeedingViewer ItemSource={Binding Items}/>
</Grid>
:
public class WhateverResourceNeedingViewer : ItemsControl
{
public WhateverResourceNeedingViewer(Dep1 d, DepResource d2)
{
...
}
...
}
不仅使用SOLID设计原则构建视图模型,而且在视图中也这样做是很好的实践。使用usercontrols可以帮助您实现这一点。
你建议的方法的缺点是,如果技术上可能的话,这个设计将违反SRP和OCP。
SRP,因为你的用户控件需要的所有依赖都必须注入到消费窗口/视图中,而这个视图可能不需要(全部)这些依赖。
和OCP,因为每当你从用户控件中添加或删除依赖项时,你也需要从消费窗口/视图中添加或删除它。
有了usercontrols,你就可以像组合服务、命令和查询处理程序等类一样组合视图。当涉及到依赖注入时,组合应用程序的位置是组合根
WPF中的ContentControls都是关于从应用程序中的其他"内容"中"组合"你的视图。
像Caliburn Micro这样的MVVM工具通常使用contentcontrols来注入带有自己视图模型的usercontrol视图(读取:xaml,没有后面的代码)。事实上,在使用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工具将使用配置约定将视图模型绑定到相应的视图。
如果你不使用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...
}
这可能被认为是一种反模式——在许多层面上——(详细信息请参阅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>();
}