Как я могу найти положение развернутого окна? - PullRequest
12 голосов
/ 26 января 2011

Мне нужно знать положение развернутого окна.

Окно WPF имеет свойства Top и Left, которые определяют местоположение окна. Тем не менее, если вы разверните окно до максимума, эти свойства сохранят значения окна в нормальном состоянии.

Если вы работаете на одноэкранной установке, максимальная позиция, естественно, равна (0,0). Однако, если у вас есть несколько экранов, это не обязательно верно. У окна будет только положение (0,0), если оно развернуто на главном экране.

Итак ... есть ли способ узнать положение развернутого окна (предпочтительно в тех же логических единицах, что и свойства Top и Left)?

Ответы [ 6 ]

10 голосов
/ 25 июля 2012

Вот решение, которое я придумал, основываясь на предыдущем обсуждении здесь (спасибо!).

Это решение ...

  • возвращает положение окна в его текущем состоянии
  • обрабатывает все состояния окна (развернуто, свернуто, восстановлено)
  • не зависит от Windows Forms (но вдохновлено ею)
  • использует дескриптор окна, чтобы надежно определить правильный монитор

Основной метод GetAbsolutePosition реализован здесь как метод расширения. Если у вас есть Window с именем myWindow, назовите его так:

Point p = myWindow.GetAbsolutePosition();

Вот полный код:

using System;
using System.Windows;
using System.Windows.Interop;
using System.Runtime.InteropServices;

static class OSInterop
{
    [DllImport("user32.dll")]
    public static extern int GetSystemMetrics(int smIndex);
    public const int SM_CMONITORS = 80;

    [DllImport("user32.dll")]
    public static extern bool SystemParametersInfo(int nAction, int nParam, ref RECT rc, int nUpdate);

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool GetMonitorInfo(HandleRef hmonitor, [In, Out] MONITORINFOEX info);

    [DllImport("user32.dll")]
    public static extern IntPtr MonitorFromWindow(HandleRef handle, int flags);

    public struct RECT
    {
        public int left;
        public int top;
        public int right;
        public int bottom;
        public int width { get { return right - left; } }
        public int height { get { return bottom - top; } }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Auto)]
    public class MONITORINFOEX
    {
        public int cbSize = Marshal.SizeOf(typeof(MONITORINFOEX));
        public RECT rcMonitor = new RECT();
        public RECT rcWork = new RECT();
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
        public char[] szDevice = new char[32];
        public int dwFlags;
    }
}

static class WPFExtensionMethods
{
    public static Point GetAbsolutePosition(this Window w)
    {
        if (w.WindowState != WindowState.Maximized)
            return new Point(w.Left, w.Top);

        Int32Rect r;
        bool multimonSupported = OSInterop.GetSystemMetrics(OSInterop.SM_CMONITORS) != 0;
        if (!multimonSupported)
        {
            OSInterop.RECT rc = new OSInterop.RECT();
            OSInterop.SystemParametersInfo(48, 0, ref rc, 0);
            r = new Int32Rect(rc.left, rc.top, rc.width, rc.height);
        }
        else
        {
            WindowInteropHelper helper = new WindowInteropHelper(w);
            IntPtr hmonitor = OSInterop.MonitorFromWindow(new HandleRef((object)null, helper.EnsureHandle()), 2);
            OSInterop.MONITORINFOEX info = new OSInterop.MONITORINFOEX();
            OSInterop.GetMonitorInfo(new HandleRef((object)null, hmonitor), info);
            r = new Int32Rect(info.rcWork.left, info.rcWork.top, info.rcWork.width, info.rcWork.height);
        }
        return new Point(r.X, r.Y);
    }
}
6 голосов
/ 15 мая 2016

Универсальный однострочный ответ, работающий для всех мониторов и всех вариантов с высоким разрешением:

Point leftTop = this.PointToScreen(new Point(0, 0));

Например, возвращает (-8, -8) для окна, которое развернуто на большом экране 1920, чей ActualWidth возвращает 1936.

Просто напыщенный ответ на другие вопросы: НИКОГДА не смешивайте логические пиксели WPF 96 dpi (используя double) с собственными реальными пикселями (используя int) - особенно просто приведя doubleИНТ!

6 голосов
/ 05 ноября 2013
public static System.Drawing.Rectangle GetWindowRectangle(this Window w)
{
    if (w.WindowState == WindowState.Maximized) {
        var handle = new System.Windows.Interop.WindowInteropHelper(w).Handle;
        var screen = System.Windows.Forms.Screen.FromHandle(handle);
        return screen.WorkingArea;
    }
    else {
        return new System.Drawing.Rectangle(
            (int)w.Left, (int)w.Top, 
            (int)w.ActualWidth, (int)w.ActualHeight);
    }
}
6 голосов
/ 29 июня 2011

Я наконец нашел решение, работающее на меня:

private System.Drawing.Rectangle getWindowRectangle()
{
    System.Drawing.Rectangle windowRectangle;

    if (this.WindowState == System.Windows.WindowState.Maximized)
    {
        /* Here is the magic:
         * Use Winforms code to find the Available space on the
         * screen that contained the window 
         * just before it was maximized
         * (Left, Top have their values from Normal WindowState)
         */
        windowRectangle = System.Windows.Forms.Screen.GetWorkingArea(
            new System.Drawing.Point((int)this.Left, (int)this.Top));
    }
    else
    {
        windowRectangle = new System.Drawing.Rectangle(
            (int)this.Left, (int)this.Top,
            (int)this.ActualWidth, (int)this.ActualHeight);
    }

    return windowRectangle;
}
0 голосов
/ 30 июня 2019

Похоже, это проблема с System.Windows.Window !!!

В развернутом окне отображаются ненадежные значения для параметра Left, Width, ActualWidth, Top, Height и ActualHeight.

После максимизации окна оно часто может сохранять значения Left и Width из предварительно развернутого окна.

Для других, читающих - нет проблем, когда окно не развернуто.

Кроме того, я нахожу странным то, что значения, которые вы читаете , находятся в координатах WPF DPI, [т.е. 1936x1096, от (-8, -8) до (1928, 1088)], но когда вы установить эти значения вы должны использовать координаты пикселя экрана, [то есть 1920x1080, используя (0,0) и т. Д ...]

@ tgr обеспечили надежное частичное решение выше, которое я 'улучшено ниже:

  • Исправление intellisense для методов расширения путем перемещения вспомогательного класса в подкласс
  • создание метода GetAbsoluteRect () для предоставления Width / Height и указания всех в одном вызове
  • рефакторинг общего кода

HerЭто решение C #:

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;

public static partial class Extensions
{
    static class OSInterop
    {
        [DllImport("user32.dll")]
        public static extern int GetSystemMetrics(int smIndex);
        public const int SM_CMONITORS = 80;

        [DllImport("user32.dll")]
        public static extern bool SystemParametersInfo(int nAction, int nParam, ref RECT rc, int nUpdate);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern bool GetMonitorInfo(HandleRef hmonitor, [In, Out] MONITORINFOEX info);

        [DllImport("user32.dll")]
        public static extern IntPtr MonitorFromWindow(HandleRef handle, int flags);

        public struct RECT
        {
            public int left;
            public int top;
            public int right;
            public int bottom;
            public int width { get { return right - left; } }
            public int height { get { return bottom - top; } }
        }

        [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Auto)]
        public class MONITORINFOEX
        {
            public int cbSize = Marshal.SizeOf(typeof(MONITORINFOEX));
            public RECT rcMonitor = new RECT();
            public RECT rcWork = new RECT();
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
            public char[] szDevice = new char[32];
            public int dwFlags;
        }
    }

    static Int32Rect _getOsInteropRect(Window w)
    {
        bool multimonSupported = OSInterop.GetSystemMetrics(OSInterop.SM_CMONITORS) != 0;
        if (!multimonSupported)
        {
            OSInterop.RECT rc = new OSInterop.RECT();
            OSInterop.SystemParametersInfo(48, 0, ref rc, 0);
            return new Int32Rect(rc.left, rc.top, rc.width, rc.height);
        }

        WindowInteropHelper helper = new WindowInteropHelper(w);
        IntPtr hmonitor = OSInterop.MonitorFromWindow(new HandleRef((object)null, helper.EnsureHandle()), 2);
        OSInterop.MONITORINFOEX info = new OSInterop.MONITORINFOEX();
        OSInterop.GetMonitorInfo(new HandleRef((object)null, hmonitor), info);
        return new Int32Rect(info.rcWork.left, info.rcWork.top, info.rcWork.width, info.rcWork.height);
    }

    public static Rect GetAbsoluteRect(this Window w)
    {
        if (w.WindowState != WindowState.Maximized)
            return new Rect(w.Left, w.Top, w.ActualWidth, w.ActualHeight);

        var r = _getOsInteropRect(w);
        return new Rect(r.X, r.Y, r.Width, r.Height);
    }

    public static Point GetAbsolutePosition(this Window w)
    {
        if (w.WindowState != WindowState.Maximized)
            return new Point(w.Left, w.Top);

        var r = _getOsInteropRect(w);
        return new Point(r.X, r.Y);
    }
}
0 голосов
/ 26 января 2011

Я понимаю, что вы работаете в WPF, и в этом ответе используется технология форм, но она должна работать без особых затруднений.

Вы можете получить коллекцию экранов с помощью My.Settings.Screens.AllScreens.Оттуда вы можете получить доступ к разрешению, над которым в данный момент работает экран.

Так как окна WPF сохраняют значения Top / Left, которые были у них при максимизации, вы можете определить, на каком экране они находятся, выясник какому экрану относятся координаты сверху / слева, а затем получить координаты сверху / слева для этого экрана.

К сожалению, я нахожусь в пути и не могу проверить это в данный момент.Если вы реализуете, я хотел бы увидеть, что вы придумали.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...