在.NET 4中最大化Window时,Window Top和Left值未正确更新
本文关键字:Window Left 更新 NET 最大化 Top | 更新日期: 2023-09-27 18:28:39
我正在尝试将一个Window居中到所有者窗口。我还需要子窗口沿着所有者窗口移动。MSDN WPF论坛上的交叉帖子可以在这里找到。
为了实现这一点,我订阅了子窗口所有者的LocationChanged
和SizeChanged
事件(以及StateChanged
事件)。当这些事件被触发时,我重新计算子窗口的位置。我在儿童窗口后面的代码中执行此操作。
代码非常直接:
Top = Owner.Top + ((Owner.ActualHeight - ActualHeight) / 2);
Left = Owner.Left + ((Owner.ActualWidth - ActualWidth) / 2);
如果您编译并运行我提供的示例程序,您将看到当主窗口保持原样并四处移动时,它可以工作。所以这部分是有效的。
当所有者窗口最大化时,问题就出现了。(最大化后,恢复正常。)因为我订阅了三个事件,所以我进入了三次重新定位功能。打印出所有者数据后,我得到了不同的结果。最令人恼火的是,所有者窗口的Top
和Left
值都关闭了。当状态发生变化时,它似乎得到了正确的Top和Left值,但ActualWidth
和ActualHeight
值是错误的。当触发LocationChanged
或SizeChanged
事件时,ActualWidth
和ActualHeight
值正常,但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值不正确?
Mataniko关于.NET 4.0中迁移问题的评论是正确的。由于当WindowState
设置为Normal
时,我的代码正在工作,所以我可以保留它。当WindowState
是Maximized
时,我不得不预见一些事情。
我实现了本机的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.WindowState
是Maximized
时,我们使用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);
}
我喜欢这个类似问题中提出的解决方案。投票最多的答案使用单一方法来获得窗口左值,而不考虑窗口状态,使用反射而不是本机代码。它也适用于实际的顶部。这是代码,以防问题/答案被删除。
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;
}
}
我不知道这是.NET 4.0中的错误还是预期行为。我的怀疑是,在登记的事件和实际开火之间存在种族状况。即使LocationChanged首先被激发,SizeChanged也会首先使用仍然不正确的Location值进行注册。您可以通过在子窗口中创建一个本地变量来轻松解决此问题,该变量在LocationChanged事件中注册Owner top和left。
示例:
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);
}
}
你试过了吗。。
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;