Wie kann ich die DPI in WPF erhalten?
Wie kann ich die DPI in WPF erhalten?
Antworten:
https://docs.microsoft.com/en-us/archive/blogs/jaimer/getting-system-dpi-in-wpf-app scheint zu funktionieren
PresentationSource source = PresentationSource.FromVisual(this);
double dpiX, dpiY;
if (source != null) {
dpiX = 96.0 * source.CompositionTarget.TransformToDevice.M11;
dpiY = 96.0 * source.CompositionTarget.TransformToDevice.M22;
}
var dpiXProperty = typeof(SystemParameters).GetProperty("DpiX", BindingFlags.NonPublic | BindingFlags.Static);
var dpiYProperty = typeof(SystemParameters).GetProperty("Dpi", BindingFlags.NonPublic | BindingFlags.Static);
var dpiX = (int)dpiXProperty.GetValue(null, null);
var dpiY = (int)dpiYProperty.GetValue(null, null);
Mit .NET 4.6.2 Preview und höher können Sie anrufen VisualTreeHelper.GetDpi(Visual visual)
. Es gibt eine DpiScale
Struktur zurück, die Ihnen die DPI angibt, bei der die angegebene Visual
gerendert wird oder wurde.
VisualTreeHelper.GetDpi()
Der einzige Weg, den ich gefunden habe, um die "echte" Monitor-dpi zu erhalten, ist der folgende. Alle anderen genannten Techniken sagen nur 96, was für die meisten Monitore nicht korrekt ist.
public class ScreenInformations
{
public static uint RawDpi { get; private set; }
static ScreenInformations()
{
uint dpiX;
uint dpiY;
GetDpi(DpiType.RAW, out dpiX, out dpiY);
RawDpi = dpiX;
}
/// <summary>
/// Returns the scaling of the given screen.
/// </summary>
/// <param name="dpiType">The type of dpi that should be given back..</param>
/// <param name="dpiX">Gives the horizontal scaling back (in dpi).</param>
/// <param name="dpiY">Gives the vertical scaling back (in dpi).</param>
private static void GetDpi(DpiType dpiType, out uint dpiX, out uint dpiY)
{
var point = new System.Drawing.Point(1, 1);
var hmonitor = MonitorFromPoint(point, _MONITOR_DEFAULTTONEAREST);
switch (GetDpiForMonitor(hmonitor, dpiType, out dpiX, out dpiY).ToInt32())
{
case _S_OK: return;
case _E_INVALIDARG:
throw new ArgumentException("Unknown error. See https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510.aspx for more information.");
default:
throw new COMException("Unknown error. See https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510.aspx for more information.");
}
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/dd145062.aspx
[DllImport("User32.dll")]
private static extern IntPtr MonitorFromPoint([In]System.Drawing.Point pt, [In]uint dwFlags);
//https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510.aspx
[DllImport("Shcore.dll")]
private static extern IntPtr GetDpiForMonitor([In]IntPtr hmonitor, [In]DpiType dpiType, [Out]out uint dpiX, [Out]out uint dpiY);
const int _S_OK = 0;
const int _MONITOR_DEFAULTTONEAREST = 2;
const int _E_INVALIDARG = -2147024809;
}
/// <summary>
/// Represents the different types of scaling.
/// </summary>
/// <seealso cref="https://msdn.microsoft.com/en-us/library/windows/desktop/dn280511.aspx"/>
public enum DpiType
{
EFFECTIVE = 0,
ANGULAR = 1,
RAW = 2,
}
RawDpi = dpiX;
ich geändert habe RawDpi = Math.Max(dpiX,dpiY);
, ist RawDpi die größere der beiden
Ich habe meine Antwort von 2015 aktualisiert. Hier ist ein Dienstprogrammcode, der die neuesten DPI-Funktionen von Windows 10 verwendet (insbesondere die GetDpiForWindow-Funktion die die einzige Methode ist, die die DPI_AWARENESS des Fensters / der Anwendung / des Prozesses usw. unterstützt), auf die jedoch zurückgegriffen wird ältere (dpi pro Monitor und Desktop-dpi), daher sollte es weiterhin mit Windows 7 funktionieren.
Es ist weder von WPF noch von Winforms abhängig, nur von Windows selbst.
// note this class considers dpix = dpiy
public static class DpiUtilities
{
// you should always use this one and it will fallback if necessary
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdpiforwindow
public static int GetDpiForWindow(IntPtr hwnd)
{
var h = LoadLibrary("user32.dll");
var ptr = GetProcAddress(h, "GetDpiForWindow"); // Windows 10 1607
if (ptr == IntPtr.Zero)
return GetDpiForNearestMonitor(hwnd);
return Marshal.GetDelegateForFunctionPointer<GetDpiForWindowFn>(ptr)(hwnd);
}
public static int GetDpiForNearestMonitor(IntPtr hwnd) => GetDpiForMonitor(GetNearestMonitorFromWindow(hwnd));
public static int GetDpiForNearestMonitor(int x, int y) => GetDpiForMonitor(GetNearestMonitorFromPoint(x, y));
public static int GetDpiForMonitor(IntPtr monitor, MonitorDpiType type = MonitorDpiType.Effective)
{
var h = LoadLibrary("shcore.dll");
var ptr = GetProcAddress(h, "GetDpiForMonitor"); // Windows 8.1
if (ptr == IntPtr.Zero)
return GetDpiForDesktop();
int hr = Marshal.GetDelegateForFunctionPointer<GetDpiForMonitorFn>(ptr)(monitor, type, out int x, out int y);
if (hr < 0)
return GetDpiForDesktop();
return x;
}
public static int GetDpiForDesktop()
{
int hr = D2D1CreateFactory(D2D1_FACTORY_TYPE.D2D1_FACTORY_TYPE_SINGLE_THREADED, typeof(ID2D1Factory).GUID, IntPtr.Zero, out ID2D1Factory factory);
if (hr < 0)
return 96; // we really hit the ground, don't know what to do next!
factory.GetDesktopDpi(out float x, out float y); // Windows 7
Marshal.ReleaseComObject(factory);
return (int)x;
}
public static IntPtr GetDesktopMonitor() => GetNearestMonitorFromWindow(GetDesktopWindow());
public static IntPtr GetShellMonitor() => GetNearestMonitorFromWindow(GetShellWindow());
public static IntPtr GetNearestMonitorFromWindow(IntPtr hwnd) => MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
public static IntPtr GetNearestMonitorFromPoint(int x, int y) => MonitorFromPoint(new POINT { x = x, y = y }, MONITOR_DEFAULTTONEAREST);
private delegate int GetDpiForWindowFn(IntPtr hwnd);
private delegate int GetDpiForMonitorFn(IntPtr hmonitor, MonitorDpiType dpiType, out int dpiX, out int dpiY);
private const int MONITOR_DEFAULTTONEAREST = 2;
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadLibrary(string lpLibFileName);
[DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
[DllImport("user32")]
private static extern IntPtr MonitorFromPoint(POINT pt, int flags);
[DllImport("user32")]
private static extern IntPtr MonitorFromWindow(IntPtr hwnd, int flags);
[DllImport("user32")]
private static extern IntPtr GetDesktopWindow();
[DllImport("user32")]
private static extern IntPtr GetShellWindow();
[StructLayout(LayoutKind.Sequential)]
private partial struct POINT
{
public int x;
public int y;
}
[DllImport("d2d1")]
private static extern int D2D1CreateFactory(D2D1_FACTORY_TYPE factoryType, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, IntPtr pFactoryOptions, out ID2D1Factory ppIFactory);
private enum D2D1_FACTORY_TYPE
{
D2D1_FACTORY_TYPE_SINGLE_THREADED = 0,
D2D1_FACTORY_TYPE_MULTI_THREADED = 1,
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("06152247-6f50-465a-9245-118bfd3b6007")]
private interface ID2D1Factory
{
int ReloadSystemMetrics();
[PreserveSig]
void GetDesktopDpi(out float dpiX, out float dpiY);
// the rest is not implemented as we don't need it
}
}
public enum MonitorDpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}
So habe ich es geschafft, einen "Skalierungsfaktor" in WPF zu erhalten. Die Auflösung meines Laptops beträgt 1920x1440.
int resHeight = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height; // 1440
int actualHeight = SystemParameters.PrimaryScreenHeight; // 960
double ratio = actualHeight / resHeight;
double dpi = resHeigh / actualHeight; // 1.5 which is true because my settings says my scale is 150%
Verwendung GetDeviceCaps
Funktion:
static void Main(string[] args)
{
// 1.25 = 125%
var dpi = GetDpi();
}
[DllImport("user32.dll")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll")]
public static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
private static float GetDpi()
{
IntPtr desktopWnd = IntPtr.Zero;
IntPtr dc = GetDC(desktopWnd);
var dpi = 100f;
const int LOGPIXELSX = 88;
try
{
dpi = GetDeviceCaps(dc, LOGPIXELSX);
}
finally
{
ReleaseDC(desktopWnd, dc);
}
return dpi / 96f;
}
25. Januar 2017 15:54 Uhr
"Aufrufen von Windows 10-APIs von einer Desktop-Anwendung" und
https://docs.microsoft.com/en-us/uwp/api/windows.devices.display.displaymonitor
"Display Monitor Class"
Namespace: Windows.Devices.Display-Baugruppen: Windows.Devices.Display.dll, Windows.dll
Enthält Informationen zu einem an das System angeschlossenen Anzeigemonitor.
Diese Daten umfassen häufig verwendete Informationen aus den EDID (Extended Display Identification Data) des Monitors, einem branchenüblichen Anzeigedeskriptorblock, mit dem fast alle Monitore Beschreibungen der unterstützten Modi und allgemeinen Geräteinformationen bereitstellen, und der DisplayID (einem neueren Industriestandard) das liefert eine Obermenge von EDID).
Raw DpiX
Ruft die physische horizontale DPI des Monitors ab (basierend auf der nativen Auflösung und der physischen Größe des Monitors).
Raw DpiY
Ruft die physische vertikale DPI des Monitors ab (basierend auf der nativen Auflösung und der physischen Größe des Monitors).
Grundlegende Monitorinformationen in Windows von 2006
https://docs.microsoft.com/en-us/windows/desktop/wmicoreprov/msmonitorclass
MSMonitorClass-Klasse
WmiMonitorRawEEdidV1Block-Klasse
WmiMonitorBasicDisplayParams-Klasse
MaxHorizontalImageSize ( EDID byte 21 )
MaxVerticalImageSize ( EDID byte 22 )
(Die EDID-Größen sind im EDID Detailed Timing Descriptor in Zentimetern über und in Millimetern angegeben
12 Horizontale Bildgröße, mm, 8 lsbits (0–4095 mm, 161 Zoll)
13 Vertikale Bildgröße, mm, 8 lsbits (0–4095 mm, 161 Zoll)
14 Bits 7–4 Horizontale Bildgröße, mm, 4 msbits
Bits 3–0 Vertikale Bildgröße, mm, 4 msbits
)
und