From e576ec178c95ccbd8a5772754ad1c30d87465d26 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Wed, 6 Jun 2018 21:06:05 +0200 Subject: [PATCH 1/6] Initial --- src/Avalonia.Controls/AppBuilderBase.cs | 45 +++--- src/Avalonia.Controls/Application.cs | 118 ++++++++++++++- src/Avalonia.Controls/ExitMode.cs | 12 ++ src/Avalonia.Controls/Window.cs | 51 ++++--- src/Avalonia.Controls/WindowCollection.cs | 134 ++++++++++++++++++ .../ApplicationTests.cs | 107 ++++++++++++++ .../WindowTests.cs | 10 +- 7 files changed, 428 insertions(+), 49 deletions(-) create mode 100644 src/Avalonia.Controls/ExitMode.cs create mode 100644 src/Avalonia.Controls/WindowCollection.cs create mode 100644 tests/Avalonia.Controls.UnitTests/ApplicationTests.cs diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs index 7af3deef340..875f5263c27 100644 --- a/src/Avalonia.Controls/AppBuilderBase.cs +++ b/src/Avalonia.Controls/AppBuilderBase.cs @@ -15,7 +15,7 @@ namespace Avalonia.Controls public abstract class AppBuilderBase where TAppBuilder : AppBuilderBase, new() { private static bool s_setupWasAlreadyCalled; - + /// /// Gets or sets the instance. /// @@ -92,7 +92,7 @@ public static TAppBuilder Configure(Application app) }; } - protected TAppBuilder Self => (TAppBuilder) this; + protected TAppBuilder Self => (TAppBuilder)this; /// /// Registers a callback to call before Start is called on the . @@ -125,7 +125,6 @@ public void Start(Func dataContextProvider = null) var window = new TMainWindow(); if (dataContextProvider != null) window.DataContext = dataContextProvider(); - window.Show(); Instance.Run(window); } @@ -143,7 +142,6 @@ public void Start(TMainWindow mainWindow, Func dataContextP if (dataContextProvider != null) mainWindow.DataContext = dataContextProvider(); - mainWindow.Show(); Instance.Run(mainWindow); } @@ -209,6 +207,17 @@ static Action GetInitializer(string assemblyName) => () => public TAppBuilder UseAvaloniaModules() => AfterSetup(builder => SetupAvaloniaModules()); + /// + /// Sets the shutdown mode of the application. + /// + /// The shutdown mode. + /// + public TAppBuilder SetExitMode(ExitMode exitMode) + { + Instance.ExitMode = exitMode; + return Self; + } + private bool CheckSetup { get; set; } = true; /// @@ -223,20 +232,20 @@ internal TAppBuilder IgnoreSetupCheck() private void SetupAvaloniaModules() { var moduleInitializers = from assembly in AvaloniaLocator.Current.GetService().GetLoadedAssemblies() - from attribute in assembly.GetCustomAttributes() - where attribute.ForWindowingSubsystem == "" - || attribute.ForWindowingSubsystem == WindowingSubsystemName - where attribute.ForRenderingSubsystem == "" - || attribute.ForRenderingSubsystem == RenderingSubsystemName - group attribute by attribute.Name into exports - select (from export in exports - orderby export.ForWindowingSubsystem.Length descending - orderby export.ForRenderingSubsystem.Length descending - select export).First().ModuleType into moduleType - select (from constructor in moduleType.GetTypeInfo().DeclaredConstructors - where constructor.GetParameters().Length == 0 && !constructor.IsStatic - select constructor).Single() into constructor - select (Action)(() => constructor.Invoke(new object[0])); + from attribute in assembly.GetCustomAttributes() + where attribute.ForWindowingSubsystem == "" + || attribute.ForWindowingSubsystem == WindowingSubsystemName + where attribute.ForRenderingSubsystem == "" + || attribute.ForRenderingSubsystem == RenderingSubsystemName + group attribute by attribute.Name into exports + select (from export in exports + orderby export.ForWindowingSubsystem.Length descending + orderby export.ForRenderingSubsystem.Length descending + select export).First().ModuleType into moduleType + select (from constructor in moduleType.GetTypeInfo().DeclaredConstructors + where constructor.GetParameters().Length == 0 && !constructor.IsStatic + select constructor).Single() into constructor + select (Action)(() => constructor.Invoke(new object[0])); Delegate.Combine(moduleInitializers.ToArray()).DynamicInvoke(); } diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 6fdca557ebc..ffe4a9c513c 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -43,11 +43,15 @@ public class Application : IApplicationLifecycle, IGlobalDataTemplates, IGlobalS private Styles _styles; private IResourceDictionary _resources; + private CancellationTokenSource _mainLoopCancellationTokenSource; + /// /// Initializes a new instance of the class. /// public Application() { + Windows = new WindowCollection(this); + OnExit += OnExiting; } @@ -158,6 +162,40 @@ public IResourceDictionary Resources /// IResourceNode IResourceNode.ResourceParent => null; + /// + /// Gets or sets the . This property indicates whether the application exits explicitly or implicitly. + /// If is set to OnExplicitExit the application is only closes if Exit is called. + /// The default is OnLastWindowClose + /// + /// + /// The shutdown mode. + /// + public ExitMode ExitMode { get; set; } + + /// + /// Gets or sets the main window of the application. + /// + /// + /// The main window. + /// + public Window MainWindow { get; set; } + + /// + /// Gets the open windows of the application. + /// + /// + /// The windows. + /// + public WindowCollection Windows { get; } + + /// + /// Gets or sets a value indicating whether this instance is existing. + /// + /// + /// true if this instance is existing; otherwise, false. + /// + internal bool IsExiting { get; set; } + /// /// Initializes the application by loading XAML etc. /// @@ -171,19 +209,81 @@ public virtual void Initialize() /// The closable to track 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); + + // Make sure we call OnExit in case an error happened and Exit() wasn't called explicitly + if (!IsExiting) + { + OnExit?.Invoke(this, EventArgs.Empty); + } + } + + /// + /// Runs the application's main loop until some condition occurs that is specified by ExitMode. + /// + /// The main window + 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); + + // Make sure we call OnExit in case an error happened and Exit() wasn't called explicitly + if (!IsExiting) + { + OnExit?.Invoke(this, EventArgs.Empty); + } } - + /// - /// Runs the application's main loop until the is cancelled. + /// Runs the application's main loop until the is canceled. /// /// The token to track public void Run(CancellationToken token) { Dispatcher.UIThread.MainLoop(token); + + // Make sure we call OnExit in case an error happened and Exit() wasn't called explicitly + if (!IsExiting) + { + OnExit?.Invoke(this, EventArgs.Empty); + } } /// @@ -191,7 +291,13 @@ public void Run(CancellationToken token) /// public void Exit() { + IsExiting = true; + + Windows.Clear(); + OnExit?.Invoke(this, EventArgs.Empty); + + _mainLoopCancellationTokenSource?.Cancel(); } /// diff --git a/src/Avalonia.Controls/ExitMode.cs b/src/Avalonia.Controls/ExitMode.cs new file mode 100644 index 00000000000..0c5ecd7171d --- /dev/null +++ b/src/Avalonia.Controls/ExitMode.cs @@ -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 + } +} \ No newline at end of file diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index 3cbfdbd6578..c19c69ce733 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -49,14 +49,6 @@ public enum SizeToContent /// public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot, INameScope { - private static List s_windows = new List(); - - /// - /// Retrieves an enumeration of all Windows in the currently running application. - /// - public static IReadOnlyList OpenWindows => s_windows; - - /// /// Defines the property. /// public static readonly StyledProperty SizeToContentProperty = @@ -75,7 +67,7 @@ public class Window : WindowBase, IStyleable, IFocusScope, ILayoutRoot, INameSco AvaloniaProperty.Register(nameof(ShowInTaskbar), true); /// - /// Enables or disables the taskbar icon + /// Represents the current window state (normal, minimized, maximized) /// public static readonly StyledProperty WindowStateProperty = AvaloniaProperty.Register(nameof(WindowState)); @@ -117,7 +109,7 @@ static Window() BackgroundProperty.OverrideDefaultValue(typeof(Window), Brushes.White); TitleProperty.Changed.AddClassHandler((s, e) => s.PlatformImpl?.SetTitle((string)e.NewValue)); HasSystemDecorationsProperty.Changed.AddClassHandler( - (s, e) => s.PlatformImpl?.SetSystemDecorations((bool) e.NewValue)); + (s, e) => s.PlatformImpl?.SetSystemDecorations((bool)e.NewValue)); ShowInTaskbarProperty.Changed.AddClassHandler((w, e) => w.PlatformImpl?.ShowTaskbarIcon((bool)e.NewValue)); @@ -149,7 +141,7 @@ public Window(IWindowImpl impl) _maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size); Screens = new Screens(PlatformImpl?.Screen); } - + /// event EventHandler INameScope.Registered { @@ -199,7 +191,7 @@ public bool HasSystemDecorations get { return GetValue(HasSystemDecorationsProperty); } set { SetValue(HasSystemDecorationsProperty, value); } } - + /// /// Enables or disables the taskbar icon /// @@ -259,6 +251,26 @@ public WindowStartupLocation WindowStartupLocation /// public event EventHandler Closing; + private static void AddWindow(Window window) + { + if (Application.Current == null) + { + return; + } + + Application.Current.Windows.Add(window); + } + + private static void RemoveWindow(Window window) + { + if (Application.Current == null) + { + return; + } + + Application.Current.Windows.Remove(window); + } + /// /// Closes the window. /// @@ -298,10 +310,9 @@ internal void Close(bool ignoreCancel) finally { if (ignoreCancel || !cancelClosing) - { - s_windows.Remove(this); + { PlatformImpl?.Dispose(); - IsVisible = false; + HandleClosed(); } } } @@ -359,7 +370,7 @@ public override void Show() return; } - s_windows.Add(this); + AddWindow(this); EnsureInitialized(); SetWindowStartupLocation(); @@ -400,7 +411,7 @@ public Task ShowDialog() throw new InvalidOperationException("The window is already being shown."); } - s_windows.Add(this); + AddWindow(this); EnsureInitialized(); SetWindowStartupLocation(); @@ -409,7 +420,7 @@ public Task ShowDialog() using (BeginAutoSizing()) { - var affectedWindows = s_windows.Where(w => w.IsEnabled && w != this).ToList(); + var affectedWindows = Application.Current.Windows.Where(w => w.IsEnabled && w != this).ToList(); var activated = affectedWindows.Where(w => w.IsActive).FirstOrDefault(); SetIsEnabled(affectedWindows, false); @@ -513,8 +524,8 @@ protected override Size MeasureOverride(Size availableSize) protected override void HandleClosed() { - IsVisible = false; - s_windows.Remove(this); + RemoveWindow(this); + base.HandleClosed(); } diff --git a/src/Avalonia.Controls/WindowCollection.cs b/src/Avalonia.Controls/WindowCollection.cs new file mode 100644 index 00000000000..c21a12f05b1 --- /dev/null +++ b/src/Avalonia.Controls/WindowCollection.cs @@ -0,0 +1,134 @@ +// 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. + +using System.Collections; +using System.Collections.Generic; + +using Avalonia.Controls; + +namespace Avalonia +{ + public class WindowCollection : IReadOnlyList + { + private readonly Application _application; + private readonly List _windows = new List(); + + public WindowCollection(Application application) + { + _application = application; + } + + /// + /// + /// Gets the number of elements in the collection. + /// + public int Count => _windows.Count; + + /// + /// + /// Gets the at the specified index. + /// + /// + /// The . + /// + /// The index. + /// + public Window this[int index] => _windows[index]; + + /// + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// An enumerator that can be used to iterate through the collection. + /// + public IEnumerator GetEnumerator() + { + return _windows.GetEnumerator(); + } + + /// + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Adds the specified window. + /// + /// The window. + internal void Add(Window window) + { + if (window == null) + { + return; + } + + _windows.Add(window); + } + + /// + /// Removes the specified window. + /// + /// The window. + internal void Remove(Window window) + { + if (window == null) + { + return; + } + + _windows.Remove(window); + + OnRemoveWindow(window); + } + + /// + /// Closes all windows and removes them from the underlying collection. + /// + internal void Clear() + { + while (_windows.Count > 0) + { + _windows[0].Close(); + } + } + + private void OnRemoveWindow(Window window) + { + if (window == null) + { + return; + } + + if (_application.IsExiting) + { + return; + } + + switch (_application.ExitMode) + { + case ExitMode.OnLastWindowClose: + if (Count == 0) + { + _application.Exit(); + } + + break; + case ExitMode.OnMainWindowClose: + if (window == _application.MainWindow) + { + _application.Exit(); + } + + break; + } + } + } +} \ No newline at end of file diff --git a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs new file mode 100644 index 00000000000..85f95b2b5c9 --- /dev/null +++ b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs @@ -0,0 +1,107 @@ +// 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. + +using System.Collections.Generic; +using Avalonia.UnitTests; +using Xunit; + +namespace Avalonia.Controls.UnitTests +{ + public class ApplicationTests + { + [Fact] + public void Should_Exit_After_MainWindow_Closed() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + Application.Current.ExitMode = ExitMode.OnMainWindowClose; + + var mainWindow = new Window(); + + mainWindow.Show(); + + Application.Current.MainWindow = mainWindow; + + var window = new Window(); + + window.Show(); + + mainWindow.Close(); + + Assert.True(Application.Current.IsExiting); + } + } + + [Fact] + public void Should_Exit_After_Last_Window_Closed() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + Application.Current.ExitMode = ExitMode.OnLastWindowClose; + + var windowA = new Window(); + + windowA.Show(); + + var windowB = new Window(); + + windowB.Show(); + + windowA.Close(); + + Assert.False(Application.Current.IsExiting); + + windowB.Close(); + + Assert.True(Application.Current.IsExiting); + } + } + + [Fact] + public void Should_Only_Exit_On_Explicit_Exit() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + Application.Current.ExitMode = ExitMode.OnExplicitExit; + + var windowA = new Window(); + + windowA.Show(); + + var windowB = new Window(); + + windowB.Show(); + + windowA.Close(); + + Assert.False(Application.Current.IsExiting); + + windowB.Close(); + + Assert.False(Application.Current.IsExiting); + + Application.Current.Exit(); + + Assert.True(Application.Current.IsExiting); + } + } + + [Fact] + public void Should_Close_All_Remaining_Open_Windows_After_Explicit_Exit_Call() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var windows = new List { new Window(), new Window(), new Window(), new Window() }; + + foreach (var window in windows) + { + window.Show(); + } + + Application.Current.Exit(); + + Assert.Empty(Application.Current.Windows); + } + } + } +} diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs index a85c4df8afe..e80ffd97cd8 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs @@ -129,7 +129,7 @@ public void Show_Should_Add_Window_To_OpenWindows() window.Show(); - Assert.Equal(new[] { window }, Window.OpenWindows); + Assert.Equal(new[] { window }, Application.Current.Windows); } } @@ -145,7 +145,7 @@ public void Window_Should_Be_Added_To_OpenWindows_Only_Once() window.Show(); window.IsVisible = true; - Assert.Equal(new[] { window }, Window.OpenWindows); + Assert.Equal(new[] { window }, Application.Current.Windows); window.Close(); } @@ -162,7 +162,7 @@ public void Close_Should_Remove_Window_From_OpenWindows() window.Show(); window.Close(); - Assert.Empty(Window.OpenWindows); + Assert.Empty(Application.Current.Windows); } } @@ -184,7 +184,7 @@ public void Impl_Closing_Should_Remove_Window_From_OpenWindows() window.Show(); windowImpl.Object.Closed(); - Assert.Empty(Window.OpenWindows); + Assert.Empty(Application.Current.Windows); } } @@ -339,7 +339,7 @@ private void ClearOpenWindows() { // HACK: We really need a decent way to have "statics" that can be scoped to // AvaloniaLocator scopes. - ((IList)Window.OpenWindows).Clear(); + Application.Current.Windows.Clear(); } } } From fca102655ea12961e18e03e616adc3bdff6c0817 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Sun, 17 Jun 2018 19:34:46 +0200 Subject: [PATCH 2/6] Add ExitMode comments --- src/Avalonia.Controls/ExitMode.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Avalonia.Controls/ExitMode.cs b/src/Avalonia.Controls/ExitMode.cs index 0c5ecd7171d..b73fe4a9633 100644 --- a/src/Avalonia.Controls/ExitMode.cs +++ b/src/Avalonia.Controls/ExitMode.cs @@ -3,10 +3,24 @@ namespace Avalonia { + /// + /// Enum for ExitMode + /// public enum ExitMode { + /// + /// Indicates an implicit call to Application.Exit when the last window closes. + /// OnLastWindowClose, + + /// + /// Indicates an implicit call to Application.Exit when the main window closes. + /// OnMainWindowClose, + + /// + /// Indicates that the application only exits on an explicit call to Application.Exit. + /// OnExplicitExit } } \ No newline at end of file From 42dadae3ecdfeedb6fdde2d5aea59bd76bd6d3c0 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Sun, 17 Jun 2018 22:46:40 +0200 Subject: [PATCH 3/6] Merge fix --- src/Avalonia.Controls/AppBuilderBase.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Avalonia.Controls/AppBuilderBase.cs b/src/Avalonia.Controls/AppBuilderBase.cs index 814c662341d..83763c08366 100644 --- a/src/Avalonia.Controls/AppBuilderBase.cs +++ b/src/Avalonia.Controls/AppBuilderBase.cs @@ -218,17 +218,6 @@ public TAppBuilder SetExitMode(ExitMode exitMode) return Self; } - private bool CheckSetup { get; set; } = true; - - /// - /// Set this AppBuilder to ignore the setup check. Used for testing purposes. - /// - internal TAppBuilder IgnoreSetupCheck() - { - CheckSetup = false; - return Self; - } - protected virtual bool CheckSetup => true; private void SetupAvaloniaModules() From 7e8f9fbf619313389065950244924c9e46cf0658 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Tue, 19 Jun 2018 21:15:22 +0200 Subject: [PATCH 4/6] Add NullArgumentException for Run with main window --- src/Avalonia.Controls/Application.cs | 8 ++++---- .../Avalonia.Controls.UnitTests/ApplicationTests.cs | 12 +++++++++++- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index ffe4a9c513c..99575771dad 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -243,14 +243,14 @@ public void Run(Window mainWindow) Dispatcher.UIThread.InvokeAsync( () => { - if (mainWindow == null) + if (MainWindow != null) { return; } - if (MainWindow != null) + if (mainWindow == null) { - return; + throw new ArgumentNullException(nameof(mainWindow)); } if (!mainWindow.IsVisible) @@ -283,7 +283,7 @@ public void Run(CancellationToken token) if (!IsExiting) { OnExit?.Invoke(this, EventArgs.Empty); - } + } } /// diff --git a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs index 85f95b2b5c9..694a6d2278b 100644 --- a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs @@ -1,6 +1,7 @@ // 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. +using System; using System.Collections.Generic; using Avalonia.UnitTests; using Xunit; @@ -103,5 +104,14 @@ public void Should_Close_All_Remaining_Open_Windows_After_Explicit_Exit_Call() Assert.Empty(Application.Current.Windows); } } + + [Fact] + public void Throws_ArgumentNullException_On_Run_If_MainWindow_Is_Null() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + Assert.Throws(() => { Application.Current.Run(null); }); + } + } } -} +} \ No newline at end of file From 0214939fd3bf865655b50ab9e0c5e592cfb8ef5c Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Tue, 19 Jun 2018 21:56:13 +0200 Subject: [PATCH 5/6] Add unit test --- tests/Avalonia.Controls.UnitTests/ApplicationTests.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs index 694a6d2278b..9602bb0460d 100644 --- a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs @@ -105,6 +105,17 @@ public void Should_Close_All_Remaining_Open_Windows_After_Explicit_Exit_Call() } } + [Fact] + public void Should_Show_MainWindow_After_Run() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var mainWindow = new Window(); + Application.Current.Run(mainWindow); + Assert.True(mainWindow.IsVisible); + } + } + [Fact] public void Throws_ArgumentNullException_On_Run_If_MainWindow_Is_Null() { From 90fdfae3bd93667abe9bdc92094688c1a8244029 Mon Sep 17 00:00:00 2001 From: Benedikt Schroeder Date: Wed, 20 Jun 2018 00:01:57 +0200 Subject: [PATCH 6/6] Remove invoke call on Application.Run --- src/Avalonia.Controls/Application.cs | 35 ++++++++----------- .../ApplicationTests.cs | 11 ------ 2 files changed, 14 insertions(+), 32 deletions(-) diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs index 99575771dad..de27aa94f80 100644 --- a/src/Avalonia.Controls/Application.cs +++ b/src/Avalonia.Controls/Application.cs @@ -240,27 +240,20 @@ public void Run(Window mainWindow) _mainLoopCancellationTokenSource = new CancellationTokenSource(); - Dispatcher.UIThread.InvokeAsync( - () => - { - if (MainWindow != null) - { - return; - } - - if (mainWindow == null) - { - throw new ArgumentNullException(nameof(mainWindow)); - } - - if (!mainWindow.IsVisible) - { - mainWindow.Show(); - } - - MainWindow = mainWindow; - }, - DispatcherPriority.Send); + if (MainWindow == null) + { + if (mainWindow == null) + { + throw new ArgumentNullException(nameof(mainWindow)); + } + + if (!mainWindow.IsVisible) + { + mainWindow.Show(); + } + + MainWindow = mainWindow; + } Dispatcher.UIThread.MainLoop(_mainLoopCancellationTokenSource.Token); diff --git a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs index 9602bb0460d..694a6d2278b 100644 --- a/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ApplicationTests.cs @@ -105,17 +105,6 @@ public void Should_Close_All_Remaining_Open_Windows_After_Explicit_Exit_Call() } } - [Fact] - public void Should_Show_MainWindow_After_Run() - { - using (UnitTestApplication.Start(TestServices.StyledWindow)) - { - var mainWindow = new Window(); - Application.Current.Run(mainWindow); - Assert.True(mainWindow.IsVisible); - } - } - [Fact] public void Throws_ArgumentNullException_On_Run_If_MainWindow_Is_Null() {