@ -8,6 +8,7 @@ using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes ;
using Avalonia.Interactivity ;
using Avalonia.Platform ;
using Avalonia.Reactive ;
using Avalonia.Threading ;
namespace Avalonia.Controls.ApplicationLifetimes
@ -18,38 +19,7 @@ namespace Avalonia.Controls.ApplicationLifetimes
private CancellationTokenSource ? _ cts ;
private bool _ isShuttingDown ;
private readonly AvaloniaList < Window > _ windows = new ( ) ;
private static ClassicDesktopStyleApplicationLifetime ? s_activeLifetime ;
static ClassicDesktopStyleApplicationLifetime ( )
{
Window . WindowOpenedEvent . AddClassHandler ( typeof ( Window ) , OnWindowOpened ) ;
Window . WindowClosedEvent . AddClassHandler ( typeof ( Window ) , OnWindowClosed ) ;
}
private static void OnWindowClosed ( object? sender , RoutedEventArgs e )
{
var window = ( Window ) sender ! ;
s_activeLifetime ? . _ windows . Remove ( window ) ;
s_activeLifetime ? . HandleWindowClosed ( window ) ;
}
private static void OnWindowOpened ( object? sender , RoutedEventArgs e )
{
var window = ( Window ) sender ! ;
if ( s_activeLifetime is not null & & ! s_activeLifetime . _ windows . Contains ( window ) )
{
s_activeLifetime . _ windows . Add ( window ) ;
}
}
public ClassicDesktopStyleApplicationLifetime ( )
{
if ( s_activeLifetime ! = null )
throw new InvalidOperationException (
"Can not have multiple active ClassicDesktopStyleApplicationLifetime instances and the previously created one was not disposed" ) ;
s_activeLifetime = this ;
}
private CompositeDisposable ? _ compositeDisposable ;
/// <inheritdoc/>
public event EventHandler < ControlledApplicationLifetimeStartupEventArgs > ? Startup ;
@ -97,9 +67,32 @@ namespace Avalonia.Controls.ApplicationLifetimes
{
return DoShutdown ( new ShutdownRequestedEventArgs ( ) , true , false , exitCode ) ;
}
public int Start ( string [ ] args )
internal void SetupCore ( string [ ] args )
{
if ( _ compositeDisposable is not null )
{
// There could be a case, when lifetime was setup without starting.
// Until developer started it manually later. To avoid API breaking changes, it will execute Setup method twice.
return ;
}
_ compositeDisposable = new CompositeDisposable (
Window . WindowOpenedEvent . AddClassHandler ( typeof ( Window ) , ( sender , _ ) = >
{
var window = ( Window ) sender ! ;
if ( ! _ windows . Contains ( window ) )
{
_ windows . Add ( window ) ;
}
} ) ,
Window . WindowClosedEvent . AddClassHandler ( typeof ( Window ) , ( sender , _ ) = >
{
var window = ( Window ) sender ! ;
_ windows . Remove ( window ) ;
HandleWindowClosed ( window ) ;
} ) ) ;
Startup ? . Invoke ( this , new ControlledApplicationLifetimeStartupEventArgs ( args ) ) ;
var options = AvaloniaLocator . Current . GetService < ClassicDesktopStyleApplicationLifetimeOptions > ( ) ;
@ -116,9 +109,14 @@ namespace Avalonia.Controls.ApplicationLifetimes
if ( lifetimeEvents ! = null )
lifetimeEvents . ShutdownRequested + = OnShutdownRequested ;
}
_ cts = new CancellationTokenSource ( ) ;
public int Start ( string [ ] args )
{
SetupCore ( args ) ;
_ cts = new CancellationTokenSource ( ) ;
// Note due to a bug in the JIT we wrap this in a method, otherwise MainWindow
// gets stuffed into a local var and can not be GCed until after the program stops.
// this method never exits until program end.
@ -137,8 +135,8 @@ namespace Avalonia.Controls.ApplicationLifetimes
public void Dispose ( )
{
if ( s_activeLifetime = = this )
s_activeLifetim e = null ;
_ compositeDisposable ? . Dispose ( ) ;
_ compositeDisposabl e = null ;
}
private bool DoShutdown (
@ -206,21 +204,65 @@ namespace Avalonia.Controls.ApplicationLifetimes
namespace Avalonia
{
/// <summary>
/// IClassicDesktopStyleApplicationLifetime related AppBuilder extensions.
/// </summary>
public static class ClassicDesktopStyleApplicationLifetimeExtensions
{
public static int StartWithClassicDesktopLifetime (
this AppBuilder builder , string [ ] args , ShutdownMode shutdownMode = ShutdownMode . OnLastWindowClose )
private static ClassicDesktopStyleApplicationLifetime PrepareLifetime ( AppBuilder builder , string [ ] args ,
Action < IClassicDesktopStyleApplicationLifetime > ? lifetimeBuilder )
{
var lifetime = AvaloniaLocator . Current . GetService < ClassicDesktopStyleApplicationLifetime > ( ) ;
if ( lifetime = = null )
{
lifetime = new ClassicDesktopStyleApplicationLifetime ( ) ;
}
var lifetime = builder . LifetimeOverride ? . Invoke ( typeof ( ClassicDesktopStyleApplicationLifetime ) ) as ClassicDesktopStyleApplicationLifetime
? ? new ClassicDesktopStyleApplicationLifetime ( ) ;
lifetime . Args = args ;
lifetime . ShutdownMode = shutdownMode ;
lifetimeBuilder ? . Invoke ( lifetime ) ;
return lifetime ;
}
/// <summary>
/// Setups the Application with a IClassicDesktopStyleApplicationLifetime, but doesn't show the main window and doesn't run application main loop.
/// </summary>
/// <param name="builder">Application builder.</param>
/// <param name="args">Startup arguments.</param>
/// <param name="lifetimeBuilder">Lifetime builder to modify the lifetime before application started.</param>
/// <returns>Exit code.</returns>
public static AppBuilder SetupWithClassicDesktopLifetime ( this AppBuilder builder , string [ ] args ,
Action < IClassicDesktopStyleApplicationLifetime > ? lifetimeBuilder = null )
{
var lifetime = PrepareLifetime ( builder , args , lifetimeBuilder ) ;
lifetime . SetupCore ( args ) ;
return builder . SetupWithLifetime ( lifetime ) ;
}
/// <summary>
/// Starts the Application with a IClassicDesktopStyleApplicationLifetime, shows main window and runs application main loop.
/// </summary>
/// <param name="builder">Application builder.</param>
/// <param name="args">Startup arguments.</param>
/// <param name="lifetimeBuilder">Lifetime builder to modify the lifetime before application started.</param>
/// <returns>Exit code.</returns>
public static int StartWithClassicDesktopLifetime (
this AppBuilder builder , string [ ] args ,
Action < IClassicDesktopStyleApplicationLifetime > ? lifetimeBuilder = null )
{
var lifetime = PrepareLifetime ( builder , args , lifetimeBuilder ) ;
builder . SetupWithLifetime ( lifetime ) ;
return lifetime . Start ( args ) ;
}
/// <summary>
/// Starts the Application with a IClassicDesktopStyleApplicationLifetime, shows main window and runs application main loop.
/// </summary>
/// <param name="builder">Application builder.</param>
/// <param name="args">Startup arguments.</param>
/// <param name="shutdownMode">Lifetime shutdown mode.</param>
/// <returns>Exit code.</returns>
public static int StartWithClassicDesktopLifetime (
this AppBuilder builder , string [ ] args , ShutdownMode shutdownMode )
{
var lifetime = PrepareLifetime ( builder , args , l = > l . ShutdownMode = shutdownMode ) ;
builder . SetupWithLifetime ( lifetime ) ;
return lifetime . Start ( args ) ;
}