[英]WPF Window startup location for Per-Monitor-DPI
努力讓 WPF Window 出現在帶有混合 DPI 監視器的輔助屏幕上。 可在 .NET Framework 4.8 和 .NET Standard 2.0 中重現
設置:
主顯示器:4K,250%
輔助顯示器:1080p,100%
步驟1:
為 PerMonitorV2 添加清單
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>
</assembly>
第2步:
public MainWindow()
{
SourceInitialized += (_, __) =>
{
WindowStartupLocation = WindowStartupLocation.Manual;
WindowState = WindowState.Normal;
Width = 1920;
Height = 1050;
Left = -1920;
Top = 0;
};
InitializeComponent();
}
結果:
MainWindow 確實顯示在輔助屏幕上,但 Left/Top 錯誤並且使用了主屏幕的 DPI。 只有寬度和高度是正確的。
參考:
我找到的唯一參考資料是關於記事本的,寫在 MFC 中:
https://github.com/Microsoft/Windows-classic-samples/tree/main/Samples/DPIAwarenessPerWindow
關於GitHub的討論(WPF workarounds)
https://github.com/do.net/wpf/issues/4127
它在說一些關於 SetThreadDpiAwarenessContext 的事情,但我不清楚如何讓它在 C# 中工作....
DPI_AWARENESS_CONTEXT previousDpiContext =
SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE);
BOOL ret = SetWindowPlacement(hwnd, wp);
SetThreadDpiAwarenessContext(previousDpiContext);
您可以將 window 移到任何顯示器的中央。 這只是一個計算問題。
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
public static class WindowHelper
{
public static void MoveToCenter(Window window)
{
if (!GetCursorPos(out POINT cursorPoint))
return;
IntPtr monitorHandle = MonitorFromPoint(cursorPoint, MONITOR_DEFAULTTO.MONITOR_DEFAULTTONULL);
MONITORINFO monitorInfo = new() { cbSize = (uint)Marshal.SizeOf<MONITORINFO>() };
if (!GetMonitorInfo(monitorHandle, ref monitorInfo))
return;
IntPtr windowHandle = new WindowInteropHelper(window).EnsureHandle();
if (!GetWindowPlacement(windowHandle, out WINDOWPLACEMENT windowPlacement))
return;
int left = monitorInfo.rcWork.left + Math.Max(0, (int)((monitorInfo.rcWork.Width - windowPlacement.rcNormalPosition.Width) / 2D));
int top = monitorInfo.rcWork.top + Math.Max(0, (int)((monitorInfo.rcWork.Height - windowPlacement.rcNormalPosition.Height) / 2D));
windowPlacement.rcNormalPosition = new RECT(left, top, windowPlacement.rcNormalPosition.Width, windowPlacement.rcNormalPosition.Height);
SetWindowPlacement(windowHandle, ref windowPlacement);
}
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetCursorPos(out POINT lpPoint);
[DllImport("User32.dll")]
private static extern IntPtr MonitorFromPoint(POINT pt, MONITOR_DEFAULTTO dwFlags);
private enum MONITOR_DEFAULTTO : uint
{
MONITOR_DEFAULTTONULL = 0x00000000,
MONITOR_DEFAULTTOPRIMARY = 0x00000001,
MONITOR_DEFAULTTONEAREST = 0x00000002,
}
[DllImport("User32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
[StructLayout(LayoutKind.Sequential)]
private struct MONITORINFO
{
public uint cbSize;
public RECT rcMonitor;
public RECT rcWork;
public uint dwFlags;
}
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowPlacement(IntPtr hWnd, out WINDOWPLACEMENT lpwndpl);
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
[StructLayout(LayoutKind.Sequential)]
private struct WINDOWPLACEMENT
{
public uint length;
public uint flags;
public uint showCmd;
public POINT ptMinPosition;
public POINT ptMaxPosition;
public RECT rcNormalPosition;
}
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public int Width => right - left;
public int Height => bottom - top;
public RECT(int x, int y, int width, int height)
{
left = x;
top = y;
right = x + width;
bottom = y + height;
}
}
}
using System.Windows;
public partial class MainWindow : Window
{
public MainWindow()
{
WindowStartupLocation = WindowStartupLocation.CenterScreen;
InitializeComponent();
}
private bool _isMoved;
protected override Size ArrangeOverride(Size arrangeBounds)
{
if (!_isMoved)
{
_isMoved = true;
WindowHelper.MoveToCenter(this);
}
return base.ArrangeOverride(arrangeBounds);
}
}
但我發現標題欄的 DPI 與主顯示器的 DPI 保持一致。 一般來說,非客戶區的 DPI 很難修復。 因此,除非刪除默認標題欄,否則此 hack 不太實用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.