| File: Application\Application.cs | Web Access |
| Project: src\src\Controls\src\Core\Controls.Core.csproj (Microsoft.Maui.Controls) |
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Maui.ApplicationModel; using Microsoft.Maui.Controls.Internals; using Microsoft.Maui.Graphics; using Microsoft.Maui.Handlers; namespace Microsoft.Maui.Controls { /// <include file="../../docs/Microsoft.Maui.Controls/Application.xml" path="Type[@FullName='Microsoft.Maui.Controls.Application']/Docs/*" /> public partial class Application : Element, IResourcesProvider, IApplicationController, IElementConfiguration<Application>, IVisualTreeElement, IApplication { readonly WeakEventManager _weakEventManager = new WeakEventManager(); readonly Lazy<PlatformConfigurationRegistry<Application>> _platformConfigurationRegistry; #pragma warning disable CS0612 // Type or member is obsolete readonly Lazy<IResourceDictionary?> _systemResources; #pragma warning restore CS0612 // Type or member is obsolete IAppIndexingProvider? _appIndexProvider; bool _isStarted; static readonly SemaphoreSlim SaveSemaphore = new SemaphoreSlim(1, 1); /// <include file="../../docs/Microsoft.Maui.Controls/Application.xml" path="//Member[@MemberName='.ctor']/Docs/*" /> public Application() : this(true) { } internal Application(bool setCurrentApplication) { if (setCurrentApplication) SetCurrentApplication(this); #pragma warning disable CS0612 // Type or member is obsolete _systemResources = new Lazy<IResourceDictionary?>(() => { var systemResources = DependencyService.Get<ISystemResourcesProvider>().GetSystemResources(); if (systemResources is not null) { systemResources.ValuesChanged += OnParentResourcesChanged; } return systemResources; }); #pragma warning restore CS0612 // Type or member is obsolete _platformConfigurationRegistry = new Lazy<PlatformConfigurationRegistry<Application>>(() => new PlatformConfigurationRegistry<Application>(this)); _platformAppTheme = AppInfo.RequestedTheme; _lastAppTheme = _platformAppTheme; } /// <include file="../../docs/Microsoft.Maui.Controls/Application.xml" path="//Member[@MemberName='Quit']/Docs/*" /> public void Quit() { Handler?.Invoke(ApplicationHandler.TerminateCommandKey); } /// <include file="../../docs/Microsoft.Maui.Controls/Application.xml" path="//Member[@MemberName='AppLinks']/Docs/*" /> public IAppLinks AppLinks { get { if (_appIndexProvider == null) throw new ArgumentException("No IAppIndexingProvider was provided"); if (_appIndexProvider.AppLinks == null) throw new ArgumentException("No AppLinks implementation was found, if in Android make sure you installed the Microsoft.Maui.Controls.AppLinks"); return _appIndexProvider.AppLinks; } } /// <include file="../../docs/Microsoft.Maui.Controls/Application.xml" path="//Member[@MemberName='SetCurrentApplication']/Docs/*" /> [EditorBrowsable(EditorBrowsableState.Never)] public static void SetCurrentApplication(Application value) => Current = value; /// <include file="../../docs/Microsoft.Maui.Controls/Application.xml" path="//Member[@MemberName='Current']/Docs/*" /> public static Application? Current { get; set; } Page? _singleWindowMainPage; /// <include file="../../docs/Microsoft.Maui.Controls/Application.xml" path="//Member[@MemberName='MainPage']/Docs/*" /> public Page? MainPage { [Obsolete("This property has been deprecated. For single-window applications, use Windows[0].Page. For multi-window applications, identify and use the appropriate Window object to access the desired Page. Additionally, each element features a Window property, accessible when it's part of the current window.")] get { if (Windows.Count == 0) return _singleWindowMainPage; return Windows[0].Page; } [Obsolete("This property is deprecated. Initialize your application by overriding Application.CreateWindow rather than setting MainPage. To modify the root page in an active application, use Windows[0].Page for applications with a single window. For applications with multiple windows, use Application.Windows to identify and update the root page on the correct window. Additionally, each element features a Window property, accessible when it's part of the current window.")] set { if (MainPage == value) return; OnPropertyChanging(); if (MainPage is not null) { ((IElementDefinition)this).RemoveResourcesChangedListener(MainPage.OnParentResourcesChanged); } _singleWindowMainPage = value; if (value is not null) { value.OnResourcesChanged(this.GetMergedResources()); ((IElementDefinition)this).AddResourcesChangedListener(value.OnParentResourcesChanged); } if (Windows.Count == 1) { Windows[0].Page = value; } OnPropertyChanged(); } } /// <include file="../../docs/Microsoft.Maui.Controls/Application.xml" path="//Member[@MemberName='NavigationProxy']/Docs/*" /> [EditorBrowsable(EditorBrowsableState.Never)] public NavigationProxy? NavigationProxy { get; private set; } internal IResourceDictionary? SystemResources => _systemResources.Value; /// <include file="../../docs/Microsoft.Maui.Controls/Application.xml" path="//Member[@MemberName='SetAppIndexingProvider']/Docs/*" /> [EditorBrowsable(EditorBrowsableState.Never)] public void SetAppIndexingProvider(IAppIndexingProvider provider) { _appIndexProvider = provider; } ResourceDictionary? _resources; bool IResourcesProvider.IsResourcesCreated => _resources != null; /// <include file="../../docs/Microsoft.Maui.Controls/Application.xml" path="//Member[@MemberName='Resources']/Docs/*" /> public ResourceDictionary Resources { get { if (_resources != null) return _resources; _resources = new ResourceDictionary(); ((IResourceDictionary)_resources).ValuesChanged += OnResourcesChanged; return _resources; } set { if (_resources == value) return; OnPropertyChanging(); if (_resources != null) ((IResourceDictionary)_resources).ValuesChanged -= OnResourcesChanged; _resources = value; OnResourcesChanged(value); if (_resources != null) ((IResourceDictionary)_resources).ValuesChanged += OnResourcesChanged; OnPropertyChanged(); } } /// <include file="../../docs/Microsoft.Maui.Controls/Application.xml" path="//Member[@MemberName='UserAppTheme']/Docs/*" /> public AppTheme UserAppTheme { get => _userAppTheme; set { if (_userAppTheme == value) return; _userAppTheme = value; TriggerThemeChangedActual(); } } public AppTheme PlatformAppTheme { get => _platformAppTheme; private set { if (_platformAppTheme == value) return; _platformAppTheme = value; TriggerThemeChangedActual(); } } /// <include file="../../docs/Microsoft.Maui.Controls/Application.xml" path="//Member[@MemberName='RequestedTheme']/Docs/*" /> public AppTheme RequestedTheme => UserAppTheme != AppTheme.Unspecified ? UserAppTheme : PlatformAppTheme; static Color? _accentColor; public static Color? AccentColor { get => _accentColor ??= GetAccentColor(); set => _accentColor = value; } static Color? GetAccentColor() { #if WINDOWS if (UI.Xaml.Application.Current.Resources.TryGetValue("SystemColorControlAccentBrush", out object accent) && accent is UI.Xaml.Media.SolidColorBrush scb) { return scb.ToColor(); } return null; #elif ANDROID var context = (Current?.Windows?.Count > 0 && Current.Windows[0].MauiContext is not null) ? Current.Windows[0].MauiContext.Context : Current?.FindMauiContext()?.Context; if (context is not null) return context?.GetAccentColor(); return null; #elif IOS return ColorExtensions.AccentColor.ToColor(); #else return Color.FromRgba(50, 79, 133, 255); #endif } public event EventHandler<AppThemeChangedEventArgs> RequestedThemeChanged { add => _weakEventManager.AddEventHandler(value); remove => _weakEventManager.RemoveEventHandler(value); } bool _themeChangedFiring; AppTheme _platformAppTheme = AppTheme.Unspecified; AppTheme _lastAppTheme = AppTheme.Unspecified; AppTheme _userAppTheme = AppTheme.Unspecified; void TriggerThemeChangedActual() { var newTheme = RequestedTheme; // On iOS the event is triggered more than once. // To minimize that for us, we only do it when the theme actually changes and it's not currently firing if (_themeChangedFiring || newTheme == _lastAppTheme) return; try { _themeChangedFiring = true; _lastAppTheme = newTheme; OnParentResourcesChanged([new KeyValuePair<string, object>(AppThemeBinding.AppThemeResource, newTheme)]); _weakEventManager.HandleEvent(this, new AppThemeChangedEventArgs(newTheme), nameof(RequestedThemeChanged)); OnPropertyChanged(nameof(UserAppTheme)); } finally { _themeChangedFiring = false; } } public event EventHandler<ModalPoppedEventArgs>? ModalPopped; public event EventHandler<ModalPoppingEventArgs>? ModalPopping; public event EventHandler<ModalPushedEventArgs>? ModalPushed; public event EventHandler<ModalPushingEventArgs>? ModalPushing; internal void NotifyOfWindowModalEvent(EventArgs eventArgs) { switch (eventArgs) { case ModalPoppedEventArgs poppedEvents: ModalPopped?.Invoke(this, poppedEvents); break; case ModalPoppingEventArgs poppingEvents: ModalPopping?.Invoke(this, poppingEvents); break; case ModalPushedEventArgs pushedEvents: ModalPushed?.Invoke(this, pushedEvents); break; case ModalPushingEventArgs pushingEvents: ModalPushing?.Invoke(this, pushingEvents); break; default: break; } } public event EventHandler<Page>? PageAppearing; public event EventHandler<Page>? PageDisappearing; /// <inheritdoc/> public IPlatformElementConfiguration<T, Application> On<T>() where T : IConfigPlatform { return _platformConfigurationRegistry.Value.On<T>(); } protected virtual void OnAppLinkRequestReceived(Uri uri) { } protected override void OnParentSet() { throw new InvalidOperationException("Setting a Parent on Application is invalid."); } protected virtual void OnResume() { } protected virtual void OnSleep() { } protected virtual void OnStart() { } internal static void ClearCurrent() => Current = null; internal static bool IsApplicationOrNull(object? element) => element == null || element is IApplication; internal static bool IsApplicationOrWindowOrNull(object? element) => element == null || element is IApplication || element is IWindow; internal override void OnParentResourcesChanged(IEnumerable<KeyValuePair<string, object>> values) { if (!((IResourcesProvider)this).IsResourcesCreated || Resources.Count == 0) { base.OnParentResourcesChanged(values); return; } var innerKeys = new HashSet<string>(StringComparer.Ordinal); var changedResources = new List<KeyValuePair<string, object>>(); foreach (KeyValuePair<string, object> c in Resources) innerKeys.Add(c.Key); foreach (KeyValuePair<string, object> value in values) { if (innerKeys.Add(value.Key)) changedResources.Add(value); } OnResourcesChanged(changedResources); } /// <include file="../../docs/Microsoft.Maui.Controls/Application.xml" path="//Member[@MemberName='SendOnAppLinkRequestReceived']/Docs/*" /> [EditorBrowsable(EditorBrowsableState.Never)] public void SendOnAppLinkRequestReceived(Uri uri) { OnAppLinkRequestReceived(uri); } internal void SendResume() { if (Current is null) Current = this; OnResume(); } internal void SendSleep() { OnSleep(); } internal void SendStart() { if (_isStarted) return; _isStarted = true; OnStart(); } internal void OnPageAppearing(Page page) => PageAppearing?.Invoke(this, page); internal void OnPageDisappearing(Page page) => PageDisappearing?.Invoke(this, page); protected internal virtual void CleanUp() { // Unhook everything that's referencing the main page so it can be collected // This only comes up if we're disposing of an embedded Forms app, and will // eventually go away when we fully support multiple windows // TODO MAUI //if (_mainPage != null) //{ // InternalChildren.Remove(_mainPage); // _mainPage.Parent = null; // _mainPage = null; //} NavigationProxy = null; } IReadOnlyList<Maui.IVisualTreeElement> IVisualTreeElement.GetVisualChildren() => this.Windows; internal const string MauiWindowIdKey = "__MAUI_WINDOW_ID__"; readonly List<Window> _windows = new(); readonly Dictionary<string, WeakReference<Window>> _requestedWindows = new(StringComparer.Ordinal); ILogger<Application>? _logger; ILogger<Application>? Logger => _logger ??= Handler?.MauiContext?.CreateLogger<Application>(); IReadOnlyList<IWindow> IApplication.Windows => _windows; public IReadOnlyList<Window> Windows => _windows; IWindow IApplication.CreateWindow(IActivationState? activationState) { Window? window = null; // try get the window that is pending if (activationState?.State?.TryGetValue(MauiWindowIdKey, out var requestedWindowId) ?? false) { if (requestedWindowId != null && _requestedWindows.TryGetValue(requestedWindowId, out var r)) { if (r.TryGetTarget(out var w)) { window = w; } _requestedWindows.Remove(requestedWindowId); } } // create a new one if there is no pending windows if (window == null) { window = CreateWindow(activationState); if (_singleWindowMainPage != null && window.Page != null && window.Page != _singleWindowMainPage) throw new InvalidOperationException($"Both {nameof(MainPage)} was set and {nameof(Application.CreateWindow)} was overridden to provide a page."); } // make sure it is added to the windows list if (!_windows.Contains(window)) AddWindow(window); return window; } void IApplication.OpenWindow(IWindow window) { if (window is Window cwindow) { OpenWindow(cwindow); } } void IApplication.CloseWindow(IWindow window) { Handler?.Invoke(nameof(IApplication.CloseWindow), window); } void IApplication.ActivateWindow(IWindow window) { if (window is Window cwindow) { ActivateWindow(cwindow); } } internal void RemoveWindow(Window window) { // Do not attempt to close the "MainPage" window if (_singleWindowMainPage != null && window.Page == _singleWindowMainPage) return; // Window was closed, stop tracking it if (window is null) return; if (window is NavigableElement ne) ne.NavigationProxy.Inner = null; if (window is Element windowElement) { RemoveLogicalChild(windowElement); } _windows.Remove(window); } public virtual void OpenWindow(Window window) { var id = Guid.NewGuid().ToString("n"); _requestedWindows.Add(id, new WeakReference<Window>(window)); var state = new PersistedState { [MauiWindowIdKey] = id }; Handler?.Invoke(nameof(IApplication.OpenWindow), new OpenWindowRequest(State: state)); } public virtual void CloseWindow(Window window) { Handler?.Invoke(nameof(IApplication.CloseWindow), window); } public virtual void ActivateWindow(Window window) { Handler?.Invoke(nameof(IApplication.ActivateWindow), window); } void IApplication.ThemeChanged() { PlatformAppTheme = AppInfo.RequestedTheme; } protected virtual Window CreateWindow(IActivationState? activationState) { var windowCreator = activationState?.Context.Services.GetService<IWindowCreator>(); var window = windowCreator?.CreateWindow(this, activationState); if (window is not null) return window; if (Windows.Count > 1) #pragma warning disable CS0618 // Type or member is obsolete throw new NotImplementedException($"Either set {nameof(MainPage)} or override {nameof(Application.CreateWindow)}."); #pragma warning restore CS0618 // Type or member is obsolete if (Windows.Count > 0) return Windows[0]; if (_singleWindowMainPage is not null) return new Window(_singleWindowMainPage); #pragma warning disable CS0618 // Type or member is obsolete throw new NotImplementedException($"Either set {nameof(MainPage)} or override {nameof(Application.CreateWindow)}."); #pragma warning restore CS0618 // Type or member is obsolete } internal void AddWindow(Window window) { _windows.Add(window); if (window is Element windowElement) { AddLogicalChild(windowElement); } if (window is NavigableElement ne) ne.NavigationProxy.Inner = NavigationProxy; // Once the window has been attached to the application. // The window will finish propagating events like `Appearing`. // // I could fire this from 'OnParentSet` inside Window but // I'd rather wait until Application is done wiring itself // up to the window before triggering any down stream life cycle // events. window.FinishedAddingWindowToApplication(this); } } }