Search code examples
c#wpfcenteringmultiple-monitors

WPF create one Window for each screen, and center them on each screen


EDIT: I solved it. See my fix in the answer below.

I'm working on an application that should open a small popup window on each screen connected to the computer it runs on. Simple enough to do on a single screen (using WindowStartupLocation = CenterScreen), but surprisingly difficult to do on multiple screens.

My current code is this:

        foreach (var s in Screen.AllScreens) //System.Windows.Forms.Screen
        {
            var b = s.Bounds;
            var w = new PopupWindow();

            var oW = w.Width; //Keep track of original size ...
            var oH = w.Height;

            w.Width = 0; //then set the size to 0, to avoid that the 
            w.Height = 0;//popup shows before it is correctly positioned

            w.Show(); //Now show it, so that we can place it (when I
                      //tried to place it before showing, the window
                      //was always repositioned when Show() was called)

            double dpiX = 1, dpiY = 1;
            var presentationsource = PresentationSource.FromVisual(w);

            if (presentationsource != null)
            {
                dpiX = presentationsource.CompositionTarget.TransformToDevice.M11;
                dpiY = presentationsource.CompositionTarget.TransformToDevice.M22;
            }

            var aW = oW*dpiX; //Calculate the actual size of the window
            var aH = oH*dpiY;

            //***** THIS IS WRONG, SEE ANSWER *****
            w.Left = (b.X + (b.Width / dpiX - aW) / 2); //Place it
            w.Top = (b.Y + (b.Height / dpiY - aH) / 2);
            //*************************************

            w.Width = oW; //And set the size back to the original size
            w.Height = oH;
        }

This seems to work only on the primary screen. On the other screens, the windows are not properly centered.

I guess this is because my knowledge of WPF and DPI is very limited, and I'm probably doing something wrong. Could somebody point me in the right direction?


Solution

  • Of course, I managed to solve it after posting it here. Looks like I did something else wrong when I tried to divide the entire location with the DPI, which led me onto the wrong path I posted above.

    The correct lines for placing the form should be this (all the other code works):

                    w.Left = (b.X + (b.Width - aW) / 2) / dpiX;
                    w.Top = (b.Y + (b.Height - aH) / 2) / dpiX;
    

    But, I still think this is a lot of code for a simple task, so if somebody has better ideas, please let me know!

    So this is the (working) code I'm using now:

        foreach (var s in Screen.AllScreens) //System.Windows.Forms.Screen
        {
            var b = s.Bounds;
            var w = new PopupWindow();
    
            var oW = w.Width; //Keep track of original size ...
            var oH = w.Height;
    
            w.Width = 0; //then set the size to 0, to avoid that the 
            w.Height = 0;//popup shows before it is correctly positioned
    
            w.Show(); //Now show it, so that we can place it (when I
                      //tried to place it before showing, the window
                      //was always repositioned when Show() was called)
    
            double dpiX = 1, dpiY = 1;
            var ps = PresentationSource.FromVisual(w);
            if (ps != null)
            {
                dpiX = ps.CompositionTarget.TransformToDevice.M11;
                dpiY = ps.CompositionTarget.TransformToDevice.M22;
            }
    
            var aW = oW*dpiX; //Calculate the actual size of the window
            var aH = oH*dpiY;
    
            w.Left = (b.X + (b.Width - aW) / 2) / dpiX;
            w.Top = (b.Y + (b.Height - aH) / 2) / dpiX;
    
            w.Width = oW; //And set the size back to the original size
            w.Height = oH;
        }