如何在UWP应用程序中使用autofac?
我在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() .As() .SingleInstance(); containerBuilder.RegisterType() .As() .SingleInstance(); containerBuilder.RegisterType() .As() .SingleInstance(); ... containerBuilder.RegisterType() .As(); // Auto-magically resolves the IFacade implementation. var facadeContainer = containerBuilder.Build(); var facadeLifetimeScope = m_facadeContainer.BeginLifetimeScope(); return facadeLifetimeScope.Resolve(); } }
我需要将我的IFacade
实例传递给不同的Page
以访问我的视图模型。 以下是我的一个页面的示例:
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负责Page
的实例化,因此它限制了我的选择。 以下是在UWP中从一个页面导航到另一个页面的方式。 从App
实例:
rootFrame.Navigate(typeof(MainPage), e.Arguments);
或者从Page
实例:
ContentFrame.Navigate(typeof(SomeOtherPage));
题
将我的IFacade
实例传递给视图模型的正确方法是什么(显然没有做任何hacky)?
因为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() .As() .SingleInstance(); containerBuilder.RegisterType() .As() .SingleInstance(); containerBuilder.RegisterType() .As() .SingleInstance(); containerBuilder.RegisterType() .As(); //...Register ViewModels as well containerBuilder.RegisterType() .AsSelf(); //... var container = containerBuilder.Build(); return container; } //... }
然后根据需要解决视图中的依赖关系。
internal sealed partial class SomePage { public SomePage() { InitializeComponent(); ViewModel = App.Container.Resolve(); this.DataContext = ViewModel; } public SomePageViewModel ViewModel { get; private set; } protected override void OnNavigatedTo(NavigationEventArgs e) { ViewModel.LoadAsync(); base.OnNavigatedTo(e); } }
另一种更复杂的方法是使用约定基础方法并使用应用程序的框架导航。
计划是订阅NavigatedTo
事件
public interface INavigationService { bool Navigate() where TView : Page; bool Navigate(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 () where TView : Page { return frame.Navigate(typeof(TView)); } public bool Navigate(object parameter = null) where TView : Page { var context = new NavigationContext(typeof(TViewModel), parameter); return frame.Navigate(typeof(TView), context); } }
并且一旦使用navigation参数传递要解析的视图模型类型并将数据绑定到视图。
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); } }
通过包装器服务访问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 { /// /// Get service of type from the . /// public static TService GetService(this IServiceProvider provider) { return (TService)provider.GetService(typeof(TService)); } /// /// Get an enumeration of services of type from the /// public static IEnumerable
您可以像启动之前那样配置应用程序,但容器将用于初始化导航服务,它将处理其余部分。
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() .As() .SingleInstance(); containerBuilder.RegisterType() .As() .SingleInstance(); containerBuilder.RegisterType() .As() containerBuilder.RegisterType() .AsSelf() .As(); 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 (); 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(); } // 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();
视图可以很简单
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(); } //... }