Table of contents
Right after I had published the Profile article, I went back to the small Windows app I had been working on to add the code that would save the form's position and size upon closing and then show it the same way next time. I added handlers for the Load
and Closed
events, and added the proper code to save and restore the position and size values using my Registry
profile class. The code just used the form's Position
, Size
, and WindowState
properties, and it worked fine... except for one small problem. If the window was minimized or maximized and I then closed it, it would reappear minimized or maximized but it would not remember what its "normal" size had been. So its normal size would be the same as its maximized or minimized size. Not good!
I went looking for a .NET solution to this problem and to my dismay could not find one. I then remembered a C++ class I had worked on and published here called, PlacementHook
. It used a simple hooking mechanism (via SetWindowLong
) to transparently handle the WM_SHOWWINDOW
and WM_DESTROY
messages and take care of saving and restoring the window's placement. The window's placement, of course! -- that's how I had been able to save the window's "normal" size. The Win32 WINDOWPLACEMENT
structure contains a rcNormalPosition
member which keeps track of that. Now, why didn't the .NET classes provide this? Hmmm...
Anyway, my new mission was clearly defined: to write a class that would encapsulate saving and restoring the form's placement. It would use my new Profile classes to store and retrieve the placement data. It would call the Win32 API GetWindowPlacement
to get the form's "normal" position and size. And like PlacementHook
, it would contain handlers for the Load
and Closed
events that would do all the work transparently.
So once again, I had to set my small Windows app aside and get to work on my new class. I called it Placement
and decided that it would be great to publish it here. I hope you find it useful in your Windows-based apps.
Design Notes
I initially created the class stand-alone -- not derived from anything. It just had a couple of constructors where the Form
object would be passed. The class would keep the Form
object inside a field and save/restore its placement inside the Load
and Closed
event handlers.
I then went back to try to convert it to a Component
class so that it could be added to the Toolbox and dropped inside a form inside the designer. This was problematic because the designer would not be smart enough to know that it needed to pass the form's object (this
) to the class's constructor. So I had to add a default constructor to the class. But I still didn't know how to make the designer generate code to set the Form
property to this
, the container. Well, after doing a bit of research I found the ComponentDesigner
class. Inside its OnSetComponentDefaults
method is where I set the Form
property, which the designer cleverly translates to this
.
It all turned out great! The Placement
class can be used just like any component and added to the designer's Toolbox. Once there, you can drag-n-drop the component on a form and pretty much forget about it. After you compile and run your program, you'll notice that each time the form comes up, it will be at the same location and with the same dimensions as it was when it was last closed. All this without having to write a single line of code!
Of course, I added several properties to the class to allow you to adjust the component if necessary. There's one for getting/setting the Profile
object that takes care of saving/restoring the profile data. You may (manually) set that to any IProfile
implementation, such as XML or Config -- by default it's Registry. There's also a Section
property that holds the name of the section under which the placement data will be stored inside the profile. And finally, I added three distinct boolean properties that allow you to individually set which aspects of the placement to restore (location, size, and state). These are all set to true
by default and can be easily adjusted inside the Properties window.
Usage
I placed the class inside the AMS.Form
namespace, and built it into a new DLL called AMS.Form.dll. I plan to add other Form
-related classes to this DLL as the need arises, but for now it only contains the Placement
class. To add it to the Visual Studio .NET Toolbox, follow these steps:
- Open your own project inside Visual Studio .NET.
- Open the file containing the form you wish to add the control(s) to.
- Right click on the Toolbox and select "Customize Toolbox".
- Click on the .NET Components tab.
- Click the Browse button and select the AMS.Form.dll file.
- The
Placement
component will be added to the Customize toolbox and will be check-marked.
- Click OK. The control will then appear on the toolbox.
- Drag and drop it to your form and modify any of its properties, if desired.
You may alternately add the Placement
class to your code manually, which is also very simple. First you need to add the DLL your project's References, which is done as follows inside Visual Studio .NET:
- Open your own project inside Visual Studio .NET.
- Inside the Solution Explorer toolbox, right click on the References folder and select "Add Reference".
- Click on the Browse button and select the AMS.Form.dll file.
- Click OK. You may now use the
AMS.Form
namespace inside your project.
Then you can add this line to your form's constructor, right after the call to InitializeComponent();
AMS.Form.Placement placement = new AMS.Form.Placement(this);
That's pretty much it! If you want to change the default settings, it's simple enough. Here's an example:
placement.Profile = new Xml("Settings.xml");
placement.Section = "Some Section";
placement.RestoreSize = false;
I thoroughly documented the class and created a help file for it, called AMS.Form.chm. I doubt you'll need it, but it's there just in case.
One thing to keep in mind is that AMS.Form.dll depends on AMS.Profile.dll (which I've included in the downloadables -- the source code is here). Just remember to keep these two DLLs together. Enjoy!
History
Version 1.0
- Nov. 7, 2003
- Nov. 12, 2003
- Re-release due to this article being published under the wrong user account.
- Changed the
Restore
method to not restore the location if doing so would cause the form to fall outside the bounds of the available screen(s). Also, if the form is initially minimized or maximized, its state will not be restored. Thanks to Maximilian H�nel and Igor Gribanov for their suggestions.