524 lines
		
	
	
	
		
			21 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			524 lines
		
	
	
	
		
			21 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
#region Apache License 2.0
 | 
						|
/*
 | 
						|
Nuclex Foundation libraries for .NET
 | 
						|
Copyright (C) 2002-2025 Markus Ewald / Nuclex Development Labs
 | 
						|
 | 
						|
Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
you may not use this file except in compliance with the License.
 | 
						|
You may obtain a copy of the License at
 | 
						|
 | 
						|
    http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
Unless required by applicable law or agreed to in writing, software
 | 
						|
distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
See the License for the specific language governing permissions and
 | 
						|
limitations under the License.
 | 
						|
*/
 | 
						|
#endregion // Apache License 2.0
 | 
						|
 | 
						|
using System;
 | 
						|
using System.Collections.Concurrent;
 | 
						|
using System.Collections.Generic;
 | 
						|
using System.Threading.Tasks;
 | 
						|
 | 
						|
using Avalonia.Controls;
 | 
						|
using Avalonia.Interactivity;
 | 
						|
 | 
						|
using Nuclex.Support;
 | 
						|
using Nuclex.Avalonia.AutoBinding;
 | 
						|
 | 
						|
namespace Nuclex.Avalonia {
 | 
						|
 | 
						|
  /// <summary>Manages an application's windows and views</summary>
 | 
						|
  public class WindowManager : Observable, IWindowManager {
 | 
						|
 | 
						|
    #region class WindowManagerScope
 | 
						|
 | 
						|
    /// <summary>Global scope that uses the WindowManager's CreateInstance()</summary>
 | 
						|
    public class WindowManagerScope : IWindowScope {
 | 
						|
 | 
						|
      /// <summary>Initializes a new global window scope</summary>
 | 
						|
      /// <param name="windowManager">
 | 
						|
      ///   Window manager whose CreateInstance() method will be used
 | 
						|
      /// </param>
 | 
						|
      public WindowManagerScope(WindowManager windowManager) {
 | 
						|
        this.windowManager = windowManager;
 | 
						|
      }
 | 
						|
 | 
						|
      /// <summary>Creates an instance of the specified type in the scope</summary>
 | 
						|
      /// <param name="type">Type an instance will be created of</param>
 | 
						|
      /// <returns>The created instance</returns>
 | 
						|
      object IWindowScope.CreateInstance(Type type) {
 | 
						|
        return this.windowManager.CreateInstance(type);
 | 
						|
      }
 | 
						|
 | 
						|
      /// <summary>Does nothing because this is the global fallback scope</summary>
 | 
						|
      void IDisposable.Dispose() {}
 | 
						|
 | 
						|
      /// <summary>WindowManager whose CreateInstance() method will be wrapped</summary>
 | 
						|
      private WindowManager windowManager;
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    #endregion // class WindwoManagerScope
 | 
						|
 | 
						|
    #region class CancellableDisposer
 | 
						|
 | 
						|
    /// <summary>Disposes an object that potentially implements IDisposable</summary>
 | 
						|
    private struct CancellableDisposer : IDisposable {
 | 
						|
 | 
						|
      /// <summary>Initializes a new cancellable disposer</summary>
 | 
						|
      /// <param name="potentiallyDisposable">
 | 
						|
      ///   Object that potentially implements IDisposable
 | 
						|
      /// </param>
 | 
						|
      public CancellableDisposer(object? potentiallyDisposable = null) {
 | 
						|
        this.potentiallyDisposable = potentiallyDisposable;
 | 
						|
      }
 | 
						|
 | 
						|
      /// <summary>
 | 
						|
      ///   Disposes the assigned object if the disposer has not been cancelled
 | 
						|
      /// </summary>
 | 
						|
      public void Dispose() {
 | 
						|
        IDisposable? disposable = this.potentiallyDisposable as IDisposable;
 | 
						|
        if(disposable != null) {
 | 
						|
          disposable.Dispose();
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      /// <summary>Cancels the call to Dispose(), keeping the object alive</summary>
 | 
						|
      public void Dismiss() {
 | 
						|
        this.potentiallyDisposable = null;
 | 
						|
      }
 | 
						|
 | 
						|
      /// <summary>Assigns a new potentially disposable object</summary>
 | 
						|
      /// <param name="potentiallyDisposable">
 | 
						|
      ///   Potentially disposable object that the disposer will dispose
 | 
						|
      /// </param>
 | 
						|
      public void Set(object? potentiallyDisposable) {
 | 
						|
        this.potentiallyDisposable = potentiallyDisposable;
 | 
						|
      }
 | 
						|
 | 
						|
      /// <summary>Object that will be disposed unless the disposer is cancelled</summary>
 | 
						|
      private object? potentiallyDisposable;
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    #endregion // class CancellableDisposer
 | 
						|
 | 
						|
    /// <summary>Initializes a new window manager</summary>
 | 
						|
    /// <param name="autoBinder">
 | 
						|
    ///   View model binder that will be used to bind all created views to their models
 | 
						|
    /// </param>
 | 
						|
    public WindowManager(IAutoBinder? autoBinder = null) {
 | 
						|
      this.autoBinder = autoBinder;
 | 
						|
 | 
						|
      this.rootWindowActivatedDelegate = rootWindowActivated;
 | 
						|
      this.rootWindowClosedDelegate = rootWindowClosed;
 | 
						|
      this.windowManagerAsScope = new WindowManagerScope(this);
 | 
						|
      this.viewTypesForViewModels = new ConcurrentDictionary<Type, Type>();
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>The currently active top-level or modal window</summary>
 | 
						|
    public Window? ActiveWindow {
 | 
						|
      get { return this.activeWindow; }
 | 
						|
      private set {
 | 
						|
        if(value != this.activeWindow) {
 | 
						|
          this.activeWindow = value;
 | 
						|
          OnPropertyChanged(nameof(ActiveWindow));
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>Opens a view as a new root window of the application</summary>
 | 
						|
    /// <typeparam name="TViewModel">
 | 
						|
    ///   Type of view model a root window will be opened for
 | 
						|
    /// </typeparam>
 | 
						|
    /// <param name="viewModel">
 | 
						|
    ///   View model a window will be opened for. If null, the view model will be
 | 
						|
    ///   created as well (unless the dialog already specifies one as a resource)
 | 
						|
    /// </param>
 | 
						|
    /// <param name="disposeOnClose">
 | 
						|
    ///   Whether the view model should be disposed when the view is closed
 | 
						|
    /// </param>
 | 
						|
    /// <returns>The window that has been opened by the window manager</returns>
 | 
						|
    public Window OpenRoot<TViewModel>(
 | 
						|
      TViewModel? viewModel = null, bool disposeOnClose = false
 | 
						|
    ) where TViewModel : class {
 | 
						|
      Window window = (Window)CreateView(viewModel);
 | 
						|
      window.Activated += this.rootWindowActivatedDelegate;
 | 
						|
      window.Closed += this.rootWindowClosedDelegate;
 | 
						|
 | 
						|
      // If we either created the view model or the user explicitly asked us to
 | 
						|
      // dispose their view model, prepare the window so that it will dispose its
 | 
						|
      // view model when the window is done.
 | 
						|
      if((viewModel == null) || disposeOnClose) {
 | 
						|
        setupViewModelDisposal(window);
 | 
						|
      }
 | 
						|
 | 
						|
      window.Show();
 | 
						|
 | 
						|
      return window;
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>Displays a view as a modal window</summary>
 | 
						|
    /// <typeparam name="TViewModel">
 | 
						|
    ///   Type of the view model for which a view will be displayed
 | 
						|
    /// </typeparam>
 | 
						|
    /// <param name="viewModel">
 | 
						|
    ///   View model a modal window will be displayed for. If null, the view model will
 | 
						|
    ///   be created as well (unless the dialog already specifies one as a resource)
 | 
						|
    /// </param>
 | 
						|
    /// <param name="disposeOnClose">
 | 
						|
    ///   Whether the view model should be disposed when the view is closed
 | 
						|
    /// </param>
 | 
						|
    /// <returns>The return value of the modal window</returns>
 | 
						|
    public Task ShowModalAsync<TViewModel>(
 | 
						|
      TViewModel? viewModel = null, bool disposeOnClose = false
 | 
						|
    ) where TViewModel : class {
 | 
						|
      return ShowModalAsync<object?, TViewModel>(viewModel, disposeOnClose);
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>Displays a view as a modal window</summary>
 | 
						|
    /// <typeparam name="TResult">
 | 
						|
    ///   Type of result the modal dialog will return to the caller
 | 
						|
    /// </typeparam>
 | 
						|
    /// <typeparam name="TViewModel">
 | 
						|
    ///   Type of the view model for which a view will be displayed
 | 
						|
    /// </typeparam>
 | 
						|
    /// <param name="viewModel">
 | 
						|
    ///   View model a modal window will be displayed for. If null, the view model will
 | 
						|
    ///   be created as well (unless the dialog already specifies one as a resource)
 | 
						|
    /// </param>
 | 
						|
    /// <param name="disposeOnClose">
 | 
						|
    ///   Whether the view model should be disposed when the view is closed
 | 
						|
    /// </param>
 | 
						|
    /// <returns>The return value of the modal window</returns>
 | 
						|
    public Task<TResult> ShowModalAsync<TResult, TViewModel>(
 | 
						|
      TViewModel? viewModel = null, bool disposeOnClose = false
 | 
						|
    ) where TViewModel : class {
 | 
						|
      if(this.activeWindow == null) {
 | 
						|
        throw new InvalidOperationException("Showing a modal dialog requires an active window");
 | 
						|
      }
 | 
						|
 | 
						|
      Window window = (Window)CreateView(viewModel);
 | 
						|
      Window? parentWindow = this.activeWindow;
 | 
						|
      window.Activated += this.rootWindowActivatedDelegate;
 | 
						|
 | 
						|
      try {
 | 
						|
        // If we either created the view model or the user explicitly asked us to
 | 
						|
        // dispose his view model, prepare the window so that it will dispose its
 | 
						|
        // view model when the window is done.
 | 
						|
        if((viewModel == null) || disposeOnClose) {
 | 
						|
          setupViewModelDisposal(window);
 | 
						|
        }
 | 
						|
 | 
						|
        return window.ShowDialog<TResult>(this.activeWindow);
 | 
						|
      }
 | 
						|
      finally {
 | 
						|
        window.Activated -= this.rootWindowActivatedDelegate;
 | 
						|
        ActiveWindow = parentWindow;
 | 
						|
 | 
						|
        if(disposeOnClose) {
 | 
						|
          disposeIfDisposable(window);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>Creates the view for the specified view model</summary>
 | 
						|
    /// <typeparam name="TViewModel">
 | 
						|
    ///   Type of view model for which a view will be created
 | 
						|
    /// </typeparam>
 | 
						|
    /// <param name="viewModel">
 | 
						|
    ///   View model a view will be created for. If null, the view model will be
 | 
						|
    ///   created as well (unless the dialog already specifies one as a resource)
 | 
						|
    /// </param>
 | 
						|
    /// <returns>The view for the specified view model</returns>
 | 
						|
    public virtual Control CreateView<TViewModel>(
 | 
						|
      TViewModel? viewModel = null
 | 
						|
    ) where TViewModel : class {
 | 
						|
      Control viewControl;
 | 
						|
      {
 | 
						|
        Type viewType = LocateViewForViewModel(typeof(TViewModel));
 | 
						|
 | 
						|
        IWindowScope scope = CreateWindowScope();
 | 
						|
        using(var scopeDisposer = new CancellableDisposer(scope)) {
 | 
						|
          viewControl = (Control)scope.CreateInstance(viewType);
 | 
						|
          using(var viewDisposer = new CancellableDisposer(viewControl)) {
 | 
						|
 | 
						|
            // Create a view model if none was provided, and in either case assign
 | 
						|
            // the view model to the view (provided it implements IView).
 | 
						|
            using(var viewModelDisposer = new CancellableDisposer()) {
 | 
						|
              if(viewModel == null) { // No view model provided, create one
 | 
						|
                if(viewControl.DataContext == null) { // View has no view model
 | 
						|
                  viewModel = (TViewModel)scope.CreateInstance(typeof(TViewModel));
 | 
						|
                  viewModelDisposer.Set(viewModel);
 | 
						|
                  viewControl.DataContext = viewModel;
 | 
						|
                } else { // There's an existing view model
 | 
						|
                  viewModel = viewControl.DataContext as TViewModel;
 | 
						|
                  if(viewModel == null) { // The existing view model is another type
 | 
						|
                    viewModel = (TViewModel)scope.CreateInstance(typeof(TViewModel));
 | 
						|
                    viewModelDisposer.Set(viewModel);
 | 
						|
                    viewControl.DataContext = viewModel;
 | 
						|
                  }
 | 
						|
                }
 | 
						|
              } else { // Caller has provided a view model
 | 
						|
                viewControl.DataContext = viewModel;
 | 
						|
              }
 | 
						|
 | 
						|
              // If an auto binder was provided, automatically bind the view to the view model
 | 
						|
              if(this.autoBinder != null) {
 | 
						|
                this.autoBinder.Bind(viewControl, viewModel);
 | 
						|
              }
 | 
						|
 | 
						|
              viewModelDisposer.Dismiss(); // Everything went well, we keep the view model
 | 
						|
            }
 | 
						|
 | 
						|
            viewDisposer.Dismiss(); // Everything went well, we keep the view
 | 
						|
          }
 | 
						|
 | 
						|
          scopeDisposer.Dismiss(); // Everything went well, we keep the scope
 | 
						|
        }
 | 
						|
 | 
						|
        setupScopeDisposal(viewControl, scope);
 | 
						|
      } // beauty scope
 | 
						|
 | 
						|
      return viewControl;
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>Creates a view model without a matching view</summary>
 | 
						|
    /// <typeparam name="TViewModel">Type of view model that will be created</typeparam>
 | 
						|
    /// <returns>The new view model</returns>
 | 
						|
    /// <remarks>
 | 
						|
    ///   <para>
 | 
						|
    ///     This is useful if a view model needs to create child view models (i.e. paged container
 | 
						|
    ///     and wants to ensure the same dependency injector (if any) if used as the window
 | 
						|
    ///     manager uses for other view models it creates.
 | 
						|
    ///   </para>
 | 
						|
    ///   <para>
 | 
						|
    ///     This way, view models can set up their child view models without having to immediately
 | 
						|
    ///     bind a view to them. Later on, views can use the window manager to create a matching
 | 
						|
    ///     child view and store it in a container.
 | 
						|
    ///   </para>
 | 
						|
    /// </remarks>
 | 
						|
    public TViewModel CreateViewModel<TViewModel>() where TViewModel : class {
 | 
						|
      return (TViewModel)CreateInstance(typeof(TViewModel));
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>Locates the view that will be used to a view model</summary>
 | 
						|
    /// <param name="viewModelType">
 | 
						|
    ///   Type of view model for which the view will be located
 | 
						|
    /// </param>
 | 
						|
    /// <returns>The type of view that should be used for the specified view model</returns>
 | 
						|
    protected virtual Type LocateViewForViewModel(Type viewModelType) {
 | 
						|
      Type? viewType;
 | 
						|
      if(!this.viewTypesForViewModels.TryGetValue(viewModelType, out viewType)) {
 | 
						|
        string viewName = viewModelType.Name;
 | 
						|
        if(viewName.EndsWith("ViewModel")) {
 | 
						|
          viewName = viewName.Substring(0, viewName.Length - 9);
 | 
						|
        }
 | 
						|
 | 
						|
        Type[] exportedTypes = viewModelType.Assembly.GetExportedTypes();
 | 
						|
        Type[] namespaceTypes = filterTypesByNamespace(exportedTypes, viewModelType.Namespace);
 | 
						|
 | 
						|
        // First, search the own namespace (because if two identical view models exist in
 | 
						|
        // different namespaces, the one in the same namespace is most likely the desired one)
 | 
						|
        viewType = findBestMatch(
 | 
						|
          namespaceTypes,
 | 
						|
          viewName + "View",
 | 
						|
          viewName + "Page",
 | 
						|
          viewName + "Form",
 | 
						|
          viewName + "Window",
 | 
						|
          viewName + "Dialog",
 | 
						|
          viewName + "Control"
 | 
						|
        );
 | 
						|
 | 
						|
        // If the view model doesn't exist in the same namespace, expand the search to
 | 
						|
        // the entire assembly the view is in.
 | 
						|
        if(viewType == null) {
 | 
						|
          viewType = findBestMatch(
 | 
						|
            exportedTypes,
 | 
						|
            viewName + "View",
 | 
						|
            viewName + "Page",
 | 
						|
            viewName + "Form",
 | 
						|
            viewName + "Window",
 | 
						|
            viewName + "Dialog",
 | 
						|
            viewName + "Control"
 | 
						|
          );
 | 
						|
        }
 | 
						|
 | 
						|
        // Still no view found? We give up!
 | 
						|
        if(viewType == null) {
 | 
						|
          throw new InvalidOperationException(
 | 
						|
            string.Format("Could not locate view for view model '{0}'", viewModelType.Name)
 | 
						|
          );
 | 
						|
        }
 | 
						|
 | 
						|
        this.viewTypesForViewModels.TryAdd(viewModelType, viewType);
 | 
						|
      }
 | 
						|
 | 
						|
      return viewType;
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>Creates an instance of the specified type</summary>
 | 
						|
    /// <param name="type">Type an instance will be created of</param>
 | 
						|
    /// <returns>The created instance</returns>
 | 
						|
    /// <remarks>
 | 
						|
    ///   Use this to wire up your dependency injection container. By default,
 | 
						|
    ///   the Activator class will be used to create instances which only works
 | 
						|
    ///   if all of your view models are concrete classes.
 | 
						|
    /// </remarks>
 | 
						|
    protected virtual object CreateInstance(Type type) {
 | 
						|
      return Activator.CreateInstance(type);
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>Creates a new scope in which window-specific instances live</summary>
 | 
						|
    /// <returns>
 | 
						|
    ///   A new scope in which scoped services requested by the window's view model
 | 
						|
    ///   will live
 | 
						|
    /// </returns>
 | 
						|
    /// <remarks>
 | 
						|
    ///   If you do not override this method, services will be constructed through
 | 
						|
    ///   the normal <see cref="CreateInstance" /> method (which actually may not
 | 
						|
    ///   work without managing your own service scope in case your dependency
 | 
						|
    ///   injector supports scopes and some of your services are scoped). By
 | 
						|
    ///   overriding this method, you can automatically cause a new scope to be
 | 
						|
    ///   created for each window or dialog. That way, an implicit service scope
 | 
						|
    ///   will cover the lifetime of each window and its view model and any
 | 
						|
    ///   non-singleton services will use new instances, avoiding, for example,
 | 
						|
    ///   that multiple dialogs access the same database connection simultaneously.
 | 
						|
    /// </remarks>
 | 
						|
    protected virtual IWindowScope CreateWindowScope() {
 | 
						|
      return this.windowManagerAsScope;
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>Called when one of the application's root windows is closed</summary>
 | 
						|
    /// <param name="sender">Window that has been closed</param>
 | 
						|
    /// <param name="arguments">Not used</param>
 | 
						|
    private void rootWindowClosed(object sender, EventArgs arguments) {
 | 
						|
      Window closedWindow = (Window)sender;
 | 
						|
      closedWindow.Closed -= this.rootWindowClosedDelegate;
 | 
						|
      closedWindow.Activated -= this.rootWindowActivatedDelegate;
 | 
						|
 | 
						|
      lock(this) {
 | 
						|
        ActiveWindow = null;
 | 
						|
      }
 | 
						|
 | 
						|
      // The IoC container already does this and it's the user's responsibility anyways
 | 
						|
      //disposeIfDisposable(closedWindow);
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>Called when one of the application's root windows is activated</summary>
 | 
						|
    /// <param name="sender">Window that has been put in the foreground</param>
 | 
						|
    /// <param name="arguments">Not used</param>
 | 
						|
    private void rootWindowActivated(object? sender, EventArgs arguments) {
 | 
						|
      lock(this) {
 | 
						|
        ActiveWindow = (Window?)sender;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>Tries to find the best match for a named type in a list of types</summary>
 | 
						|
    /// <param name="types">List of types the search will take place in</param>
 | 
						|
    /// <param name="typeNames">
 | 
						|
    ///   The candidates the method will look for, starting with the best match
 | 
						|
    /// </param>
 | 
						|
    /// <returns>The best match in the list of types, if any match was found</returns>
 | 
						|
    private static Type? findBestMatch(Type[] types, params string[] typeNames) {
 | 
						|
      int bestMatchFound = typeNames.Length;
 | 
						|
 | 
						|
      Type? type = null;
 | 
						|
      for(int index = 0; index < types.Length; ++index) {
 | 
						|
        for(int nameIndex = 0; nameIndex < bestMatchFound; ++nameIndex) {
 | 
						|
          if(types[index].Name == typeNames[nameIndex]) {
 | 
						|
            bestMatchFound = nameIndex;
 | 
						|
            type = types[index];
 | 
						|
 | 
						|
            if(bestMatchFound == 0) { // There can be no better match
 | 
						|
              return type;
 | 
						|
            }
 | 
						|
 | 
						|
            break;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      return type;
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>Disposes the specified object if it implements IDisposable</summary>
 | 
						|
    /// <typeparam name="T">Type of object that will disposed if possible</typeparam>
 | 
						|
    /// <param name="instance">Object that the method will attempt to dispose</param>
 | 
						|
    private static void disposeIfDisposable<T>(T instance) where T : class {
 | 
						|
      var disposable = instance as IDisposable;
 | 
						|
      if(disposable != null) {
 | 
						|
        disposable.Dispose();
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>Attaches a view model disposer to a control</summary>
 | 
						|
    /// <param name="control">
 | 
						|
    ///   Control whose view model will be disposed when it is itself disposed
 | 
						|
    /// </param>
 | 
						|
    private static void setupViewModelDisposal(Control control) {
 | 
						|
      IDisposable? disposableViewModel = control.DataContext as IDisposable;
 | 
						|
      if(disposableViewModel != null) {
 | 
						|
        control.Unloaded += delegate(object? sender, RoutedEventArgs arguments) {
 | 
						|
          disposableViewModel.Dispose();
 | 
						|
          control.DataContext = null;
 | 
						|
        };
 | 
						|
      }
 | 
						|
 | 
						|
      //window.Tag = "DisposeViewModelOnClose"; // TODO: Wrap SetProp() instead?
 | 
						|
      //window.SetValue(DisposeViewModelOnCloseProperty, true);
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>Attaches a scope disposer to a control</summary>
 | 
						|
    /// <param name="control">
 | 
						|
    ///   Control that will dispose a scope when it is itself disposed
 | 
						|
    /// </param>
 | 
						|
    /// <param name="scope">
 | 
						|
    ///   Scope that will be disposed together with the control 
 | 
						|
    /// </param>
 | 
						|
    private void setupScopeDisposal(Control control, IWindowScope scope) {
 | 
						|
      IDisposable disposableScope = (IDisposable)scope;
 | 
						|
      if(disposableScope != null) {
 | 
						|
        control.Unloaded += delegate(object? sender, RoutedEventArgs arguments) {
 | 
						|
          disposableScope.Dispose();
 | 
						|
        };
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>Filters a list of types to contain only those in a specific namespace</summary>
 | 
						|
    /// <param name="exportedTypes">List of exported types that will be filtered</param>
 | 
						|
    /// <param name="filteredNamespace">
 | 
						|
    ///   Namespace the types in the filtered list will be in
 | 
						|
    /// </param>
 | 
						|
    /// <returns>A subset of the specified types that are in the provided namespace</returns>
 | 
						|
    private static Type[] filterTypesByNamespace(Type[] exportedTypes, string? filteredNamespace) {
 | 
						|
      var filteredTypes = new List<Type>(exportedTypes.Length / 2);
 | 
						|
      for(int index = 0; index < exportedTypes.Length; ++index) {
 | 
						|
        Type exportedType = exportedTypes[index];
 | 
						|
        if(exportedType.Namespace == filteredNamespace) {
 | 
						|
          filteredTypes.Add(exportedType);
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      return filteredTypes.ToArray();
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>The application's currently active root window</summary>
 | 
						|
    private Window? activeWindow;
 | 
						|
    /// <summary>Invoked when a root window is put in the foreground</summary>
 | 
						|
    private EventHandler rootWindowActivatedDelegate;
 | 
						|
    /// <summary>Invoked when a root window has been closed</summary>
 | 
						|
    private EventHandler rootWindowClosedDelegate;
 | 
						|
    /// <summary>Scope that uses the WindowManager's global CreateInstance() method</summary>
 | 
						|
    private WindowManagerScope windowManagerAsScope;
 | 
						|
    /// <summary>View model binder that will be used on all created views</summary>
 | 
						|
    private IAutoBinder? autoBinder;
 | 
						|
    /// <summary>Caches the view types to use for a view model</summary>
 | 
						|
    private ConcurrentDictionary<Type, Type> viewTypesForViewModels;
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
} // namespace Nuclex.Avalonia
 |