因为 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>();
}
//...
}