Application.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. using System;
  2. using System.Collections.Generic;
  3. using Avalonia.Animation;
  4. using Avalonia.Controls;
  5. using Avalonia.Controls.ApplicationLifetimes;
  6. using Avalonia.Controls.Templates;
  7. using Avalonia.Data;
  8. using Avalonia.Input;
  9. using Avalonia.Input.Platform;
  10. using Avalonia.Input.Raw;
  11. using Avalonia.Media;
  12. using Avalonia.Platform;
  13. using Avalonia.Rendering;
  14. using Avalonia.Styling;
  15. using Avalonia.Threading;
  16. namespace Avalonia
  17. {
  18. /// <summary>
  19. /// Encapsulates a Avalonia application.
  20. /// </summary>
  21. /// <remarks>
  22. /// The <see cref="Application"/> class encapsulates Avalonia application-specific
  23. /// functionality, including:
  24. /// - A global set of <see cref="DataTemplates"/>.
  25. /// - A global set of <see cref="Styles"/>.
  26. /// - A <see cref="FocusManager"/>.
  27. /// - An <see cref="InputManager"/>.
  28. /// - Registers services needed by the rest of Avalonia in the <see cref="RegisterServices"/>
  29. /// method.
  30. /// - Tracks the lifetime of the application.
  31. /// </remarks>
  32. public class Application : AvaloniaObject, IDataContextProvider, IGlobalDataTemplates, IGlobalStyles, IThemeVariantHost, IResourceHost2, IApplicationPlatformEvents, IOptionalFeatureProvider
  33. {
  34. /// <summary>
  35. /// The application-global data templates.
  36. /// </summary>
  37. private DataTemplates? _dataTemplates;
  38. private Styles? _styles;
  39. private IResourceDictionary? _resources;
  40. private Action<IReadOnlyList<IStyle>>? _stylesAdded;
  41. private Action<IReadOnlyList<IStyle>>? _stylesRemoved;
  42. private IApplicationLifetime? _applicationLifetime;
  43. private bool _setupCompleted;
  44. private EventHandler<ResourcesChangedToken>? _resourcesChanged2;
  45. /// <summary>
  46. /// Defines the <see cref="DataContext"/> property.
  47. /// </summary>
  48. public static readonly StyledProperty<object?> DataContextProperty =
  49. StyledElement.DataContextProperty.AddOwner<Application>();
  50. /// <inheritdoc cref="ThemeVariantScope.ActualThemeVariantProperty" />
  51. public static readonly StyledProperty<ThemeVariant> ActualThemeVariantProperty =
  52. ThemeVariantScope.ActualThemeVariantProperty.AddOwner<Application>();
  53. /// <inheritdoc cref="ThemeVariantScope.RequestedThemeVariantProperty" />
  54. public static readonly StyledProperty<ThemeVariant?> RequestedThemeVariantProperty =
  55. ThemeVariantScope.RequestedThemeVariantProperty.AddOwner<Application>();
  56. /// <inheritdoc/>
  57. public event EventHandler<ResourcesChangedEventArgs>? ResourcesChanged;
  58. event EventHandler<ResourcesChangedToken>? IResourceHost2.ResourcesChanged2
  59. {
  60. add => _resourcesChanged2 += value;
  61. remove => _resourcesChanged2 -= value;
  62. }
  63. [Obsolete("Use Application.Current.TryGetFeature<IActivatableLifetime>() instead.")]
  64. public event EventHandler<UrlOpenedEventArgs>? UrlsOpened;
  65. /// <inheritdoc/>
  66. public event EventHandler? ActualThemeVariantChanged;
  67. /// <summary>
  68. /// Creates an instance of the <see cref="Application"/> class.
  69. /// </summary>
  70. public Application()
  71. {
  72. Name = "Avalonia Application";
  73. }
  74. /// <summary>
  75. /// Gets or sets the Applications's data context.
  76. /// </summary>
  77. /// <remarks>
  78. /// The data context property specifies the default object that will
  79. /// be used for data binding.
  80. /// </remarks>
  81. public object? DataContext
  82. {
  83. get => GetValue(DataContextProperty);
  84. set => SetValue(DataContextProperty, value);
  85. }
  86. /// <inheritdoc cref="ThemeVariantScope.RequestedThemeVariant"/>
  87. public ThemeVariant? RequestedThemeVariant
  88. {
  89. get => GetValue(RequestedThemeVariantProperty);
  90. set => SetValue(RequestedThemeVariantProperty, value);
  91. }
  92. /// <inheritdoc />
  93. [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1031", Justification = "This property is supposed to be a styled readonly property.")]
  94. [System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1030", Justification = "False positive.")]
  95. public ThemeVariant ActualThemeVariant => GetValue(ActualThemeVariantProperty);
  96. /// <summary>
  97. /// Gets the current instance of the <see cref="Application"/> class.
  98. /// </summary>
  99. /// <value>
  100. /// The current instance of the <see cref="Application"/> class.
  101. /// </value>
  102. public static Application? Current
  103. {
  104. get => AvaloniaLocator.Current.GetService<Application>();
  105. }
  106. /// <summary>
  107. /// Gets or sets the application's global data templates.
  108. /// </summary>
  109. /// <value>
  110. /// The application's global data templates.
  111. /// </value>
  112. public DataTemplates DataTemplates => _dataTemplates ?? (_dataTemplates = new DataTemplates());
  113. /// <summary>
  114. /// Gets the application's input manager.
  115. /// </summary>
  116. /// <value>
  117. /// The application's input manager.
  118. /// </value>
  119. internal InputManager? InputManager
  120. {
  121. get;
  122. private set;
  123. }
  124. /// <summary>
  125. /// Gets the application's global resource dictionary.
  126. /// </summary>
  127. public IResourceDictionary Resources
  128. {
  129. get => _resources ??= new ResourceDictionary(this);
  130. set
  131. {
  132. value = value ?? throw new ArgumentNullException(nameof(value));
  133. _resources?.RemoveOwner(this);
  134. _resources = value;
  135. _resources.AddOwner(this);
  136. }
  137. }
  138. /// <summary>
  139. /// Gets the application's global styles.
  140. /// </summary>
  141. /// <value>
  142. /// The application's global styles.
  143. /// </value>
  144. /// <remarks>
  145. /// Global styles apply to all windows in the application.
  146. /// </remarks>
  147. public Styles Styles => _styles ??= new Styles(this);
  148. /// <inheritdoc/>
  149. bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null;
  150. /// <inheritdoc/>
  151. bool IResourceNode.HasResources => (_resources?.HasResources ?? false) ||
  152. (((IResourceNode?)_styles)?.HasResources ?? false);
  153. /// <summary>
  154. /// Gets the styling parent of the application, which is null.
  155. /// </summary>
  156. IStyleHost? IStyleHost.StylingParent => null;
  157. /// <inheritdoc/>
  158. bool IStyleHost.IsStylesInitialized => _styles != null;
  159. /// <summary>
  160. /// Application lifetime, use it for things like setting the main window and exiting the app from code
  161. /// Currently supported lifetimes are:
  162. /// - <see cref="IClassicDesktopStyleApplicationLifetime"/>
  163. /// - <see cref="ISingleViewApplicationLifetime"/>
  164. /// - <see cref="IControlledApplicationLifetime"/>
  165. /// - <see cref="IActivatableApplicationLifetime"/>
  166. /// </summary>
  167. public IApplicationLifetime? ApplicationLifetime
  168. {
  169. get => _applicationLifetime;
  170. set
  171. {
  172. if (_setupCompleted)
  173. {
  174. throw new InvalidOperationException($"It's not possible to change {nameof(ApplicationLifetime)} after Application was initialized.");
  175. }
  176. _applicationLifetime = value;
  177. }
  178. }
  179. /// <summary>
  180. /// Represents a contract for accessing global platform-specific settings.
  181. /// </summary>
  182. /// <remarks>
  183. /// PlatformSettings can be null only if application wasn't initialized yet.
  184. /// <see cref="TopLevel"/>'s <see cref="TopLevel.PlatformSettings"/> is an equivalent API
  185. /// which should always be preferred over a global one,
  186. /// as specific top levels might have different settings set-up.
  187. /// </remarks>
  188. public IPlatformSettings? PlatformSettings => this.TryGetFeature<IPlatformSettings>();
  189. event Action<IReadOnlyList<IStyle>>? IGlobalStyles.GlobalStylesAdded
  190. {
  191. add => _stylesAdded += value;
  192. remove => _stylesAdded -= value;
  193. }
  194. event Action<IReadOnlyList<IStyle>>? IGlobalStyles.GlobalStylesRemoved
  195. {
  196. add => _stylesRemoved += value;
  197. remove => _stylesRemoved -= value;
  198. }
  199. /// <summary>
  200. /// Initializes the application by loading XAML etc.
  201. /// </summary>
  202. public virtual void Initialize() { }
  203. /// <inheritdoc/>
  204. public bool TryGetResource(object key, ThemeVariant? theme, out object? value)
  205. {
  206. value = null;
  207. return (_resources?.TryGetResource(key, theme, out value) ?? false) ||
  208. Styles.TryGetResource(key, theme, out value);
  209. }
  210. void IResourceHost.NotifyHostedResourcesChanged(ResourcesChangedEventArgs e)
  211. {
  212. _resourcesChanged2?.Invoke(this, ResourcesChangedToken.Create());
  213. ResourcesChanged?.Invoke(this, e);
  214. }
  215. void IResourceHost2.NotifyHostedResourcesChanged(ResourcesChangedToken token)
  216. {
  217. _resourcesChanged2?.Invoke(this, token);
  218. ResourcesChanged?.Invoke(this, ResourcesChangedEventArgs.Empty);
  219. }
  220. void IStyleHost.StylesAdded(IReadOnlyList<IStyle> styles)
  221. {
  222. _stylesAdded?.Invoke(styles);
  223. }
  224. void IStyleHost.StylesRemoved(IReadOnlyList<IStyle> styles)
  225. {
  226. _stylesRemoved?.Invoke(styles);
  227. }
  228. /// <summary>
  229. /// Register's the services needed by Avalonia.
  230. /// </summary>
  231. public virtual void RegisterServices()
  232. {
  233. AvaloniaSynchronizationContext.InstallIfNeeded();
  234. InputManager = new InputManager();
  235. if (PlatformSettings is { } settings)
  236. {
  237. settings.ColorValuesChanged += OnColorValuesChanged;
  238. OnColorValuesChanged(settings, settings.GetColorValues());
  239. }
  240. AvaloniaLocator.CurrentMutable
  241. .Bind<IAccessKeyHandler>().ToTransient<AccessKeyHandler>()
  242. .Bind<IGlobalDataTemplates>().ToConstant(this)
  243. .Bind<IGlobalStyles>().ToConstant(this)
  244. .Bind<IThemeVariantHost>().ToConstant(this)
  245. .Bind<IInputManager>().ToConstant(InputManager)
  246. .Bind< IToolTipService>().ToConstant(new ToolTipService(InputManager))
  247. .Bind<IKeyboardNavigationHandler>().ToTransient<KeyboardNavigationHandler>()
  248. .Bind<IDragDropDevice>().ToConstant(DragDropDevice.Instance);
  249. // TODO: Fix this, for now we keep this behavior since someone might be relying on it in 0.9.x
  250. if (AvaloniaLocator.Current.GetService<IPlatformDragSource>() == null)
  251. AvaloniaLocator.CurrentMutable
  252. .Bind<IPlatformDragSource>().ToTransient<InProcessDragSource>();
  253. AvaloniaLocator.CurrentMutable.Bind<IGlobalClock>()
  254. .ToConstant(MediaContext.Instance.Clock);
  255. _setupCompleted = true;
  256. }
  257. public virtual void OnFrameworkInitializationCompleted()
  258. {
  259. }
  260. void IApplicationPlatformEvents.RaiseUrlsOpened(string[] urls)
  261. {
  262. UrlsOpened?.Invoke(this, new UrlOpenedEventArgs (urls));
  263. }
  264. private string? _name;
  265. /// <summary>
  266. /// Defines Name property
  267. /// </summary>
  268. public static readonly DirectProperty<Application, string?> NameProperty =
  269. AvaloniaProperty.RegisterDirect<Application, string?>("Name", o => o.Name, (o, v) => o.Name = v);
  270. /// <summary>
  271. /// Application name to be used for various platform-specific purposes
  272. /// </summary>
  273. public string? Name
  274. {
  275. get => _name;
  276. set => SetAndRaise(NameProperty, ref _name, value);
  277. }
  278. /// <summary>
  279. /// Queries for an optional feature.
  280. /// </summary>
  281. /// <param name="featureType">Feature type.</param>
  282. /// <remarks>
  283. /// Features currently supported by <see cref="Application.TryGetFeature"/>:
  284. /// <list type="bullet">
  285. /// <item>IPlatformSettings</item>
  286. /// <item>IActivatableApplicationLifetime</item>
  287. /// </list>
  288. /// </remarks>
  289. public object? TryGetFeature(Type featureType)
  290. {
  291. if (featureType == typeof(IPlatformSettings))
  292. {
  293. return AvaloniaLocator.Current.GetService<IPlatformSettings>();
  294. }
  295. if (featureType == typeof(IActivatableLifetime))
  296. {
  297. return AvaloniaLocator.Current.GetService<IActivatableLifetime>();
  298. }
  299. // Do not return just any service from AvaloniaLocator.
  300. return null;
  301. }
  302. protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
  303. {
  304. base.OnPropertyChanged(change);
  305. if (change.Property == RequestedThemeVariantProperty)
  306. {
  307. if (change.GetNewValue<ThemeVariant>() is {} themeVariant && themeVariant != ThemeVariant.Default)
  308. SetValue(ActualThemeVariantProperty, themeVariant);
  309. else
  310. ClearValue(ActualThemeVariantProperty);
  311. }
  312. else if (change.Property == ActualThemeVariantProperty)
  313. {
  314. ActualThemeVariantChanged?.Invoke(this, EventArgs.Empty);
  315. }
  316. }
  317. private void OnColorValuesChanged(object? sender, PlatformColorValues e)
  318. {
  319. SetValue(ActualThemeVariantProperty, (ThemeVariant)e.ThemeVariant, BindingPriority.Template);
  320. }
  321. }
  322. }