Introduction
Many applications store the size and location of a form so that the next time it is opened it restores to the same size and location. This is a great feature, but can present a problem when the display environment is not always the same. For example, a notebook computer may be connected to a secondary monitor at the office, but not when on the road. A form that is set to load on the secondary monitor will be displayed off the visible screen when the notebook is on the road. The code in this article shows how to get the benefits of this feature without this problem.
A quick search of the Internet will find many ways to save the form position. None that I found seemed to take multiple monitors (or more precisely, an inconsistent monitor setup) into account. I also easily found a code example that allowed me to specify which monitor to start a form on. The problem with this code is that it assumes that the coordinate system is based on position 0,0 being the top left corner of the specified monitor, not necessarily the top left corner of the primary monitor, while the form coordinates are given based on the primary monitor. So, if I positioned the form in the middle of my left (secondary) monitor and exited, I would store an X position of -600 (600 pixels to the left of the primary monitor), but when loading the form and specifying the startup monitor, I need to tell it to move 424 pixels to the right from its origin on the secondary monitor. All very confusing.
The key was to determine the bounds of the monitor used. This uses the same coordinates referenced in the form's location, so a 1024×768 monitor to the left of the primary monitor (also 1024×768) would have its upper left corner be -1024,0. We can subtract the bounds coordinates from the saved coordinates to find the coordinates relative to the top left corner of the monitor used. I also check to verify that the position stored is within the bounds of the selected monitor, and if not, display the form in the upper left corner of the selected monitor. This way, the form is never displayed in a position where it can not be seen.
Another problem with the code I found for specifying which screen to start on was that it did not verify the existence of that screen before trying to change to it. So, if a monitor was disconnected, you would end up with an error. Storing the selected monitor also wound up being a bit more of a challenge than expected. The property Screen.DisplayName
pads the end of the string with a number of hidden characters. Trimming off the extra characters solved my problems with this section, but it was annoying to track down.
Using the code
The code is in two segments. The first is placed in the Form_Load
procedure, and the second in the Form_FormClosing
procedure, as marked. The example shown stores the form position in an INI file. An XML file or the Registry could be used as easily. The ReadINI
and WriteINI
procedures referenced in the code are not shown here. There are many methods available for performing these functions.
The following code goes in the Form_Load
procedure:
Dim posScreen As Integer
Dim posX As Integer
Dim posY As Integer
Dim screen As Screen
posScreen = Val(ReadIni(File, “Main”, “PosScreen”))
screen = screen.AllScreens(0)
If screen.AllScreens.Length > posScreen + 1 Then
screen = screen.AllScreens(0)
Else
screen = screen.AllScreens(posScreen)
End If
posX = Val(ReadIni(File, “Main”, “PosX”))
posY = Val(ReadIni(File, “Main”, “PosY”))
Dim pt As New Point(posX, posY)
If screen.Bounds().Contains(pt) Then
pt.X = posX - screen.Bounds().X
pt.Y = posY - screen.Bounds().Y
Else
pt.X = 0
pt.Y = 0
End If
Me.StartPosition = FormStartPosition.Manual
Me.Location = screen.Bounds.Location + pt
The following code goes in the Form_FormClosing
procedure:
For i As Integer = 0 To
screen.AllScreens().Length - 1
If stringtoasc(screen.AllScreens(i).DeviceName.ToString) _
= stringtoasc(screen.FromControl(Me).DeviceName.ToString) Then
WriteIni(File, “Main”, “PosScreen”, i)
End If
Next
WriteIni(File, “Main”, “PosX”, Me.Location.X)
WriteIni(File, “Main”, “PosY”, Me.Location.Y)