How to use autofac in an UWP app?

后端 未结 4 737
伪装坚强ぢ
伪装坚强ぢ 2021-02-04 18:58

I am using autofac in an UWP application. In my App instance, I am setting up the dependency, like this:

public sealed partial class App
{
   privat         


        
相关标签:
4条回答
  • 2021-02-04 19:31

    Because UWP is responsible for the Page's instantiation it removes the ability to explicitly inject dependencies into views.

    The simplest approach would be to have an accessible service locator and register your dependencies with it.

    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;
       }
    
       //...
    }
    

    And then resolve dependencies as needed in the views.

    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);
        }
    }
    

    Another more complicated way would be to use a convention base approach and tapping into the application's Frame navigation.

    The plan would be to subscribe to the 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);
        }
    }
    

    and once there using the navigation argument to pass the view model type to be resolved and data bind to the view.

    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 is accessed via a wrapper service that extracts it from the current window

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

    And the following extension classes provide utility support

    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);
        }
    }
    

    You could configure the application like you would before at start up but the container will be used to initialize the navigation service and it will handle the rest.

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

    By using the above convention the Frame extensions allow some generic navigation.

    ContentFrame.Navigate<SomeOtherPage, SomeOtherPageViewModel>();
    

    Views can be as simple as

    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);
        }
    }
    

    As the navigation service would also set the data context of the view after navigation.

    Navigation can also be initiated by view models that have the INavigationService injected

    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>();
        }
    
        //...
    }
    
    0 讨论(0)
  • 2021-02-04 19:38

    Just to add to Nkosi's fantastic and deep question, when I create pages in UWP I use the following pattern:

    private IDependency _dep1;
    
    public Page()
    {
        _dep1 = ServiceLocator.Current.Resolve<IDependency>();
        init();
    }
    
    public Page(IDependency dep1, ...)
    {
        _dep1 = dep1;
        init();
    }
    
    private void init()
    {
        /* Do initialization here, i.e. InitializeComponent() */
    }
    

    The benefit that this gives is that it allows you to still write testable code because you inject your dependencies in your unit tests. The service locator only runs at run time.

    As Nkosi points out, the Frame is responsible ultimately for instantiation of the Page via the Navigate method. At the point that Microsoft exposes the ability to intercept or override the instantiation, it'll be possible to have a DI container do the instantiation. Until then, we're stuck with using a Service Locator pattern at runtime.

    0 讨论(0)
  • 2021-02-04 19:42

    I'm very new to UWP (but a very old hand at .NET/WPF etc etc), so this might be hacky, but I by-passed the Frame.Navigate method altogether by putting the following at the top of my OnLaunched method:

            if (Window.Current.Content == null)
            {
                var windowFrame = new Frame()
                {
                    Content = new MainPage()
                };
    
                Window.Current.Content = windowFrame;
                Window.Current.Activate();
    
                return;
            }
    

    You then control the creation of your mainpage - if you want to support DI, then you can use something like:

                var windowFrame = new Frame()
                {
                    Content = container.GetInstance<MainPage>()
                };
    

    This then means that MainPage becomes your CompositionRoot and your DI flows from there.

    My guess is that you then don't get any navigate back/forward functionality, although I may be wrong...my app is only single-page so I've not seen if that is a problem or not...

    0 讨论(0)
  • 2021-02-04 19:51

    Instead of using a smelly locator or some other hack to intercept the frame's navigation, I recommend to avoid the Frame as content host and also avoid Page as content.

    In WPF, the recommended view management should be based on the view-model-first pattern. Also in WPF it is not recommended to use the heavy Frame as content host. Both recommendations also apply to UWP.

    We should view a UWP application as single-page application (SPA) in terms of pages based on the Page class and the hosting root frame.

    This means MainPage.xaml is only used as host for our custom page system, which is based on a ContentControl, a set of page view models and a set of DataTemplate definition for each page view model and a PageNavigationViewModel class that controls the page navigation. MainPage (or Page) is the equivalent to the WPF Window: the element tree root or visual host.

    The pattern

    The following example shows the pattern by seetting up a basic two page application (a landing page and a settings page) using dependency injection with Autofac (which of course can be replaced with any other IoC framework).
    The important detail is not the IoC framework or the IoC container configuration, but the way the application is structured to allow page navigation combined with dependency injection.

    The Goal
    The goal is to display a LandingPage view (UserControl) based on a LandingPageViewModel and a SettingsPage view based on a SettingsPageViewModel.
    All view model instances are created by the IoC container, while all associated views are instantiated implicitly by the UWP framework using DataTemplate.

    The example is structured into three sections:

    1. Setting up the navigation infrastructure
    2. Creating the pages/views
    3. Bootstrapping the application

    There is some extra complexity like factories introduced due to depndency injection. In a real world example, we would depend on interfaces instead of concrete implementations (Dependencsy Inversion principle). For simplicity there are no interfaces used except those that are relevant to the infrastructure.


    1 Setting up the navigation infrastructure

    PageId.cs
    Each view is identified by an enum. This makes selecting a page model e.g., via command parameter and refactoring easier. It also eliminates magic strings.

    public enum PageId
    {
      Undefined = 0,
      LandingPage,
      SettingsPage
    }
    

    Factory delegates (Autofac specific details)
    The delegates are required to allow Autofac to create factories. Other IoC frameworks may h ave a different requirement to generate factories. MEF for example uses the ExportFactory<T> type as constructor depndency. The framework would then automatically generate the appropriate factory.

    IoC generated factories allow dynamic type creation, where the instances are wired up according to the IoC container configuration. Factories are used to avoid passing around a reference to the IoC container (or even worse making the container a Singleton). Such practice is an anti-pattern, which contradicts the use of the IoC container.

    The delagates should be added to the common namespace of the PageIndexFactory and Bootstrapper classes (see below) and marked as internal.

    private LandingPageModelFactory LandingPageModelFactory { get; }
    private SettingsPageModelFactory SettingsPageModelFactory { get; }
    

    PageIndexFactory.cs
    The individual page view models are initialized by a PageIndexFactory, which is injected into the PageNavigationViewModel. The purpose is to create the navigation index. PageIndexFactory makes use of delegate factories.
    Every serious IoC framework supports auto-generation of factories. This way the IoC container is still able to wire up the dependencies (note that passing around the original IoC container instance is an anti-pattern).

    public class PageIndexFactory
    {
      private LandingPageModelFactory LandingPageModelFactory { get; }
      private SettingsPageModelFactory SettingsPageModelFactory { get; }
    
      public PageIndexFactory(LandingPageModelFactory landingPageModelFactory,
        SettingsPageModelFactory settingsPageModelFactory)
      {
        this.LandingPageModelFactory = landingPageModelFactory;
        this.SettingsPageModelFactory = settingsPageModelFactory;
      }
    
      public Dictionary<PageId, IPageModel> CreateIndex()
      {
        var index = new Dictionary<PageId, IPageModel>()
        {
          {PageId.LandingPage, this.LandingPageModelFactory.Invoke()},
          {PageId.SettingsPage, this.SettingsPageModelFactory.Invoke()}
        };
        return index;
      }
    }
    

    PageNavigationViewModel.cs
    This is the view model that handles the navigation. It exposes a SelectViewCommand, which can be assigned to an ICommandSource like a Button. The CommandParameter must be the PageId, which actually selects the IPageModel from the page index. The PageNavigationViewModel is assigned to the application's original MainPage, which is the host of the custom navigation infrastructure.

    The PageNavigationViewModel exposes a SelectedView property which holds a view model e.g., IPageModel. This property is bound to the hosting ContentControl.Content property.

    public class PageNavigationViewModel : INotifyPropertyChanged
    {
      public PageNavigationViewModel(PageIndexFactory pageIndexFactory)
      {
        this.PageIndex = pageIndexFactory.CreateIndex();
        if (this.PageIndex.TryGetValue(PageId.LandingPage, out IPageModel welcomePageModel))
        {
          this.SelectedView = welcomePageModel;
        }
      }
    
      private void ExecuteSelectPage(object commandParameter)
      {
        var pageId = (PageId) commandParameter;
        if (this.PageIndex.TryGetValue(pageId, out IPageModel selectedPageModel))
        {
          this.SelectedView = selectedPageModel;
        }
      }
    
      public ICommand SelectViewCommand => new RelayCommand(ExecuteSelectPage);
    
      private Dictionary<PageId, IPageModel> PageIndex { get; }
    
      private IPageModel selectedView;   
      public IPageModel SelectedView
      {
        get => this.selectedView;
        set 
        { 
          this.selectedView = value; 
          OnPropertyChanged();
        }
      }
    }
    

    IPageModel.cs
    The interface, which must be implemented by the individual page view models.

    public interface IPageModel : INotifyPropertyChanged
    {
      string PageTitle { get; }
    }
    

    INavigationHost.cs
    This interface is implemented by the application Page host e.g., MainPage. It allows to assign the PageNavigationViewModel anonymously.

    interface INavigationHost
    {
      PageNavigationViewModel NavigationViewModel { get; set; }
    }
    

    MainPage.xaml.cs
    The host of the custom navigation infrastructure. This instance is created via reflection by the hosting Frame. We use the implementation of INavigationHost to initialze this class with an instance of PageNavigationviewModel (see App.xaml.cs* below).
    MainPage is the only class that is not instantiated by the IoC container. As this class has no reponsibilities, except exposing the PageNavigationViewModel and hosting the ContentControl (to host the real pages/views), it will have no relevant dependencies.

    public sealed partial class MainPage : Page, INavigationHost
    {
      private PageNavigationViewModel navigationViewModel;
    
      public PageNavigationViewModel NavigationViewModel
      {
        get => this.navigationViewModel;
        set
        {
          this.navigationViewModel = value;
          this.DataContext = this.NavigationViewModel;
        }
      }
    
      public MainPage()
      {
        this.InitializeComponent();
      }
    }
    

    MainPage.xaml
    Hosts the real view and the DataTemplateSelector for the ContentControl. host and optionally the navigation elements like navigation buttons.

    The ContentControl loads the DataTemplate, that is associated with the IPageModel instance. This DataTemplate contains e.g., a UserControl, which hosts the actual page content.

    The DataContext of each view is set by the ContentControl and is the current Content (which is the PageNavigationViewModel.SelectedView).

    <Page>
      <Page.Resources>
        <local:PageTemplateSelector x:Key="PageTemplateSelector">
          <local:PageTemplateSelector.DataTemplateCollection>
            <DataTemplate x:DataType="local:LandingPageViewModel" 
                          local:Element.DataType="local:LandingPageViewModel">
              <local:LandingPage />
            </DataTemplate>
    
            <DataTemplate x:DataType="local:SettingsPageViewModel" 
                          local:Element.DataType="local:SettingsPageViewModel">
              <local:SettingsPage />
            </DataTemplate>
          </local:PageTemplateSelector.DataTemplateCollection>
        </local:PageTemplateSelector>
      </Page.Resources>
    
      <StackPanel>
    
        <!-- Optional navigation section -->
        <Button Content="Show Settings Page" 
                Command="{x:Bind NavigationViewModel.SelectViewCommand}">
          <Button.CommandParameter>
            <local:PageId>SettingsPage</local:PageId>
          </Button.CommandParameter>
        </Button>
        <Button Content="Show Welcome Page" 
                Command="{x:Bind NavigationViewModel.SelectViewCommand}">
          <Button.CommandParameter>
            <local:PageId>LandingPage</local:PageId>
          </Button.CommandParameter>
        </Button>
    
        <!-- The host of the views -->
        <ContentControl Content="{x:Bind NavigationViewModel.SelectedView, Mode=OneWay}" 
                        ContentTemplateSelector="{StaticResource PageTemplateSelector}" />
      </StackPanel>
    </Page>
    

    PageTemplateSelector.cs
    UWP does not support implicit data templates like WPF. Therefore we have to use a template selector that is assigned to the hosting ContentControl. ContentControl.Content will hold the PageNavigationViewModel.SelectedViw view model and the PageTemplateSelector will select the matching DataTemplate. Since DataTemplate has not DataType property (opposed to the WPF version), we have to introduce an attached property to hoöd this value. Note, that x:DataType is a compiler directive and not accessible by code.

    public class PageTemplateSelector : DataTemplateSelector
    {
      public DataTemplateCollection DataTemplateCollection { get; set; }
      #region Overrides of DataTemplateSelector
    
      public PageTemplateSelector()
      {
        this.DataTemplateCollection = new DataTemplateCollection();
      }
    
      /// <inheritdoc />
      protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
      {
        if (item != null
            && this.DataTemplateCollection.First(template => Element.GetDataType(template) == item.GetType()) is DataTemplate dataTemplate)
        {
          return dataTemplate;
        }
    
        return base.SelectTemplateCore(item, container);
      }
    
      /// <inheritdoc />
      protected override DataTemplate SelectTemplateCore(object item)
      {
        if (item != null
            && this.DataTemplateCollection.First(template => Element.GetDataType(template) == item.GetType()) is
              DataTemplate dataTemplate)
        {
          return dataTemplate;
        }
    
        return base.SelectTemplateCore(item);
      }
    
      #endregion
    }
    

    DataTemplateCollection.cs
    The XAML collection to hold the page view DataTemples definitions. It is used by the PageTemplateSelector.

    public class DataTemplateCollection : List<DataTemplate>
    {}
    

    Element.cs
    Class that defines the attached DataType property, which is used by the PageTemplateSelector to filter the DataTemplate definitions. This attached property should be therefore set on every DataTemplate that is associated with a page view model.

    public class Element : DependencyObject
    {
      #region Type attached property
    
      public static readonly DependencyProperty DataTypeProperty = DependencyProperty.RegisterAttached(
        "DataType",
        typeof(Type),
        typeof(Element),
        new PropertyMetadata(default(Type)));
    
      public static void SetDataType([NotNull] DependencyObject attachingElement, Type value) => attachingElement.SetValue(Element.DataTypeProperty, value);
    
      public static Type GetDataType([NotNull] DependencyObject attachingElement) =>
        (Type) attachingElement.GetValue(Element.DataTypeProperty);
    
      #endregion
    }
    

    2 Creating the pages/views

    All dependencies are resolved by the IoC container. The PageIndexfactory controls the instantiation via auto-generated factories.

    To keep it short, the following example implementation only shows the LandingPage related UserControl and LandingPageViewModel. The same pattern applies to the SettingsPage and SettingsPageViewModel.

    LandingPageViewModel.cs
    The view model for the welcome view.

    public class LandingPageViewModel : IPageModel, INotifyPropertyChanged
    {
      public LandingPageViewModel(IFacade someExampleDependency)
      {
        this.Facade = someExampleDependency;
        this.PageTitle = "Welcome Page";
      }
    
      public string PageTitle { get; }
      private string IFacade Facade { get; }
    }
    

    LandingPage.xaml.cs
    The DataContext is set implicitly by the hosting ContentControl.

    public sealed partial class LandingPage : UserControl
    {
      // Enable x:Bind
      public LandingPageViewModel ViewModel { get; private set; }
    
      public LandingPage()
      {
        this.InitializeComponent();
    
        // Delegate the DataContext to the ViewModel property to enable x:Bind
        this.DataContextChanged += OnDataContextChanged;
      }
    
      private void OnDataContextChanged(FrameworkElement sender, DataContextChangedEventArgs args)
      {
        this.ViewModel = args.NewValue as LandingPageViewModel;
      }
    }
    

    LandingPage.xaml
    The view that is displayed via DataTemplate by the ContentControl.

    <UserControl>    
      <Grid>
        <TextBlock Text="{x:Bind ViewModel.PageTitle}" />
      </Grid>
    </UserControl>
    

    3 Bootstrapping the application

    The following section shows how to set up the IoC container and bootstrap the UWP application.

    Bootstrapper.cs
    Encapsulates the IoC container configuration and bootstrapping of the application.

    internal sealed class Bootstrapper
    {
      internal static void InitializeApplication(INavigationHost navigationHost)
      {
        // Don't use the dependency container outside this class
        using (IContainer services = Bootstrapper.BuildDependencies())
        {
          PageNavigationViewModel navigationViewModel = services.Resolve<PageNavigationViewModel>();
          navigationHost.NavigationViewModel = navigationViewModel;
        }
      }
    
      internal static IContainer BuildDependencies()
      {
        var builder = new ContainerBuilder();
        builder.RegisterType<PageIndexFactory>();
        builder.RegisterType<PageNavigationViewModel>();
        builder.RegisterType<LandingPageViewModel>();
        builder.RegisterType<SettingsPageViewModel>();
    
        // Dependency to address your question
        builder.RegisterType<Facade>().As<IFacade>();
    
        // Don't forget to dispose the IContainer instance (caller's responsibility)
        return builder.Build();
      }
    }
    

    App.xaml.cs
    Bootstarpping the UWP application.

    sealed partial class App : Application
    {
        /// <summary>
        /// Initializes the singleton application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().
        /// </summary>
        public App()
        {
            this.InitializeComponent();
            this.Suspending += OnSuspending;
        }
    
        /// <summary>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used such as when the application is launched to open a specific file.
        /// </summary>
        /// <param name="e">Details about the launch request and process.</param>
        protected override void OnLaunched(LaunchActivatedEventArgs e)
        {
          Frame rootFrame = Window.Current.Content as Frame;
    
          // Do not repeat app initialization when the Window already has content,
          // just ensure that the window is active
          if (rootFrame == null)
          {
            // Create a Frame to act as the navigation context and navigate to the first page
            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;
          }
    
          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(typeof(MainPage), e.Arguments);
    
    /****************** Build depndencies and initialize navigation *************/
    
              if (rootFrame.Content is INavigationHost navigationHost)
              {
                Bootstrapper.InitializeApplication(navigationHost);
              }
            }
    
            // Ensure the current window is active
            Window.Current.Activate();
          }
        }
    
        ...
    }
    
    0 讨论(0)
提交回复
热议问题