我正在设置窗口的样式,但是我注意到WindowChrome的这种奇怪行为(在external Microsoft.Windows.Shell dll的.NET FW 4.0中)。
我使用AllowTransparency = true和WindowStyle = None设置WindowChrome。
如果我将WindowChrome的ResizeBorderThickness设置为ResizeBorderThickness="8"
或更多,当窗口最大化时,我不能将其从屏幕顶部边缘附近的最后一个顶部像素拖动,并且每超过+1的+1,我必须开始从边缘向下拖动1个像素。
这很烦人,因为它在关闭窗口时禁用了常见行为,迫使我将其设置为7或更小。
有人可以向我解释这种行为吗?
谢谢!
最佳答案
窗口没有异常行为。取而代之的是,该窗口具有两个奇怪的行为。
(A)第一个奇怪的行为:
“ [...]当窗口最大化时,我无法将其从屏幕顶部附近的最后一个顶部像素拖动[...]”
此行为是由于当窗口更改为其最大化状态时,要调整大小的边缘仍处于活动状态。确实,这一优势一直很活跃。设置ResizeBorderThickness属性后,WindowChrome会保留该像素数量,以控制调整窗口大小的行为。考虑到在最大化模式下不允许调整大小事件,那么您会注意到这些像素不允许任何行为。正是因为WindowChrome专门保留了那些控制调整大小行为的像素。
解决办法是什么?您需要通知WindowChrome必须更改以在窗口最大化时将ResizeBorderThickness属性设置为0。只需通过xaml中的Trigger再次设置WindowChrome即可完成此操作:
<Trigger Property="WindowState" Value="Maximized">
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome ResizeBorderThickness="0" [...] />
</Setter.Value>
</Setter>
</Trigger>
注意:这也可以在运行时代码中执行
(B)第二种奇怪的行为:
“ [...]如果我将WindowChrome的ResizeBorderThickness设置为
照顾自己。实际上,此行为不是由于ResizeBorderThickness中设置的值,而是由于设置了属性WindowStyle = None。设置此属性后,最大化时窗口将呈现奇怪的行为:
窗口的左上边缘不在当前屏幕的点(0,0)上,而是不规则地变为负数(在您的情况下,在Y轴上,该值似乎为-7)。
正常情况下,窗口的大小应为当前屏幕的当前工作区域(除任务栏等之外的当前屏幕)的大小,否则窗口的大小即为当前屏幕的大小。
这种带有窗口的奇怪行为使得为“ WindowChrome”保留的7个像素在当前屏幕上不可见(显然带有ResizeBorderThickness =“ 7”),因此给您的感觉是,属性ResizeBorderThickness =“ 7”可以正常工作正确的,如果不是的话。实际上,这可以证明ResizeBorderThickness取值为8或更大时的行为是合理的。
解决办法是什么?当最大化当前屏幕工作区域上的大小和位置时,有必要强制窗口。警告:如果仅在主屏幕上执行此操作,则最大化事件不适用于多个屏幕。
通过调用外部API解决了此问题的代码:
[DllImport("user32")]
internal static extern bool GetMonitorInfo(IntPtr hMonitor, MONITORINFO lpmi);
[DllImport("user32")]
internal static extern IntPtr MonitorFromWindow(IntPtr handle, int flags);
定义类和结构:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 4)]
public class MONITORINFO
{
public int cbSize = Marshal.SizeOf(typeof(MONITORINFO));
public RECT rcMonitor = new RECT();
public RECT rcWork = new RECT();
public int dwFlags = 0;
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
public POINT(int x, int y) { this.x = x; this.y = y; }
}
[StructLayout(LayoutKind.Sequential)]
public struct MINMAXINFO
{
public POINT ptReserved;
public POINT ptMaxSize;
public POINT ptMaxPosition;
public POINT ptMinTrackSize;
public POINT ptMaxTrackSize;
}
最后定义将钩子WndProc添加到窗口的函数:
public static void CompatibilityMaximizedNoneWindow(Window window)
{
WindowInteropHelper wiHelper = new WindowInteropHelper(window);
System.IntPtr handle = wiHelper.Handle;
HwndSource.FromHwnd(handle).AddHook(
new HwndSourceHook(CompatibilityMaximizedNoneWindowProc));
}
private static System.IntPtr CompatibilityMaximizedNoneWindowProc(
System.IntPtr hwnd,
int msg,
System.IntPtr wParam,
System.IntPtr lParam,
ref bool handled)
{
switch (msg)
{
case 0x0024: // WM_GETMINMAXINFO
MINMAXINFO mmi =
(MINMAXINFO)Marshal.PtrToStructure(lParam, typeof(MINMAXINFO));
// Adjust the maximized size and position
// to fit the work area of the correct monitor
// int MONITOR_DEFAULTTONEAREST = 0x00000002;
System.IntPtr monitor = MonitorFromWindow(hwnd, 0x00000002);
if (monitor != System.IntPtr.Zero)
{
MONITORINFO monitorInfo = new MONITORINFO();
GetMonitorInfo(monitor, monitorInfo);
RECT rcWorkArea = monitorInfo.rcWork;
RECT rcMonitorArea = monitorInfo.rcMonitor;
mmi.ptMaxPosition.x =
Math.Abs(rcWorkArea.left - rcMonitorArea.left);
mmi.ptMaxPosition.y =
Math.Abs(rcWorkArea.top - rcMonitorArea.top);
mmi.ptMaxSize.x =
Math.Abs(rcWorkArea.right - rcWorkArea.left);
mmi.ptMaxSize.y =
Math.Abs(rcWorkArea.bottom - rcWorkArea.top);
}
Marshal.StructureToPtr(mmi, lParam, true);
handled = true;
break;
}
return (System.IntPtr)0;
}
使用CompatibilityMaximizedNoneWindow API,您只需在窗口的构造函数中调用该API,如下所示:
public MyWindow
{
[...]
MyNamespace.CompatibilityMaximizedNoneWindow(this);
}
第二种奇怪的行为必须解决。您会注意到该代码可以正常工作,必须添加引用PresentationFramework和名称空间System.Windows.Interop。