Search code examples
c#multiple-monitorsbounds

C# Secondary Monitor Bounds Issue


I have made a bouncing screensaver in C#. When there is only a single monitor to display it works perfectly. However, when I have a dual monitor display the bounds do not work properly. The position of secondary monitor seems to have an effect and there does not seem to be a right bound (secondary screen is on the right side).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace LIUScreensaver
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            if (args.Length > 0)
            {
                string firstArg = args[0].ToLower().Trim();
                string secondArg = null;

                //handle cases where arguments are separated by colon
                if (firstArg.Length > 2)
                {
                    secondArg = firstArg.Substring(3).Trim();
                    firstArg = firstArg.Substring(0, 2);
                }
                else if (args.Length > 1)
                {
                    secondArg = args[1];
                }

                if (firstArg == "/c")
                {
                    MessageBox.Show("This screensaver does not allow configuration changes");
                }
                else if (firstArg == "/p")
                {
                    if (secondArg == null)
                     {
                        MessageBox.Show("Sorry, but there was an error.", "Screensaver", MessageBoxButtons.OK, 
                            MessageBoxIcon.Error);
                        return;
                    }
                    IntPtr preview = new IntPtr(long.Parse(secondArg));
                    Application.Run(new ScreensaverForm(preview));
                }
                else if (firstArg == "/s")
                {
                    ShowScreenSaver();
                    Application.Run();
                }
                else
                {
                    MessageBox.Show("Invalid command line argument. \"" + firstArg + "\" is not a valid ", "Screensaver",
                        MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }

            }
            else
            {
                MessageBox.Show("There has been an error displaying the screensaver.  Please report this issue.");
            }
        }
        public static void ShowScreenSaver()
        {
            //creates form on each display
            int i = 0;
            Screen[] screen = Screen.AllScreens;
            for (i = 0; i < screen.Length; i++)
            {
                ScreensaverForm screensaver = new ScreensaverForm(screen[i].Bounds);
                screensaver.Show();
            }
        }
    }    
}

//Screensaver form

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace LIUScreensaver
{
public partial class ScreensaverForm : Form
{

    [DllImport("user32.dll")]
    static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

    [DllImport("user32.dll")]
    static extern int SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

    [DllImport("user32.dll", SetLastError = true)]
    static extern int GetWindowLong(IntPtr hWnd, int nIndex);

    [DllImport("user32.dll")]
    static extern bool GetClientRect(IntPtr hWnd, out Rectangle lpRect);

    private Boolean previewMode = false;
    private int mouseX = 0, mouseY = 0;
    private int imgX = 0, imgY = 0;
    private Boolean bot = true, left = true, right = false, top = false;
    Point p;

    //constructor with no arguments
    public ScreensaverForm()
    {
        InitializeComponent();
    }

    //constructor with bound arguments
    public ScreensaverForm(Rectangle Bounds)
    {
        InitializeComponent();
        this.Bounds = Bounds;
    }

    //preview constructor
    public ScreensaverForm(IntPtr preview)
    {
        InitializeComponent();

        SetParent(this.Handle, preview);

        SetWindowLong(this.Handle, -16, new IntPtr(GetWindowLong(this.Handle, -16) | 0x40000000));

        Rectangle parentRec;
        GetClientRect(preview, out parentRec);
        Size = parentRec.Size;
        Location = new Point(0, 0);

        txtLabel.Font = new System.Drawing.Font("Arial", 6);

        previewMode = true;
    }

    //form load
    private void ScreensaverForm_Load(object sender, EventArgs e)
    {
        Cursor.Hide();
        TopMost = true;
        System.Drawing.Color background = System.Drawing.ColorTranslator.FromHtml("#002668");
        this.BackColor = background;

        moveTimer.Interval = 1;
        moveTimer.Tick += new EventHandler(moveTimer_Tick);
        moveTimer.Start();
    }

    //
    //-----------------------------------------------------------------------------------------------------
    //The following code ends the application and exits the screensaver
    private void ScreensaverForm_KeyPress(object sender, KeyPressEventArgs e)
    {
        if (!previewMode)
        {
            Application.Exit();
        }
    }

    private void ScreensaverForm_MouseMove(object sender, MouseEventArgs e)
    {
        if (!previewMode)
        {
            if (mouseX == 0)
            {
                mouseX = e.X;
                mouseY = e.Y;
            }

            if ((mouseX - e.X) > 5 || (mouseY - e.Y) > 5)
            {
                Application.Exit();
            }
        }
    }

    private void ScreensaverForm_MouseClick(object sender, MouseEventArgs e)
    {
        if (!previewMode)
        {
            Application.Exit();
        }
    }

    //
    //
    //timer to bounce the image across the screen
    private void moveTimer_Tick(object sender, System.EventArgs e)
    {
        // Move text to new location
        bounce();
    }

    //function that moves the image
    private void bounce()
    {

        //Checks boundaries
       if (txtLabel.Location.Y + txtLabel.Image.Height - this.Bounds.Bottom>= 0)
       {
           top = true;
           bot = false;
       }
       if (txtLabel.Location.X + txtLabel.Image.Width - this.Bounds.Right >= 0)
       {
           right = false;
           left = true;
       }
       if (txtLabel.Location.X <= this.Bounds.Left)
       {
           right = true;
           left = false;
       }

       if (txtLabel.Location.Y <= this.Bounds.Top)
       {
           top = false;
           bot = true;
       }

        //moves image
        if (bot == true)
        {
            if (right == true)
            {
                ++imgX;
                ++imgY;

                p.X = imgX;
                p.Y = imgY;
                txtLabel.Location = p;
            }
            else if (left == true)
            {
                --imgX;
                ++imgY;

                p.X = imgX;
                p.Y = imgY;
                txtLabel.Location = p;
            }

        }
        if (top == true)
        {
            if (right == true)
            {
                ++imgX;
                --imgY;

                p.X = imgX;
                p.Y = imgY;
                txtLabel.Location = p;
            }
            else if (left == true)
            {
                --imgX;
                --imgY;

                p.X = imgX;
                p.Y = imgY;
                txtLabel.Location = p;
            }
        }

        Invalidate();
    }

}

}

Solution

  • The problem is the label's Location is its position relative to the monitor that it's on while the bounds you are comparing it too are an absolute position across all monitors.

    So if the bounds of the primary monitor are Top: 0, Bottom: 900, Left: 0, Right: 1600 the bounds of the secondary monitor might be something like Top: 0, Bottom: 900, Left: 1600, Right: 3200. While the Location of the label on the secondary monitor will returning its position relative to the secondary monitor so for example Location.X: 200, Location.Y: 300.

    You need to change the bounce Method to make the comparsion use either only absolute coordinates or only relative coordinates.

    Here is the comparison code modified to use relative position of the label on the monitor;

    if (txtLabel.Location.Y + txtLabel.Image.Height - (this.Bounds.Bottom - this.Bounds.Top) >= 0)
    {
        top = true;
        bot = false;
    }
    if (txtLabel.Location.X + txtLabel.Image.Width - (this.Bounds.Right - this.Bounds.Left) >= 0)
    {
        right = false;
        left = true;
    }
    // in relative coordinates left is always 0
    if (txtLabel.Location.X <= 0)
    {
        right = true;
        left = false;
    }
    // in relative coordinates top is always 0
    if (txtLabel.Location.Y <= 0)
    {
        top = false;
        bot = true;
    }