Issues with Storing Window Position With Multiple Monitors


Photo by SOYBEANTOWN

While working on the Document Viewer Component I ran into a few issues with the standard WinDev window position restore logic with dual monitors, so I created a couple global procedure to work around the issues.

WinDev will save the size and position of each window in your application. This allows end users to adjust the size and position and those setting be remembered, personalizing the application for their use. While working on the Document Viewer Component I ran into a few issues with this not working correctly with Dual Monitors. Even when I moved the document viewer to the second monitor, it would always reopen on the primary monitor.  So I did a little investigation in how WinDev does their Window Restore logic and created some of my own to supplement it for dual monitors.

Since I am piggy backing my solution on top of WinDev’s existing logic, the first thing to do is enable their logic. On the Window Description, go to the GUI tab, and turn on “Store the size and position of the window”.

Next we need to look at what WinDev is storing in the registry, but where does WinDev store the information? Turns out it depends on whether you running the compiled EXE or using the run-time testing. Fortunately WinDev gives us a function to tell us the registry branch that the application is using.

ProjectInfo(piRegistry)

Using this funcition I discovered that in run-time mode the branch used is HKEY_CURRENT_USERSOFTWAREPC SOFTWinDev16.0ModeTestApplicationName, and when running the compiled EXE it is HKEY_CURRENT_USERSOFTWAREApplications WinDevApplicationName. ApplicationName is the actual name of you application. So let’s take a look at what they are storing in the registry:

Ah, should have seen that one coming, the keys are in French, so after a quick trip over to google translate and I learned that:

MaximiseeOuverture = Maximized Opening
PositionOuverture = Opening Position
TailleOuverture = Opening Size

You might notice that there is a third value stored on Opening Position, and it seems to correspond to the monitor number, but in my testing it never seemed to work. I created a global procedure called RestoreWindowPosition and then called it from the Initialization arae of the Window Code. See Below:

//Open the image in the current display mode
IF gsCurrentFileName <> "" THEN
   InitImage(gsCurrentFileName)
END
RestoreWindowPosition() 
sStoredValue is string
sStoredValue = RegistryQueryValue(ProjectInfo(piRegistry)+ "" + WinInput() + "TBAR_Navigation","PositionOuverture")
IF sStoredValue <> "" THEN
   TBAR_Navigation..X = ExtractString(sStoredValue,1,",")
   TBAR_Navigation..Y = ExtractString(sStoredValue,2,",")
END

The first few lines is doing some work of the Document Viewer, the next line is the call to the global procedure. And the final lines are some extra code that I use to also position the Floating Toolbar at the correct location. I didn’t create a procedure for the floating toolbar, since I don’t use that many, but with the use of WinDev’s indirection ability, you could definitively create a procedure that would allow you to pass in the control name and reposition any control.

While working on this logic I tried to use the registry entries that WinDev was writing to but it seemed that WinDev was writing to them after any custom code that I could place. It was overwriting whatever I put in the values, so I had to add a few new entries to the Registry. On the closing code of the window we will call a global procedure named StoreWindowPosition:

StoreWindowPosition()

And the global procedure for Storing the Window Position:

PROCEDURE StoreWindowPosition()

RegistrySetValue(ProjectInfo(piRegistry)+ "" + WinInput(),"WindowPosition",MyWindow..X + "," + MyWindow..Y)
RegistrySetValue(ProjectInfo(piRegistry)+ "" + WinInput(),"Maximized",WinSize(WinInput()))

This code is storing two new entries in the same Registry location for the Window as WinDev. We are using the ProjectInfo(piRegistry) function so our code will work regardless of what branch of the Registry WinDev is currently using. WinInput() returns the name of the Window current running, which becomes part of the Registry branch. WindowPosition holds the X and Y coordinates of our window, we get those my using the X and Y properties of the Window and the special constant MyWindow which always refers to the active window. The X and Y coordinates are pixel locations and treat the entire space of all your monitors as one location. Depending on how your second monitor is configured the values will be slightly different. For instance in my configuration my second monitor is above my main monitor (not a lot of room to spread out in an RV!). When I place a Window on my second monitor the Y position becomes a negative number, representing the number of pixels above the main monitor. The second line is using the WinSize function to find out if the window is currently maximized. We will discuss this in more detail when we look at the code for Restoring the Window. Here’s a look at the Registry with the new entries added in.

Now for the code of the global procedure for Restoring the Window:

PROCEDURE RestoreWindowPosition()
StoredValue is string
nXPos is int
nYPos is int
x,y,x1,y1 is int

MyWindow..Visible = False

StoredValue = RegistryQueryValue(ProjectInfo(piRegistry)+ "" + WinInput(), "WindowPosition")

IF StoredValue <> "" THEN
   nXPos = ExtractString(StoredValue,1,",")
   nYPos = ExtractString(StoredValue,2,",")
   IF RegistryQueryValue(ProjectInfo(piRegistry)+ "" + WinInput(), "Maximized") = 2 THEN
      nXPos += 5
      nYPos += 5
      MyWindow..X = nXPos
      MyWindow..Y = nYPos
      x = ExtractString(WinScreenRectangle(MyWindow,screenAll),1,TAB)
      x1 = ExtractString(WinScreenRectangle(MyWindow,screenAll),3,TAB)
      y = ExtractString(WinScreenRectangle(MyWindow,screenAll),2,TAB)
      y1 = ExtractString(WinScreenRectangle(MyWindow,screenAll),4,TAB)
      MyWindow..X = x
      MyWindow..Y = y
      MyWindow..Width = Abs(x-x1)
      MyWindow..Height = Abs(y-y1)
      Maximize(MyWindow)
   ELSE
      MyWindow..X = nXPos
      MyWindow..Y = nYPos
   END
END
MyWindow..Visible = True

The first four lines are creating some local variables that we will be using. Next I hide the window, so the user isn’t distracted with screen flashing as we reposition the window.

Next we retrieve the values from WindowPosition from the Registry. WindowPosition is the entry that we added when the window was closed. Again we are using the  ProjectInfo(piRegistry) function so our code will work regardless of what branch of the Registry WinDev is currently using.  The IF statement skips all of our code if this is the first time the window has been ran and there are no entries in the Registry.

The next two lines extract the X and Y position from the string that we retrieved from the Registry. I have written about the ExtractString function  before, its a very handy function that lets you extract entries from a delimited string based on their position.

Next we check to see if the window was maximized. I found the value stored by WinDev was not a 100% reliable so I added my own, which is what the second line of code in the Store Window Postion Function was doing. We need to know if the Window was maximized because we need to treat the restore a little different. First we add 5 to the X and Y positions, I don’t know why but if the window is Maximized the X and Y properties return a value that is 5 less than it should be. Then I set the X and Y properties with these new values. This repositions the window onto the correct monitor. The window is still not sized correctly, but once it is on the correct monitor, we use the WinScreenRectangle Function to get a tab delimited list of the coordinates of the monitor size.  Notice we are using the ExtractString Function again, this time with the special constant Tab because our list is Tab delimited. Now we set the position of the window to the top left corner of the monitor and the height and width to use the entire monitor. Finally we use the Maximize function so the window will be treated as maximized, instead of just sized to fill the monitor. The reason I didn’t just use the Maximize function by itself is because it leaves an area of space to account for the windows task bar.

The ELSE statement handles repositioning the window if it was not maximized. We don’t need to adjust the size of the window here, because the WinDev code is doing it just fine. And then finally we make the window visible.

Summary

We now have a couple of global procedures that we can use with any application to correctly save and restore the window position even on a multiple monitor system. Your homework is to create a second group of functions using indirection so that you can pass in the name of any floating Toolbars and have them correctly relocated as well. As always if you have any questions please leave a comment below.

[suffusion-the-author display=’author’]

Pete Halsted[suffusion-the-author display=’description’]

 

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s