using System;
using System.Collections.Generic;
using System.Reactive.Concurrency;
using System.Threading;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Input.Raw;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Styling;
using Avalonia.Threading;
#nullable enable
namespace Avalonia
{
///
/// Encapsulates a Avalonia application.
///
///
/// The class encapsulates Avalonia application-specific
/// functionality, including:
/// - A global set of .
/// - A global set of .
/// - A .
/// - An .
/// - Registers services needed by the rest of Avalonia in the
/// method.
/// - Tracks the lifetime of the application.
///
public class Application : AvaloniaObject, IDataContextProvider, IGlobalDataTemplates, IGlobalStyles, IResourceHost, IApplicationPlatformEvents
{
///
/// The application-global data templates.
///
private DataTemplates? _dataTemplates;
private readonly Lazy _clipboard =
new Lazy(() => (IClipboard)AvaloniaLocator.Current.GetService(typeof(IClipboard)));
private readonly Styler _styler = new Styler();
private Styles? _styles;
private IResourceDictionary? _resources;
private bool _notifyingResourcesChanged;
private Action>? _stylesAdded;
private Action>? _stylesRemoved;
///
/// Defines the property.
///
public static readonly StyledProperty DataContextProperty =
StyledElement.DataContextProperty.AddOwner();
///
public event EventHandler? ResourcesChanged;
public event EventHandler? UrlsOpened;
///
/// Creates an instance of the class.
///
public Application()
{
Name = "Avalonia Application";
}
///
/// Gets or sets the Applications's data context.
///
///
/// The data context property specifies the default object that will
/// be used for data binding.
///
public object? DataContext
{
get { return GetValue(DataContextProperty); }
set { SetValue(DataContextProperty, value); }
}
///
/// Gets the current instance of the class.
///
///
/// The current instance of the class.
///
public static Application Current
{
get { return AvaloniaLocator.Current.GetService(); }
}
///
/// Gets or sets the application's global data templates.
///
///
/// The application's global data templates.
///
public DataTemplates DataTemplates => _dataTemplates ?? (_dataTemplates = new DataTemplates());
///
/// Gets the application's focus manager.
///
///
/// The application's focus manager.
///
public IFocusManager? FocusManager
{
get;
private set;
}
///
/// Gets the application's input manager.
///
///
/// The application's input manager.
///
public InputManager? InputManager
{
get;
private set;
}
///
/// Gets the application clipboard.
///
public IClipboard Clipboard => _clipboard.Value;
///
/// Gets the application's global resource dictionary.
///
public IResourceDictionary Resources
{
get => _resources ??= new ResourceDictionary(this);
set
{
value = value ?? throw new ArgumentNullException(nameof(value));
_resources?.RemoveOwner(this);
_resources = value;
_resources.AddOwner(this);
}
}
///
/// Gets the application's global styles.
///
///
/// The application's global styles.
///
///
/// Global styles apply to all windows in the application.
///
public Styles Styles => _styles ??= new Styles(this);
///
bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null;
///
bool IResourceNode.HasResources => (_resources?.HasResources ?? false) ||
(((IResourceNode?)_styles)?.HasResources ?? false);
///
/// Gets the styling parent of the application, which is null.
///
IStyleHost? IStyleHost.StylingParent => null;
///
bool IStyleHost.IsStylesInitialized => _styles != null;
///
/// Application lifetime, use it for things like setting the main window and exiting the app from code
/// Currently supported lifetimes are:
/// -
/// -
/// -
///
public IApplicationLifetime? ApplicationLifetime { get; set; }
event Action> IGlobalStyles.GlobalStylesAdded
{
add => _stylesAdded += value;
remove => _stylesAdded -= value;
}
event Action> IGlobalStyles.GlobalStylesRemoved
{
add => _stylesRemoved += value;
remove => _stylesRemoved -= value;
}
///
/// Initializes the application by loading XAML etc.
///
public virtual void Initialize() { }
///
bool IResourceNode.TryGetResource(object key, out object? value)
{
value = null;
return (_resources?.TryGetResource(key, out value) ?? false) ||
Styles.TryGetResource(key, out value);
}
void IResourceHost.NotifyHostedResourcesChanged(ResourcesChangedEventArgs e)
{
ResourcesChanged?.Invoke(this, e);
}
void IStyleHost.StylesAdded(IReadOnlyList styles)
{
_stylesAdded?.Invoke(styles);
}
void IStyleHost.StylesRemoved(IReadOnlyList styles)
{
_stylesRemoved?.Invoke(styles);
}
///
/// Register's the services needed by Avalonia.
///
public virtual void RegisterServices()
{
AvaloniaSynchronizationContext.InstallIfNeeded();
FocusManager = new FocusManager();
InputManager = new InputManager();
AvaloniaLocator.CurrentMutable
.Bind().ToTransient()
.Bind().ToConstant(this)
.Bind().ToConstant(this)
.Bind().ToConstant(FocusManager)
.Bind().ToConstant(InputManager)
.Bind().ToTransient()
.Bind().ToConstant(_styler)
.Bind().ToConstant(AvaloniaScheduler.Instance)
.Bind().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() == null)
AvaloniaLocator.CurrentMutable
.Bind().ToTransient();
var clock = new RenderLoopClock();
AvaloniaLocator.CurrentMutable
.Bind().ToConstant(clock)
.GetService()?.Add(clock);
}
public virtual void OnFrameworkInitializationCompleted()
{
}
void IApplicationPlatformEvents.RaiseUrlsOpened(string[] urls)
{
UrlsOpened?.Invoke(this, new UrlOpenedEventArgs (urls));
}
private void NotifyResourcesChanged(ResourcesChangedEventArgs e)
{
if (_notifyingResourcesChanged)
{
return;
}
try
{
_notifyingResourcesChanged = true;
ResourcesChanged?.Invoke(this, ResourcesChangedEventArgs.Empty);
}
finally
{
_notifyingResourcesChanged = false;
}
}
private void ThisResourcesChanged(object sender, ResourcesChangedEventArgs e)
{
NotifyResourcesChanged(e);
}
private string? _name;
///
/// Defines Name property
///
public static readonly DirectProperty NameProperty =
AvaloniaProperty.RegisterDirect("Name", o => o.Name, (o, v) => o.Name = v);
///
/// Application name to be used for various platform-specific purposes
///
public string? Name
{
get => _name;
set => SetAndRaise(NameProperty, ref _name, value);
}
}
}