开发者

Window ActualTop, ActualLeft

开发者 https://www.devze.com 2023-01-13 12:26 出处:网络
What is the correct way to retrieve a window\'s position in WPF? Here\'s some attempts I made. First attempt, the obvious

What is the correct way to retrieve a window's position in WPF?

Here's some attempts I made. First attempt, the obvious

Point GetPosition(Window win)
{
    return new Point(win.Top, win.Left);
}

but this returns the "wrong" position when the window is maximized. Second attempt:

Point GetPosition(Window win)
{
    if (win.WindowState == WindowState.Maximized)
        return new Point(0, 0);
    else
        return new Poin开发者_开发问答t(win.Top, win.Left);
}

Almost there, but there is still an issue: when you have two (or more) screens and the window is maximized in the second screen you get a (0, 0) position that does not reflect the window's actual position.

I noticed that Window has _actualTop and _actualLeft private members, but no public property to expose them.

How do you retrieve the correct value?


You can use reflection if you don't want to rely on Winforms or PI

private double GetWindowLeft(Window window)
    {
        if (window.WindowState == WindowState.Maximized)
        {
            var leftField = typeof(Window).GetField("_actualLeft", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            return (double)leftField.GetValue(window);
        }
        else
            return window.Left;
    }


Here's a solution that builds on P/Invoke of GetWindowRect, but uses extension methods to make your code look nicer. You use myWindow.ActualTop() and myWindow.ActualLeft() in your code.

static class WindowExtensions
{
    [StructLayout(LayoutKind.Sequential)]
    struct RECT
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);


    public static double ActualTop(this Window window)
    {
        switch (window.WindowState)
        {
            case WindowState.Normal:
                return window.Top;
            case WindowState.Minimized:
                return window.RestoreBounds.Top;
            case WindowState.Maximized:
                {
                    RECT rect;
                    GetWindowRect((new WindowInteropHelper(window)).Handle, out rect);
                    return rect.Top; 
                }
        }
        return 0;
    }
    public static double ActualLeft(this Window window)
    {
        switch (window.WindowState)
        {
            case WindowState.Normal:
                return window.Left;
            case WindowState.Minimized:
                return window.RestoreBounds.Left;
            case WindowState.Maximized:
                {
                    RECT rect;
                    GetWindowRect((new WindowInteropHelper(window)).Handle, out rect);
                    return rect.Left;
                }
        }
        return 0;
    }
}


Yes, awkward. You've got bigger problems, (0, 0) won't be valid even on the primary monitor if the user put the taskbar on the left or the top. Like I did. You can get help from the Windows Forms Screen class. Use its FromPoint() method, then the WorkingArea property. Or the Bounds property if you allow the window to go full-screen.

Personally, I'd just P/Invoke GetWindowRect().


When the window is moved by the user, the Top and Left properties are not updated according to the change of location. But it is possible to use RestoreBounds.Top and RestoreBounds.Left as "ActualTop" and "ActualLeft". Ofcauce the maximized and minimized case is special, and must be treated separately

0

精彩评论

暂无评论...
验证码 换一张
取 消