2
votes

I am working on a WPF/Winforms hybrid app. We have a winforms window where the user can choose which displays that this window will use, and then the window is positioned and scaled so that it takes up the entire display(s).

We have come across a weird issue with windows. We use the Screen.AllScreens to get the bounds of the screens and these bounds are used to snap our window to the screen. However, we have found that when the scaling settings are different on different displays, then there are actual gaps between the displays.

For example, I have a 3 monitor system where monitor 3 is set to 100% and monitors 1 and 2 are set to 125%. The system dialog looks like this (no gaps). enter image description here

We have an in-app visualization of what the Screen.AllScreens returns as the bounds for each monitor and here is what it looks likeenter image description here

And here is a printout of the screen coordinates for each monitor

\\.\DISPLAY1: {X=0,Y=0,Width=2048,Height=1536}
\\.\DISPLAY3: {X=-2400,Y=561,Width=2400,Height=1350}
\\.\DISPLAY2: {X=2560,Y=0,Width=2048,Height=1536}

Judging by the numbers, it looks like display 2's x coordinate is 2560 because it is trying to give room for display 1's 2048 width * 125% = 2560. However, it seems that this gap only exists in the bad coordinates as spanning a window across this gap does not hide part of the window. What's weirder, is the printout says that display 3's size is 2400x1350 which is 125% bigger than its actual size of 1920x1080.

So long story short, I know that Screen.AllScreens is a winforms call and perhaps a little outdated. Is there a better way to grab screen coordinates that wont be affected by scaling or am I just stuck to warning the user if I detect that there are gaps between the screens?

1
@HansPassant good article, though I'm not sure it really translates to a winforms solutionspectacularbob
If you don't want a WPF answer then don't use the tag.Hans Passant

1 Answers

0
votes

Notify the OS that your application is dpiAware before the Application.Run() method is called as the example below demonstrates. Doing this will automatically set your resolution results from the Screen[] to the native resolutions of the monitors instead of the scaled numbers.

However, a user can still set their screen to be offset with a gap. I have several monitors and one of them is off to my side on purpose.

static class Program
{
    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern bool SetProcessDPIAware();

    static void Main(string[] args)
    {
        if (Environment.OSVersion.Version.Major >= 6)
            SetProcessDPIAware();

        Application.Run(new UIMonitor());
    }
}

NOTE: When running inside Visual Studio, dpiAware is automatically used and then when the application is run outside of Visual Studio it is not, unless you use the SetProcessDPIAware() method.