| By Richard Arthur | Article Rating: |
|
| November 17, 2006 12:15 PM EST | Reads: |
15,439 |
Microsoft Windows provides great power in manipulating the forms in your application, however, the .NET Framework masks a lot of what can be done to your forms for consistency and ease of use.
You have probably seen applications that control their size and positions with greater fluidity than you can get with normal .NET Forms, such as maintaining an aspect ratio while resizing, or docking to the side of a screen. Thankfully there are ways of gaining access to the more powerful aspects of Windows, but they are a bit ugly. In this article, I want to help you write a subclass of Form, which has greater power over its position - namely, I would like to keep the form from ever leaving the screen, not even a part of the side. In the process, we will explore ways to find out about the messages that Windows sends to forms, and how to retrieve their extra data and process those messages in custom ways.
The code in this article was written using C# and Visual Studio 2005. This code can be converted to VB and created using Visual Studio 2002 and 2003.
Appearance While Forms Move
As you move a form around, Windows can do one of two things, depending on how it is configured:
- Appearance (A): Windows will move the form with your mouse.
- Performance (B): Windows will give you an outlined preview of where the form will be placed and will move the form only when the mouse is released.
- Right-click on "My Computer" and select "Properties."
- Go to the "Advanced" tab.
- Under "Performance" click "Settings."
- Select "Adjust for best performance" or "Adjust for best appearance," depending on which you would prefer.
- Click "OK."
Pure .NET Approach
To keep a form on the screen using a typical approach in C#, we would create a form and in the OnMove method we would write the code in Listing 1.
Depending on whether you have Windows, move window contents with your mouse (A), or not, (B), and you will see two different effects:
- A: As you move the form outside the bounds of the screen, it will flicker back and forth between where Windows wants to put it, and where you want to put it.
- B: The preview outline will go outside the screen, and on mouse-up, the form will appear inside the screen.
A Message-Based OS
Win32 is a message-based operating system. The Move event we caught in Listing 1 was the result of a common message that Win32 generates for us. The process of how this event is normally handled by programmers is illustrated in Figure 1. Windows first generates the "move" message, which is defined by a constant called WM_MOVE, and places that message into the "message queue" of your application (step 1). At the root of all Windows applications is a special piece of code called the "message pump" (see figure 1) which continually grabs and processes messages from the queue. In this case, it takes the WM_MOVE message (step 2) and hands it off to the WndProc method of the form that needs to process it (step 3). We will further explore the WndProc method later. The .NET Framework then processes the message and ultimately calls the OnMove method. This method raises the Move event, which calls all registered Move event handlers (step 4). In one of these handlers, programmers typically perform custom processing relevant to that event. To save some time, we subclassed Form directly, then performed an override of the OnMove method, which is where we put our custom code - the style of programming to use when catching events on the object that raises them. You should always call the base implementation of all OnWhatever methods you override so the base class can raise the associated event for any listeners. Catching events through event handlers is usually best for external classes being notified when things happen. For example, a form will register for a Button Click event because the Form class is external to the Button class.
Every time you press a button on your keyboard, Win32 typically generates three messages that get processed by your application: a WM_KEYDOWN, WM_KEYUP, and WM_CHAR. Pressing mouse buttons typically generates two other messages (e.g., WM_LBUTTONDOWN and WM_LBUTTONUP). As an application is moved on the screen, properties are changed, or painting needs to occur, dozens to hundreds of messages are generated. Across the life of an application, thousands of messages are generated and passed to your application, and sometimes from your application back to Windows.
Your application even generates messages and sends them to itself. All these messages must be processed by the message pump, and can only be generated by the thread the message pump is on; otherwise Win32 cannot keep messages and their data synchronized. As a result, alternate threads that try to alter properties on a Form must do so by using the Control.Invoke method. I bring this up to warn and inform you about the legacy of Win32 code: you will have to read up on the details of that method on your own.
Microsoft has thousands of different messages defined for different events that occur in Win32. These messages are the common ones, such as painting a form, typing characters, and mouse movements. There are also exotic messages such as tree-view node expansion, click or collapse, and logon/logoff events. If you want a look at some of these messages, search for and open the "WinUser.h" file on your computer: it will be deep inside the "Program Files" directory, wherever you have installed the Visual C++ (VC7 or VC8), if you have it installed. This file contains many of the messages that Win32 will process. Here is a taste of the contents of this file:
#define WM_KEYDOWN 0x0100
#define WM_KEYUP 0x0101
#define WM_CHAR 0x0102
There are a lot of #define statements in this file; some of them are messages, and others are constants related to these messages. Thankfully, Microsoft is a very rigid company when it comes to their coding standards. Once you know some key things about reading their source code, you can read just about anything written there. For example, many of the messages sent by Win32 start with WM_, which is short for "Windows Message." Constants that are related all start with the same characters, followed by an underscore. If you have your MSDN documents installed and unfiltered, you can type in one of these message names into the index and read all about that message. For example, type in WM_SIZE, and you can find out about the Size event, and the different constants that can be passed in with it (each constant starts with SIZE_).
The WndProc method
The message pump is the core piece of code that grabs messages from the OS and dispatches them to your application, but the WndProc method is where all the magic happens. This method is one giant switch statement that takes in the message and then runs the appropriate code. With each message, there are usually pieces of information passed along that give more details, like which key was pressed, the device context (Graphics object) to paint your form in, or the bounds of where your form is supposed to be placed.
The WndProc method is where we will concentrate our efforts because there are messages it receives, but that the .NET Framework does not pass to us. One of these is a message called WM_MOVING (which is different from WM_MOVE, which we handled earlier). This message does three things for us:
- It tells our form that it is going to be moved.
- It tells our form where it will be moved to.
- It gives our form the chance to tell Windows to move our form somewhere else.
Published November 17, 2006 Reads 15,439
Copyright © 2006 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
More Stories By Richard Arthur
Richard Arthur is currently an instructor of C# at Northface University (www.northface.edu) in Salt Lake City, Utah. He has gained extensive experience doing Automation of MS Office products through VBA and COM at previous jobs.
In his spare time he learns about the inner workings of many current and future .NET Technologies, including Windows Forms, ASP.NET, Generics, P/Invoke, and COM Interoperability.
If anyone has any questions regarding this article, please contact Richard at startether@startether.com.
- Kindle 2 vs Nook
- Wave on Ulitzer: Confessions of a Google Wave Fanboy
- Confessions of a Ulitzer Addict
- IBM Hardware Chief, Intel VC Exec Arrested in Insider Trading Scam
- Cloud Computing Best Practices
- Tactical Cloud Computing Panel at 1st Annual GovIT Expo
- Ulitzer.com Named Exclusive "New Media" Sponsor of Cloud Computing Conference & Expo
- Infrastructure-as-a-Service Will Mature in 2010: Microsoft's David Chou
- Windows 7 – Microsoft’s First Step to the Cloud
- Cloud Computing & Federal IT - What Does the Future Hold?
- Jill Tummler Singer, Deputy CIO of CIA, Keynotes at GovIT Expo
- Cloud Expo and the End of Tech Recession
- Kindle 2 vs Nook
- The Difference Between Web Hosting and Cloud Computing
- Ajax in RichFaces 3.3, JSF 2 and RichFaces 4
- Wave on Ulitzer: Confessions of a Google Wave Fanboy
- Confessions of a Ulitzer Addict
- Cloud Computing Best Practices
- IBM Hardware Chief, Intel VC Exec Arrested in Insider Trading Scam
- Tactical Cloud Computing Panel at 1st Annual GovIT Expo
- Ulitzer.com Named Exclusive "New Media" Sponsor of Cloud Computing Conference & Expo
- Eval JavaScript in a Global Context
- Infrastructure-as-a-Service Will Mature in 2010: Microsoft's David Chou
- Windows 7 – Microsoft’s First Step to the Cloud
- Google Maps and ASP.NET
- Crystal Reports XI & How It Has Changed
- Converting VB6 to VB.NET, Part I
- Creating Controls for.NET Compact Framework in Visual Studio 2005
- Where Are RIA Technologies Headed in 2008?
- How to Write High-Performance C# Code
- AJAX World RIA Conference & Expo Kicks Off in New York City
- Implementing Tab Navigation with ASP.NET 2.0
- i-Technology Photo Exclusive: Bill Gates & Steve Jobs In "Nerds"
- .NET Archives: Getting Reacquainted with the Father of C#
- i-Technology Viewpoint: "SOA Sucks"
- Programmatically Posting Data to ASP .NET Web Applications


































