I have a winforms application which must display an emergency message on two screens under particular conditions.
The two messages are delivered as two identical Forms, each instantiated on each of the screens. Each form contains a configurable PictureBox with an image.
When the conditions are met, the two forms start correctly on the two screens, but the picture is only displayed in one of the two (primary screen) and not on the other.
The forms are instantiated on a foreach so they use the same constructor only with a different screen.
Form class:
public partial class FormClearingMessage : Form
{
public FormClearingMessage(Screen s)
{
InitializeComponent();
SetParameters(s);
}
private void SetParameters(Screen s)
{
this.Top = s.Bounds.Top;
this.Left = s.Bounds.Left;
this.Width = s.Bounds.Width;
this.Height = s.Bounds.Height;
this.pictureBox1.Top = this.Top + 20;
this.pictureBox1.Left = this.Left + 20;
this.pictureBox1.Width = this.Width - 40;
this.pictureBox1.Height = this.Height - 40;
this.pictureBox1.MaximumSize = new Size(this.pictureBox1.Width, this.pictureBox1.Height);
this.LoadImage();
}
private void LoadImage()
{
string imagePath = Utils.ResolveSymbolicPath(c.ReadCfgEntry("clearingMessage/imagePath", null));
if (File.Exists(imagePath))
this.pictureBox1.Load(imagePath);
else
throw new Exception();
}
}
Initialization:
// Initialize Clearing Message forms
Screen[] screens = Screen.AllScreens;
_clearingMessageForms = new List<FormClearingMessage>();
foreach (Screen s in screens)
{
_clearingMessageForms.Add(new FormClearingMessage(s));
}
Istructions when conditions are met:
if (singleMessage.InformationType == _infoTypeForEmergency)
{
if (_clearingMessageForms != null)
{
foreach (FormClearingMessage fcm in _clearingMessageForms)
{
if (!fcm.Visible)
this.Invoke(new Action(() => {
fcm.Show();
}));
}
}
}
Result:
I can't seem to find anything wrong with the setup but obviously I'm missing something and I was hoping someone could help me out.
Thanks in advance!
In Windows, there is a concept called the "virtual screen". When you have a single monitor, then the virtual screen is identical to the physical screen, and you can blissfully ignore it. However, when you have multiple monitors, then it begins to matter.
In short, the virtual screen is the bounding rectangle of all the monitors that form the Windows desktop. The picture below is an example—the virtual screen is shown in gray:
What makes things tricky is that, for compatibility reasons, the point (0, 0) is always at the top-left corner of the primary monitor. Thus, if your secondary monitor is positioned to the right and/or bottom of your primary monitor, it has coordinates that are larger than the primary monitor, and all is well. However, if your secondary monitor is positioned to the left and/or top of your primary monitor, then its coordinates are negative.
This would be the case in your situation. The right-hand monitor is clearly set as your primary display, so its upper-left corner has coordinates (0, 0). This means that the left-hand monitor has negative coordinates. Thus, when you retrieve the Top
and Left
properties from the Bounds
property of its corresponding Screen
object, you get negative values.
This would be correct if you were using these coordinates to position a form, since forms (and any other top-level windows) use virtual-screen coordinates for positioning.
However, you are positioning a child form (i.e., a control) on a form. Child windows use client coordinates, which are relative to the parent. In client coordinates, the top-left corner of the parent is always at (0, 0), so if you want your PictureBox positioned at the form's upper-left corner, then you should hardcode its Top
and Left
properties to be 0.
See also this other answer.