如何在 UWP 应用中使用依赖注入?

2023-12-19

我在 UWP 应用程序中使用 autofac。在我的App例如,我正在设置依赖项,如下所示:

public sealed partial class App
{
   private readonly IFacade m_facade;

   public App()
   {
       InitializeComponent();

       m_facade = InitializeDependencies();

       Suspending += OnSuspending;
   }

   private IFacade InitializeDependencies()
   {
       var containerBuilder = new ContainerBuilder();

       //  Registers all the platform-specific implementations of services.
       containerBuilder.RegisterType<LoggingService>()
                       .As<ILoggingService>()
                       .SingleInstance();

       containerBuilder.RegisterType<SQLitePlatformService>()
                       .As<ISQLitePlatformService>()
                       .SingleInstance();

       containerBuilder.RegisterType<DiskStorageService>()
                       .As<IDiskStorageService>()
                       .SingleInstance();

       ...

       containerBuilder.RegisterType<Facade>()
                       .As<IFacade>();

       //  Auto-magically resolves the IFacade implementation.
       var facadeContainer = containerBuilder.Build();
       var facadeLifetimeScope = m_facadeContainer.BeginLifetimeScope();

       return facadeLifetimeScope.Resolve<IFacade>();
   }
}

我需要通过我的IFacade围绕不同的实例Pages 到达我的视图模型。这是我的一个页面的示例:

internal sealed partial class SomePage
{
    public SomePageViewModel ViewModel { get; }

    public SomePage()
    {
        ViewModel = new SomePageViewModel(/* need an IFacade implementation here!! */);

        InitializeComponent();
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        ViewModel.LoadAsync();

        base.OnNavigatedTo(e);
    }
}

UWP 负责Pages 实例化,因此它限制了我的选择。以下是在 UWP 中如何完成从一个页面到另一页面的导航。来自App实例:

rootFrame.Navigate(typeof(MainPage), e.Arguments);

或者从一个Page实例:

ContentFrame.Navigate(typeof(SomeOtherPage));

Question

通过我的正确方法是什么IFacade围绕视图模型的实例(显然没有做任何黑客行为)?


因为 UWP 负责Page的实例化消除了将依赖项显式注入视图的能力。

最简单的方法是拥有一个可访问的服务定位器并用它注册您的依赖项。

public sealed partial class App {

    public App() {
        InitializeComponent();

        Container = ConfigureServices();

        Suspending += OnSuspending;
    }

    public static IContainer Container { get; set; }

    private IContainer ConfigureServices() {
        var containerBuilder = new ContainerBuilder();

        //  Registers all the platform-specific implementations of services.
        containerBuilder.RegisterType<LoggingService>()
                       .As<ILoggingService>()
                       .SingleInstance();

        containerBuilder.RegisterType<SQLitePlatformService>()
                       .As<ISQLitePlatformService>()
                       .SingleInstance();

        containerBuilder.RegisterType<DiskStorageService>()
                       .As<IDiskStorageService>()
                       .SingleInstance();

        containerBuilder.RegisterType<Facade>()
                       .As<IFacade>();

        //...Register ViewModels as well

        containerBuilder.RegisterType<SomePageViewModel>()
            .AsSelf();

        //...

        var container = containerBuilder.Build();
        return container;
   }

   //...
}

然后根据需要在视图中解决依赖关系。

internal sealed partial class SomePage {

    public SomePage() {
        InitializeComponent();
        ViewModel = App.Container.Resolve<SomePageViewModel>();
        this.DataContext = ViewModel;
    }

    public SomePageViewModel ViewModel { get; private set; }

    protected override void OnNavigatedTo(NavigationEventArgs e) {
        ViewModel.LoadAsync();
        base.OnNavigatedTo(e);
    }
}

另一种更复杂的方法是使用基于约定的方法并利用应用程序的框架导航。

该计划将是订阅NavigatedTo event

public interface INavigationService {
    bool Navigate<TView>() where TView : Page;
    bool Navigate<TView, TViewModel>(object parameter = null) where TView : Page;
}

public class NavigationService : INavigationService {
    private readonly Frame frame;
    private readonly IViewModelBinder viewModelBinder;

    public NavigationService(IFrameProvider frameProvider, IViewModelBinder viewModelBinder) {
        frame = frameProvider.CurrentFrame;
        frame.Navigating += OnNavigating;
        frame.Navigated += OnNavigated;
        this.viewModelBinder = viewModelBinder;
    }

    protected virtual void OnNavigating(object sender, NavigatingCancelEventArgs e) { }

    protected virtual void OnNavigated(object sender, NavigationEventArgs e) {
        if (e.Content == null)
            return;

        var view = e.Content as Page;
        if (view == null)
            throw new ArgumentException("View '" + e.Content.GetType().FullName +
                "' should inherit from Page or one of its descendents.");

        viewModelBinder.Bind(view, e.Parameter);
    }

    public bool Navigate<TView>() where TView : Page {
        return frame.Navigate(typeof(TView));
    }

    public bool Navigate<TView, TViewModel>(object parameter = null) where TView : Page {
        var context = new NavigationContext(typeof(TViewModel), parameter);
        return frame.Navigate(typeof(TView), context);
    }
}

然后使用导航参数传递要解析的视图模型类型并将数据绑定到视图。

public interface IViewModelBinder {
    void Bind(FrameworkElement view, object viewModel);
}

public class ViewModelBinder : IViewModelBinder {
    private readonly IServiceProvider serviceProvider;

    public ViewModelBinder(IServiceProvider serviceProvider) {
        this.serviceProvider = serviceProvider;
    }

    public void Bind(FrameworkElement view, object viewModel) {
        InitializeComponent(view);

        if (view.DataContext != null)
            return;

        var context = viewModel as NavigationContext;
        if (context != null) {
            var viewModelType = context.ViewModelType;
            if (viewModelType != null) {
                viewModel = serviceProvider.GetService(viewModelType);
            }

            var parameter = context.Parameter;
            //TODO: figure out what to do with parameter
        }

        view.DataContext = viewModel;
    }

    static void InitializeComponent(object element) {
        var method = element.GetType().GetTypeInfo()
            .GetDeclaredMethod("InitializeComponent");

        method?.Invoke(element, null);
    }
}

The Frame通过包装服务访问,该服务从当前窗口中提取它

public interface IFrameProvider {
    Frame CurrentFrame { get; }
}

public class DefaultFrameProvider : IFrameProvider {
    public Frame CurrentFrame {
        get {
            return (Window.Current.Content as Frame);
        }
    }
}

以下扩展类提供实用程序支持

public static class ServiceProviderExtension {
    /// <summary>
    /// Get service of type <typeparamref name="TService"/> from the <see cref="IServiceProvider"/>.
    /// </summary>
    public static TService GetService<TService>(this IServiceProvider provider) {
        return (TService)provider.GetService(typeof(TService));
    }
    /// <summary>
    /// Get an enumeration of services of type <paramref name="serviceType"/> from the <see cref="IServiceProvider"/>
    /// </summary>
    public static IEnumerable<object> GetServices(this IServiceProvider provider, Type serviceType) {
        var genericEnumerable = typeof(IEnumerable<>).MakeGenericType(serviceType);
        return (IEnumerable<object>)provider.GetService(genericEnumerable);
    }
    /// <summary>
    /// Get an enumeration of services of type <typeparamref name="TService"/> from the <see cref="IServiceProvider"/>.
    /// </summary>
    public static IEnumerable<TService> GetServices<TService>(this IServiceProvider provider) {
        return provider.GetServices(typeof(TService)).Cast<TService>();
    }
    /// <summary>
    /// Get service of type <paramref name="serviceType"/> from the <see cref="IServiceProvider"/>.
    /// </summary>
    public static object GetRequiredService(this IServiceProvider provider, Type serviceType) {
        if (provider == null) {
            throw new ArgumentNullException("provider");
        }

        if (serviceType == null) {
            throw new ArgumentNullException("serviceType");
        }

        var service = provider.GetService(serviceType);
        if (service == null) {
            throw new InvalidOperationException(string.Format("There is no service of type {0}", serviceType));
        }
        return service;
    }
    /// <summary>
    /// Get service of type <typeparamref name="T"/> from the <see cref="IServiceProvider"/>.
    /// </summary>
    public static T GetRequiredService<T>(this IServiceProvider provider) {
        if (provider == null) {
            throw new ArgumentNullException("provider");
        }
        return (T)provider.GetRequiredService(typeof(T));
    }
}

public class NavigationContext {
    public NavigationContext(Type viewModelType, object parameter = null) {
        ViewModelType = viewModelType;
        Parameter = parameter;
    }
    public Type ViewModelType { get; private set; }
    public object Parameter { get; private set; }
}

public static class NavigationExtensions {
    public static bool Navigate<TView>(this Frame frame) where TView : Page {
        return frame.Navigate(typeof(TView));
    }

    public static bool Navigate<TView, TViewModel>(this Frame frame, object parameter = null) where TView : Page {
        var context = new NavigationContext(typeof(TViewModel), parameter);
        return frame.Navigate(typeof(TView), context);
    }
}

您可以像以前一样在启动时配置应用程序,但容器将用于初始化导航服务,并将处理其余的事情。

public sealed partial class App {

    public App() {
        InitializeComponent();

        Container = ConfigureServices();

        Suspending += OnSuspending;
    }

    public static IContainer Container { get; set; }

    private IContainer ConfigureServices() {
        //... code removed for brevity

        containerBuilder
            .RegisterType<DefaultFrameProvider>()
            .As<IFrameProvider>()
            .SingleInstance();

        containerBuilder.RegisterType<ViewModelBinder>()
            .As<IViewModelBinder>()
            .SingleInstance();

        containerBuilder.RegisterType<AutofacServiceProvider>()
            .As<IServiceProvider>()

        containerBuilder.RegisterType<NavigationService>()
            .AsSelf()
            .As<INavigationService>();


        var container = containerBuilder.Build();
        return container;
    }

    protected override void OnLaunched(LaunchActivatedEventArgs e) {
        Frame rootFrame = Window.Current.Content as Frame;
        if (rootFrame == null) {
            rootFrame = new Frame();
            rootFrame.NavigationFailed += OnNavigationFailed;
            if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) {
                //TODO: Load state from previously suspended application
            }
            // Place the frame in the current Window
            Window.Current.Content = rootFrame;
        }

        //Activating navigation service
        var service = Container.Resolve<INavigationService>();

        if (e.PrelaunchActivated == false) {
            if (rootFrame.Content == null) {
                // When the navigation stack isn't restored navigate to the first page,
                // configuring the new page by passing required information as a navigation
                // parameter
                rootFrame.Navigate<SomePage, SomePageViewModel>();
            }
            // Ensure the current window is active
            Window.Current.Activate();
        }
    }

    public class AutofacServiceProvider : IServiceProvider
        public object GetService(Type serviceType) {
            return App.Container.Resolve(serviceType);
        }
    }

   //...
}

通过使用上述约定Frame扩展允许一些通用导航。

ContentFrame.Navigate<SomeOtherPage, SomeOtherPageViewModel>();

视图可以很简单

internal sealed partial class SomePage {
    public SomePage() {
        InitializeComponent();
    }

    public SomePageViewModel ViewModel { get { return (SomePageViewModel)DataContext;} }

    protected override void OnNavigatedTo(NavigationEventArgs e) {
        ViewModel.LoadAsync();
        base.OnNavigatedTo(e);
    }
}

由于导航服务还会在导航后设置视图的数据上下文。

导航也可以由具有以下功能的视图模型启动INavigationService注入

public class SomePageViewModel : ViewModel {
    private readonly INavigationService navigation;
    private readonly IFacade facade;

    public SomePageViewModel(IFacade facade, INavigationService navigation) {
        this.navigation = navigation;
        this.facade = facade;
    }

    //...

    public void GoToSomeOtherPage() {
        navigation.Navigate<SomeOtherPage, SomeOtherPageViewModel>();
    }

    //...
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 UWP 应用中使用依赖注入? 的相关文章

  • 如何在C++中实现模板类协变?

    是否可以以这样一种方式实现类模板 如果模板参数相关 一个对象可以转换为另一个对象 这是一个展示这个想法的例子 当然它不会编译 struct Base struct Derived Base template
  • 如何在我的应用程序中使用 Windows Key

    Like Windows Key E Opens a new Explorer Window And Windows Key R Displays the Run command 如何在应用程序的 KeyDown 事件中使用 Windows
  • 为什么 POSIX 允许在只读模式下超出现有文件结尾 (fseek) 进行搜索

    为什么寻找文件结尾很有用 为什么 POSIX 让我们像示例中那样在以只读方式打开的文件中进行查找 c http en cppreference com w c io fseek http en cppreference com w c io
  • C# 中可空类型是什么?

    当我们必须使用nullable输入 C net 任何人都可以举例说明 可空类型 何时使用可空类型 https web archive org web http broadcast oreilly com 2010 11 understand
  • 如何在 WPF RichTextBox 中跟踪 TextPointer?

    我正在尝试了解 WPF RichTextBox 中的 TextPointer 类 我希望能够跟踪它们 以便我可以将信息与文本中的区域相关联 我目前正在使用一个非常简单的示例来尝试弄清楚发生了什么 在 PreviewKeyDown 事件中 我
  • c 中的错误:声明隐藏了全局范围内的变量

    当我尝试编译以下代码时 我收到此错误消息 错误 声明隐藏了全局范围内的变量 无效迭代器 节点 根 我不明白我到底在哪里隐藏或隐藏了之前声明的全局变量 我怎样才能解决这个问题 typedef node typedef struct node
  • c# Asp.NET MVC 使用FileStreamResult下载excel文件

    我需要构建一个方法 它将接收模型 从中构建excel 构建和接收部分完成没有问题 然后使用内存流导出 让用户下载它 不将其保存在服务器上 我是 ASP NET 和 MVC 的新手 所以我找到了指南并将其构建为教程项目 public File
  • 当 Cortex-M3 出现硬故障时如何保留堆栈跟踪?

    使用以下设置 基于 Cortex M3 的 C gcc arm 交叉工具链 https launchpad net gcc arm embedded 使用 C 和 C FreeRtos 7 5 3 日食月神 Segger Jlink 与 J
  • .Net Core / 控制台应用程序 / 配置 / XML

    我第一次尝试使用新的 ConfigurationBuilder 和选项模式进入 Net Core 库 这里有很多很好的例子 https docs asp net en latest fundamentals configuration ht
  • 在 ASP.Net Core 2.0 中导出到 Excel

    我曾经使用下面的代码在 ASP NET MVC 中将数据导出到 Excel Response AppendHeader content disposition attachment filename ExportedHtml xls Res
  • A* 之间的差异 pA = 新 A;和 A* pA = 新 A();

    在 C 中 以下两个动态对象创建之间的确切区别是什么 A pA new A A pA new A 我做了一些测试 但似乎在这两种情况下 都调用了默认构造函数 并且仅调用了它 我正在寻找性能方面的任何差异 Thanks If A是 POD 类
  • Windows 10 中 Qt 桌面应用程序的缩放不当

    我正在为 Windows 10 编写一个简单的 Qt Widgets Gui 应用程序 我使用的是 Qt 5 6 0 beta 版本 我遇到的问题是它根本无法缩放到我的 Surfacebook 的屏幕上 这有点难以判断 因为 SO 缩放了图
  • .NET 选项将视频文件流式传输为网络摄像头图像

    我有兴趣开发一个应用程序 它允许我从 xml 构建视频列表 包含视频标题 持续时间等 并将该列表作为我的网络摄像头流播放 这意味着 如果我要访问 ustream tv 或在实时通讯软件上激活我的网络摄像头 我的视频播放列表将注册为我的活动网
  • 用 C 实现 Unix shell:检查文件是否可执行

    我正在努力用 C 语言实现 Unix shell 目前正在处理相对路径的问题 特别是在输入命令时 现在 我每次都必须输入可执行文件的完整路径 而我宁愿简单地输入 ls 或 cat 我已经设法获取 PATH 环境变量 我的想法是在 字符处拆分
  • EPPlus Excel 更改单元格颜色

    我正在尝试将给定单元格的颜色设置为另一个单元格的颜色 该单元格已在模板中着色 但worksheet Cells row col Style Fill BackgroundColor似乎没有get财产 是否可以做到这一点 或者我是否必须在互联
  • 如何在内存中存储分子?

    我想将分子存储在内存中 这些可以是简单的分子 Methane CH4 C H bond length 108 7 pm H H angle 109 degrees But also more complex molecules like p
  • Bing 地图运行时错误 Windows 8.1

    当我运行带有 Bing Map 集成的 Windows 8 1 应用程序时 出现以下错误 Windows UI Xaml Markup XamlParseException 类型的异常 发生在 DistanceApp exe 中 但未在用户
  • 如何使用 ReactiveList 以便在添加新项目时更新 UI

    我正在创建一个带有列表的 Xamarin Forms 应用程序 itemSource 是一个reactiveList 但是 向列表添加新项目不会更新 UI 这样做的正确方法是什么 列表定义 listView new ListView var
  • C++ 成员函数中的“if (!this)”有多糟糕?

    如果我遇到旧代码if this return 在应用程序中 这种风险有多严重 它是一个危险的定时炸弹 需要立即在应用程序范围内进行搜索和销毁工作 还是更像是一种可以悄悄留在原处的代码气味 我不打算writing当然 执行此操作的代码 相反
  • 将 viewbag 从操作控制器传递到部分视图

    我有一个带有部分视图的 mvc 视图 控制器中有一个 ActionResult 方法 它将返回 PartialView 因此 我需要将 ViewBag 数据从 ActionResult 方法传递到 Partial View 这是我的控制器

随机推荐

  • XSLT:如何通过另一个节点查找该节点的值

    我不确定我是否正确地提出了这个问题 这就是为什么我在任何地方都找不到答案 但基本上我需要将一个节点与另一个节点进行匹配 并使用同级节点作为值 这是一个例子
  • Angular 6 中使用 Typescript 进行 Datalayer.push

    当我点击按钮时 我应该发送到数据层信息 但我不知道该怎么做 因为我使用的是 Angular 6 所以我需要使用 Typescript 和 window dataLayer push 不起作用 给我这个错误 Form
  • 如何检测 iOS 8 中 UITextField 上的删除键?

    我对 UITextField 进行了子类化 并实现了 UIKeyInput 协议的 deleteBackward 方法来检测按下的退格键 这在 iOS 7 上工作正常 但在 iOS 8 上不行 当我按退格键时 UITextField 上不再
  • 删除溢出的内联元素行之间的边距

    我正在创建一个基于图块的游戏 并使用块渲染来更新大量图块 我试图以最简单的方式做到这一点 所以我一直在尝试使用 HTML 的默认布局 现在我正在创建 内联块 省略元素之间的空白以避免它们之间的水平空间 但是当块溢出并创建新行时 会有一些垂直
  • 如何在 Swift 中更改 UIBezierPath 的颜色?

    我有一个实例UIBezierPath我想将描边的颜色更改为黑色以外的颜色 有谁知道如何在 Swift 中做到这一点 有了 Swift 5 UIColor has a setStroke https developer apple com d
  • 过滤 Chrome 控制台消息

    有没有办法在 Chrome 控制台中过滤消息 例如 我不想看到来自 包含 JQMIGRATE 的消息 您可以通过在前面添加来否定过滤器 例如 JQMIGRATE将排除包含字符串 JQMIGRATE 的消息 正则表达式过滤器也可以通过这种方式
  • 自动装配依赖项注入失败

    我在 Java EE 应用程序中使用 Spring 和 Hibernate 该项目托管于这个 GitHub 存储库 http github com whirlwin niths 我通过服务使用 Autowired 时遇到问题 如下所示 pa
  • Django 信号 - kwargs['update_fields'] 在通过 django admin 进行模型更新时始终为 None

    我的 django 应用程序中有一个信号 我想检查模型中的某个字段是否已更新 以便我可以继续执行某些操作 我的模型看起来像这样 class Product models Model name models CharField max len
  • Auth::attempt() 在 Laravel 5.5 中不起作用

    我的注册表单正在运行 它将用户存储到数据库 但是当用户登录时 Auth attempt 返回 false 这是我的登录代码 我将密码以 sha1 加密形式存储在 db 中 Route post login function creds ar
  • 将 Pip 包传输到 conda

    我目前正在使用一台共享的 Ubuntu 机器 其中有蟒蛇2 7以及通过安装的多个软件包pip python version Python 2 7 12 pip version pip 18 0 from usr local lib pyth
  • 验证十进制数

    我正在阅读一些 csv 文件 其中包含表示十进制数的字符串 我的麻烦是 很多时候我接收使用不同区域设置的文件写入 例如 file1 csv的price列的值为129 13 是小数点分隔符 file1 csv的price列值为129 13 为
  • 如何计算密码学中的对数?

    我正在尝试对字节执行非线性函数来实现 SAFER 该算法需要计算字节的以 45 为底的对数 我不明白如何做到这一点 log45 201 1 39316393 当我将其分配给一个字节时 该值被截断为 1 并且我无法恢复确切的结果 我该怎么处理
  • 为什么这段 Javascript 代码这么慢?

    我有这段 Javascript 代码 在 Internet Explorer 中每次调用大约需要 600 毫秒 在其他浏览器中花费的时间可以忽略不计 var nvs currentTab var nvs zoomfield var nvs
  • 异步目录搜索器 (LDAP)

    我正在活动目录中执行长时间搜索 并且非常想使用 DirectorySearcher Asynchronous True 微软提供的文档很少MSDN http msdn microsoft com en us library system d
  • PHP 类:从被调用的方法访问调用实例

    很抱歉这个奇怪的话题 但我不知道如何用其他方式表达它 我正在尝试从调用类访问方法 就像这个例子一样 class normalClass public function someMethod this method shall access
  • Javascript/vue.js接收json

    我正在尝试在我的 vue js 应用程序中接收 json 如下所示 new Vue el body data role company list created function this getJson methods getJson f
  • 将对象重新放入 ConcurrentHashMap 是否会导致“发生在”内存关系?

    我正在与existing具有 ConcurrentHashMap 形式的对象存储的代码 映射内存储了可供多个线程使用的可变对象 根据设计 没有两个线程会尝试同时修改一个对象 我关心的是线程之间修改的可见性 目前 对象的代码在 setter
  • dojo multipleDefine与mapkitJS和ArcGIS esri-loader的错误

    我不知道在哪里MapkitJS and esri loader在一起有问题 从这里和其他地方的研究来看 似乎可能与另一个包存在命名冲突 这里有一个link https github com Esri esri loader issues 1
  • 在 v7 中使用 setViewCube 更新视图

    如何在 v7 中使用 setViewCube 更新视图 我在 v6 中使用了以下代码 但它在 v7 中不起作用 viewer setViewCube top front 在 v6 到 v7 的迁移指南中 它说 我应该通过扩展来调用它 ext
  • 如何在 UWP 应用中使用依赖注入?

    我在 UWP 应用程序中使用 autofac 在我的App例如 我正在设置依赖项 如下所示 public sealed partial class App private readonly IFacade m facade public A