Click here to Skip to main content
15,884,353 members
Articles / Mobile Apps / Windows Mobile

Handling tap-and-hold

Rate me:
Please Sign up or sign in to vote.
4.77/5 (20 votes)
28 Feb 2003CPOL3 min read 221.1K   43   72
Tips on how to handle tap-and-hold user commands.

Introduction

This article results from my experience on handling the tap-and-hold operations on Windows CE 3.0 (PocketPC 2002) devices. Documentation on this issue is not always exact nor immediately available. To make matters worse, the support for tap-and-hold provided by MFC 3.0 is flawed.

Tap-and-hold

Broadly speaking, the tap-and-hold (TAH) gesture is used in Windows CE applications as a replacement for the mouse right-click. One can see this in all Microsot-provided applications as a means to bring up a context menu, making its use quite pervasive. The user receives visual feedback from the system when a tap-and-hold operation begins through a number of red circles showing up in a clockwise circular fashion around the place where the user tapped the screen.

Detection and Handling

Detection of a TAH is handled by the SHRecognizeGesture API. It is generally used in the WM_LBUTTONDOWN message handler in either direct mode or notification mode. In direct mode the function returns GN_CONTEXTMENU if it detected a tap-and-hold command, or 0 otherwise. In notification mode, it will send a WM_NOTIFY message with a GN_CONTEXTMENU to the parent window.

Don't Trust MFC

MFC's handling is done in CWnd::OnLButtonDown and, unfortunately, is flawed. If you look in wincore.cpp you see how it is implemented:

BOOL CWnd::SHRecognizeGesture(CPoint point, 
                              BOOL bSendNotification /* = TRUE */)
{
    SHRGINFO shrgi = {0};

    shrgi.cbSize = sizeof(SHRGINFO);
    shrgi.hwndClient = m_hWnd;
    shrgi.ptDown.x = point.x;
    shrgi.ptDown.y = point.y;
    shrgi.dwFlags = SHRG_RETURNCMD;

    if(GN_CONTEXTMENU == ::SHRecognizeGesture(&shrgi))
    {
        if(bSendNotification)
        {
            shrgi.dwFlags = SHRG_NOTIFYPARENT;
            ::SHRecognizeGesture(&shrgi);    // Again???
        }
        return TRUE;
    }
    else
        return FALSE;
}

void CWnd::OnLButtonDown (UINT nState, CPoint point)
{
    if (!SHRecognizeGesture(point))
        Default();
}

That's why all MFC windows respond to TAH commands twice, not once. Worse, it makes all of your windows respond to TAH. As a matter of fact, you may not want this to happen. The solution is quite simple: make sure your OnLButtonDown does not call CWnd's but Default() instead:

void CMyWnd::OnLButtonDown(UINT nState, CPoint point)
{
    Default();
}

Unfortunately, you will have to do this for all the windows that you don't want to handle the TAH command. There are two exceptions, though: CListCtrl and CTreeCtrl, but more on these later.

Generic Handling Example

Let's assume you want to handle TAH on your CWnd-derived window class. One option is:

void CMyWnd::OnLButtonDown(UINT nFlags, CPoint point) 
{
    SHRGINFO shrgi = {0};

    shrgi.cbSize        = sizeof(SHRGINFO);
    shrgi.hwndClient    = m_hWnd;
    shrgi.ptDown.x      = point.x;
    shrgi.ptDown.y      = point.y;
    shrgi.dwFlags       = SHRG_RETURNCMD;

    if(GN_CONTEXTMENU == ::SHRecognizeGesture(&shrgi))
        ContextMenu(point);
    else
        Default();
}

This is the direct mode, where SHRecognizeGesture returns a value that notifies if a TAH happened. Note that I'm using the API version of the method, not CWnd's. If a TAH is detected, the ContextMenu method is called. Here's how it might be implemented:

void CMyCwnd::ContextMenu(CPoint point)
{
    CMenu        mnuCtxt;
    CMenu*        pMenu;
    CWnd*        pWnd;

    if(!m_nMenuID)
        return;

    if(mnuCtxt.LoadMenu(m_nMenuID))
    {
        pWnd = (m_pWndMenu ? m_pWndMenu : AfxGetMainWnd());

        pMenu = 
        mnuCtxt.GetSubMenu(0);
        if(pMenu)
            {

            ClientToScreen(&point);pMenu->TrackPopupMenu(TPM_LEFTALIGN,
                point.x, point.y, pWnd);
        }
    }
}

In this class, m_nMenuID is a UINT that holds the context menu's ID. If NULL, no menu is shown. The m_pWndMenu member variable holds a pointer to the command-processing window. This is useful if you want to show TAH context menus on controls placed in dialogs. In these situations you cannot use AfxGetMainWnd() because your menu commands might be either grayed, or worse, handled by another (hidden) window. So, m_pWndMenu will have the containing CDialog pointer.

The same code may be used in notification mode (see next section).

Note: The way OnLButtonDown was implemented is not mandatory. In some situations you may have to always call Default(), depending on the underlying window functionality.

CListCtrl and CTreeCtrl

These are two different beasts, because they do implement the correct TAH functionality at the system level. Unfortunately, MFC's encapsulation destroys it. So, unlike other windows, if you want these two to have a correct TAH behaviour, call Default() on OnLButtonDown. Intuitive, right? These controls' TAH implementation use the notification method, so there are a couple of other things to do. Here is a sample for a CListCtrl-derived class:

BEGIN_MESSAGE_MAP(CMyListCtrl, CListCtrl)
    //{{AFX_MSG_MAP(CMyListCtrl)
    ON_WM_LBUTTONDOWN   ()
    ON_WM_LBUTTONUP     ()
    ON_NOTIFY_REFLECT   (GN_CONTEXTMENU,    OnListContextMenu)
    .
    .
    .
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()


// CMyListCtrl::OnListContextMenu
//
//        Handles the list context menu
//
void CMyListCtrl::OnListContextMenu(NMHDR* pNMHDR, LRESULT* pResult)
{
    CMenu        mnuCtxt;
    CMenu*        pMenu;
    CWnd*        pWnd;
    NMRGINFO*    pInfo;

    if(!m_nLstMenu)
        return;

    if(mnuCtxt.LoadMenu(m_nLstMenu))
    {
        pWnd = (m_pWndMenu ? m_pWndMenu : AfxGetMainWnd());

        pMenu = mnuCtxt.GetSubMenu(0);
        if(pMenu)
        {
            UINT    uFlags;
            CPoint    pt;

            pInfo = (NMRGINFO*)pNMHDR;
            pt = pInfo->ptAction;

            ScreenToClient(&pt);
            m_iItemOnMenu =  HitTest(pt, &uFlags); 
            // 
            // Signal this is a tap and hold operation  
            //

            m_bTapAndHold = RUE;
            pMenu->TrackPopupMenu(TPM_LEFTALIGN, 
                                  pInfo->ptAction.x,pInfo->ptAction.y, pWnd);
        }
    }
}


// CMyListCtrl::OnLButtonDown
//
//        Special handler for WM_LBUTTONDOWN.
//        Check for a tap and hold gesture. Never trust MFC...
//
void CMyListCtrl::OnLButtonDown(UINT nFlags, CPoint point) 
{
    m_bTapAndHold = FALSE;

    Default();
}


// CMyListCtrl::OnLButtonUp
//
//        The user released the stylus
//
void CMyListCtrl::OnLButtonUp(UINT nFlags, CPoint point) 
{
    CListCtrl::OnLButtonUp(nFlags, point);

    m_bTapAndHold = FALSE;
}

The concept is similar, with two exceptions. First, we store the TAH status in the boolean m_bTapAndHold variable. Second, we store the clicked item on the integer m_iItemOnMenu.

The first may be use when processing NM_CLICK notifications. In these notifications it is impossible to know if the user simply clicked the control, or if she is issuing a TAH command. By testing the m_bTapAndHold we can implement the same behaviour of the Contacts application, where a single click means edit, and a TAH means context menu.

The second variable may be used by the ON_UPDATE_COMMAND_UI handles to determine what menu options are available. Remember that m_iItemOnMenu will be -1 if the user tapped outside the items area.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Frotcom International
Portugal Portugal
I work on R&D for Frotcom International, a company that develops web-based fleet management solutions.

Comments and Discussions

 
GeneralI can get the PopUp Menu, but how can it respond ON_COMMAND Pin
chenjianjack5-Apr-10 17:14
chenjianjack5-Apr-10 17:14 
GeneralNon Windows Mobile Pin
filipe_madureira25-Feb-10 5:12
filipe_madureira25-Feb-10 5:12 
GeneralRe: Non Windows Mobile Pin
filipe_madureira26-Feb-10 1:08
filipe_madureira26-Feb-10 1:08 
QuestionTAH on Edit Box Pin
Chien-Hung27-Mar-08 7:48
Chien-Hung27-Mar-08 7:48 
GeneralRe: TAH on Edit Box Pin
João Paulo Figueira27-Mar-08 8:01
professionalJoão Paulo Figueira27-Mar-08 8:01 
QuestionSample code illustrating T-A-H using Win32-API? Pin
l_d_allan2-Aug-07 18:01
l_d_allan2-Aug-07 18:01 
Is anyone aware of a link providing a working evc3/evc4/vs2005 project that implements Tap-And-Hold using the Win32 API?

TAH seems like one of those functionalities where there are a number of different "pieces" that have to be "knit together" just right to get it to work.

AnswerRe: Sample code illustrating T-A-H using Win32-API? Pin
DaveyWavey25-Nov-07 15:45
DaveyWavey25-Nov-07 15:45 
GeneralSample code would be greatly appreciated Pin
l_d_allan2-Aug-07 10:48
l_d_allan2-Aug-07 10:48 
GeneralRe: Sample code would be greatly appreciated Pin
João Paulo Figueira2-Aug-07 11:55
professionalJoão Paulo Figueira2-Aug-07 11:55 
GeneralRe: Sample code would be greatly appreciated Pin
l_d_allan2-Aug-07 17:56
l_d_allan2-Aug-07 17:56 
GeneralTAH and CComboBox problem Pin
l_d_allan2-Aug-07 10:44
l_d_allan2-Aug-07 10:44 
GeneralRe: TAH and CComboBox problem Pin
João Paulo Figueira2-Aug-07 11:52
professionalJoão Paulo Figueira2-Aug-07 11:52 
GeneralRe: TAH and CComboBox problem Pin
l_d_allan2-Aug-07 17:14
l_d_allan2-Aug-07 17:14 
GeneralDLL PocketPC Pin
kolokolo_team23-Jun-06 2:28
kolokolo_team23-Jun-06 2:28 
QuestionProblems on PPC 2002 with dialog box Pin
PeterSmithson29-Mar-06 2:17
PeterSmithson29-Mar-06 2:17 
AnswerRe: Problems on PPC 2002 with dialog box Pin
João Paulo Figueira29-Mar-06 2:24
professionalJoão Paulo Figueira29-Mar-06 2:24 
GeneralRe: Problems on PPC 2002 with dialog box Pin
PeterSmithson29-Mar-06 3:52
PeterSmithson29-Mar-06 3:52 
QuestionContext menu on column headings for CListCtrl Pin
PeterSmithson23-Mar-06 0:09
PeterSmithson23-Mar-06 0:09 
AnswerRe: Context menu on column headings for CListCtrl Pin
João Paulo Figueira29-Mar-06 2:25
professionalJoão Paulo Figueira29-Mar-06 2:25 
AnswerRe: Context menu on column headings for CListCtrl Pin
João Paulo Figueira29-Mar-06 2:29
professionalJoão Paulo Figueira29-Mar-06 2:29 
GeneralRe: Context menu on column headings for CListCtrl Pin
PeterSmithson29-Mar-06 4:05
PeterSmithson29-Mar-06 4:05 
GeneralNeedd help Pin
luckylaeeq30-Jan-06 23:40
luckylaeeq30-Jan-06 23:40 
GeneralRe: Needd help Pin
João Paulo Figueira31-Jan-06 5:34
professionalJoão Paulo Figueira31-Jan-06 5:34 
GeneralRe: Needd help Pin
luckylaeeq31-Jan-06 19:30
luckylaeeq31-Jan-06 19:30 
GeneralSeem it can NOT work on Windows Mobile 5.0 Pin
super4861-Dec-05 22:26
super4861-Dec-05 22:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.