c# - HorizontalOffset goes in the wrong direction for High DPI primary screen -


i working on high dpi issues in our wpf app (.net 4.6.1 - system dpi-awareness active).

generally app expect - scale depending on current displays dpi setting, when moving screen @ 100% screen b @ 150% changes it's overall scale correctly "at half-point".

most of open issues there because had pixel-/dip-based calculations did not took dpi-setting consideration. fixed calculating in correct dpi values:

var source = presentationsource.fromvisual(this); var dpix = source?.compositiontarget?.transformtodevice.m11 ?? 1; var dpiy = source?.compositiontarget?.transformtodevice.m22 ?? 1; 

there found out first strange thing (at least me):

  1. if primary display set e.g. 125% 1.25 dpix screens, secondary screen @ 100%, there pixel-values multiplied 1.25 (meaning 1600x1200 pixel screen has working size of 2000x1500).
  2. and other way around if primary screen @ 100% , secondary screen @ e.g. 150%: 1 dpix, values correct , no correction necessary (=> or multiply/dived 1 not break it).

but actual problem:
have pop-ups placing @ center of placement-targets following binding:

<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> 

and converter:

public class centerconverter : markupextension, imultivalueconverter {     public override object providevalue(iserviceprovider serviceprovider) => this;      public object convert(object[] values, type targettype, object parameter, cultureinfo culture)     {         if (values.any(v => v == dependencyproperty.unsetvalue))             return double.nan;          double placementtargetwidth = (double)values[0];         double elementwidth = (double)values[1];          var offset = (placementtargetwidth - elementwidth) / 2;          ////if (values.length >= 3 && values[2] visual)         ////{         ////    var source = presentationsource.fromvisual((visual)values[2]);         ////    var dpix = source?.compositiontarget?.transformtodevice.m11 ?? 1;         ////    offset *= -1; //dpix;         ////}          return offset;     }      public object[] convertback(object value, type[] targettypes, object parameter, cultureinfo culture) { throw new notsupportedexception(); } } 

for case 2 works correctly without commented out code, case 1 tried dividing , multiplying dpi value, in end correct thing multiply -1 work correctly.

why ist case?
, how can savely detect when needed? dpix > 1?

i open other solutions scaling issue or center-placement whole.

p.s.: running windows 10 1703 .net 4.7 installed (app still targets 4.6.1 other reasons).

update:
created demo-solution: https://github.com/chrfin/horizontaloffseterror
if main screen @ 100% correct:
correct
if main screen e.g. 125% off:
wrong
if add *-1 offset correct again:
corrected

...but why?

i've done similar. had go winforms accomplish it:

/// <summary> /// calculates , sets correct start location dialog appear. /// </summary> /// <param name="form">the dialog displayed</param> /// <param name="screen">desired screen</param> public static void setstartlocation(form form, screen screen) {            form.startposition = formstartposition.manual;     // calculate new top left corner of form, 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); } 

run code in debugger @ screen inspect screen variable , make sure make sense, won't. making assumption.

look @ getscreen()

/// <summary>     /// handles drags go "outside" screen , returns mouse delta last mouse position.     /// when user wants edit value greater, mouse @ edge of screen want wrap mouse position.     ///      /// wrapping mouse non trival multiple monitor setups forms has screen class encapsolates      /// low level calls required determine screen user working on , it's bounds.     /// wrapping confusing because there edge cases mess coordinate system. example, if primary monitor     /// second monitor on right , app on left screen mouse      /// coordinates in negative , second monitors mouse coords max @ 1920 ( depending on resolution ).     /// alternatively if screen 1 primary , screen 2 secondary x=3xxx far right max mouse position.     ///      /// when wrap, need take account , not have delta go out of whack.     /// note: mouse wrapping works same unity when user value drag.     /// note: when mouse wrap, musn't set position until next move event, or set fail.     /// </summary>     /// <param name="delta"> amount mouse movement has changed since last called</param>     /// <returns>true if delta gotten succesfully</returns>     private bool getscreenwrappeddragvector(out vector delta)     {         delta = new vector(); // set out parameter          // need determine our window is, otherwise coordinate system off if in child window on other monitor!         var element = mouse.directlyover 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;     } 

Comments

Popular posts from this blog

neo4j - finding mutual friends in a cypher statement starting with three or more persons -

php - How to remove letter in front of the word laravel -

minify - Minimizing css files -