A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

349 lines
13 KiB

using System;
using System.Collections.Generic;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Input.Raw;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Styling;
using Avalonia.Threading;
namespace Avalonia
{
/// <summary>
/// Encapsulates a Avalonia application.
/// </summary>
/// <remarks>
/// The <see cref="Application"/> class encapsulates Avalonia application-specific
/// functionality, including:
/// - A global set of <see cref="DataTemplates"/>.
/// - A global set of <see cref="Styles"/>.
/// - A <see cref="FocusManager"/>.
/// - An <see cref="InputManager"/>.
/// - Registers services needed by the rest of Avalonia in the <see cref="RegisterServices"/>
/// method.
/// - Tracks the lifetime of the application.
/// </remarks>
public class Application : AvaloniaObject, IDataContextProvider, IGlobalDataTemplates, IGlobalStyles, IThemeVariantHost, IResourceHost, IOptionalFeatureProvider
{
/// <summary>
/// The application-global data templates.
/// </summary>
private DataTemplates? _dataTemplates;
private Styles? _styles;
private IResourceDictionary? _resources;
private Action<IReadOnlyList<IStyle>>? _stylesAdded;
private Action<IReadOnlyList<IStyle>>? _stylesRemoved;
private IApplicationLifetime? _applicationLifetime;
private bool _setupCompleted;
/// <summary>
/// Defines the <see cref="DataContext"/> property.
/// </summary>
public static readonly StyledProperty<object?> DataContextProperty =
StyledElement.DataContextProperty.AddOwner<Application>();
/// <inheritdoc cref="ThemeVariantScope.ActualThemeVariantProperty" />
public static readonly StyledProperty<ThemeVariant> ActualThemeVariantProperty =
ThemeVariantScope.ActualThemeVariantProperty.AddOwner<Application>();
/// <inheritdoc cref="ThemeVariantScope.RequestedThemeVariantProperty" />
public static readonly StyledProperty<ThemeVariant?> RequestedThemeVariantProperty =
ThemeVariantScope.RequestedThemeVariantProperty.AddOwner<Application>();
/// <inheritdoc/>
public event EventHandler<ResourcesChangedEventArgs>? ResourcesChanged;
/// <inheritdoc/>
public event EventHandler? ActualThemeVariantChanged;
/// <summary>
/// Creates an instance of the <see cref="Application"/> class.
/// </summary>
public Application()
{
Name = "Avalonia Application";
}
/// <summary>
/// Gets or sets the Applications's data context.
/// </summary>
/// <remarks>
/// The data context property specifies the default object that will
/// be used for data binding.
/// </remarks>
public object? DataContext
{
get => GetValue(DataContextProperty);
set => SetValue(DataContextProperty, value);
}
/// <inheritdoc cref="ThemeVariantScope.RequestedThemeVariant"/>
public ThemeVariant? RequestedThemeVariant
{
get => GetValue(RequestedThemeVariantProperty);
set => SetValue(RequestedThemeVariantProperty, value);
}
/// <inheritdoc />
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1031", Justification = "This property is supposed to be a styled readonly property.")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("AvaloniaProperty", "AVP1030", Justification = "False positive.")]
public ThemeVariant ActualThemeVariant => GetValue(ActualThemeVariantProperty);
/// <summary>
/// Gets the current instance of the <see cref="Application"/> class.
/// </summary>
/// <value>
/// The current instance of the <see cref="Application"/> class.
/// </value>
public static Application? Current
{
get => AvaloniaLocator.Current.GetService<Application>();
}
/// <summary>
/// Gets or sets the application's global data templates.
/// </summary>
/// <value>
/// The application's global data templates.
/// </value>
public DataTemplates DataTemplates => _dataTemplates ?? (_dataTemplates = new DataTemplates());
/// <summary>
/// Gets the application's input manager.
/// </summary>
/// <value>
/// The application's input manager.
/// </value>
internal InputManager? InputManager
{
get;
private set;
}
/// <summary>
/// Gets the application's global resource dictionary.
/// </summary>
public IResourceDictionary Resources
{
get => _resources ??= new ResourceDictionary(this);
set
{
value = value ?? throw new ArgumentNullException(nameof(value));
_resources?.RemoveOwner(this);
_resources = value;
_resources.AddOwner(this);
}
}
/// <summary>
/// Gets the application's global styles.
/// </summary>
/// <value>
/// The application's global styles.
/// </value>
/// <remarks>
/// Global styles apply to all windows in the application.
/// </remarks>
public Styles Styles => _styles ??= new Styles(this);
/// <inheritdoc/>
bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null;
/// <inheritdoc/>
bool IResourceNode.HasResources => (_resources?.HasResources ?? false) ||
(((IResourceNode?)_styles)?.HasResources ?? false);
/// <summary>
/// Gets the styling parent of the application, which is null.
/// </summary>
IStyleHost? IStyleHost.StylingParent => null;
/// <inheritdoc/>
bool IStyleHost.IsStylesInitialized => _styles != null;
/// <summary>
/// Application lifetime, use it for things like setting the main window and exiting the app from code
/// Currently supported lifetimes are:
/// - <see cref="IClassicDesktopStyleApplicationLifetime"/>
/// - <see cref="ISingleViewApplicationLifetime"/>
/// - <see cref="ISingleTopLevelApplicationLifetime"/>
/// - <see cref="IControlledApplicationLifetime"/>
/// </summary>
public IApplicationLifetime? ApplicationLifetime
{
get => _applicationLifetime;
set
{
if (_setupCompleted)
{
throw new InvalidOperationException($"It's not possible to change {nameof(ApplicationLifetime)} after Application was initialized.");
}
_applicationLifetime = value;
}
}
/// <summary>
/// Represents a contract for accessing global platform-specific settings.
/// </summary>
/// <remarks>
/// PlatformSettings can be null only if application wasn't initialized yet.
/// <see cref="TopLevel"/>'s <see cref="TopLevel.PlatformSettings"/> is an equivalent API
/// which should always be preferred over a global one,
/// as specific top levels might have different settings set-up.
/// </remarks>
public IPlatformSettings? PlatformSettings => this.TryGetFeature<IPlatformSettings>();
event Action<IReadOnlyList<IStyle>>? IGlobalStyles.GlobalStylesAdded
{
add => _stylesAdded += value;
remove => _stylesAdded -= value;
}
event Action<IReadOnlyList<IStyle>>? IGlobalStyles.GlobalStylesRemoved
{
add => _stylesRemoved += value;
remove => _stylesRemoved -= value;
}
/// <summary>
/// Initializes the application by loading XAML etc.
/// </summary>
public virtual void Initialize() { }
/// <inheritdoc/>
public bool TryGetResource(object key, ThemeVariant? theme, out object? value)
{
value = null;
return (_resources?.TryGetResource(key, theme, out value) ?? false) ||
Styles.TryGetResource(key, theme, out value);
}
void IResourceHost.NotifyHostedResourcesChanged(ResourcesChangedEventArgs e)
{
ResourcesChanged?.Invoke(this, e);
}
void IStyleHost.StylesAdded(IReadOnlyList<IStyle> styles)
{
_stylesAdded?.Invoke(styles);
}
void IStyleHost.StylesRemoved(IReadOnlyList<IStyle> styles)
{
_stylesRemoved?.Invoke(styles);
}
/// <summary>
/// Register's the services needed by Avalonia.
/// </summary>
public virtual void RegisterServices()
{
AvaloniaSynchronizationContext.InstallIfNeeded();
InputManager = new InputManager();
if (PlatformSettings is { } settings)
{
settings.ColorValuesChanged += OnColorValuesChanged;
OnColorValuesChanged(settings, settings.GetColorValues());
}
AvaloniaLocator.CurrentMutable
.Bind<IAccessKeyHandler>().ToTransient<AccessKeyHandler>()
.Bind<IGlobalDataTemplates>().ToConstant(this)
.Bind<IGlobalStyles>().ToConstant(this)
.Bind<IThemeVariantHost>().ToConstant(this)
.Bind<IInputManager>().ToConstant(InputManager)
.Bind< IToolTipService>().ToConstant(new ToolTipService(InputManager))
.Bind<IKeyboardNavigationHandler>().ToTransient<KeyboardNavigationHandler>()
.Bind<IDragDropDevice>().ToConstant(DragDropDevice.Instance);
// TODO: Fix this, for now we keep this behavior since someone might be relying on it in 0.9.x
if (AvaloniaLocator.Current.GetService<IPlatformDragSource>() == null)
AvaloniaLocator.CurrentMutable
.Bind<IPlatformDragSource>().ToTransient<InProcessDragSource>();
AvaloniaLocator.CurrentMutable.Bind<IGlobalClock>()
.ToConstant(MediaContext.Instance.Clock);
_setupCompleted = true;
}
public virtual void OnFrameworkInitializationCompleted()
{
}
private string? _name;
/// <summary>
/// Defines Name property
/// </summary>
public static readonly DirectProperty<Application, string?> NameProperty =
AvaloniaProperty.RegisterDirect<Application, string?>("Name", o => o.Name, (o, v) => o.Name = v);
/// <summary>
/// Application name to be used for various platform-specific purposes
/// </summary>
public string? Name
{
get => _name;
set => SetAndRaise(NameProperty, ref _name, value);
}
/// <summary>
/// Queries for an optional feature.
/// </summary>
/// <param name="featureType">Feature type.</param>
/// <remarks>
/// Features currently supported by <see cref="Application.TryGetFeature"/>:
/// <list type="bullet">
/// <item>IPlatformSettings</item>
/// <item>IActivatableApplicationLifetime</item>
/// </list>
/// </remarks>
public object? TryGetFeature(Type featureType)
{
if (featureType == typeof(IPlatformSettings))
{
return AvaloniaLocator.Current.GetService<IPlatformSettings>();
}
if (featureType == typeof(IActivatableLifetime))
{
return AvaloniaLocator.Current.GetService<IActivatableLifetime>();
}
// Do not return just any service from AvaloniaLocator.
return null;
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (change.Property == RequestedThemeVariantProperty)
{
if (change.GetNewValue<ThemeVariant>() is {} themeVariant && themeVariant != ThemeVariant.Default)
SetValue(ActualThemeVariantProperty, themeVariant);
else
ClearValue(ActualThemeVariantProperty);
}
else if (change.Property == ActualThemeVariantProperty)
{
ActualThemeVariantChanged?.Invoke(this, EventArgs.Empty);
}
}
private void OnColorValuesChanged(object? sender, PlatformColorValues e)
{
SetValue(ActualThemeVariantProperty, (ThemeVariant)e.ThemeVariant, BindingPriority.Template);
}
}
}