As you can see in the image, I want to open a fixed sized WPF window at a location of Launch window (which is WinForms app). How do I make sure that the WPF windows about to open is placed such that it is fully visible on any side of that Launch window. The similar behaviour is there in Windows desktop's right click menu as if you click on extreme edge of the screen, it would open context menu at the left and if you are in middle of the screen, it would open either side.
I have tried few things already and also this SO answer also but still figuring out how to calculate Windows's bounds such that it is within visible area.
CodePudding user response:
The process would be:
- Get the rectangle of the window using its Handle.
- Get the handle of the monitor where the window locates.
- Get the information (in particular, rectangle of working area) of the monitor using its Handle.
- Calculate the direction of available space.
using System;
using System.Runtime.InteropServices;
public enum Direction { None, TopLeft, TopRight, BottomRight, BottomLeft }
public static class WindowHelper
{
public static Direction GetAvailableDirection(IntPtr windowHandle)
{
if (!GetWindowRect(windowHandle, out RECT buffer))
return Direction.None;
System.Drawing.Rectangle windowRect = buffer;
IntPtr monitorHandle = MonitorFromWindow(windowHandle, MONITOR_DEFAULTTO.MONITOR_DEFAULTTONULL);
if (monitorHandle == IntPtr.Zero)
return Direction.None;
MONITORINFO info = new() { cbSize = (uint)Marshal.SizeOf<MONITORINFO>() };
if (!GetMonitorInfo(monitorHandle, ref info))
return Direction.None;
System.Drawing.Rectangle workingAreaRect = info.rcWork;
bool isWindowAlignedTop = (windowRect.Top - workingAreaRect.Top) < (workingAreaRect.Bottom - windowRect.Bottom);
bool isWindowAlignedLeft = (windowRect.Left - workingAreaRect.Left) < (workingAreaRect.Right - windowRect.Right);
return (isWindowAlignedTop, isWindowAlignedLeft) switch
{
(true, true) => Direction.BottomRight,
(true, false) => Direction.BottomLeft,
(false, true) => Direction.TopRight,
(false, false) => Direction.TopLeft
};
}
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(
IntPtr hWnd,
out RECT lpRect);
[DllImport("User32.dll")]
private static extern IntPtr MonitorFromWindow(
IntPtr hwnd,
MONITOR_DEFAULTTO dwFlags);
private enum MONITOR_DEFAULTTO : uint
{
MONITOR_DEFAULTTONULL = 0x00000000,
MONITOR_DEFAULTTOPRIMARY = 0x00000001,
MONITOR_DEFAULTTONEAREST = 0x00000002,
}
[DllImport("User32.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetMonitorInfo(
IntPtr hMonitor,
ref MONITORINFO lpmi);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct MONITORINFO
{
public uint cbSize;
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public static implicit operator System.Drawing.Rectangle(RECT rect)
{
return new System.Drawing.Rectangle(
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top);
}
}
}
Please note that calling app must have an application manifest which includes DPI awareness for correct calculation.