【问题标题】:How can I find the position of a maximized window?如何找到最大化窗口的位置?
【发布时间】:2011-06-15 22:04:17
【问题描述】:

我需要知道最大化的窗口的位置。

WPF Window 具有指定窗口位置的 Top 和 Left 属性。但是,如果您最大化窗口,这些属性会使窗口的值保持在正常状态。

如果您在单屏设置上运行,则最大化位置自然是 (0,0)。但是,如果您有多个屏幕,则不一定正确。只有在主屏幕上最大化时,窗口才会有位置 (0,0)。

那么...有没有办法找出最大化窗口的位置(最好与 Top 和 Left 属性在相同的逻辑单元中)?

【问题讨论】:

    标签: wpf window


    【解决方案1】:

    这是我根据之前的讨论提出的解决方案(谢谢!)。

    这个解决方案...

    • 返回当前状态下窗口的位置
    • 处理所有窗口状态(最大化、最小化、恢复)
    • 不依赖于 Windows 窗体(但受到它的启发)
    • 使用窗口句柄可靠地确定正确的监视器

    main方法GetAbsolutePosition在这里实现为扩展方法。如果您有一个名为 myWindowWindow,请这样称呼它:

    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);
        }
    }
    

    【讨论】:

    • 非常有帮助,谢谢。我还建议向您的 WPFExtensionMethods 添加另一个方法到 GetAbsoluteRect,返回一个新的 Rect(rX, rY, r.Width, r.Height),因为您仍然不能依赖 window.ActualWidth 或 window.Left 当屏幕未最大化。
    【解决方案2】:
    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);
        }
    }
    

    【讨论】:

    • 金发姑娘!第一个解决方案紧凑但错误,第二个可能正确但不紧凑。这个恰到好处 :) 出于某种原因,这 3 个的投票顺序与其实际值相反。
    • 是的!验证简单并且与多屏幕完美配合!谢谢!
    【解决方案3】:

    我终于找到了适合我的解决方案:

    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;
    }
    

    【讨论】:

    • 我认为如果您将 GetWorkingArea 调用更改为:windowRectangle = System.Windows.Forms.Screen.GetWorkingArea(new System.Drawing.Point((int)window.Left + (int)(window. ActualWidth / 2), (int)window.Top + (int)(window.ActualHeight / 2)));这将解决它。您需要获取窗口的中心,而不是左上角,因为这是 WPF 用来确定要最大化到哪个窗口的点,如果窗口跨越多个监视器。
    • 好主意,但这真的有帮助吗? if 子句的这个分支仅在 WindowState 最大化时使用,因此 ActualWith 和 -Height 将与屏幕大小有关,而不是(恢复的)窗口大小
    • 是的,我明白你在说什么。您可以从 RestoreBounds 获取恢复的窗口大小,然后使用它来代替 ActualWidth/ActualHeight 吗?
    • 如果应用程序在多个监视器之一上以最大化状态启动,则会失败。
    • 不幸的是,这个解决方案有时只能给出正确的答案。错误的是假设在最大化状态下窗口的 Left 和 Top 属性是它们在最大化之前的状态。经过一番检查,结果是假的。
    【解决方案4】:

    适用于所有显示器和所有高 DPI 变体的通用单线答案:

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

    例如,对于在 1920 宽屏幕上最大化的窗口返回 (-8, -8),其 ActualWidth 返回 1936。

    只是对其他答案的咆哮:永远不要将逻辑 96 dpi WPF 像素(使用 double)与本机真实像素(使用 int)混合 - 特别是通过将 double 转换为 int!

    【讨论】:

    • 它显然不适用于多显示器设置。
    • @Kilazu 为什么很明显?我最初在所有 3 台显示器上的三重头上对此进行了测试。任务栏是垂直的,而不是在主屏幕上,仅此一项就吓坏了 50% 的所有软件。
    • 点 (0,0) 在窗口的客户区内,并不返回实际的窗口框架的上/左角。如果您尝试使用这些坐标来放置其他东西,那就错了。
    • 恼人的是这么简单的事情会阻碍一切
    • 您可以使用this question中的答案来调整非客户区。这是给我一致结果的唯一答案,也是最简单的。
    【解决方案5】:

    我知道您在 WPF 中工作,并且此答案使用了 Forms 技术,但应该没有太大困难。

    您可以通过 My.Settings.Screens.AllScreens 获取屏幕集合。从那里您可以访问屏幕当前工作的分辨率。

    由于 WPF 窗口保留了它们最大化时的 Top/Left 值,因此您可以通过确定 Top/Left 坐标所指的屏幕来确定它们所在的屏幕,然后获取 top/left 坐标对于那个屏幕。

    很遗憾,我正在路上,目前无法测试这个。如果您确实实施,我很想看看您的想法。

    【讨论】:

      【解决方案6】:

      这似乎是 System.Windows.Window 的问题!!!

      最大化窗口为 Left、Width、ActualWidth、Top、Height 和 ActualHeight 提供不可靠的值。

      最大化一个窗口后,它通常可以保留预先最大化窗口的 Left 和 Width 值。

      对于其他人阅读 - 窗口未最大化时没有问题。

      另外,我觉得奇怪的是您读取的值在 WPF DPI 坐标中,[即1936x1096,从 (-8, -8) 到 (1928, 1088)],但是当您设置这些值时,您必须使用屏幕像素坐标,[即1920x1080,使用 (0,0) 等...]

      @tgr 在上面提供了一个可靠的部分解决方案,我在下面进行了改进:

      • 通过将辅助类移动到子类来修复扩展方法的智能感知
      • 创建 GetAbsoluteRect() 方法以提供 Width/Height 并在一次调用中全部指向
      • 重构通用代码

      这是 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);
          }
      }
      

      【讨论】:

        【解决方案7】:

        我还没有找到解决您问题的方法,但如果您需要定位窗口只是为了创建一个新窗口,您可以执行以下操作:

        ...
        Window windowNew = new Window();
        ConfigureWindow(this, windowNew);
        Window.Show();
        ...
        
        static public void ConfigureWindow(Window windowOld, Window windowNew)
            {
                windowNew.Height = windowOld.ActualHeight;
                windowNew.Width = windowOld.ActualWidth;
        
                if (windowOld.WindowState == WindowState.Maximized)
                {
                    windowNew.WindowState = WindowState.Maximized;
                }
                else
                {
                    windowNew.Top = windowOld.Top;
                    windowNew.Left = windowOld.Left;
                }
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-07-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多