【问题标题】:Form Location and Size behaviour表单位置和大小行为
【发布时间】:2017-01-28 20:35:20
【问题描述】:

我想将表单定位在屏幕的左上角。

我尝试过this.Location = new Point(0,0),但窗口位于 (7,0) - 窗口顶部位于屏幕顶部,但左侧距离屏幕边缘 7 个像素。 我创建了新的 WinForms 应用程序进行测试并仅添加了以下代码:

private void Form1_Load(object sender, EventArgs e)
{
    Point p = new Point(0, 0);

    WindowState = FormWindowState.Maximized;
    Debug.WriteLine("\nMaximized");
    Debug.WriteLine("Location: " + Location);
    Debug.WriteLine("Size: " + Size);
    Debug.WriteLine("PointToScreen(0,0): " + PointToScreen(p));

    WindowState = FormWindowState.Normal;
    Location = p;            
    Debug.WriteLine("\nNormal");
    Debug.WriteLine("Location: " + Location);
    Debug.WriteLine("Size: " + Size);
    Debug.WriteLine("PointToScreen(0,0): " + PointToScreen(p));

    Debug.Write("\nScreen.PrimaryScreen.WorkingArea: ");
    Debug.WriteLine(Screen.PrimaryScreen.WorkingArea);
}

输出是:

Maximized  
Location: {X=-8,Y=-8}  
Size: {Width=1936, Height=1056}
PointToScreen(0,0): {X=0,Y=23}

Normal
Location: {X=0,Y=0}
Size: {Width=300, Height=300}
PointToScreen(0,0): {X=8,Y=31}

Screen.PrimaryScreen.WorkingArea: {X=0,Y=0,Width=1920,Height=1040}

为什么Location = new Point(0,0) 不在 (0,0) 上定位表单? 这是由于我的系统上的某些问题吗?我有Win10和VS2015。任务栏在底部,我的桌面左侧什么都没有。 为了将它定位在(0,0)上,我实际上必须将它定位在(-7,0)上。此外,报告的最大化表单宽度比屏幕宽度大 16 个像素。我知道由于窗口边缘、标题栏等原因,客户区大小和表单大小之间存在差异,但事实并非如此。当表单最大化时,没有左右边缘(客户区宽度=桌面宽度),但表单宽度为+16px。表格的 4 边各有 +8px,但 Y 位置没问题。为什么 Y 位置可以,而 X 位置不行?

【问题讨论】:

  • 您将位置设置为0,0,而Debug.WriteLine 显示Location: {X=0,Y=0}。所以有什么问题?还有PointToScreen(0,0): {X=8,Y=31}也可以,就是窗体客户区0,0点的屏幕坐标。
  • 我之前在 Win10 或 8.1 上没有注意到这个定位问题。如果我不得不猜测,我会说观察到的问题是无法再禁用的桌面窗口管理器组合的结果。需要调查的新事物。谢谢。 :)
  • 问题是,就像我写的那样,窗口的位置(实际上,就是我能看到的)是右侧 7 个像素。注意客户端坐标 (8,31) - X 为 8,因为窗口位置为 7,加上窗口边缘的 1 个像素,Y 为 31,因为标题栏。这就是为什么当我将位置设置为 (-7,0) 时,窗口就在角落里。所以,客户端坐标没问题——客户端矩形真的从 (8,31) 开始。表单坐标不正确 - 表单位置真的 (7,0),而不是它所说的 (0,0)。

标签: c# .net forms winforms location


【解决方案1】:

感谢Uwe Keim 和他自己的answer 和他的question,我创建了MoveForm 函数,它可以计算偏移量并正确设置表单的位置,无论Windows 版本如何(即边框大小):

    void MoveForm(Point p)   // Move form to point 'p'
    {
        this.WindowState = FormWindowState.Normal;
        this.Location = new Point(0, 0);
        Rectangle rec = WindowHelper.GetWindowRectangle(this.Handle);
        p.Offset(-rec.Location.X, -rec.Location.Y);
        this.Location = p;
    }

MoveForm 函数使用来自Uwe KeimpostWindowHelper 类:

public static class WindowHelper
{
    // https://code.google.com/p/zscreen/source/browse/trunk/ZScreenLib/Global/GraphicsCore.cs?r=1349

    /// <summary>
    /// Get real window size, no matter whether Win XP, Win Vista, 7 or 8.
    /// </summary>
    public static Rectangle GetWindowRectangle(IntPtr handle)
    {
        if (Environment.OSVersion.Version.Major < 6)
        {
            return GetWindowRect(handle);
        }
        else
        {
            Rectangle rectangle;
            return DWMWA_EXTENDED_FRAME_BOUNDS(handle, out rectangle) ? rectangle : GetWindowRect(handle);
        }
    }

    [DllImport(@"dwmapi.dll")]
    private static extern int DwmGetWindowAttribute(IntPtr hwnd, int dwAttribute, out Rect pvAttribute, int cbAttribute);

    private enum Dwmwindowattribute
    {
        DwmwaExtendedFrameBounds = 9
    }

    [Serializable, StructLayout(LayoutKind.Sequential)]
    private struct Rect
    {
        // ReSharper disable MemberCanBePrivate.Local
        // ReSharper disable FieldCanBeMadeReadOnly.Local
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
        // ReSharper restore FieldCanBeMadeReadOnly.Local
        // ReSharper restore MemberCanBePrivate.Local

        public Rectangle ToRectangle()
        {
            return Rectangle.FromLTRB(Left, Top, Right, Bottom);
        }
    }

    private static bool DWMWA_EXTENDED_FRAME_BOUNDS(IntPtr handle, out Rectangle rectangle)
    {
        Rect rect;
        var result = DwmGetWindowAttribute(handle, (int)Dwmwindowattribute.DwmwaExtendedFrameBounds,
            out rect, Marshal.SizeOf(typeof(Rect)));
        rectangle = rect.ToRectangle();
        return result >= 0;
    }

    [DllImport(@"user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetWindowRect(IntPtr hWnd, out Rect lpRect);

    private static Rectangle GetWindowRect(IntPtr handle)
    {
        Rect rect;
        GetWindowRect(handle, out rect);
        return rect.ToRectangle();
    }
}

【讨论】:

  • 很高兴您找到了解决方法。我找到了一个类似的解决方案here,它还表明将SubsystemVersion 设置为6.0 可以解决问题,但它只适用于Vista 到Win 8。大部分偏移是由于BorderWidth 的综合影响和PaddedBorderWidth 在注册表中的HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics 下设置。将它们都设置为零,但仍然会留下 1 px 的偏移量。
  • 我见过类似的设置 SubsystemVersion 的解决方案,但这个更“优雅”(如果这个问题/解决方案中的任何东西都可以这样称呼的话)。这里 SubsystemVersion 没有改变,但是根据版本,选择测量真实窗口矩形的两种不同方法之一。感谢 WindowMetric 注册表信息。
  • 更多信息。如果您覆盖Form.OnHandleCreated 并调用SetWindowTheme(this.Handle, "", ""),这将禁用表单本身的主题。您现在将看到围绕表单绘制的完整边框。看起来好像左边框上的偏移实际上是一个透明的边框区域。
  • 是的,看起来那个偏移是透明的边框区域,但是整个事情都搞砸了,我希望微软能解决这个问题。毕竟WinForms和Windows名字里都有“windows”和“forms”,可笑的是windows/forms的大小和位置都有问题。
【解决方案2】:

我发现了问题。

刚刚设置

this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;

并测试结果。对我来说是这样的:

Maximized
Location: {X=0,Y=0}
Size: {Width=1280, Height=800}
PointToScreen(0,0): {X=0,Y=0}

Normal
Location: {X=0,Y=0}
Size: {Width=477, Height=321}
PointToScreen(0,0): {X=0,Y=0}

一切都与边界有关。

【讨论】:

  • 如果你想要无边框窗口也没关系,但我需要一个普通窗口。
  • 那么,您应该进行一些计算以获得正确的位置和大小。窗口的边框始终为 8 和 31 像素。
  • 它们并不总是那么大,这是(部分)问题。除了不同的 Windows 版本及其默认设置,用户还可以自定义这些设置...幸运的是,DWMWA_EXTENDED_FRAME_BOUNDS 可用于获取真实值
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-02
  • 2019-03-16
相关资源
最近更新 更多