diff --git a/src/Avalonia.Controls/Primitives/PopupRoot.cs b/src/Avalonia.Controls/Primitives/PopupRoot.cs
index 5e5177b29d..86c1b47521 100644
--- a/src/Avalonia.Controls/Primitives/PopupRoot.cs
+++ b/src/Avalonia.Controls/Primitives/PopupRoot.cs
@@ -70,32 +70,12 @@ namespace Avalonia.Controls.Primitives
this.PlatformImpl.Dispose();
}
- ///
- /// Hides the popup.
- ///
- public void Hide()
- {
- PlatformImpl.Hide();
- IsVisible = false;
- }
-
- ///
- /// Shows the popup.
- ///
- public void Show()
- {
- EnsureInitialized();
- PlatformImpl.Show();
- LayoutManager.Instance.ExecuteInitialLayoutPass(this);
- IsVisible = true;
- }
-
///
protected override void OnTemplateApplied(TemplateAppliedEventArgs e)
{
base.OnTemplateApplied(e);
- if (Parent.TemplatedParent != null)
+ if (Parent?.TemplatedParent != null)
{
if (_presenterSubscription != null)
{
@@ -109,16 +89,6 @@ namespace Avalonia.Controls.Primitives
}
}
- private void EnsureInitialized()
- {
- if (!this.IsInitialized)
- {
- var init = (ISupportInitialize)this;
- init.BeginInit();
- init.EndInit();
- }
- }
-
private void SetTemplatedParentAndApplyChildTemplates(IControl control)
{
var templatedParent = Parent.TemplatedParent;
diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs
index 4de0256a7d..eaa69b4d4b 100644
--- a/src/Avalonia.Controls/TopLevel.cs
+++ b/src/Avalonia.Controls/TopLevel.cs
@@ -208,6 +208,16 @@ namespace Avalonia.Controls
return PlatformImpl.PointToScreen(p);
}
+ ///
+ /// Handles a closed notification from .
+ ///
+ protected virtual void HandleClosed()
+ {
+ Closed?.Invoke(this, EventArgs.Empty);
+ Renderer?.Dispose();
+ Renderer = null;
+ _applicationLifecycle.OnExit -= OnApplicationExiting;
+ }
///
/// Handles a resize notification from .
@@ -267,17 +277,6 @@ namespace Avalonia.Controls
return result;
}
- ///
- /// Handles a closed notification from .
- ///
- private void HandleClosed()
- {
- Closed?.Invoke(this, EventArgs.Empty);
- Renderer?.Dispose();
- Renderer = null;
- _applicationLifecycle.OnExit -= OnApplicationExiting;
- }
-
private void OnApplicationExiting(object sender, EventArgs args)
{
HandleApplicationExiting();
diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs
index e66b1beb1e..d7a7a9f0eb 100644
--- a/src/Avalonia.Controls/Window.cs
+++ b/src/Avalonia.Controls/Window.cs
@@ -190,6 +190,7 @@ namespace Avalonia.Controls
{
s_windows.Remove(this);
PlatformImpl.Dispose();
+ IsVisible = false;
}
protected override void HandleApplicationExiting()
@@ -216,22 +217,25 @@ namespace Avalonia.Controls
///
/// Hides the window but does not close it.
///
- public void Hide()
+ public override void Hide()
{
using (BeginAutoSizing())
{
PlatformImpl.Hide();
}
+
+ IsVisible = false;
}
///
/// Shows the window.
///
- public void Show()
+ public override void Show()
{
s_windows.Add(this);
EnsureInitialized();
+ IsVisible = true;
LayoutManager.Instance.ExecuteInitialLayoutPass(this);
using (BeginAutoSizing())
@@ -265,6 +269,7 @@ namespace Avalonia.Controls
s_windows.Add(this);
EnsureInitialized();
+ IsVisible = true;
LayoutManager.Instance.ExecuteInitialLayoutPass(this);
using (BeginAutoSizing())
@@ -344,6 +349,12 @@ namespace Avalonia.Controls
return size;
}
+ protected override void HandleClosed()
+ {
+ IsVisible = false;
+ base.HandleClosed();
+ }
+
///
protected override void HandleResized(Size clientSize)
{
@@ -354,16 +365,6 @@ namespace Avalonia.Controls
base.HandleResized(clientSize);
}
-
- private void EnsureInitialized()
- {
- if (!this.IsInitialized)
- {
- var init = (ISupportInitialize)this;
- init.BeginInit();
- init.EndInit();
- }
- }
}
}
diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs
index ac6eea8641..18cc9a3419 100644
--- a/src/Avalonia.Controls/WindowBase.cs
+++ b/src/Avalonia.Controls/WindowBase.cs
@@ -29,6 +29,13 @@ namespace Avalonia.Controls
AvaloniaProperty.RegisterDirect(nameof(IsActive), o => o.IsActive);
private bool _isActive;
+ private bool _ignoreVisibilityChange;
+
+ static WindowBase()
+ {
+ IsVisibleProperty.OverrideDefaultValue(false);
+ IsVisibleProperty.Changed.AddClassHandler(x => x.IsVisibleChanged);
+ }
public WindowBase(IWindowBaseImpl impl) : this(impl, AvaloniaLocator.Current)
{
@@ -59,7 +66,6 @@ namespace Avalonia.Controls
public new IWindowBaseImpl PlatformImpl => (IWindowBaseImpl) base.PlatformImpl;
-
///
/// Gets a value that indicates whether the window is active.
///
@@ -95,6 +101,43 @@ namespace Avalonia.Controls
PlatformImpl.Activate();
}
+ ///
+ /// Hides the popup.
+ ///
+ public virtual void Hide()
+ {
+ _ignoreVisibilityChange = true;
+
+ try
+ {
+ PlatformImpl.Hide();
+ IsVisible = false;
+ }
+ finally
+ {
+ _ignoreVisibilityChange = false;
+ }
+ }
+
+ ///
+ /// Shows the popup.
+ ///
+ public virtual void Show()
+ {
+ _ignoreVisibilityChange = true;
+
+ try
+ {
+ EnsureInitialized();
+ IsVisible = true;
+ LayoutManager.Instance.ExecuteInitialLayoutPass(this);
+ PlatformImpl.Show();
+ }
+ finally
+ {
+ _ignoreVisibilityChange = false;
+ }
+ }
///
/// Begins an auto-resize operation.
@@ -126,6 +169,34 @@ namespace Avalonia.Controls
return base.ArrangeOverride(PlatformImpl.ClientSize);
}
+ ///
+ /// Ensures that the window is initialized.
+ ///
+ protected void EnsureInitialized()
+ {
+ if (!this.IsInitialized)
+ {
+ var init = (ISupportInitialize)this;
+ init.BeginInit();
+ init.EndInit();
+ }
+ }
+
+ protected override void HandleClosed()
+ {
+ _ignoreVisibilityChange = true;
+
+ try
+ {
+ IsVisible = false;
+ base.HandleClosed();
+ }
+ finally
+ {
+ _ignoreVisibilityChange = false;
+ }
+ }
+
///
/// Handles a resize notification from .
///
@@ -180,6 +251,21 @@ namespace Avalonia.Controls
Deactivated?.Invoke(this, EventArgs.Empty);
}
+ private void IsVisibleChanged(AvaloniaPropertyChangedEventArgs e)
+ {
+ if (!_ignoreVisibilityChange)
+ {
+ if ((bool)e.NewValue)
+ {
+ Show();
+ }
+ else
+ {
+ Hide();
+ }
+ }
+ }
+
///
/// Starts moving a window with left button being held. Should be called from left mouse button press event handler
///
diff --git a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
index 8ca4ca0fbc..5cd3c57e2e 100644
--- a/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/TopLevelTests.cs
@@ -89,6 +89,7 @@ namespace Avalonia.Controls.UnitTests
var target = new TestTopLevel(impl.Object)
{
+ IsVisible = true,
Template = CreateTemplate(),
Content = new TextBlock
{
@@ -103,8 +104,6 @@ namespace Avalonia.Controls.UnitTests
}
}
-
-
[Fact]
public void Width_And_Height_Should_Not_Be_Set_After_Layout_Pass()
{
diff --git a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs
index 43dbcdc610..d1f8d5b912 100644
--- a/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/WindowBaseTests.cs
@@ -34,7 +34,8 @@ namespace Avalonia.Controls.UnitTests
{
Width = 321,
Height = 432,
- }
+ },
+ IsVisible = true,
};
LayoutManager.Instance.ExecuteInitialLayoutPass(target);
@@ -95,6 +96,93 @@ namespace Avalonia.Controls.UnitTests
}
}
+ [Fact]
+ public void IsVisible_Should_Initially_Be_False()
+ {
+ using (UnitTestApplication.Start(TestServices.MockWindowingPlatform))
+ {
+ var target = new TestWindowBase();
+
+ Assert.False(target.IsVisible);
+ }
+ }
+
+ [Fact]
+ public void IsVisible_Should_Be_True_After_Show()
+ {
+ using (UnitTestApplication.Start(TestServices.StyledWindow))
+ {
+ var target = new TestWindowBase();
+
+ target.Show();
+
+ Assert.True(target.IsVisible);
+ }
+ }
+
+ [Fact]
+ public void IsVisible_Should_Be_False_Atfer_Hide()
+ {
+ using (UnitTestApplication.Start(TestServices.StyledWindow))
+ {
+ var target = new TestWindowBase();
+
+ target.Show();
+ target.Hide();
+
+ Assert.False(target.IsVisible);
+ }
+ }
+
+ [Fact]
+ public void IsVisible_Should_Be_False_Atfer_Impl_Signals_Close()
+ {
+ var windowImpl = new Mock();
+ windowImpl.Setup(x => x.Scaling).Returns(1);
+ windowImpl.SetupProperty(x => x.Closed);
+
+ using (UnitTestApplication.Start(TestServices.StyledWindow))
+ {
+ var target = new TestWindowBase(windowImpl.Object);
+
+ target.Show();
+ windowImpl.Object.Closed();
+
+ Assert.False(target.IsVisible);
+ }
+ }
+
+ [Fact]
+ public void Setting_IsVisible_True_Shows_Window()
+ {
+ var windowImpl = new Mock();
+ windowImpl.Setup(x => x.Scaling).Returns(1);
+
+ using (UnitTestApplication.Start(TestServices.StyledWindow))
+ {
+ var target = new TestWindowBase(windowImpl.Object);
+ target.IsVisible = true;
+
+ windowImpl.Verify(x => x.Show());
+ }
+ }
+
+ [Fact]
+ public void Setting_IsVisible_False_Hides_Window()
+ {
+ var windowImpl = new Mock();
+ windowImpl.Setup(x => x.Scaling).Returns(1);
+
+ using (UnitTestApplication.Start(TestServices.StyledWindow))
+ {
+ var target = new TestWindowBase(windowImpl.Object);
+ target.Show();
+ target.IsVisible = false;
+
+ windowImpl.Verify(x => x.Hide());
+ }
+ }
+
private FuncControlTemplate CreateTemplate()
{
return new FuncControlTemplate(x =>
@@ -109,6 +197,11 @@ namespace Avalonia.Controls.UnitTests
{
public bool IsClosed { get; private set; }
+ public TestWindowBase()
+ : base(Mock.Of())
+ {
+ }
+
public TestWindowBase(IWindowBaseImpl impl)
: base(impl)
{
diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs
index 7b5e3197df..96afecc966 100644
--- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs
@@ -28,5 +28,91 @@ namespace Avalonia.Controls.UnitTests
windowImpl.Verify(x => x.SetTitle("Hello World"));
}
}
+
+ [Fact]
+ public void IsVisible_Should_Initially_Be_False()
+ {
+ using (UnitTestApplication.Start(TestServices.MockWindowingPlatform))
+ {
+ var window = new Window();
+
+ Assert.False(window.IsVisible);
+ }
+ }
+
+ [Fact]
+ public void IsVisible_Should_Be_True_After_Show()
+ {
+ using (UnitTestApplication.Start(TestServices.StyledWindow))
+ {
+ var window = new Window();
+
+ window.Show();
+
+ Assert.True(window.IsVisible);
+ }
+ }
+
+ [Fact]
+ public void IsVisible_Should_Be_True_After_ShowDialog()
+ {
+ using (UnitTestApplication.Start(TestServices.StyledWindow))
+ {
+ var window = new Window();
+
+ var task = window.ShowDialog();
+
+ Assert.True(window.IsVisible);
+ }
+ }
+
+ [Fact]
+ public void IsVisible_Should_Be_False_Atfer_Hide()
+ {
+ using (UnitTestApplication.Start(TestServices.StyledWindow))
+ {
+ var window = new Window();
+
+ window.Show();
+ window.Hide();
+
+ Assert.False(window.IsVisible);
+ }
+ }
+
+ [Fact]
+ public void IsVisible_Should_Be_False_Atfer_Close()
+ {
+ using (UnitTestApplication.Start(TestServices.StyledWindow))
+ {
+ var window = new Window();
+
+ window.Show();
+ window.Close();
+
+ Assert.False(window.IsVisible);
+ }
+ }
+
+ [Fact]
+ public void IsVisible_Should_Be_False_Atfer_Impl_Signals_Close()
+ {
+ var windowImpl = new Mock();
+ windowImpl.SetupProperty(x => x.Closed);
+ windowImpl.Setup(x => x.Scaling).Returns(1);
+
+ var services = TestServices.StyledWindow.With(
+ windowingPlatform: new MockWindowingPlatform(() => windowImpl.Object));
+
+ using (UnitTestApplication.Start(services))
+ {
+ var window = new Window();
+
+ window.Show();
+ windowImpl.Object.Closed();
+
+ Assert.False(window.IsVisible);
+ }
+ }
}
}
diff --git a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs b/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs
index 74a1fbc735..d996f881d2 100644
--- a/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs
+++ b/tests/Avalonia.Layout.UnitTests/FullLayoutTests.cs
@@ -55,6 +55,7 @@ namespace Avalonia.Layout.UnitTests
}
};
+ window.Show();
LayoutManager.Instance.ExecuteInitialLayoutPass(window);
Assert.Equal(new Size(400, 400), border.Bounds.Size);
@@ -96,6 +97,7 @@ namespace Avalonia.Layout.UnitTests
}
};
+ window.Show();
LayoutManager.Instance.ExecuteInitialLayoutPass(window);
Assert.Equal(new Size(800, 600), window.Bounds.Size);
diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs
index 23670257ff..1aaba9cf6b 100644
--- a/tests/Avalonia.LeakTests/ControlTests.cs
+++ b/tests/Avalonia.LeakTests/ControlTests.cs
@@ -42,6 +42,8 @@ namespace Avalonia.LeakTests
Content = new Canvas()
};
+ window.Show();
+
// Do a layout and make sure that Canvas gets added to visual tree.
LayoutManager.Instance.ExecuteInitialLayoutPass(window);
Assert.IsType