WPF – Center Window to Screen

Issue

It is frustrating when windows open on another display other than the one you clicked on or display partially off screen, requiring you to move and resize the window.

With WPF (Windows Presentation Foundation), this issue occurs when you configure the window to resize to its contents. The final values for ActualHeight and ActualWidth are set after the window has been loaded, displayed and its contents rendered. Therefore, centering the window on the Loaded event results in the top left corner being centered near the middle of the screen with the bottom right portion of the window possibly off screen.

Solution

System.Windows.Forms.Screen provides a one line solution to this issue unlike the solutions based on importing user32.dll, which requires declaring several structures to use with GetWindowPlacement().

To re-center a window when it resizes to accommodate content or when a user resizes it, add an event handler for the window SizeChanged event. The SizeChanged event occurs when either the ActualHeight or the ActualWidth properties change value.


realWindow.SizeChanged += (sender, e) => ReCenterWindowEventHandler(sender, e, realWindow);

Next put the event handler in the base class of your application presentation code.


/// Recenter this window to parent window.
public void ReCenterWindowEventHandler(object sender, EventArgs e, System.Windows.Window window)
{
    System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
    {
        if (!double.IsNaN(window.ActualHeight)
         && !double.IsNaN(window.ActualWidth)
         && !double.IsNaN(window.Owner.ActualHeight)
         && !double.IsNaN(window.Owner.ActualWidth))
        {
            WindowPlacement.ConstrainAndCenterWindowToScreen(window: window);
        }
    }));
}

The class below, WindowPlacement, provides methods to recenter a window to the screen it is displayed in or the screen of another window. See the code comments detailing what each method does.


using System.Windows;
using System.Windows.Forms;
using System.Windows.Interop;


namespace Presentation
{
    static internal class WindowPlacement
    {
        /// <summary>
        /// Resize window to fit screen, then center window to screen.
        /// </summary>
        /// <param name="window">Window to be centered.</param>
        public static void ConstrainAndCenterWindowToScreen(Window window)
        {
            var screen = Screen.FromHandle(new WindowInteropHelper(window).Handle);

            // If actual height or width of window is greater than the screen then set height or width.
            if (window.ActualHeight > screen.WorkingArea.Height)
            {
                window.Height = screen.WorkingArea.Height;
                window.MaxHeight = screen.WorkingArea.Height;
            }

            if (window.ActualWidth > screen.WorkingArea.Width)
            {
                window.Width = screen.WorkingArea.Width;
                window.MaxWidth = screen.WorkingArea.Width;
            }

            // Recenter window within the screen.
            // Starting on left side of screen add half width of screen to get to center of screen then 
            // subract half width of window to get to left side of window.
            window.Left = screen.WorkingArea.Left + (screen.WorkingArea.Width - window.ActualWidth) / 2;
            window.Top = screen.WorkingArea.Top + (screen.WorkingArea.Height - window.ActualHeight) / 2;
        }


        /// <summary>
        /// Determine host screen of parent window, resize child window to fit screen and center child window to screen.
        /// </summary>
        /// <param name="childWindow">Window to be centered.</param>
        /// <param name="parentWindow">Window to be centered within.</param>
        public static void ConstrainAndCenterWindowToScreenOfParentWindow(Window childWindow, Window parentWindow)
        {
            // Get screen of parent window.
            var screen = Screen.FromHandle(new WindowInteropHelper(parentWindow).Handle);

            // If actual height or width of child window is greater than the screen then set height or width to that of
            // the screen.
            if (childWindow.ActualHeight > screen.WorkingArea.Height)
            {
                childWindow.Height = screen.WorkingArea.Height;
                childWindow.MaxHeight = screen.WorkingArea.Height;
            }

            if (childWindow.ActualWidth > screen.WorkingArea.Width)
            {
                childWindow.Width = screen.WorkingArea.Width;
                childWindow.MaxWidth = screen.WorkingArea.Width;
            }

            // Recenter child window within the screen of the parent window.
            // Starting on left side of screen add half width of screen to get to center of screen then 
            // subract half width of window to get to left side of window.
            childWindow.Left = screen.WorkingArea.Left + (screen.WorkingArea.Width - childWindow.ActualWidth) / 2;
            childWindow.Top = screen.WorkingArea.Top + (screen.WorkingArea.Height - childWindow.ActualHeight) / 2;
        }
    }
}

To understand how these methods work, we need to understand the display coordinate system in Windows. The primary display’s top left corner is the center of the X, Y axis with positive values increasing to the right and down. Displays to the left of the primary display have negative X values and displays to the right have positive X values. Displays above the primary display have negative Y values and displays below the primary display have positive Y values. Here is an example of two displays with the primary below the secondary.

Screen coordinates of two displays with the primary below the secondary.
Screen coordinates of two displays with the primary below the secondary.

The System.Windows.Forms.Screen.AllScreens property returns an array containing all displays. Each array item contains the coordinates of the entire screen and the working area of the screen. Notice the values for the left, top, right and bottom properties for both the Bounds and WorkingArea.

Screen.AllScreens[0]
Screen.AllScreens[0] Secondary display.
Screen.AllScreens[1]
Screen.AllScreens[1] Primary display.

Conclusion

Searching which screen a window opened in or resizing a window to fit the monitor screen is frustrating. Programmatically centering windows within the screen the user is working in and resizing them to fit the screen provides a much better user experience.