This post demonstrates how to manually center a window with respect to both a WPF and non-WPF owner window.
Centering a Window with a WPF Owner Window
To center a window over another window in WPF, you need to do two things. First, you need to set the WindowStartupLocation property of the Window you want centered to WindowStartupLocation.CenterOwner:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.CenteredWindow"
Title="Centered Window"
WindowStartupLocation="CenterOwner"
Width="250" Height="250">
...
</Window>
using System.Windows; // Window
namespace SDKSample
{
public partial class CenteredWindow : Window
{
public CenteredWindow()
{
InitializeComponent();
}
}
}
Second, when you open the window to be centered, you *must* set its Owner property with the Window that opens it:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.OwnerWindow"
Title="Owner Window"
Width="300" Height="300">
...
<Button Click="button_Click">
Show Centered Window
</Button>
...
</Window>
using System.Windows; // Window, RoutedEventArgs
namespace SDKSample
{
public partial class OwnerWindow : Window
{
public OwnerWindow()
{
InitializeComponent();
}
void button_Click(object sender, RoutedEventArgs e)
{
// Open window and set this window as its owner.
// This causes the window to be centered with
// respect to this window
CenteredWindow cw = new CenteredWindow();
cw.Owner = this;
cw.Show();
}
}
}
When the button in this example is clicked, the owned window appears centered with respect to the owner window, as shown in the following figure:
Centering a Window with a Non-WPF Owner Window
WPF does allow you to set a non-WPF window as the owner of a WPF window. For example, a Visual Studio 2005 (VS05) add-in that shows a WPF window whose owner is the main VS05 IDE window. The following shows a generalized version of code that sets a WPF window with a non-WPF owner, using WindowInteropHelper:
using System.Windows; // Window
using System.Windows.Interop; // WindowInteropHelper
...
// Instantiate the owned WPF window
CenteredWindow cw = new CenteredWindow();
// Get the handle to the non-WPF owner window
IntPtr ownerWindowHandle = ...;
// Set the owned WPF window’s Owner property with
// the non-WPF owner window
WindowInteropHelper helper = new WindowInteropHelper(cw);
helper.Owner = (IntPtr)_applicationObject.MainWindow.HWnd;
// Show the owned WPF window
cw.Show();
When a WPF window has a non-WPF owner, WPF honors the owner window/owned window relationship eg when a non-WPF owner window is minimized, the owned WPF window follows suit. However, if the owned WPF window has its WindowStartupLocation property set to WindowStartupLocation.CenterOwner, WPF does not center the owned WPF window over the non-WPF owner window. Instead, you’ll need to set the WindowStartupLocation of the owned window to WindowStartupLocation.Manual and manually calculate its Top and Left properties with values that make it appear centered:
using System.Windows; // Window, WindowStartupLocation
using System.Windows.Interop; // WindowInteropHelper
...
// Instantiate the owned WPF window
CenteredWindow cw = new CenteredWindow();
// Get the handle to the non-WPF owner window
IntPtr ownerWindowHandle = ...; // Get hWnd for non-WPF window
// Set the owned WPF window’s owner with the non-WPF owner window
WindowInteropHelper helper = new WindowInteropHelper(cw);
helper.Owner = ownerWindowHandle;
// Manually calculate Top/Left to appear centered
int nonWPFOwnerLeft = ...; // Get non-WPF owner’s Left
int nonWPFOwnerWidth = ...; // Get non-WPF owner’s Width
int nonWPFOwnerTop = ...; // Get non-WPF owner’s Top
int nonWPFOwnerHeight = ...; // Get non-WPF owner’s Height
cw.WindowStartupLocation = WindowStartupLocation.Manual;
cw.Left = nonWPFOwnerLeft + (nonWPFOwnerWidth - cw.Width) / 2;
cw.Top = nonWPFOwnerTop + (nonWPFOwnerHeight - cw.Height) / 2;
// Show the owned WPF window
cw.Show();
There is one issue with this code; WPF supports device-independence, which means that, irrespective of DPI; this code will center a WPF window with respect to a WPF owner window in both low and high DPI. However, this code may not center a WPF window with respect to a non-WPF owner window in high DPI. In this case, you need to perform a little extra work to convert the non-WPF owner window’s location and size into device-independent versions that the WPF owned window can calculate against. This work is facilitated by HwndSource:
using System.Windows; // Window, WindowStartupLocation, Point
using System.Windows.Interop; // WindowInteropHelper, HwndSource
using System.Windows.Media; // Matrix
...
// Instantiate the owned WPF window
CenteredWindow cw = new CenteredWindow();
// Get the handle to the non-WPF owner window
IntPtr ownerWindowHandle = ...; // Get hWnd for non-WPF window
// Set the owned WPF window’s owner with the non-WPF owner window
WindowInteropHelper helper = new WindowInteropHelper(cw);
helper.Owner = ownerWindowHandle;
// Center window
// Note - Need to use HwndSource to get handle to WPF owned window,
// and the handle only exists when SourceInitialized has been
// raised
cw.SourceInitialized += delegate
{
// Get WPF size and location for non-WPF owner window
int nonWPFOwnerLeft = ...; // Get non-WPF owner’s Left
int nonWPFOwnerWidth = ...; // Get non-WPF owner’s Width
int nonWPFOwnerTop = ...; // Get non-WPF owner’s Top
int nonWPFOwnerHeight = ...; // Get non-WPF owner’s Height
// Get transform matrix to transform non-WPF owner window
// size and location units into device-independent WPF
// size and location units
HwndSource source = HwndSource.FromHwnd(helper.Handle);
if (source == null) return;
Matrix matrix = source.CompositionTarget.TransformFromDevice;
Point ownerWPFSize = matrix.Transform(
new Point(nonWPFOwnerWidth, nonWPFOwnerHeight));
Point ownerWPFPosition = matrix.Transform(
new Point(nonWPFOwnerLeft, nonWPFOwnerTop));
// Center WPF window
cw.WindowStartupLocation = WindowStartupLocation.Manual;
cw.Left = ownerWPFPosition.X + (ownerWPFSize.X - cw.Width) / 2;
cw.Top = ownerWPFPosition.Y + (ownerWPFSize.Y - cw.Height) / 2;
};
// Show WPF owned window
cw.Show();
This code basically converts the non-device-independent size and position of the non-WPF owner window, and converts them to device-independent values. This allows you to calculate the Top and Left values that will cause the owned WPF window to be appeared centered. This requires a little help from HwndSource, which is used to do the co-ordinate system conversion (or transforms). HwndSource relies on the WPF window having a hWnd, which doesn’t happen until the SourceInitialized event is raised. Hence the anonymous delegate code.
This solution is generalized, but does work in both normal and high DPI.