diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs
index 3802f2b6ea..3535510ce3 100644
--- a/src/Avalonia.Controls/Window.cs
+++ b/src/Avalonia.Controls/Window.cs
@@ -183,6 +183,15 @@ namespace Avalonia.Controls
set { SetValue(IconProperty, value); }
}
+ ///
+ /// Gets or sets the startup location of the window.
+ ///
+ public WindowStartupLocation WindowStartupLocation
+ {
+ get;
+ set;
+ }
+
///
Size ILayoutRoot.MaxClientSize => _maxPlatformClientSize;
@@ -246,6 +255,7 @@ namespace Avalonia.Controls
s_windows.Add(this);
EnsureInitialized();
+ SetWindowStartupLocation();
IsVisible = true;
LayoutManager.Instance.ExecuteInitialLayoutPass(this);
@@ -285,6 +295,7 @@ namespace Avalonia.Controls
s_windows.Add(this);
EnsureInitialized();
+ SetWindowStartupLocation();
IsVisible = true;
LayoutManager.Instance.ExecuteInitialLayoutPass(this);
@@ -321,6 +332,23 @@ namespace Avalonia.Controls
}
}
+ void SetWindowStartupLocation()
+ {
+ if (WindowStartupLocation == WindowStartupLocation.CenterScreen)
+ {
+ var positionAsSize = PlatformImpl.MaxClientSize / 2 - ClientSize / 2;
+ Position = new Point(positionAsSize.Width, positionAsSize.Height);
+ }
+ else if (WindowStartupLocation == WindowStartupLocation.CenterOwner)
+ {
+ if (Owner != null)
+ {
+ var positionAsSize = Owner.ClientSize / 2 - ClientSize / 2;
+ Position = Owner.Position + new Point(positionAsSize.Width, positionAsSize.Height);
+ }
+ }
+ }
+
///
void INameScope.Register(string name, object element)
{
diff --git a/src/Avalonia.Controls/WindowBase.cs b/src/Avalonia.Controls/WindowBase.cs
index fbdf64b14a..dd1e8fbef1 100644
--- a/src/Avalonia.Controls/WindowBase.cs
+++ b/src/Avalonia.Controls/WindowBase.cs
@@ -29,6 +29,12 @@ namespace Avalonia.Controls
public static readonly DirectProperty IsActiveProperty =
AvaloniaProperty.RegisterDirect(nameof(IsActive), o => o.IsActive);
+ ///
+ /// Defines the property.
+ ///
+ public static readonly StyledProperty OwnerProperty =
+ AvaloniaProperty.Register(nameof(Owner));
+
private bool _hasExecutedInitialLayoutPass;
private bool _isActive;
private bool _ignoreVisibilityChange;
@@ -100,6 +106,15 @@ namespace Avalonia.Controls
private set;
}
+ ///
+ /// Gets or sets the owner of the window.
+ ///
+ public WindowBase Owner
+ {
+ get { return GetValue(OwnerProperty); }
+ set { SetValue(OwnerProperty, value); }
+ }
+
///
/// Activates the window.
///
diff --git a/src/Avalonia.Controls/WindowStartupLocation.cs b/src/Avalonia.Controls/WindowStartupLocation.cs
new file mode 100644
index 0000000000..1818636076
--- /dev/null
+++ b/src/Avalonia.Controls/WindowStartupLocation.cs
@@ -0,0 +1,23 @@
+namespace Avalonia.Controls
+{
+ ///
+ /// Determines the startup location of the window.
+ ///
+ public enum WindowStartupLocation
+ {
+ ///
+ /// The startup location is defined by the Position property.
+ ///
+ Manual,
+
+ ///
+ /// The startup location is the center of the screen.
+ ///
+ CenterScreen,
+
+ ///
+ /// The startup location is the center of the owner window. If the owner window is not specified, the startup location will be .
+ ///
+ CenterOwner
+ }
+}
diff --git a/src/Avalonia.DotNetCoreRuntime/AppBuilder.cs b/src/Avalonia.DotNetCoreRuntime/AppBuilder.cs
index 2b9b3083b1..bf8d7a20fd 100644
--- a/src/Avalonia.DotNetCoreRuntime/AppBuilder.cs
+++ b/src/Avalonia.DotNetCoreRuntime/AppBuilder.cs
@@ -10,6 +10,9 @@ using Avalonia.Shared.PlatformSupport;
namespace Avalonia
{
+ ///
+ /// Initializes platform-specific services for an .
+ ///
public sealed class AppBuilder : AppBuilderBase
{
///
diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs
index e0dd908bbb..83f86ce5a0 100644
--- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs
@@ -68,7 +68,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
- public void IsVisible_Should_Be_False_Atfer_Hide()
+ public void IsVisible_Should_Be_False_After_Hide()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
@@ -82,7 +82,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
- public void IsVisible_Should_Be_False_Atfer_Close()
+ public void IsVisible_Should_Be_False_After_Close()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
@@ -96,7 +96,7 @@ namespace Avalonia.Controls.UnitTests
}
[Fact]
- public void IsVisible_Should_Be_False_Atfer_Impl_Signals_Close()
+ public void IsVisible_Should_Be_False_After_Impl_Signals_Close()
{
var windowImpl = new Mock();
windowImpl.SetupProperty(x => x.Closed);
@@ -191,5 +191,76 @@ namespace Avalonia.Controls.UnitTests
// AvaloniaLocator scopes.
((IList)Window.OpenWindows).Clear();
}
+
+ [Fact]
+ public void Window_Should_Be_Centered_When_Window_Startup_Location_Is_Center_Screen()
+ {
+ var windowImpl = new Mock();
+ windowImpl.SetupProperty(x => x.Position);
+ windowImpl.Setup(x => x.ClientSize).Returns(new Size(800, 480));
+ windowImpl.Setup(x => x.MaxClientSize).Returns(new Size(1920, 1080));
+ windowImpl.Setup(x => x.Scaling).Returns(1);
+
+ using (UnitTestApplication.Start(TestServices.StyledWindow))
+ {
+ var window = new Window();
+ window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
+ window.Position = new Point(60, 40);
+
+ window.Show();
+
+ var expectedPosition = new Point(
+ window.PlatformImpl.MaxClientSize.Width / 2 - window.ClientSize.Width / 2,
+ window.PlatformImpl.MaxClientSize.Height / 2 - window.ClientSize.Height / 2);
+
+ Assert.Equal(window.Position, expectedPosition);
+ }
+ }
+
+ [Fact]
+ public void Window_Should_Be_Centered_Relative_To_Owner_When_Window_Startup_Location_Is_Center_Owner()
+ {
+ var parentWindowImpl = new Mock();
+ parentWindowImpl.SetupProperty(x => x.Position);
+ parentWindowImpl.Setup(x => x.ClientSize).Returns(new Size(800, 480));
+ parentWindowImpl.Setup(x => x.MaxClientSize).Returns(new Size(1920, 1080));
+ parentWindowImpl.Setup(x => x.Scaling).Returns(1);
+
+ var windowImpl = new Mock();
+ windowImpl.SetupProperty(x => x.Position);
+ windowImpl.Setup(x => x.ClientSize).Returns(new Size(320, 200));
+ windowImpl.Setup(x => x.MaxClientSize).Returns(new Size(1920, 1080));
+ windowImpl.Setup(x => x.Scaling).Returns(1);
+
+ var parentWindowServices = TestServices.StyledWindow.With(
+ windowingPlatform: new MockWindowingPlatform(() => parentWindowImpl.Object));
+
+ var windowServices = TestServices.StyledWindow.With(
+ windowingPlatform: new MockWindowingPlatform(() => windowImpl.Object));
+
+ using (UnitTestApplication.Start(parentWindowServices))
+ {
+ var parentWindow = new Window();
+ parentWindow.Position = new Point(60, 40);
+
+ parentWindow.Show();
+
+ using (UnitTestApplication.Start(windowServices))
+ {
+ var window = new Window();
+ window.WindowStartupLocation = WindowStartupLocation.CenterOwner;
+ window.Position = new Point(60, 40);
+ window.Owner = parentWindow;
+
+ window.Show();
+
+ var expectedPosition = new Point(
+ parentWindow.Position.X + parentWindow.ClientSize.Width / 2 - window.ClientSize.Width / 2,
+ parentWindow.Position.Y + parentWindow.ClientSize.Height / 2 - window.ClientSize.Height / 2);
+
+ Assert.Equal(window.Position, expectedPosition);
+ }
+ }
+ }
}
}