Wie erhalte ich Windows-Anzeigeeinstellungen?

74

In Windows 7 gibt es Einstellungen für die Anzeige (Systemsteuerung -> Anzeige). Hiermit können Sie die Größe des Texts und anderer Elemente auf dem Bildschirm ändern. Ich benötige diese Einstellung, um einige Funktionen in meiner C # -Anwendung basierend auf dem Einstellungswert ein- und ausschalten zu können. Ist das möglich?

neues Mitglied
quelle
1
Für welche Einstellung interessieren Sie sich? Es gibt viele.
David Heffernan
Wenn Sie diese Seite mit einer Einstellung öffnen - Systemsteuerung -> Anzeige. Es gibt nur eine Einstellung, mit der Sie die Größe des Texts und anderer Elemente auf dem Bildschirm ändern können. Die Optionen sind "Kleiner", "Mittel" und "Größer"
Newmember

Antworten:

52

Diese Einstellung ist die Bildschirm-DPI oder Punkte pro Zoll.

Lesen Sie es so:

float dpiX, dpiY;
Graphics graphics = this.CreateGraphics();
dpiX = graphics.DpiX;
dpiY = graphics.DpiY;

Ich denke nicht, dass es im Moment möglich ist, dass die X- und Y-Werte unterschiedlich sind. Ein Wert von 96 entspricht einer Skalierung von 100% (kleiner), 120 einer Skalierung von 125% (mittel) und 144 einer Skalierung von 150% (größer). Benutzer können jedoch andere Werte als diese Standardwerte festlegen.

Beachten Sie, dass die von Ihnen beobachteten Werte möglicherweise einer DPI-Virtualisierung unterliegen, sofern Ihre Anwendung nicht als DPI-fähig deklariert wird.

David Heffernan
quelle
7
Ich habe versucht, diesen Code mit den verschiedenen Einstellungen und dem Wert der Grafik
auszuführen. DpiX
1
DpiX ist 90?! Ich habe mich ab und zu angemeldet und es hat sich von 96 auf 120 geändert. Ich habe eine neue WinForms-App verwendet. Ich frage mich, ob Sie in Ihrer App etwas Besonderes tun. Haben Sie es in einem brandneuen WinForms-Projekt versucht?
David Heffernan
1
Ich habe versucht, die mittlere Einstellung einzustellen und - es funktioniert! DpiX ist 120, aber für die größere Einstellung ist es 96 ...
Newmember
9
Richtig, ich glaube ich verstehe es jetzt. Das Problem ist, dass Ihre App nicht als DPI-fähig markiert ist und daher wahrscheinlich alles unscharf erscheint. Sie sollten Ihre App im Manifest als DPI-fähig markieren.
David Heffernan
3
@ColonelPanic thisist einfach ein beliebiges Steuerelement. msdn.microsoft.com/en-us/library/…
David Heffernan
81

Sowohl Graphics.DpiX als auch DeviceCap.LOGPIXELSX geben in Surface Pro in allen Skalierungsstufen 96 zurück.

Stattdessen konnte ich den Skalierungsfaktor folgendermaßen berechnen:

[DllImport("gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
public enum DeviceCap
{
    VERTRES = 10,
    DESKTOPVERTRES = 117,

    // http://pinvoke.net/default.aspx/gdi32/GetDeviceCaps.html
}  


private float getScalingFactor()
{
    Graphics g = Graphics.FromHwnd(IntPtr.Zero);
    IntPtr desktop = g.GetHdc();
    int LogicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.VERTRES);
    int PhysicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.DESKTOPVERTRES); 

    float ScreenScalingFactor = (float)PhysicalScreenHeight / (float)LogicalScreenHeight;

    return ScreenScalingFactor; // 1.25 = 125%
}
Farshid T.
quelle
9
Wow, ich habe lange gesucht, wie man den wahren Skalierungsfaktor erhält, und das hat endlich meine Probleme gelöst!
Andreas
10
In dem Moment, in dem du gerade weinen und aufgeben willst und dann auf so etwas stößt.
Der Muffin-Mann
5
Bitte beachten Sie, dass Sie wahrscheinlich sowohl diese Lösung als auch die als akzeptiert gekennzeichnete Lösung verwenden möchten . Auf meinem System gibt das Lesen von g.DpiX 120 zurück, aber der Skalierungsfaktor beträgt immer noch 1,0.
Todd Myhre
6
Zu Ihrer Information: Auf einem Dell Precision 5510-Laptop mit Win 10 64-Bit, Auflösung 3840 x 2160, Anzeigeskala auf 200% eingestellt (vom rechten Klick auf Desktop >> Anzeigeeinstellungen) geben sowohl LogicalScreenHeight als auch PhysicalScreenHeight im obigen Code dasselbe zurück. 2160. Dieser Code funktioniert also nicht, um in diesem Szenario die gewünschte 200% -Anzeigeskala zu erhalten.
Nerdtron
6
Ich habe das gleiche Problem wie bei @Nerdtron mit ähnlicher Hardware beobachtet. Dieser Code funktioniert leider nicht zuverlässig auf allen Monitoren.
OfficeAddinDev
17

Der meiner Meinung nach einfachste Weg ist die Verwendung der GetDeviceCapsFunktion. Von pinvoke.net :

[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int GetDeviceCaps(IntPtr hDC, int nIndex);

public enum DeviceCap
{
  /// <summary>
  /// Logical pixels inch in X
  /// </summary>
  LOGPIXELSX = 88,
  /// <summary>
  /// Logical pixels inch in Y
  /// </summary>
  LOGPIXELSY = 90

  // Other constants may be founded on pinvoke.net
}      

Und Verwendung:

Graphics g = Graphics.FromHwnd(IntPtr.Zero);            
IntPtr desktop = g.GetHdc();

int Xdpi = GetDeviceCaps(desktop, (int)DeviceCap.LOGPIXELSX);
int Ydpi = GetDeviceCaps(desktop, (int)DeviceCap.LOGPIXELSY);    

Bei diesem Ansatz müssen Sie Ihre App nicht als dpi-fähig markieren.

Anton Semenov
quelle
12
Vergessen Sie nicht, ReleaseHdc
BitsEvolved am
@ BitsEvolved absolut. Dies löst "meinen" Speicherverlust (in einem völlig anderen Kontext)! Danke vielmals.
Wolf
1
In den Kommentaren zur GetDeviceCaps-Funktion steht unter dem von Ihnen angegebenen Link für LOGPIXELSX und LOGPIXELSY Folgendes: "In einem System mit mehreren Anzeigemonitoren ist dieser Wert für alle Monitore gleich." Etwas zu beachten ...
RenniePet
16

So können Sie es in WPF machen. Der Rückgabewert ist in den logischen Einheiten von WPF angegeben, die 1/96 Zoll entsprechen. Wenn Ihre Bildschirm-DPI auf 96 eingestellt ist, erhalten Sie den Wert 1.

Matrix m =
PresentationSource.FromVisual(Application.Current.MainWindow).CompositionTarget.TransformToDevice;
double dx = m.M11; // notice it's divided by 96 already
double dy = m.M22; // notice it's divided by 96 already

( Quelle )

Eternal21
quelle
1
Danke, ich denke das ist der einfachste Weg für wpf.
Mitra M
10

Hier ist eine Lösung, die unter Windows 10 gut funktioniert. DPI-Erkennung oder ähnliches ist nicht erforderlich.

public static int GetWindowsScaling()
{
    return (int)(100 * Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth);
}
Kyle Delaney
quelle
Interessant ist, dass Screen.PrimaryScreen.Bounds.Width manchmal nicht die tatsächliche Bildschirmbreite zurückgibt und dieselbe wie SystemParameters.PrimaryScreenWidth zurückgibt. Ich habe dieses Problem beim Arbeiten mit der UI-Automatisierung. Und ich musste stattdessen Screen.PrimaryScreen.WorkingArea.Width verwenden.
Sarh
8

Ich verwende diese Methode in meiner Konsolenanwendung:

float dpiX, dpiY;
using (Graphics graphics = Graphics.FromHwnd(IntPtr.Zero))
{
    dpiX = graphics.DpiX;
    dpiY = graphics.DpiY;
}
honzakuzel1989
quelle
8

Die Verwendung der Antwort von Farshid T als Basis funktioniert in jedem Skalierungsfaktor mit Ausnahme von 125%. Ich habe ungefähr 20 verschiedene Skalierungsfaktoren getestet, und der DPI gibt immer 96 zurück, außer wenn er auf 125% eingestellt ist, was einen DPI von 120 ergibt. 120/96 = 1,25. Ich bin mir nicht sicher, warum dies der Fall ist, aber dieser Code scheint für jede Skalierungseinstellung zu funktionieren.

    [DllImport("gdi32.dll")]
    static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
    public enum DeviceCap
    {
        VERTRES = 10,
        DESKTOPVERTRES = 117,
        LOGPIXELSY = 90,

        // http://pinvoke.net/default.aspx/gdi32/GetDeviceCaps.html

und Verwendung:

        Graphics g = Graphics.FromHwnd(IntPtr.Zero);
        IntPtr desktop = g.GetHdc();
        int LogicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.VERTRES);
        int PhysicalScreenHeight = GetDeviceCaps(desktop, (int)DeviceCap.DESKTOPVERTRES);
        int logpixelsy = GetDeviceCaps(desktop, (int)DeviceCap.LOGPIXELSY);
        float screenScalingFactor = (float)PhysicalScreenHeight / (float)LogicalScreenHeight;
        float dpiScalingFactor = (float)logpixelsy / (float)96;

        if (screenScalingFactor > 1 ||
            dpiScalingFactor > 1)
        {
            // do something nice for people who can't see very well...
        }
Ric Gaudet
quelle
2
Sollten Sie aus Sicherheitsgründen nicht auch Folgendes hinzufügen: g.ReleaseHdc (Desktop);
Don
6

Verwenden Sie im Fall von WPF das folgende Snippet:

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;
        }
Anees Deen
quelle
5

Dies ist eine sehr alte Frage, aber seit Windows 8.1 kann man verschiedene andere Funktionen verwenden, wie z GetDpiForWindow

In C #:

[DllImport("user32.dll")]
static extern int GetDpiForWindow(IntPtr hWnd);

public float GetDisplayScaleFactor(IntPtr windowHandle)
{
    try
    {
        return GetDpiForWindow(windowHandle) / 96f;
    }
    catch
    {
        // Or fallback to GDI solutions above
        return 1;
    }
}

Damit dies unter Windows 10 ordnungsgemäß funktioniert, müssen Sie app.manifestIhrem C # -Projekt Folgendes hinzufügen :

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
          manifestVersion="1.0">
  <application xmlns="urn:schemas-microsoft-com:asm.v3">
    <windowsSettings>
      <!-- The combination of below two tags have the following effect : 
      1) Per-Monitor for >= Windows 10 Anniversary Update
      2) System < Windows 10 Anniversary Update -->
      <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiAwareness>
      <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True/PM</dpiAware>
    </windowsSettings>
  </application>

</assembly>
Ziriax
quelle
Sie sagen, dies sollte für Windows 8.1 und höher funktionieren, aber in dem Microsoft-Dokument, auf das Sie verweisen, steht "Anforderungen: Windows 10, Version 1607".
RenniePet
Oh ja, guter Fang. Ich glaube, ich habe dies mit dem GetDpiForMonitor-API-Aufruf verwechselt. docs.microsoft.com/en-us/windows/win32/api/shellscalingapi/…
Ziriax
1

Ich denke, dies sollte Ihnen die Informationen liefern, nach denen Sie suchen:

http://www.pinvoke.net/default.aspx/user32.getsystemmetrics

http://pinvoke.net/default.aspx/Enums.SystemMetric

Bearbeiten - Oh, tut mir leid, es gibt einen einfacheren Weg, diese Informationen jetzt ohne Pinvoke zu erhalten.

http://msdn.microsoft.com/en-us/library/system.windows.forms.systeminformation.aspx

asawyer
quelle
Leider spiegeln die Systeminformationen nicht die dpi-Skala wider. Es scheint auch, dass die dpiX / dpiY-Eigenschaft des Grafikkontexts 96 für 150% zurückgibt (zumindest auf einem Surface Pro 2). Sie können also nicht zwischen 100% und 150% Zoom unterscheiden. Kennt jemand eine richtige Methode?
Zuppa
1

Eine vollständigere Version von Ric Gaudets Antwort:

[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int GetDeviceCaps(IntPtr hDC, int nIndex);

public enum DeviceCap
{
    VERTRES = 10,
    DESKTOPVERTRES = 117
}

static double GetWindowsScreenScalingFactor(bool percentage = true)
{
    //Create Graphics object from the current windows handle
    Graphics GraphicsObject = Graphics.FromHwnd(IntPtr.Zero);
    //Get Handle to the device context associated with this Graphics object
    IntPtr DeviceContextHandle = GraphicsObject.GetHdc();
    //Call GetDeviceCaps with the Handle to retrieve the Screen Height
    int LogicalScreenHeight = GetDeviceCaps(DeviceContextHandle, (int)DeviceCap.VERTRES);
    int PhysicalScreenHeight = GetDeviceCaps(DeviceContextHandle, (int)DeviceCap.DESKTOPVERTRES);
    //Divide the Screen Heights to get the scaling factor and round it to two decimals
    double ScreenScalingFactor = Math.Round((double)PhysicalScreenHeight / (double)LogicalScreenHeight, 2);
    //If requested as percentage - convert it
    if (percentage)
    {
        ScreenScalingFactor *= 100.0;
    }
    //Release the Handle and Dispose of the GraphicsObject object
    GraphicsObject.ReleaseHdc(DeviceContextHandle);
    GraphicsObject.Dispose();
    //Return the Scaling Factor
    return ScreenScalingFactor;
}
Ultimativer Luki
quelle