8
8
using Avalonia . Controls . ApplicationLifetimes ;
9
9
using Avalonia . Interactivity ;
10
10
using Avalonia . Platform ;
11
+ using Avalonia . Reactive ;
11
12
using Avalonia . Threading ;
12
13
13
14
namespace Avalonia . Controls . ApplicationLifetimes
@@ -18,38 +19,7 @@ public class ClassicDesktopStyleApplicationLifetime : IClassicDesktopStyleApplic
18
19
private CancellationTokenSource ? _cts ;
19
20
private bool _isShuttingDown ;
20
21
private readonly AvaloniaList < Window > _windows = new ( ) ;
21
-
22
- private static ClassicDesktopStyleApplicationLifetime ? s_activeLifetime ;
23
-
24
- static ClassicDesktopStyleApplicationLifetime ( )
25
- {
26
- Window . WindowOpenedEvent . AddClassHandler ( typeof ( Window ) , OnWindowOpened ) ;
27
- Window . WindowClosedEvent . AddClassHandler ( typeof ( Window ) , OnWindowClosed ) ;
28
- }
29
-
30
- private static void OnWindowClosed ( object ? sender , RoutedEventArgs e )
31
- {
32
- var window = ( Window ) sender ! ;
33
- s_activeLifetime ? . _windows . Remove ( window ) ;
34
- s_activeLifetime ? . HandleWindowClosed ( window ) ;
35
- }
36
-
37
- private static void OnWindowOpened ( object ? sender , RoutedEventArgs e )
38
- {
39
- var window = ( Window ) sender ! ;
40
- if ( s_activeLifetime is not null && ! s_activeLifetime . _windows . Contains ( window ) )
41
- {
42
- s_activeLifetime . _windows . Add ( window ) ;
43
- }
44
- }
45
-
46
- public ClassicDesktopStyleApplicationLifetime ( )
47
- {
48
- if ( s_activeLifetime != null )
49
- throw new InvalidOperationException (
50
- "Can not have multiple active ClassicDesktopStyleApplicationLifetime instances and the previously created one was not disposed" ) ;
51
- s_activeLifetime = this ;
52
- }
22
+ private CompositeDisposable ? _compositeDisposable ;
53
23
54
24
/// <inheritdoc/>
55
25
public event EventHandler < ControlledApplicationLifetimeStartupEventArgs > ? Startup ;
@@ -97,9 +67,32 @@ public bool TryShutdown(int exitCode = 0)
97
67
{
98
68
return DoShutdown ( new ShutdownRequestedEventArgs ( ) , true , false , exitCode ) ;
99
69
}
100
-
101
- public int Start ( string [ ] args )
70
+
71
+ internal void SetupCore ( string [ ] args )
102
72
{
73
+ if ( _compositeDisposable is not null )
74
+ {
75
+ // There could be a case, when lifetime was setup without starting.
76
+ // Until developer started it manually later. To avoid API breaking changes, it will execute Setup method twice.
77
+ return ;
78
+ }
79
+
80
+ _compositeDisposable = new CompositeDisposable (
81
+ Window . WindowOpenedEvent . AddClassHandler ( typeof ( Window ) , ( sender , _ ) =>
82
+ {
83
+ var window = ( Window ) sender ! ;
84
+ if ( ! _windows . Contains ( window ) )
85
+ {
86
+ _windows . Add ( window ) ;
87
+ }
88
+ } ) ,
89
+ Window . WindowClosedEvent . AddClassHandler ( typeof ( Window ) , ( sender , _ ) =>
90
+ {
91
+ var window = ( Window ) sender ! ;
92
+ _windows . Remove ( window ) ;
93
+ HandleWindowClosed ( window ) ;
94
+ } ) ) ;
95
+
103
96
Startup ? . Invoke ( this , new ControlledApplicationLifetimeStartupEventArgs ( args ) ) ;
104
97
105
98
var options = AvaloniaLocator . Current . GetService < ClassicDesktopStyleApplicationLifetimeOptions > ( ) ;
@@ -116,9 +109,14 @@ public int Start(string[] args)
116
109
117
110
if ( lifetimeEvents != null )
118
111
lifetimeEvents . ShutdownRequested += OnShutdownRequested ;
112
+ }
119
113
120
- _cts = new CancellationTokenSource ( ) ;
114
+ public int Start ( string [ ] args )
115
+ {
116
+ SetupCore ( args ) ;
121
117
118
+ _cts = new CancellationTokenSource ( ) ;
119
+
122
120
// Note due to a bug in the JIT we wrap this in a method, otherwise MainWindow
123
121
// gets stuffed into a local var and can not be GCed until after the program stops.
124
122
// this method never exits until program end.
@@ -137,8 +135,8 @@ private void ShowMainWindow()
137
135
138
136
public void Dispose ( )
139
137
{
140
- if ( s_activeLifetime == this )
141
- s_activeLifetime = null ;
138
+ _compositeDisposable ? . Dispose ( ) ;
139
+ _compositeDisposable = null ;
142
140
}
143
141
144
142
private bool DoShutdown (
@@ -206,21 +204,65 @@ public class ClassicDesktopStyleApplicationLifetimeOptions
206
204
207
205
namespace Avalonia
208
206
{
207
+ /// <summary>
208
+ /// IClassicDesktopStyleApplicationLifetime related AppBuilder extensions.
209
+ /// </summary>
209
210
public static class ClassicDesktopStyleApplicationLifetimeExtensions
210
211
{
211
- public static int StartWithClassicDesktopLifetime (
212
- this AppBuilder builder , string [ ] args , ShutdownMode shutdownMode = ShutdownMode . OnLastWindowClose )
212
+ private static ClassicDesktopStyleApplicationLifetime PrepareLifetime ( AppBuilder builder , string [ ] args ,
213
+ Action < IClassicDesktopStyleApplicationLifetime > ? lifetimeBuilder )
213
214
{
214
- var lifetime = AvaloniaLocator . Current . GetService < ClassicDesktopStyleApplicationLifetime > ( ) ;
215
-
216
- if ( lifetime == null )
217
- {
218
- lifetime = new ClassicDesktopStyleApplicationLifetime ( ) ;
219
- }
215
+ var lifetime = builder . LifetimeOverride ? . Invoke ( typeof ( ClassicDesktopStyleApplicationLifetime ) ) as ClassicDesktopStyleApplicationLifetime
216
+ ?? new ClassicDesktopStyleApplicationLifetime ( ) ;
220
217
221
218
lifetime . Args = args ;
222
- lifetime . ShutdownMode = shutdownMode ;
219
+ lifetimeBuilder ? . Invoke ( lifetime ) ;
220
+
221
+ return lifetime ;
222
+ }
223
+
224
+ /// <summary>
225
+ /// Setups the Application with a IClassicDesktopStyleApplicationLifetime, but doesn't show the main window and doesn't run application main loop.
226
+ /// </summary>
227
+ /// <param name="builder">Application builder.</param>
228
+ /// <param name="args">Startup arguments.</param>
229
+ /// <param name="lifetimeBuilder">Lifetime builder to modify the lifetime before application started.</param>
230
+ /// <returns>Exit code.</returns>
231
+ public static AppBuilder SetupWithClassicDesktopLifetime ( this AppBuilder builder , string [ ] args ,
232
+ Action < IClassicDesktopStyleApplicationLifetime > ? lifetimeBuilder = null )
233
+ {
234
+ var lifetime = PrepareLifetime ( builder , args , lifetimeBuilder ) ;
235
+ lifetime . SetupCore ( args ) ;
236
+ return builder . SetupWithLifetime ( lifetime ) ;
237
+ }
238
+
239
+ /// <summary>
240
+ /// Starts the Application with a IClassicDesktopStyleApplicationLifetime, shows main window and runs application main loop.
241
+ /// </summary>
242
+ /// <param name="builder">Application builder.</param>
243
+ /// <param name="args">Startup arguments.</param>
244
+ /// <param name="lifetimeBuilder">Lifetime builder to modify the lifetime before application started.</param>
245
+ /// <returns>Exit code.</returns>
246
+ public static int StartWithClassicDesktopLifetime (
247
+ this AppBuilder builder , string [ ] args ,
248
+ Action < IClassicDesktopStyleApplicationLifetime > ? lifetimeBuilder = null )
249
+ {
250
+ var lifetime = PrepareLifetime ( builder , args , lifetimeBuilder ) ;
251
+ builder . SetupWithLifetime ( lifetime ) ;
252
+ return lifetime . Start ( args ) ;
253
+ }
223
254
255
+ /// <summary>
256
+ /// Starts the Application with a IClassicDesktopStyleApplicationLifetime, shows main window and runs application main loop.
257
+ /// </summary>
258
+ /// <param name="builder">Application builder.</param>
259
+ /// <param name="args">Startup arguments.</param>
260
+ /// <param name="shutdownMode">Lifetime shutdown mode.</param>
261
+ /// <returns>Exit code.</returns>
262
+ public static int StartWithClassicDesktopLifetime (
263
+ this AppBuilder builder , string [ ] args , ShutdownMode shutdownMode )
264
+ {
265
+ var lifetime = PrepareLifetime ( builder , args , l => l . ShutdownMode = shutdownMode ) ;
224
266
builder . SetupWithLifetime ( lifetime ) ;
225
267
return lifetime . Start ( args ) ;
226
268
}
0 commit comments