I Just Want To Be Friends With Your Window
July 4th, 2007A proper application should have proper persistence of its windows state between sessions. Ummm…yeah. What I meant to say was…
Don’t you hate it when you close a window and the next time you start your application it’s not in the same place? Or even worse: I have a laptop that sits in a dock connected to two twenty-two inch widescreen monitors. Each monitor is 1680×900 and I like to splay all my applications across my 3360×900 pixels of screen. This is great until I’m working in bed late at night and half my applications open far off screen. (Good thing for alt+space+m or I’d have gone mad.)
The crazy thing is how easy it is to put together a smart window. Normally I wouldn’t blog on such a simple topic but at last count I have twelve frequently used applications on my machine that drive me bonkers. (Yes, Times Reader is one of them and yes I did receive your email and yes we’re going to fix it and no I don’t normally keep my head up there.)
After performing some major grueling research, I found that there are three typical ways applications on my computer remember their state.
- The Normal Lazy Way
- The Kind of Smart Way
- The Smart Way
There’s also a fourth way that I came up with while goofing around.
- The Scaling Way
In case those weren’t descriptive enough I’ll go into further detail.
The Normal Lazy Way
This is when you don’t implement anything and just let the window run with it’s default behavior.
Below are screen shots of an app I put together. The first image shows the state of the window on exit. The second image shows the same application’s state right after it was executed. The main difference being the first image is with my laptop docked to two enormous 1680×900 resolution monitors, while the second image is my laptop undocked and using its puny 1024×768 resolution screen.
|
The downside to using the default functionality is your window never remembers its dimensions, nor its placement on the screen. On the plus side, your app never opens outside the dimensions of your screen…like it does with the next method.
The Kind of Smart Way
This is where the developer tries to be smart. Upon exiting, the app somehow stores the windows dimensions and placement on the screen. The next time the application starts, it uses these stored values to draw itself. You can see an example of the load and save methods possibly used by clicking here. If you haven’t already guessed, there’s one problem.
|
ARRRGH! I know you’ve all experienced this, so don’t try to deny it. ALT+SPACE+M…arrowkey arrowkey arrowkey arrowkey…
Because of this one limitation, I’d rather folks just left the default window behavior active instead. Or better yet, implement it correctly! Like so…
The Smart Way
Now the smart way actually takes a little bit of brain power. Instead of just loading and saving settings, you actually have to perform some checks to make sure that when your window draws, it’s actually on the screen. Before we get into details, I should introduce you to the Virtual Screen.
|
The Virtual Screen represents the combined dimensions of all connected monitors/screens. For example, the dimension of my virtual screen when my laptop is docked is 3360×900, while my undocked laptop and its single screen have a virtual screen with a 1024×768 dimension. (Same as my “Primary Screen”.)
If you’re working in WPF you can find information on your virtual screen under the SystemParameters namespace. (I believe WinForms folks can find the equivalent under System.Windows.Forms.SystemInformation.VirtualScreen.) In my code I just use the values of SystemParameters.VirtualScreenWidth and SystemParameters.VirtualScreenHeight. I use these two values to make sure my window placement isn’t outside of my visual screen. I also make sure that the width and height values of the window are less than the dimensions of my viewable screen. You can see the sample load and save methods here or just look at the pretty picture.
|
I know it looks like the window is outside the edge of the laptop screen but that’s because my code moved the window from the far left to the inside of the screen and placed it on the very edge.
I think this is probably the best method someone could implement. Of course that didn’t stop me from fiddling and coming up with…
The Scaling Way
This is pretty much the same code as the “Kind Of Smart Way” except I don’t store the hard values of the window’s placement and dimensions. Instead, I store the windows proportions relative to the virtual desktop. So if you close your window and it’s taking up 25% of your vertical space and 75% of your horizontal space, the next time you open it (even on a smaller/larger screen) it will be proportionally smaller/larger to the change in screen size, but it will still be using the same percentage of screen space. Did that make sense to you? Yeah..me neither. I did the same thing for placing the window. If you really want to understand it, you can view my code here.
There’s one reason I don’t like this simple approach. While it works great when you’re going from one resolution to another, it does look a little odd when switching from a dual monitor setup to a single monitor. The following image illustrates the weirdness.
|
Since my width changes by 2336 pixels while my height only changes by 132 pixels, the ratios became a little skewed…and so did my window.
Finally…the Conclusion
So I’ve just broken my first rule of blogging: keep it short. This probably means most of you aren’t even reading anymore. But if that means my next favorite app has smart windows, it’ll be worth it.
UPDATE: 06/05/07
As Franz points out in the comment section below, there’s a flaw in my code examples. Actually, there are two flaws.
First Flaw I discovered: You don’t want to save the windows dimensions and position when it is maximized. That makes as much since as saving it when it’s minimized. You want the app to have the proper restore values for when they start the app and then “un-maximize” it.
Second Flaw Franz Discovered: If the window is maximized on a monitor that isn’t your primary monitor it will still load maximized on your primary monitor. I believe this is because I load the window settings before it is drawn so it “technically” is on the primary screen when it is given the maximized value. I see two ways of avoiding this.
- You could not call the Load method until your window is loaded. The downside being your window will load, jump across to the second monitor and then maximize. Kind of trippy.
- The other solution is to remove the line in the Load method that sets the WindowState and move it to the window’s Loaded event. This has a better visual loading affect but really tears apart the elegance of having a single load method and a single save method.
I choose the less elegant but better user experience for my code sample below. If you have a better way let me know.









