Bug? Compilation window repositions itself!

agraham

Expert
Licensed User
Longtime User
I have a secondary display to the left of my main desktop display. I like to keep the compilation window on the second display as it is a nuisance on the main display as it is always on top. If I position the compilation window anywhere along the the top of the secondary display it moves itself to the top left of the main display when i compile. If I position it lower on the secondary display it stays where I want it. Ideally I want it at the top right of secondary display but it won't stay there.:(
 

agraham

Expert
Licensed User
Longtime User
No. See this screenshot of the top right of the secondary and the top left of the main displays. The compilation window is well away from the screen edges but still moves.
Screenshot.jpg
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
I've checked the suspected point of failure which is the code that verifies that the top left corner is inside the screen bounds. It looks correct.
Maybe .Net reports the wrong bounds for your secondary screen?

The C# code:
B4X:
  bool inScreen = false;
            foreach (System.Windows.Forms.Screen s in System.Windows.Forms.Screen.AllScreens)
            {
                if (s.Bounds.Contains(left, top))
                {
                    inScreen = true;
                    break;
                }
            }
            if (!inScreen)
            {
                left = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Left;
                top = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Top;
            }
            window.Left = left;
            window.Top = top;
 

Sandman

Expert
Licensed User
I'm not sure if this is relevant, but just to let you know that my usual workstation connects to the win computer with B4X using RDP on a big screen. When at home, I connect to the win computer using RDP using a laptop with a small screen. When I do that all dialogs (for New Class, for instance) show outside the screen, which always is somewhat painful to fix.
 

agraham

Expert
Licensed User
Longtime User
Bounds values look correct. I suspect its the value of top in this case that causes the problem. Is the value of top relative to the physical screen rather than the virtual screen?

If I align the second screen in display settings so the top matches the main screen I get the bounds
Left Top Width Height
0 0 4500 3000 : main
-3480 0 3840 2160 : secondary
This works as expected and does not reposition the window

If I move the second screen in settings so the top is below that of the main screen I get, for example, the bounds
Left Top Width Height
0 0 4500 3000 : main
-3480 217 3840 2160 : secondary
The secondary screen is now 217 pixels offset downwards from the main screen. This repositions the window to the main screen if I locate it at the top right of the secondary display. However it seems that if I move the window on the secondary display down so that it's top position relative to the top of the secondary screen is greater than the top value of the secondary screen relative to the main screen then the window stays put. If I further offset the secondary screen down then I have to further position the window down to get it to remain on the secondary screen. So it looks like the value you are using for top of the window is relative to the secondary screen and not the whole virtual screen.
 
Last edited:

agraham

Expert
Licensed User
Longtime User
Can't see a problem here. Seems to work as expected, even with the secondary screen offset vertically from the main screen,
C#:
namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            bool inScreen = false;
            foreach (System.Windows.Forms.Screen s in System.Windows.Forms.Screen.AllScreens)
            {
                if (s.Bounds.Contains(this.Left, this.Top))
                {
                    inScreen = true;
                    break;
                }
            }
            MessageBox.Show(inScreen.ToString(), "In Bounds");
        }
    }
}
 

agraham

Expert
Licensed User
Longtime User
It's a WPF/WinForms interop compatibility problem!

I transcribed the above program to a WPF app and it fails. The problem appears to be, as I surmised earlier, the Top value being used in the comparison. The WPF value for Top appears to be returned as a nominal 96dpi pixel value whereas the Windows Forms value for the window Top is in physical pixels as are the WindowsForms Bounds values . My displays are double density so the Top value of the window under WPF is being returned as half the value it would have under WinForms so the bounds comparison fails until the window is offset vertically down far enough to give a Top value that succeeds.
 

agraham

Expert
Licensed User
Longtime User
Had trouble finding a neat way to get the individual screen DPI in WPF. Only .NET 4.6.1 and later seem to have an actual function but the following compiles for .NET 4.0 so should be universal. It seems to work fine except that, as with your original code, it won't move the window if it is bridged across two monitors as it is only checking the top left corner.
C#:
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            PresentationSource source = PresentationSource.FromVisual(this);
            if (source == null)
                return; // leave window where it is if this won't work

            double scaleX = source.CompositionTarget.TransformToDevice.M11;
            double scaleY = source.CompositionTarget.TransformToDevice.M22;

            string msg = "";
            bool inScreen = false;
            foreach (Screen s in Screen.AllScreens)
            {
                msg = msg + s.Bounds.Top.ToString() + " ";
                if (s.Bounds.Contains((int)(this.Left*scaleX), (int)(this.Top*scaleY)))
                {
                    inScreen = true;
                    break;
                }
            }
            int left = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Left;
            int top = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Top;
        }
 
Top