Share via


Exercise 1: Build a Multitouch Gesture Application

Task 1 – Create the MFC application using the Application Wizard

  1. Start Visual Studio 2010
  2. Create a new MFC application project with the name TouchGestureDemo and press OK

  3. In the Application Type dialog of the MFC Application Wizard, select Single Document. To keep the application simple, select (or deselect) the options in the various dialogs of the MFC Application Wizard based on the screenshots below and then press Next > after each dialog.
  4. Continue clicking Next until you finally hit Finish on the Generated Classes wizard dialog:

Task 2 – Test the Existence and Readiness of Multitouch Hardware

  1. The application that we are building requires touch enabled hardware, so we need to check for this in the application. In TouchGestureDemo.cpp, add the following code at the end of CTouchGestureDemoApp::InitInstance():BYTE digitizerStatus = (BYTE) GetSystemMetrics(SM_DIGITIZER);
    if ((digitizerStatus & (0x80 + 0x40)) == 0) //Stack Ready + MultiTouch
    {
    AfxMessageBox(L"No touch input is currently available.");
    return FALSE;
    }

    BYTE nInputs = (BYTE) GetSystemMetrics(SM_MAXIMUMTOUCHES);

    CString str;
    str.Format(L"Touch input available with %d touch points.", nInputs);
    AfxMessageBox(str);

    return TRUE;
  2. You can see that besides checking for touch availability, we also find out the number of touch inputs that the hardware support.
  3. Build and run.
  4. Depending on the number of touch inputs you have on your machine, you should see output similar to the following screenshot:

Task 3 – Add the Drawing Object Source and Header Files to the Project, and Draw the Rectangle

In the Starter folder, you will find two files: DrawingObject.h and DrawingObject.cpp. Copy them to the project folder and use “Add Existing item…” to add them to the project.

  1. Add an #include DrawingObject.h line near the at the end of stdafx.h header file, just after #include <afxcontrolbars.h>: #include "DrawingObject.h"
  2. Add the following protected member variable definition to ChildView.h. This is the main drawing object: // The drawing object
    CDrawingObject m_drawingObject;
  3. In addition, add the following protected member variables to ChildView.h. These are needed to support positioning and sizing of the drawing object: // Needed for drawing object position and size calculations
    double m_dblZoomRatioStart;
    double m_dblZoomRatioTotal;
    CPoint m_ptCenter;
  4. To draw the rectangle, add the following line in ChildView.cpp at the end of CChildCiew::OnPaint(): m_drawingObject.Paint(&dc);
  5. When the window is resized, we want to reset the drawing rectangle to its default location and size (making it easy to find it, if it moves outside the client area boundaries), so let’s add a WM_SIZE message handler.
  6. Switch to the Class View (tab at the bottom of the Solution Explorer window) and Select the CChildView class.
  7. In the Properties page, go to the Messages property sheet (by pressing icon) and navigate to WM_SIZE, then add the OnSize() message handler from the drop down box:

  8. Inside the CChildView::OnSize() handler (after the TODO comment) append the following code to reset the drawing object: m_drawingObject.ResetObject(cx, cy);
  9. Build and run.
  10. After responding to the initial message about the availability of touch inputs, a red rectangle should appear in the middle of the window:

Task 4 – Touch Now!

It’s time to start! By default a touch-enabled system provides WM_GESTURE messages to the target window. This is somewhat similar to mouse and keyboard messages. The system consumes the low-level touch input events and calculates the resulting gesture for us. MFC provides a number of message handler overrides that can receive each of the gesture types and each returns a Boolean value. If a gesture input is processed by the application, the corresponding override should return TRUE; otherwise it returns FALSE.

  1. Let’s move! Add the following five protected method definitions to ChildView.h:// Overrides
    protected:

    // Gesture handlers
    virtual BOOL OnGestureZoom(CPoint ptCenter, long lDelta);
    virtual BOOL OnGesturePan(CPoint ptFrom, CPoint ptTo);
    virtual BOOL OnGestureRotate(CPoint ptCenter, double dblAngle);
    virtual BOOL OnGesturePressAndTap(CPoint ptFirstFinger, long lDelta);
    virtual BOOL OnGestureTwoFingerTap(CPoint ptCenter);
  2. Starting with the Pan Gesture handler, add this code to ChildView.cpp:BOOL CChildView::OnGesturePan(CPoint ptFrom, CPoint ptTo)
    {
    int dx = ptTo.x - ptFrom.x;
    int dy = ptTo.y - ptFrom.y;

    if (dx != 0 || dy != 0)
    {
    m_drawingObject.Move(dx, dy);
    RedrawWindow();
    }
    return TRUE;
    }
  3. Now, the Zoom Gesture implementation, add this code to ChildView.cpp:BOOL CChildView::OnGestureZoom(CPoint ptCenter, long lDelta)
    {
    if ((m_pCurrentGestureInfo->dwFlags & GF_BEGIN) == GF_BEGIN)
    {
    m_dblZoomRatioStart = m_dblZoomRatioTotal = lDelta;
    }
    else if (lDelta != 0)
    {
    m_dblZoomRatioTotal += lDelta;
    double zoomFactor = (double)m_dblZoomRatioTotal / m_dblZoomRatioStart;

    m_drawingObject.Zoom(zoomFactor, ptCenter.x, ptCenter.y);

    m_dblZoomRatioStart = m_dblZoomRatioTotal;
    RedrawWindow();
    }

    return TRUE;
    }
  4. Add the Rotate Gesture handler in ChildView.cpp:BOOL CChildView::OnGestureRotate(CPoint ptCenter, double dblAngle)
    {
    if ((m_pCurrentGestureInfo->dwFlags & GF_BEGIN) == GF_BEGIN)
    {
    // Make the first center, the rotating one
    m_ptCenter = ptCenter;
    }
    else if (dblAngle != 0.)
    {
    m_drawingObject.Rotate(dblAngle * PI / 100.0, m_ptCenter.x, m_ptCenter.y);
    RedrawWindow();
    }

    return TRUE;
    }
  5. Similarly, add the handlers for the “Press and Tap” and “Two Finger Taps” Gestures in ChildView.cpp:BOOL CChildView::OnGesturePressAndTap(CPoint ptFirstFinger, long lDelta)
    {
    if ((m_pCurrentGestureInfo->dwFlags & GF_BEGIN) != 0)
    {
    m_drawingObject.ShiftColor();
    RedrawWindow();
    }

    return TRUE;
    }

    BOOL CChildView::OnGestureTwoFingerTap(CPoint ptCenter)
    {
    m_drawingObject.TogleDrawDiagonals();
    RedrawWindow();

    return TRUE;
    }
  6. Build and run.
  7. Try to move the rectangle with two fingers; you can see that it follows your fingers’ movement.
  8. Try to move the rectangle without touching it; instead touch the screen in an empty area of the Window. It moves! We didn’t perform “hit testing” to check if the touch location is inside the rectangles’ boundaries. We respond to any gesture within the whole client area.
  9. Try using the remaining gestures as well: Rotate, Zoom, Two Finger Tap and Finger Roll.

Task 5 – There Is a Bug!

  1. Try to rotate the rectangle. What happened? By default, a Window receives all gestures except rotation. However, we can configure the touch engine to supply any gestures we want. MFC provides a class that can help, CGestureConfig, which allows you to customize Windows gesture features such as zoom, pan or rotate.
  2. Add this member variable to ChildView.h:// Fields
    protected:
    // Holds gesture configuration
    CGestureConfig m_gestureConfig;
  3. Switch to the Class View and Select the CChildView class. In the Properties page, go to the Messages property sheet and navigate to WM_CREATE, then add the OnCreate() message handler from the drop down box:

  4. Inside the CChildView::OnCreate() handler (after the TODO comment) append the following code to enable receiving the rotation gesture: GetGestureConfig(&m_gestureConfig);

    // Only rotation is not enabled by default
    m_gestureConfig.EnableRotate();

    SetGestureConfig(&m_gestureConfig);
  5. Build and run.
  6. Try to rotate the rectangle. It works! Well done!
  7. Here is a screenshot for the demo in action, after “Rotation”, “Press and Tap” and “Two Finger Taps” gestures have all been applied: