UWP 模板 10 和服务依赖注入 (MVVM) 不是 WPF

rab*_*woo 5 uac dependency-injection inversion-of-control uwp template10

我花了两个多星期的时间搜索 google、bing、stack overflow 和 msdn 文档,试图找出如何为我正在开发的移动应用程序进行适当的依赖注入。需要明确的是,我每天都在 Web 应用程序中进行 DI。我不需要关于什么、谁以及为什么 DI 很重要的速成课程。我知道它是,并且我总是拥抱它。

我需要了解的是这在移动应用程序世界中是如何工作的,尤其是 UWP 模板 10 移动应用程序。

从我过去开始,在 .net/Asp 应用程序中,我可以“RegisterType(new XYZ).Singleton() blah”{请原谅语法;只是 App_Start.ConfigureServices 中的一个示例}。这在 .netcore 中的工作原理几乎相同,但进行了一些语法更改。

我的问题是现在我试图将我的 api 提供给需要消化我的 IXYZ 服务的 UWP 应用程序。我绝不认为他们应该每次都“新建”一个实例。必须有一种方法将其注入 UWP 端的容器中;我觉得我在这个过程中遗漏了一些非常简单的东西。

这是我的代码:

应用程序.xaml.cs

public override async Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)
    {
        // TODO: add your long-running task here

        //if (args.Kind == ActivationKind.LockScreen)
        //{

        //}
        RegisterServices();
        await NavigationService.NavigateAsync(typeof(Views.SearchCompanyPage));

    }

public static IServiceProvider Container { get; private set; }

private static void RegisterServices()
{
    var services = new ServiceCollection();
    services.AddSingleton<IXYZ, XYZ>();
    Container = services.BuildServiceProvider();
}
Run Code Online (Sandbox Code Playgroud)

MainPage.xaml.cs:

public MainPage()
{
   InitializeComponent();
   NavigationCacheMode = NavigationCacheMode.Enabled;
}
Run Code Online (Sandbox Code Playgroud)

主页面视图模型:

public class MainPageViewModel : ViewModelBase
{
    private readonly IXYZ _xyz;
    public MainPageViewModel(IXYZ xyz)
    {
        //Stuff
        _xyz= xyz;
    }

}
Run Code Online (Sandbox Code Playgroud)

我现在收到错误消息: 无法构造 XAML MainPage...ViewModel 类型。为了在 XAML 中构造,类型不能是抽象的、接口嵌套的泛型或结构,并且必须具有公共默认构造函数。

我愿意使用任何品牌的 IoC Container,但我需要的是如何在 UWP 应用程序中正确使用 DI 服务的示例。99.9% 的关于 DI 的问题是关于视图(即 Prism?),而不仅仅是服务的简单 DI(即 DataRepo;又名 API/DataService)。

再次,我觉得我错过了一些明显的东西,需要朝正确的方向推动。有人可以向我展示一个示例项目、基本代码或关于我不应该成为程序员的基本信息...请不要那样做(我不知道我的自我是否可以接受)。

rab*_*woo 0

在 @mvermef 和 SO 问题Dependency Injection using Template 10的帮助下,我找到了一个解决方案。事实证明,这是一个兔子洞,我每次都会遇到问题。

第一个问题是让依赖注入发挥作用。一旦我能够从上面的来源中弄清楚这一点,我就能够开始将我的服务注入到 ViewModel 中,并将它们设置到后面代码中的 DataContext 中。

然后,我在将 IXYZ 服务注入 UserControls 的 ViewModel 时遇到了注入问题。

页面及其 ViewModel 工作得很好,但我遇到了 UserControl 的 DataContext 未注入 UserControl 的 ViewModel 的问题。相反,它们是由持有它的页面的 ViewModel 注入的。

最终的解决方案是确保 UserControlXAML中设置 DataContext ,而不是在后面的代码中设置,就像我们对页面所做的那样,然后在后面的代码中创建 DependencyProperty。

要显示基本解决方案,请阅读下文。

为了让它发挥作用,我从以下开始:

应用程序XAML.CS

public override async Task OnStartAsync(StartKind startKind, IActivatedEventArgs args)
{
        // long-running startup tasks go here
       RegisterServices();
       await Task.CompletedTask;
}

private static void RegisterServices()
{
        var services = new ServiceCollection();
        services.AddSingleton<IRepository, Repository>();
        services.AddSingleton<IBinderService, BinderServices>();

        **//ViewModels**
        **////User Controls**
        services.AddSingleton<AddressesControlViewModel, AddressesControlViewModel>();
        services.AddSingleton<CompanyControlViewModel, CompanyControlViewModel>();

        **//ViewModels**
        **////Pages**
        services.AddSingleton<CallListPageViewModel, CallListPageViewModel>();
        services.AddSingleton<CallListResultPageViewModel, CallListResultPageViewModel>();
        etc....

        Container = services.BuildServiceProvider();
}

public override INavigable ResolveForPage(Page page, NavigationService navigationService)
{
       **//INJECT THE VIEWMODEL FOR EACH PAGE**
       **//ONLY THE PAGE NOT USERCONTROL**
        if (page is CallListPage)
        {
            return Container.GetService<CallListPageViewModel>();
        }
        if (page is CallListResultPage)
        {
            return Container.GetService<CallListResultPageViewModel>();
        }

        etc...
        return base.ResolveForPage(page, navigationService);
    }
Run Code Online (Sandbox Code Playgroud)

在页面后面的代码中

调用列表页.XAML.CS

public CallListPage()
    {
        InitializeComponent();
    }

    CallListPageViewModel _viewModel;

    public CallListPageViewModel ViewModel
    {
        get { return _viewModel ?? (_viewModel = (CallListPageViewModel)DataContext); }
    }
Run Code Online (Sandbox Code Playgroud)

在您的 XAML 中添加您的 UserControl

调用列表页.XAML

<binder:CompanyControl Company="{x:Bind ViewModel.SelectedCompany, Mode=TwoWay}"/>

在您的 UserControl 中,确保将 DataContext 添加到 XAML,而不是像我们对页面所做的那样添加到后面的代码。

公司控制.XAML

<UserControl.DataContext>
    <viewModels:CompanyControlViewModel x:Name="ViewModel" />
</UserControl.DataContext>
Run Code Online (Sandbox Code Playgroud)

在 UserControl 代码后面添加一个依赖属性

公司控制.XAML.CS

    public static readonly DependencyProperty CompanyProperty = DependencyProperty.Register(
        "Company", typeof(Company), typeof(CompanyControl), new PropertyMetadata(default(Company), SetCompany));

    public CompanyControl()
    {
        InitializeComponent();
    }

    public Company Company
    {
        get => (Company) GetValue(CompanyProperty);
        set => SetValue(CompanyProperty, value);
    }

    private static void SetCompany(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var control = d as CompanyControl;
        var viewModel = control?.ViewModel;
        if (viewModel != null)
            viewModel.Company = (Company) e.NewValue;
    }
Run Code Online (Sandbox Code Playgroud)

最后,我不确定这是否是一个优雅的解决方案,但它确实有效。