Introduction
As pre-release versions of Microsoft’s Windows 7 surface lately, so do a lot of new features for the world of Windows. Some of these are simple ideas that can be easily implemented into existing .NET applications. For example, a handy feature in the Beta release is the ability to dock windows to the side of the screen. In this article, we’ll implement in C# another new feature, Aero Shake.
Aero Shake
Obviously, to implement the feature into our own applications, we have to know what it does. Aero Shake allows users to “shake” a window (by moving it around with the mouse). Once the window detects the shake, all other open windows are minimized. If the user shakes the window again, the windows that were minimized are restored.
Implementation Overview
Implementing the Aero Shake feature into WinForms is broken down into two parts:
- Create a Windows Form that triggers an event when a user “shake” is detected
- Create a class to encapsulate the functions to minimize and maximize other applications appropriately
Notice that the code will be far from complicated, but implementing it cleanly will be important if we want to make use of the functions later. Another advantage of breaking up the feature into small sections of code is that it gives developers flexibility. Improving different aspects of the function and fine-tuning the behavior of the code will be easier.
Detecting Window Shakes
Arguably, the most important part will be detecting when a Windows Form was shaken. There might be more complex algorithms, but I opted for a simple 3-part check:
- Time. The Form measures the time difference between the start of a “shake” and the end. This will allow us to filter out slow shakes because they will take more time.
- Distance. Also important to measure is how far the Form was moved overall. This allows us to fine-tune the “area” of what constitutes a shake. Also, it helps filter out regular Form dragging.
- Amount. This metric determines how much the window has to be shaken to count as a “shake”.
Once each value is obtained, we check if each value falls within a certain range. Notice that this gives a lot of flexibility to what constitutes a “shake”. [As a side note, this is something that people are complaining about in the actual Windows 7 Aero Shake].
To measure these values, we have two options: we can use events to detect when the location of a Form changes, or we can check the messages passed to the Form via WndProc
. For this particular implementation, I opted for WndProc
since it is easier to check when the mouse is clicking on the title bar.
Time
Getting the time that a shake takes is the easiest value to calculate. We simply use two DateTime
field variables to store the appropriate time intervals. WndProc
makes it simple to detect Mouse Up and Mouse Down actions on the title bar (the source code shows all the messy constants).
Notice how this is not a critical time measurement, which makes DateTime
good enough. The StopWatch
class is more precise, but it seems like overkill for our purposes.
Distance
The distance value is slightly more complicated. What we need to do is flag when the user clicks on the Form’s title bar. Then, whenever the position of the Form on the screen changes, if the flag is set, the position is stored in a list of points. The list of points is a field that is reset whenever the shake-flag is first set.
Once the user lets go off the title bar, we will have a list of all the locations of the Form during the “shake” or whatever other movement was done. We can use the list of points to calculate the average location:
private Point GetAveragePoint(List<Point> points)
{
Point avg = new Point();
foreach (Point p in points)
{
avg.X += p.X;
avg.Y += p.Y;
}
avg.X /= points.Count;
avg.Y /= points.Count;
return avg;
}
By comparing the average location with the current window location, we can determine how far the window moved overall. A low value is good since it means most of the movement happened around a center point, which is a good description of what shaking a window looks like.
Amount
Just how much the window was shaken can be easily gauged by simply checking how many points were stored in the list we used to measure distance.
Shake Event
Now that we have all the technical details on detecting a shake, the only thing left to do is to wrap it up cleanly into an event. The easiest way in my opinion is to create a new class, ShakeForm
, that inherits the regular Form
class.
Our ShakeForm
class overrides WndProc
to keep track of the user interactions with the Form’s position on the screen. Whenever a movement is detected, we process the list of positions traversed to see if they describe a shake. If they do, we trigger our own FormShaken
event.
How do we use this? Any current Windows Form is modified to inherit ShakeForm
instead of Form
. Then, a method is tied to the FormShaken
event like any other Form
event. Since ShakeForm
inherits the regular Form
class, all other Form
properties and events will be intact.
Minimizing and Maximizing
Now, we have a clean way to trigger code when a window is shaken. The second part of implementing Aero Shake is minimizing and maximizing all other applications accordingly. To do this, we will have to rely on some API calls.
The popular method to change the window state of all open windows is to use the SendMessage
and FindWindow
API calls. For example:
IntPtr lHwnd = FindWindow("Shell_TrayWnd", null);
SendMessage(lHwnd, WM_COMMAND, (IntPtr)MIN_ALL, IntPtr.Zero);
The problem, however, is that the code above minimizes the window that was shaken too. Since we want to keep that window as the only one not minimized, we are going to have to minimize each open window one at a time.
The API calls we will use instead are:
GetWindowPlacement
: This will tell us the current window state of other applications.ShowWindow
: This will let us change the window state of other applications.SetForegroundWindow
: This one is simply to make sure our window stays on top when other windows are restored.
The way to go about it is to using the System.Diagnostics.Process
class to get a list of all the currently running processes. For each process, first check that it has a window (since some processes are background workers), then check that it is not the process corresponding to our current window (since we want to exclude it).
For each process that has a window that will be minimized, we store the handle of the window in one of two lists: one for windows that were maximized, and one for windows that were normal state. After we store the handle, we minimize the window with ShowWindow
.
Later, when we want to restore the minimized windows to their previous state, we simply iterate through each of the lists and use ShowWindow
again to restore them either into a normal window state or a maximized window state. Since restoring windows also moves them to the top of the screen, calling SetForegroundWindow
at the end allows us to move the shaken Form back to the front. Why do we not use this.BringToFront()
? Simply because the maximize function is inside a static class in this implementation.
The code will work like Aero Shake in Windows 7 in that if something was minimized prior to shaking, it will be left alone.
The downfall of this approach is that the original window state of the running applications is restored, but the original stacked order is not necessarily kept the same.
Conclusion
As mentioned before, the individual parts of the code are not very complicated. It is the structure of the code that is crucial to making the implementation useful in the future. This also makes improving parts of the Aero Shake easy. For example, you can choose to rewrite the WndProc
code to use .NET events instead, or you might have a better algorithm for detecting shakes, or you might just want to adjust how sensitive the detection is.