|
||
|
Adding Mouse Gesture Support to .NET Windows ApplicationsEnhancing usability in mouse-driven applications Use of mouse gestures to control application is becoming increasingly common in the more sophisticated web browsers. This sample demonstrates how you can support a range of mouse gestures in .NET Windows Forms Applications by implementing an IMessageFilter. VB.NET and C# code provided. About Mouse GesturesMost Mac users would agree - having a right mouse button is a great thing. But you'll notice it doesn't get to do that much in Windows applications except popping up a context menu when its released. The idea of mouse gestures gives the button something more to do whilst its held down. The idea is that if you want a popup menu, you're unlikely to move the mouse significantly until you release the button. If you do, then the movement is a candidate to be interpreted as a "gesture". In theory, you could have an unlimited number of gestures of arbitrary complexity, for example, recognising when someone draws out a picture of an octopus rather than a squid with the mouse. However, in practice gestures are most useful there aren't very many of them and they're nice and simple. The figure below shows some of the sort of gestures users find easy to get used to and aren't too challenging to recognise: Even this set of gestures is more than you would normally want to support in a single application, as it is easy to forget how to draw a North then East gesture as opposed to East then North, unless you one of the alpha-males with the type of spacial intelligence all males of the species are supposedly automatically inbued with but somehow I never got. Typically, you might want to support the four main directions (left and right can represent back and next, whilst up and down can be used for closing and opening windows) and one or two of the compound gestures. Message Loops and Their UsesThe .NET Framework System.Windows.Forms namespace includes one feature that I'd always wanted in VB: access to the Message Loop for an application. All Windows applications include a single message loop. You can read more about the message loop in the MSDN C++ Q&A article "Sending Messages in Windows, Adding Hot Keys to your Application" by Paul DiLascia. Fundamentally, Windows applications work on the basis of messages. Whenever Windows tells your application its Window needs to be repainted, or when a mouse event occurs, a message is either sent or posted to your application. Messages that are sent are direct calls to the WndProc function of a particular Window and must be acted upon straight away. Messages that are posted are intended to be picked up whenever the application has time to process them, and are directed into a queue. A Windows application receives messages from this queue through its message loop function. Since there is only one message loop for an application, it is a great place to pick up on messages regardless of which Window or Control they have been directed at. In the case of processing Mouse Gestures, this is a requirement since you normally want to be able to act on a Mouse Gesture regardless of which window or control the mouse is drawing over when the gesture occurs. The other thing about the message loop is that if you can filter out a message from the queue rather than dispatching it as normal then to the application it looks like the event never occurred. Working with the Message Loop in .NETThe .NET Framework supports reading the Message Loop by writing an object which extends the IMessageFilter interface. This interface has one method, PreFilterMessage which provides you with the message that has just been received and you can return either false to continue processing the message or true to filter the message out. To install the filter, you use the static (shared in VB.NET) AddMessageFilter method of the Application object. To uninstall it when you are finished, call corresponding RemoveMessageFilter method. Note that whilst the documentation is peppered with warnings about not doing too much in the message filter to prevent performance degradation, you don't really need to worry about this too much (obviously you don't want to search your hard drives for all MP3 files during each message, but realistically it shouldn't be a problem). Implementing a MouseGesture recognizerGiven that information, implementing a MouseGesture has the following stages:
Note there is a slight catch with the last stage. If you filter a mouse up message, Windows is thrown into a slight state of confusion since it still thinks the mouse is captured by the control or form the mouse was initially pressed over. This means it ignores the next mouse down. To resolve this, we need to tell Windows that the mouse has been released, but since we don't want it to cause a right-click action the mouse up needs to appear to occur outside the boundaries of the original control or form. Unfortunately, it is not possible to modify a message received through the message filter. The alternative performed here is to consume the original mouse up and then post a new one to the same window at an off-screen location. Configuring Available Mouse GesturesFirst, the available mouse gestures which can be recognized are configured as a series of flags so the class can be configured to only pick up certain gestures: /// Creating the Basic FilterThe basic filter checks the message for right button down, tracks the mouse movement when it is and checks the gesture when the mouse is released, as well as posting the mouse up if it is needed: /// BeginMouseGesture simply initiates the gestures ArrayList to a new instance and sets the tracking flag whilst AddToMouseGesture adds the mouse position to gestures. Implemented Gesture RecognitionThe last step is to determine which mouse gesture was performed. The code does this by detecting how far the mouse moved horizontally and vertically, then checking whether most of the travel occurred vertically or horizontally during that motion: /// This code typically generates the correct results. A better alternative to the deterministic algorithms shown above would be to use Fuzzy Logic to determine which gesture was the most likely. This is the subject of a future article, in the meantime information about implementing Fuzzy Logic please go to CodeProject and search on the author "pseudonym67". Using the MouseGestureFilter classThe demonstration application shows how to use the filter. Basically you need code that instantiates the class, installs the filter and responds to the MouseGesture event: using vbAccelerator.Components.Win32; MouseGestureFilter mgf = new MouseGestureFilter(); Application.AddMessageFilter(mgf); mgf.MouseGesture += new MouseGestureEventHandler( frmMouseGesture_MouseGesture); private void frmMouseGesture_MouseGesture( object sender, MouseGestureEventArgs args) { // Process the gesture here, args.GestureType contains // the gesture that was performed. args.AcceptGesture = true; } ConclusionThis article provides a reusable class you can add to your application for recognising Mouse Gestures. It also demonstrates how you can use the Message Loop to process mouse events regardless of which form or control they are intended for, which can be a very useful technique.
|
|
|