在.NET 4中最大化窗口时,窗口顶部和左侧值未正确更新

时间:2012-03-21 20:58:26

标签: c# .net wpf

我正在尝试将窗口置于所有者窗口中心。我还需要子窗口沿所有者窗口移动。可以在here找到MSDN WPF论坛上的交叉帖子。

为实现此目的,我订阅了我的子窗口所有者的LocationChangedSizeChanged事件(以及StateChanged事件)。当这些事件被触发时,我重新计算子窗口的位置。我在子窗口的代码隐藏中执行此操作。

代码非常简单:

Top = Owner.Top + ((Owner.ActualHeight - ActualHeight) / 2);
Left = Owner.Left + ((Owner.ActualWidth - ActualWidth) / 2);

如果您编译并运行我提供的示例程序,您将看到它在主窗口原样并且四处移动时有效。这部分有效。

当所有者窗口最大化时出现问题。 (并且在最大化之后,恢复正常。)因为我订阅了三个事件,所以我进入三次重定位函数。打印出所有者数据后,我得到不同的结果。最令人讨厌的是,所有者窗口的TopLeft值已关闭。当状态发生变化时,它似乎会获得正确的Top和Left值,但ActualWidthActualHeight值是错误的。触发LocationChangedSizeChanged事件时,ActualWidthActualHeight值均正常,但Top和Left值不正确。看来这些是以前的价值观。怎么会这样?是什么造成的?对此有适当的解决方法吗?

由于相同的代码似乎在.net 3.5中工作,我的印象是在.net 4中发生了变化。(或者我有一个奇怪的计时问题导致问题不会出现。)但我找不到任何记录改变这一部分。

.NET 3.5:

OnOwnerLocationChanged
T: -8; L: -8; W: 640; H: 480
OnOwnerStateChanged
T: -8; L: -8; W: 640; H: 480
OnOwnerSizeChanged
T: -8; L: -8; W: 1936; H: 1066

.NET 4.0:

OnOwnerLocationChanged
T: -8; L: -8; W: 640; H: 480
OnOwnerStateChanged
T: 494; L: 33; W: 640; H: 480
OnOwnerSizeChanged
T: 494; L: 33; W: 1936; H: 1066

所以主要问题仍然存在:为什么所有者的Top和Left值不正确?

4 个答案:

答案 0 :(得分:14)

Mataniko的comment关于.NET 4.0中的迁移问题是正确的。由于我的代码在WindowState设置为Normal时正常工作,我可以保留它。当WindowStateMaximized时,我只能预见到一些事情。

我实现了原生GetWindowRect()功能来实现这一目标,因为它可以为我提供合适的尺寸。

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public int Left;
    public int Top;
    public int Right;
    public int Bottom;
}

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

// Make sure RECT is actually OUR defined struct, not the windows rect.
public static RECT GetWindowRectangle(Window window)
{
    RECT rect;
    GetWindowRect((new WindowInteropHelper(window)).Handle, out rect);

    return rect;
}

接下来,当Owner.WindowStateMaximized时,我们会使用GetWindowRectangle函数来获取实际尺寸。此时我们不关心边框,但如果需要,可以使用GetSystemMetrics函数合并它。

if (Owner.WindowState == WindowState.Maximized)
{
    var rect = GetWindowRectangle(Owner);

    Top = rect.Top + ((rect.Bottom - ActualHeight) / 2);
    Left = rect.Left + ((rect.Right - ActualWidth) / 2);
}

答案 1 :(得分:2)

我不知道它是.NET 4.0中的错误还是预期的行为。 我怀疑是在登记和实际射击之间存在竞争条件。即使首先触发LocationChanged,也会首先注册SizeChanged,其中仍然使用不正确的Location值。 您可以通过在子窗口中创建一个局部变量来轻松解决这个问题,该局部变量在LocationChanged事件中注册所有者的顶部和左侧。

示例:

private Point _ownerLocation;

private void OnOwnerLocationChanged(object sender, EventArgs e)
    {
        Console.WriteLine("OnOwnerLocationChanged");
        _ownerLocation = new Point(Owner.Top, Owner.Left);
        SetLocationToOwner();
    }

private void SetLocationToOwner()
    {
        if (IsVisible && (Owner != null))
        {
            Console.WriteLine("T: {0}; L: {1}; W: {2}; H: {3}", Owner.Top, Owner.Left, Owner.ActualWidth, Owner.ActualHeight);

            Top = _ownerLocation.X + ((Owner.ActualHeight - ActualHeight) / 2);
            Left = _ownerLocation.Y + ((Owner.ActualWidth - ActualWidth) / 2);
        }
    }

答案 2 :(得分:1)

我喜欢在this类似问题中提出的解决方案。最高投票的答案使用单一方法来获取窗口左值,而不管使用反射而不是本机代码的窗口状态。它也适用于实际顶级。这是代码,以防问题/答案被删除。

public static class WindowExtensions
{
    /// <summary>
    /// Gets the window left.
    /// </summary>
    /// <param name="window">The window.</param>
    /// <returns></returns>
    public static double GetWindowLeft(this 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;
    }

    /// <summary>
    /// Gets the window top.
    /// </summary>
    /// <param name="window">The window.</param>
    /// <returns></returns>
    public static double GetWindowTop(this Window window)
    {
        if (window.WindowState == WindowState.Maximized)
        {
            var topField = typeof(Window).GetField("_actualTop", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
            return (double)topField.GetValue(window);
        }
        else
            return window.Top;
    }
}

答案 3 :(得分:0)

你试过..

int left, top, width, height;
bool maximised;

if (WindowState == FormWindowState.Maximized)
{
    maximised = true;
    left = RestoreBounds.X;
    top = RestoreBounds.Y;
    width = RestoreBounds.Width;
    height = RestoreBounds.Height;
}
else
{
    maximised = false;
    left = Left;
    top = Top;
    width = Width;
    height = Height;
}

要恢复(顺序很重要)......

StartPosition = FormStartPosition.Manual;
Location = new Point(left, top);
Size = new Size(width, height);

if (maximised)
    WindowState = FormWindowState.Maximized;