| By Richard Arthur | Article Rating: |
|
| November 17, 2006 12:15 PM EST | Reads: |
15,464 |
Notice that this code references an object of type RECT. This type is the Win32 Rectangle, and is used in all places where 4 pixel-based integers are used. Deep in the .NET Framework, there is an implementation of this type to help .NET convert from Win32 (unmanaged) to .NET (managed) code; however, we cannot reach that type of declaration, so we have to implement our own. Because working with the Rectangle structure is much easier, I also provide some explicit casts to and from Rectangle so that it can be used more intuitively. The code for the RECT type is provided in Listing 3.
The code provided in Listings 2 and 3 perform a few key functions. First, it identifies when the WM_MOVING message is passed in. Second, it gets where Win32 proposes to place your form. Third, it changes those bounds to a specific location in OnFormMoving. Finally it gives Win32 the bounds of its desired destination. We will explore these steps in the next couple paragraphs.
The WndProc method gets passed a Message structure which has some important properties:
- Handle: The Win32 handle of the form or control processing the message
- Msg: The ID of the message
- LParam: This contains extra information for the message, if any
- WParam: This contains extra information for the message, if any
The Handle property can be ignored: it is used by Win32 to identify where to send the message. However, the LParam and WParam properties often have information related to message. These two properties are integers, so they can hold each 4 bytes of information. But notice that the RECT structure holds four integers, or 16 bytes of information. In this instance, LParam is actually holding an unmanaged pointer to another place in memory which has the whole RECT structure. To get the value out of the unmanaged world into the managed world we use the Marshal object to copy that memory back and forth. PtrToStructure copies the unmanaged values into our managed structure, whereas the StructureToPtr method copies our managed structure back to where the unmanaged pointer is pointing.
Now, let's run our code and see what happens. In this case, when you try and move the form, it's forced to go to one place on the screen and does not move from that spot. If you are in A mode, you could also set the size of the form in the destination location, but if you're in B mode, the form will only move to the desired location and will not take on the size you specify.
Improving Form Positioning
We want this to work
under all Windows configurations. The form will show up at the location
the user places it, but it will not be sized; therefore, once it's
placed, we need to update its size. Thankfully, when the form is
finally moved, Win32 sends the WM_EXITSIZEMOVE message, which we can
catch and use to set the form's size. Add the following class-level
variables:
private const int WM_EXITSIZEMOVE = 0x0232;
private Rectangle lastBounds;
Then add these lines to the beginning of the WndProc method, before the call to the base implementation of WndProc:
if (m.Msg == WM_EXITSIZEMOVE)
this.Size = lastBounds.Size;
Replace the contents of the OnFormMoving method with this code:
lastBounds = new Rectangle(100, 200, 500, 100);
destination = lastBounds;
return true;
Whether you run your application in A or B mode, it will have the same output when the user moves the form and releases the mouse. In the .NET Framework 2.0, they have added the ResizeBegin and ResizeEnd events that correspond to the WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE messages. In this case, you may use the ResizeEnd events instead of catching WM_EXITSIZEMOVE.
Keeping the Form on the Screen
It is time to
smoothly keep the form inside the screen. Replace the contents of the
OnFormMoving method with the code in Listing 4.
Since we are not trying to change the size of the form, you should remove the code related to the WM_EXITSIZEMOVE message. When you run the application, the form will stay on the screen the mouse is on. If you do not want the form to cover any application bars you have docked (like the task bar), find the first line of the OnFormMoving method and change Bounds to WorkingArea. As you attempt to position the form outside of the bounds of the screen, you will notice that the bounds code keeps the form inside the screen, but the mouse no longer stays relative to the original click-point on the title bar as we move away from the borders. This is not very user-friendly. It would be much better if the form were a little more "springy" (i.e., it got pushed around near the borders, but snapped back when the mouse moves far enough back from the borders). A good way of doing this is to:
- Track where the mouse went down on the border of the Form.
- Try and position the bounds relative to that mouse position.
- If near a border, shift the bounds accordingly.
private const int WM_NCLBUTTONDOWN = 0x00A1;
private int deltaX;
private int deltaY;
Then add the following code to the top of the WndProc method:
if (m.Msg == WM_NCLBUTTONDOWN)
{
deltaX = MousePosition.X - this.Left;
deltaY = MousePosition.Y - this.Top;
}
Finally, add the following code just after the first two lines of code in the OnFormMoving method:
if ((MousePosition.X - manipulate.Left) != deltaX)
manipulate.X = MousePosition.X - deltaX;
if ((MousePosition.Y - manipulate.Top) != deltaY)
manipulate.Y = MousePosition.Y - deltaY;
When you run this code, you will finally have a form that can be positioned by the user, and it will stay within the bounds of the screen. This form will also reflect its future position correctly when Windows is in performance mode (B).
Wrap Up
Windows has an incredible number of
messages that it sends to forms during their lives. Although .NET forms
do not directly expose these messages to us, .NET developers can catch
them and respond in useful ways. With these kinds of messages, you
could build applications that do any of the following things, and more:
- When nearing the side of the screen (i.e., within 50 pixels):
- "Snap" to that edge of the screen
- "Dock" to that edge of the screen (fill that side up)
- "Snap" forms to each other, and even have two forms that move in sync
- Maintain an aspect ratio, instead of being limited by the MinimumSize and MaximumSize properties on the form
Microsoft Windows is an incredibly powerful operating system, and even in the .NET world, we can take advantage of the messages provided by Windows. These messages allow us to create applications that are smoother and slicker, as compared with normal .NET development, with regards to how they allow user interaction. Knowing how Windows is built allows us to write applications more intelligently (like using Control.Invoke in multi-threaded apps), and deepens our respect for the vast code base Microsoft has provided to make our lives richer.
Published November 17, 2006 Reads 15,464
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
- Confessions of a Ulitzer Addict
- 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
- Infrastructure-as-a-Service Will Mature in 2010: Microsoft's David Chou
- Windows 7 – Microsoft’s First Step to the Cloud
- Cloud Expo and the End of Tech Recession
- Jill Tummler Singer, Deputy CIO of CIA, Keynotes at GovIT Expo
- Reality Check at the Cloud Computing Expo
- Visual Studio 2010 Is Cloud Friendly
- Fired SCO CEO Fires Back
- Kindle 2 vs Nook
- The Difference Between Web Hosting and Cloud Computing
- Ajax in RichFaces 3.3, JSF 2 and RichFaces 4
- Confessions of a Ulitzer Addict
- Wave on Ulitzer: Confessions of a Google Wave Fanboy
- 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
- Eval JavaScript in a Global Context
- 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






























