2017-09-12 1 views
3

Ich arbeite derzeit an einigen hohen DPI-Probleme in unserer WPF-App (.NET 4.6.1 - System DPI-Bewusstsein ist aktiv).HorizontalOffset geht in die falsche Richtung für High DPI primären Bildschirm

Im Allgemeinen macht die App das, was wir erwarten - skalieren Sie abhängig von der aktuellen DPI-Einstellung, auch wenn Sie von Bildschirm A @ 100% auf Bildschirm B @ 150% bewegt wird, ändert sich die Gesamtskalierung korrekt "zur Hälfte -Punkt".

Die meisten der offenen Probleme waren dort, weil wir einige Pixel-/DIP-basierte Berechnungen hatten, die die DPI-Einstellung nicht berücksichtigten. Das habe ich festgelegt, indem sie in den richtigen DPI-Werte Berechnung:

var source = PresentationSource.FromVisual(this); 
var dpiX = source?.CompositionTarget?.TransformToDevice.M11 ?? 1; 
var dpiY = source?.CompositionTarget?.TransformToDevice.M22 ?? 1; 

Dort fand ich die erste Merkwürdige aus (zumindest für mich):

  1. Wenn die primäre Anzeige eingestellt ist z.B. 125% bekomme ich 1,25 für dpiX für alle Bildschirme, sogar den sekundären Bildschirm @ 100%, aber dort sind alle Pixelwerte bereits mit 1,25 multipliziert (was bedeutet, dass ein 1600x1200 Pixel Bildschirm eine Arbeitsgröße von 2000x1500 hat).
  2. Und es ist genau umgekehrt, wenn der primäre Bildschirm bei 100% ist und der sekundäre Bildschirm bei z. 150%: Ich bekomme immer 1 für dpiX, aber alle Werte sind bereits richtig und es ist keine Korrektur notwendig (=> oder multiplizieren/dividieren mit 1 bricht es nicht).

Aber nun zu meinem eigentlichen Problem:
Ich habe einige Popups ich mit der folgenden Bindung in der Mitte ihrer Platzierung Ziele am Abdrucken:

<Popup.HorizontalOffset> 
    <MultiBinding Converter="{lth:CenterConverter}"> 
     <Binding RelativeSource="{RelativeSource Self}" Path="PlacementTarget.ActualWidth" /> 
     <Binding RelativeSource="{RelativeSource Self}" Path="Child.ActualWidth" /> 
     <Binding RelativeSource="{RelativeSource Self}" Path="." /> 
    </MultiBinding> 
</Popup.HorizontalOffset> 

und Konverter:

Für den Fall 2 funktioniert alles schon ohne den auskommentierten Code, aber für den Fall 1 habe ich versucht, den DPI-Wert zu teilen und zu multiplizieren, aber am Ende war das Richtige zu mult iply es von -1, damit es richtig funktioniert.

Warum ist das der Fall?
Und wie kann ich sicher feststellen, wenn dies benötigt wird? dpiX > 1?

Ich bin auch offen für andere Lösungen für die Skalierung oder die Center-Platzierung als Ganzes.

P.S .: Ich habe Windows 10 1703 mit .NET 4.7 installiert (App zielt aus anderen Gründen immer noch auf 4.6.1).

UPDATE:
ich eine Demo-Lösung erstellt: https://github.com/chrfin/HorizontalOffsetError
Wenn der Hauptbildschirm bei 100% ist, ist es richtig:
Correct
aber wenn der Hauptbildschirm zum Beispiel ist 125% ist es aus:
Wrong
aber wenn ich als ein * -1 bis der Offset es wieder korrekt ist:
Corrected

... aber warum?

+0

Warum brauchen Sie mit dpi umgehen, wenn die Positionierung? Messen Sie die tatsächliche Größe (nicht skalieren) und positionieren Sie sie. – Sinatr

+0

@Sinatr: Weil es in Fall 1 nicht funktioniert ;-). Die Pop-ups werden in diesem Fall auf die falsche Seite verschoben. Beispiel: Ziel ist 130 Pixel breit, Popup ist 230 Pixel -> Offset = -50 Pixel => in Fall 2 Einstellung der horizontalen Offset zu -50 platziert das Pop-up in der Mitte des Ziels, aber für Fall 1 ist es dann 100 Pixel aus anstatt 0 oder 50px, ABER das Offset auf +50 setzt das Pop-up korrekt und ich weiß nicht warum oder wie zu erkennen wann + und wann -.... – ChrFin

+0

@HansPassant: Danke für deine Eingabe, aber ich denke ich war mit diesem Teil nicht klar - das Problem tritt auch auf, wenn es nur einen Bildschirm mit zB gibt 150% oder das Fenster wird nicht bewegt. Sobald der primäre (oder einzige) Bildschirm nicht auf 100% steht, scheint das Problem aufzutreten. Ich werde versuchen, morgen eine kleine Demo-Lösung zu machen ... – ChrFin

Antwort

0

Ich habe etwas ähnliches getan. Ich musste WinForms gehen, um es zu erreichen:

/// <summary> 
/// Calculates and sets the correct start location for a dialog to appear. 
/// </summary> 
/// <param name="form">The dialog to be displayed</param> 
/// <param name="screen">Desired screen</param> 
public static void SetStartLocation(Form form, Screen screen) 
{ 
      form.StartPosition = FormStartPosition.Manual; 
    // Calculate the new top left corner of the form, so it will be centered. 
    int newX = (screen.WorkingArea.Width - form.Width)/2 + screen.WorkingArea.X; 
    int newY = (screen.WorkingArea.Height - form.Height)/2 + screen.WorkingArea.Y; 

    form.Location = new Point(newX, newY); 
} 

Führen Sie diesen Code in dem Debugger dann auf dem Bildschirm schauen alle auf den Bildschirm Variable überprüfen und sicherstellen, dass es sie sinnvoll sind, werden sie nicht. Sie machen eine Vermutung.

Blick auf GetScreen()

/// <summary> 
    /// Handles drags that go "outside" the screen and returns the mouse delta from the last mouse position. 
    /// When the user wants to edit a value greater, but the mouse is at the edge of the screen we want to wrap the mouse position. 
    /// 
    /// Wrapping the mouse is non trival do to multiple monitor setups but Forms has a screen class that encapsolates the 
    /// low level calls required to determine the screen the user is working on and it's bounds. 
    /// Wrapping is confusing because there are edge cases which mess with the coordinate system. For example, if your primary monitor 
    /// is your second monitor which is on the right and the app is on your left screen the mouse 
    /// coordinates will be in the negative and second monitors mouse coords would max at 1920 (depending on resolution). 
    /// Alternatively if screen 1 is your primary and screen 2 is your secondary then X=3xxx will be your far right max mouse position. 
    /// 
    /// When we wrap, we need to take that into account and not have the delta go out of whack. 
    /// Note: This mouse wrapping works exactly the same as unity does when the user does a value drag. 
    /// Note: When the mouse does a wrap, we musn't set get the position until the next move event, or the set will fail. 
    /// </summary> 
    /// <param name="delta"> the amount the mouse movement has changed since this was last called</param> 
    /// <returns>true if delta was gotten succesfully</returns> 
    private bool GetScreenWrappedDragVector(out Vector delta) 
    { 
     delta = new Vector(); // Always set the out parameter 

     // We need to determine what our window is, otherwise the coordinate system will be off if your in a child window on other monitor! 
     var element = Mouse.DirectlyOver as UIElement; 
     if (element != null) 
     { 
      Window parentWindow = Window.GetWindow(element); 

      if (parentWindow != null) 
      { 
       System.Windows.Forms.Screen screen = GetScreen(parentWindow); 

       var mousePos = Win32.GetCursorPos(); 

       if ((int)mousePos.X >= screen.WorkingArea.Right - 1) 
       { 
        Win32.SetCursorPos(screen.WorkingArea.Left, (int)mousePos.Y); 
        m_lastOnMouseMoveValue.X = screen.WorkingArea.Left; 
        return false; 
       } 
       if ((int)mousePos.X <= screen.WorkingArea.Left) 
       { 
        Win32.SetCursorPos(screen.WorkingArea.Right, (int)mousePos.Y); 
        m_lastOnMouseMoveValue.X = screen.WorkingArea.Right; 
        return false; 
       } 

       delta = mousePos - m_lastOnMouseMoveValue; 
       m_lastOnMouseMoveValue = mousePos; 
      } 
     } 
     if (delta.Length <= 0) 
      return false; 

     return true; 
    } 
+0

Ich bin nicht sicher, wie das bei meinem Problem hilft? Kannst du ein wenig mehr ausarbeiten? – ChrFin

+0

Sie können das DPI nicht so erkennen, wie Sie es tun. Verwenden Sie stattdessen GetScreen und schauen Sie sich diese Werte an und bestimmen Sie die DPI. https://stackoverflow.com/questions/1918877/how-can-i-get-the-dpi-in-wpf in dieser Verbindung funktionieren die meisten dieser Möglichkeiten einfach nicht. Probieren Sie GetScreen aus und versuchen Sie es mit nur Code, um zuerst zu validieren. Es gibt so viele Fehler in WPF, wenn es um die Erkennung des Monitors geht. –

+0

Meine Visuals sind bereits auf dem Bildschirm (nur nicht sichtbar), so dass der richtige Bildschirm und DPI ist nicht mein Problem - das Teil ist verifiziert und funktioniert in meinem Fall ... – ChrFin

Verwandte Themen