I'm currently developing a small productivity tool which has no start-up window defined. Whenever I press a hot-key, a window shows up and offers me some, hopefully useful, options. When I started writing the app I set the WindowStartupLocation
property to CenterScreen
. That was OK for me until I decided it's crap moving the mouse cursor from one corner of the screen to another. So I wanted this window showing up centered under the cursor, regardless on which screen I'm working. In addition, it shouldn't overlap the current screen boundary.
Quite easy task:
- get the active screen
- get the screen resolution
- get the mouse position
- place the window (centered) below.
Well, at least these were my thoughts.
I didn't take too much time figuring out that there is not such a nice Screen
property like in Windows.Forms
. At least you can get some information about your (primary) screen resolution in System.Windows.SystemParameters
. Unfortunately if you are using more than one monitor and they don't have the same resolution, you are on your own. And oh, how to get the mouse position when you don't have anything you could pass to Mouse.GetPosition( IInputElement )
?
I had a little chat with my friend Google. A lot of guys told him to add a reference to Windows Forms - check my last question[^] here on CodeProject why I couldn't do this.
However, to make a long story short. I decided to come up with an extension method for the Windows.Window
class. I'm invoking the native API to gather all information I need. I think it might be useful for some of you out there. At least I saw a lot of questions to this topic.
Check out the code. I dropped most of the comments for readability Kindly suggest improvements or alternatives. I'd like to see if there is a much better way. And oh, English isn't my first language. Kindly tell me what I did wrong so that I can correct it
The extension methods:
namespace ExtensionMethods
{
using System;
using System.Runtime.InteropServices;
using System.Windows;
public static class WindowExtensions
{
#region Public Methods
public static bool ActivateCenteredToMouse( this Window window )
{
ComputeTopLeft( ref window );
return window.Activate();
}
public static void ShowCenteredToMouse( this Window window )
{
WindowStartupLocation oldLocation = window.WindowStartupLocation;
window.WindowStartupLocation = WindowStartupLocation.Manual;
ComputeTopLeft( ref window );
window.Show();
window.WindowStartupLocation = oldLocation;
}
#endregion
#region Methods
private static void ComputeTopLeft( ref Window window )
{
W32Point pt = new W32Point();
if ( !GetCursorPos( ref pt ) )
{
Marshal.ThrowExceptionForHR( Marshal.GetHRForLastWin32Error() );
}
IntPtr monHandle = MonitorFromPoint( pt, 0x00000002 );
W32MonitorInfo monInfo = new W32MonitorInfo();
monInfo.Size = Marshal.SizeOf( typeof( W32MonitorInfo ) );
if ( !GetMonitorInfo( monHandle, ref monInfo ) )
{
Marshal.ThrowExceptionForHR( Marshal.GetHRForLastWin32Error() );
}
W32Rect monitor = monInfo.WorkArea;
double offsetX = Math.Round( window.Width / 2 );
double offsetY = Math.Round( window.Height / 2 );
double top = pt.Y - offsetY;
double left = pt.X - offsetX;
Rect screen = new Rect (
new Point( monitor.Left, monitor.Top ),
new Point( monitor.Right, monitor.Bottom ) );
Rect wnd = new Rect(
new Point( left, top ),
new Point( left + window.Width, top + window.Height ) );
window.Top = wnd.Top;
window.Left = wnd.Left;
if ( !screen.Contains( wnd ) )
{
if ( wnd.Top < screen.Top )
{
double diff = Math.Abs( screen.Top - wnd.Top );
window.Top = wnd.Top + diff;
}
if ( wnd.Bottom > screen.Bottom )
{
double diff = wnd.Bottom - screen.Bottom;
window.Top = wnd.Top - diff;
}
if ( wnd.Left < screen.Left )
{
double diff = Math.Abs( screen.Left - wnd.Left );
window.Left = wnd.Left + diff;
}
if ( wnd.Right > screen.Right )
{
double diff = wnd.Right - screen.Right;
window.Left = wnd.Left - diff;
}
}
}
#endregion
#region W32 API
[DllImport( "user32.dll", SetLastError = true )]
[return: MarshalAs( UnmanagedType.Bool )]
private static extern bool GetCursorPos( ref W32Point pt );
[DllImport( "user32.dll", SetLastError = true )]
[return: MarshalAs( UnmanagedType.Bool )]
private static extern bool GetMonitorInfo( IntPtr hMonitor, ref W32MonitorInfo lpmi );
[DllImport( "user32.dll" )]
private static extern IntPtr MonitorFromPoint( W32Point pt, uint dwFlags );
[StructLayout( LayoutKind.Sequential )]
public struct W32Point
{
public int X;
public int Y;
}
[StructLayout( LayoutKind.Sequential )]
internal struct W32MonitorInfo
{
public int Size;
public W32Rect Monitor;
public W32Rect WorkArea;
public uint Flags;
}
[StructLayout( LayoutKind.Sequential )]
internal struct W32Rect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
#endregion
}
}
How to use it:
Create an instance of any WPF window, e.g. FancyWindow
or just MainWindow
. Afterwards you can call the two extension methods.
FancyWindow window = new FancyWindow();
...
window.ShowCenteredToMouse();
window.ActivateCenteredToMouse();
I hope this is useful for at least one of you. And thanks for reading