用于初学者的 Unity WPF 依赖注入






4.78/5 (19投票s)
本文讨论了对于WPF和Unity新手来说,WPF中依赖注入的基础知识。
引言
当人们第一次开始编写WPF应用程序时,一个很常见的做法是,他们会发现它与传统的Windows Form应用程序非常相似,因此会将所有代码写在代码隐藏文件中。但在几天之后,代码变得庞大且难以管理。
在使用WPF几天后,人们开始了解它的数据绑定功能,这减少了代码隐藏文件中的代码量。
如果使用Model View ViewModel(MVVM)设计模式,可以大大减少代码隐藏文件中的代码量。这种设计模式实际上利用了WPF的数据绑定功能,并提供了良好的关注点分离。因此,视图(XAML)可以由其他人单独开发/设计,而核心开发人员可以专注于业务逻辑。我不打算深入讨论MVVM,可以在这里了解详细信息。
如果使用Unity依赖注入容器的强大功能,这种关注点分离可以进一步增强。依赖注入是构建松散耦合应用程序的首要技术。它提供了处理对象之间依赖关系的方法。例如,一个处理客户信息的对象可能依赖于访问数据存储、验证信息和检查用户是否被授权执行更新的其他对象。依赖注入技术可以确保客户类正确地实例化并填充所有这些对象,特别是在依赖关系可能是抽象的情况下。您可以在这里了解更多关于Unity的信息。
Unity依赖注入容器的概念和工作流程乍一看可能就像阿拉丁神灯中的魔法一样。
像处理神灯一样,Unity DI容器需要谨慎且智能地处理。本文主要介绍我们如何通过一些示例代码轻松地使用这种魔法。(为了方便起见,我附上了这里讨论的示例应用程序的完整代码库。)
在这里,我主要讨论了构造函数注入和属性注入,以便人们可以将其用作快速参考。
Unity依赖容器可以被视为有用对象的“银行”。通常在银行里,我们存钱以便在需要时使用。
因此,在应用程序初始化时,我们将存储各种窗口/用户控件等在应用程序生命周期中将需要的所有对象,如下所示
IUnityContainer container = new UnityContainer();
container.RegisterType<IDataServices, TextDataServices>();
container.RegisterType<ITextViewModel, TextViewModel>();
var window = container.Resolve<MainWindow>();
window.Show();
在我们的应用程序中,我们有一个TextDataServices
,它主要返回一个硬编码的string
,并且我们有一个TextViewModel
,其中我们只有一个string
属性,该属性应该在某个UI标签中设置。在MainWindow.XAML中,我们有两个Label
控件,一个在左边(LabelLeft
),一个在右边(LabelRight
),如下所示
<Label Content="{Binding Path=LabelContnet,FallbackValue=Left}"
Height="28" HorizontalAlignment="Left" Margin="90,85,0,0"
Name="LabelLeft" VerticalAlignment="Top" Width="222" Grid.ColumnSpan="2" />
<Label Content="{Binding Path=LabelContnet,FallbackValue=Right}"
Grid.Column="1" Height="28" HorizontalAlignment="Left"
Margin="90,85,0,0" Name="LabelRight" VerticalAlignment="Top" Width="222" />
现在让我们专注于这个XAML文件的代码隐藏文件(MainWindow.xaml.cs)
public partial class MainWindow : Window
{
/// <summary>
/// Initializes a new instance of the <see cref="MainWindow"> class.
/// </see></summary/>
/// The text view model.
public MainWindow(ITextViewModel textViewModel)
{
InitializeComponent();
Loaded += MainWindow_Loaded;
DataContext = textViewModel;
////Following code actually throws exception as
////Dependency injected after constructed has been called.
////LabelLeft.Content = Services.GetData();
}
[Dependency]
public IDataServices Services { get; set; }
/// <summary>
/// Handles the Loaded event of the MainWindow control.
/// </summary>
/// The source of the event.
/// The <see cref="System.Windows.RoutedEventArgs"/>
/// instance containing the event data.
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
LabelLeft.Content = Services.GetData();
}
在构造函数中,您可以看到有一个类型为ITextViewModel
的参数,当此窗口从App.xaml.cs的OnStartUp
事件中解析时,它将被Unity注入。
在代码隐藏文件中,您还将看到一个类型为IDataServices
的属性,它在顶部有[Dependency]
属性。
当此窗口在运行时解析时,与IDataServices
关联的对象也将被注入。但请记住,在构造函数中,您将无法使用此属性,因为这将在构造函数被调用后注入。这种类型的注入称为属性注入。
为了使用属性注入的优势,您可以在Windows.Loaded
事件中使用此属性,或者您可以在可注入属性的set属性上编写一些代码。
因此,当我们运行代码时,左侧标签将从构造函数注入的TextViewModel
填充,右侧标签将从属性注入的DataServices
填充!
我希望这能让你初步了解如何在WPF中使用构造函数注入和属性注入。当您开始一个大规模的WPF应用程序时,您需要在您的应用程序中使用Unity、MVVM,最终使用Prism。