Click here to Skip to main content
15,868,016 members
Articles / Programming Languages / C#
Article

Form Placement Component

Rate me:
Please Sign up or sign in to vote.
3.91/5 (11 votes)
11 Nov 20036 min read 64.1K   1.4K   33   8
A component class that restores a form's placement (location, size and state) to what it was when it was last closed.

Sample Image

Table of contents

Introduction

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:

  1. Open your own project inside Visual Studio .NET.
  2. Open the file containing the form you wish to add the control(s) to.
  3. Right click on the Toolbox and select "Customize Toolbox".
  4. Click on the .NET Components tab.
  5. Click the Browse button and select the AMS.Form.dll file.
  6. The Placement component will be added to the Customize toolbox and will be check-marked.
  7. Click OK. The control will then appear on the toolbox.
  8. 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:

  1. Open your own project inside Visual Studio .NET.
  2. Inside the Solution Explorer toolbox, right click on the References folder and select "Add Reference".
  3. Click on the Browse button and select the AMS.Form.dll file.
  4. 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();

C#
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:

C#
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
    • Initial release.
  • 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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
I've done extensive work with C++, MFC, COM, and ATL on the Windows side. On the Web side, I've worked with VB, ASP, JavaScript, and COM+. I've also been involved with server-side Java, which includes JSP, Servlets, and EJB, and more recently with ASP.NET/C#.

Comments and Discussions

 
GeneralRestoreBounds Pin
cmykColour7-Jan-07 16:23
cmykColour7-Jan-07 16:23 
QuestionWhat about make an IExtenderProvider out of your component Pin
c_marius8-Dec-04 4:52
c_marius8-Dec-04 4:52 
AnswerRe: What about make an IExtenderProvider out of your component Pin
Alvaro Mendez23-Jan-05 14:07
Alvaro Mendez23-Jan-05 14:07 
GeneralAny books you can recommend on design time integration Pin
Uri Dor30-Aug-04 2:46
Uri Dor30-Aug-04 2:46 
GeneralRe: Any books you can recommend on design time integration Pin
Alvaro Mendez30-Aug-04 15:36
Alvaro Mendez30-Aug-04 15:36 
GeneralComponentDesigner.OnSetComponentDefaults() question Pin
I G 19816-Nov-03 2:44
I G 19816-Nov-03 2:44 
GeneralRe: ComponentDesigner.OnSetComponentDefaults() question Pin
Alvaro Mendez16-Nov-03 12:27
Alvaro Mendez16-Nov-03 12:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.