-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Application ExitMode #1617
Application ExitMode #1617
Changes from 9 commits
a83d0ed
627a4ec
0ce8802
41f8037
c51a14f
f4ddc2e
de71587
6156e97
19ad25c
010281b
7ccfd53
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,11 +43,15 @@ public class Application : IApplicationLifecycle, IGlobalDataTemplates, IGlobalS | |
private Styles _styles; | ||
private IResourceDictionary _resources; | ||
|
||
private CancellationTokenSource _mainLoopCancellationTokenSource; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="Application"/> class. | ||
/// </summary> | ||
public Application() | ||
{ | ||
Windows = new WindowCollection(this); | ||
|
||
OnExit += OnExiting; | ||
} | ||
|
||
|
@@ -158,6 +162,40 @@ public IResourceDictionary Resources | |
/// <inheritdoc/> | ||
IResourceNode IResourceNode.ResourceParent => null; | ||
|
||
/// <summary> | ||
/// Gets or sets the <see cref="ExitMode"/>. This property indicates whether the application exits explicitly or implicitly. | ||
/// If <see cref="ExitMode"/> is set to OnExplicitExit the application is only closes if Exit is called. | ||
/// The default is OnLastWindowClose | ||
/// </summary> | ||
/// <value> | ||
/// The shutdown mode. | ||
/// </value> | ||
public ExitMode ExitMode { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the main window of the application. | ||
/// </summary> | ||
/// <value> | ||
/// The main window. | ||
/// </value> | ||
public Window MainWindow { get; set; } | ||
|
||
/// <summary> | ||
/// Gets the open windows of the application. | ||
/// </summary> | ||
/// <value> | ||
/// The windows. | ||
/// </value> | ||
public WindowCollection Windows { get; } | ||
|
||
/// <summary> | ||
/// Gets or sets a value indicating whether this instance is existing. | ||
/// </summary> | ||
/// <value> | ||
/// <c>true</c> if this instance is existing; otherwise, <c>false</c>. | ||
/// </value> | ||
internal bool IsExiting { get; set; } | ||
|
||
/// <summary> | ||
/// Initializes the application by loading XAML etc. | ||
/// </summary> | ||
|
@@ -171,27 +209,95 @@ public virtual void Initialize() | |
/// <param name="closable">The closable to track</param> | ||
public void Run(ICloseable closable) | ||
{ | ||
var source = new CancellationTokenSource(); | ||
closable.Closed += OnExiting; | ||
closable.Closed += (s, e) => source.Cancel(); | ||
Dispatcher.UIThread.MainLoop(source.Token); | ||
if (_mainLoopCancellationTokenSource != null) | ||
{ | ||
throw new Exception("Run should only called once"); | ||
} | ||
|
||
closable.Closed += (s, e) => Exit(); | ||
|
||
_mainLoopCancellationTokenSource = new CancellationTokenSource(); | ||
|
||
Dispatcher.UIThread.MainLoop(_mainLoopCancellationTokenSource.Token); | ||
|
||
if (!IsExiting) | ||
{ | ||
OnExit?.Invoke(this, EventArgs.Empty); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Runs the application's main loop until some condition occurs that is specified by ExitMode. | ||
/// </summary> | ||
/// <param name="mainWindow">The main window</param> | ||
public void Run(Window mainWindow) | ||
{ | ||
if (_mainLoopCancellationTokenSource != null) | ||
{ | ||
throw new Exception("Run should only called once"); | ||
} | ||
|
||
_mainLoopCancellationTokenSource = new CancellationTokenSource(); | ||
|
||
Dispatcher.UIThread.InvokeAsync( | ||
() => | ||
{ | ||
if (mainWindow == null) | ||
{ | ||
return; | ||
} | ||
|
||
if (MainWindow != null) | ||
{ | ||
return; | ||
} | ||
|
||
if (!mainWindow.IsVisible) | ||
{ | ||
mainWindow.Show(); | ||
} | ||
|
||
MainWindow = mainWindow; | ||
}, | ||
DispatcherPriority.Send); | ||
|
||
Dispatcher.UIThread.MainLoop(_mainLoopCancellationTokenSource.Token); | ||
|
||
if (!IsExiting) | ||
{ | ||
OnExit?.Invoke(this, EventArgs.Empty); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Runs the application's main loop until the <see cref="CancellationToken"/> is cancelled. | ||
/// Runs the application's main loop until the <see cref="CancellationToken"/> is canceled. | ||
/// </summary> | ||
/// <param name="token">The token to track</param> | ||
public void Run(CancellationToken token) | ||
{ | ||
Dispatcher.UIThread.MainLoop(token); | ||
|
||
if (!IsExiting) | ||
{ | ||
OnExit?.Invoke(this, EventArgs.Empty); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Exits the application | ||
/// </summary> | ||
public void Exit() | ||
{ | ||
IsExiting = true; | ||
|
||
while (Windows.Count > 0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I have tow Windows that this loop never exit. I think you should write remove the first windows. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's wrong because this line always removes the first window of the collection until none is left. A close removes the window from the list. I will add unit tests to prove that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Gillibald Good |
||
{ | ||
Windows[0].Close(); | ||
} | ||
|
||
OnExit?.Invoke(this, EventArgs.Empty); | ||
|
||
_mainLoopCancellationTokenSource?.Cancel(); | ||
} | ||
|
||
/// <inheritdoc/> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// Copyright (c) The Avalonia Project. All rights reserved. | ||
// Licensed under the MIT license. See licence.md file in the project root for full license information. | ||
|
||
namespace Avalonia | ||
{ | ||
public enum ExitMode | ||
{ | ||
OnLastWindowClose, | ||
OnMainWindowClose, | ||
OnExplicitExit | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is not exiting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This case is needed to call Exit if an error happened. We know that an error happened if Exit wasn't called explicitly so IsExiting isn't set.