AvalonDock icon indicating copy to clipboard operation
AvalonDock copied to clipboard

Per monitor DPI application has issue with splitter ghost while dragging in the wrong place

Open rwg0 opened this issue 3 years ago • 2 comments

The splitter ghost bar can appear in the wrong place if you make the application high DPI aware and put it on a high DPI monitor in a system with monitors with two different DPIs.

Repro steps:

  1. OS - Windows 10 - recent edition
  2. Monitor setup : 2 monitors, main monitor (left) set to 100% scaling. Second monitor (right) set to 150% scaling (which will be 144 dpi)
  3. Enable Per monitor DPI awareness. Modify 'TestApp' as follows:
  • Change net40 to net48 in csproj

  • Add a manifest (app.manifest - atttached) app.manifest.zip

  • Ensure app.manifest is selected in TestApp->Properties->Application->Manifest

  • Rebuild and check that when moved to the high DPI monitor the app becomes bigger with no fuzzy fonts

  1. Make the ghost window easily visible - when creating the _resizerWindowHost in LayoutGridControl, give it a visible background Background = new SolidColorBrush(Color.FromArgb(64, 255,0,0)),
  2. Drag one of the splitters with the window on the normal DPI monitor - you should see the splitter is correct and a red shading over the area where the splitter can move.
  3. Move the window onto the right hand high DPI monitor (so it is on the left hand side of that monitor). Now drag the splitter and the red resize host appears in the wrong place (on the left monitor - see screenshot). Capture
  4. Now move the TestApp window to the right hand side of the high DPI monitor and drag the splitter - everything works as expected.

Probable cause:

ShowResizerOverlayWindow calls PointToScreenDPIWithoutFlowDirection, which adjusts the window position for the screen DPI in the result. Initially I thought the problem was that the adjustment (dividing by 1.5 for 150% scaling) was incorrectly being applied to the whole of the position, which meant that the offset to get to the top-left of the second monitor (the width of the 100% scaled left hand monitor was also being adjusted). However, offsetting for that didn't fix the problem.

It turns out (I think) that this adjustment is fine, but the adjustments can mean that the scaled resize host co-ordinates fall within the left hand (100% scaling screen), so they get displayed with the DPI settings for that screen.

After a bit of experimentation, I found that uncommenting this line

//Owner = Window.GetWindow(this),

in the creation of the resize host fixes the problem without requiring more changes. I think this is because by setting the owner when the resize host window is created, the resize host is assigned to the same screen (and DPI) as it's parent window, so the co-ordinates are interpreted correctly by the system. The current code sets the parent window in the .Loaded event, which presumably allows the resize host the opportunity to get attached to the wrong screen and the wrong DPI.

Looking back over the git history, the commenting out of that line goes back to the first commit, so it's difficult to tell why it was commented out in the first place. It may be possible to make some sort of adjustment inside the handler of the .Loaded event to fix the problem, but as yet I am not sure what (AdjustWindowRectExForDpi looks like it might be helpful though). I'm happy to tinker around looking for a solution of that sort, but would rather not if the setting of the Owner property is an acceptable fix.

cheers,

Robin

PS. there is a similar problem when tearing off a docked window to floating state with mixed DPI settings - the torn off window ends up offscreen initially. Still looking into why :)

rwg0 avatar Jan 18 '22 15:01 rwg0

pull request submitted : https://github.com/Dirkster99/AvalonDock/pull/322

rwg0 avatar Jan 18 '22 21:01 rwg0

Any news on this Issue? Setting the owner for the ResizerOverlayWindow commited by @rwg0 in #321 was revoked by @GonzRu with 3cd644869f71c2bcf4a6a48c2470a423aa09cda3 this reintroduced the Problem with the splitter ghost. Apperantly setting the owner of the ResizeOverlayWindow causes the "unloaded" event of the docking manager to not be called.

dcm99 avatar Feb 04 '24 18:02 dcm99