Click here to Skip to main content
Click here to Skip to main content

Handling tap-and-hold

, 28 Feb 2003
Rate this:
Please Sign up or sign in to vote.
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)

Share

About the Author

João Paulo Figueira
Software Developer Frotcom International
Portugal Portugal
I work on R&D for Frotcom International, a company that develops web-based fleet management solutions.
Follow on   Twitter   LinkedIn

Comments and Discussions

 
GeneralI can get the PopUp Menu, but how can it respond ON_COMMAND Pinmemberchenjianjack5-Apr-10 17:14 
GeneralNon Windows Mobile Pinmemberfilipe_madureira25-Feb-10 5:12 
GeneralRe: Non Windows Mobile Pinmemberfilipe_madureira26-Feb-10 1:08 
QuestionTAH on Edit Box PinmemberChien-Hung27-Mar-08 7:48 
GeneralRe: TAH on Edit Box PinmemberJoao Paulo Figueira27-Mar-08 8:01 
QuestionSample code illustrating T-A-H using Win32-API? Pinmemberl_d_allan2-Aug-07 18:01 
AnswerRe: Sample code illustrating T-A-H using Win32-API? PinmemberDaveyWavey25-Nov-07 15:45 
GeneralSample code would be greatly appreciated Pinmemberl_d_allan2-Aug-07 10:48 
GeneralRe: Sample code would be greatly appreciated PinmemberJoao Paulo Figueira2-Aug-07 11:55 
GeneralRe: Sample code would be greatly appreciated Pinmemberl_d_allan2-Aug-07 17:56 
GeneralTAH and CComboBox problem Pinmemberl_d_allan2-Aug-07 10:44 
GeneralRe: TAH and CComboBox problem PinmemberJoao Paulo Figueira2-Aug-07 11:52 
GeneralRe: TAH and CComboBox problem Pinmemberl_d_allan2-Aug-07 17:14 
GeneralDLL PocketPC Pinmemberkolokolo_team23-Jun-06 2:28 
QuestionProblems on PPC 2002 with dialog box PinmemberPeterSmithson29-Mar-06 2:17 
AnswerRe: Problems on PPC 2002 with dialog box PinmemberJoão Paulo Figueira29-Mar-06 2:24 
GeneralRe: Problems on PPC 2002 with dialog box PinmemberPeterSmithson29-Mar-06 3:52 
QuestionContext menu on column headings for CListCtrl PinmemberPeterSmithson23-Mar-06 0:09 
AnswerRe: Context menu on column headings for CListCtrl PinmemberJoão Paulo Figueira29-Mar-06 2:25 
AnswerRe: Context menu on column headings for CListCtrl PinmemberJoão Paulo Figueira29-Mar-06 2:29 
GeneralRe: Context menu on column headings for CListCtrl PinmemberPeterSmithson29-Mar-06 4:05 
GeneralNeedd help Pinmemberluckylaeeq30-Jan-06 23:40 
GeneralRe: Needd help PinmemberJoão Paulo Figueira31-Jan-06 5:34 
GeneralRe: Needd help Pinmemberluckylaeeq31-Jan-06 19:30 
GeneralSeem it can NOT work on Windows Mobile 5.0 Pinmembersuper4861-Dec-05 22:26 
GeneralRe: Seem it can NOT work on Windows Mobile 5.0 PinmemberJoão Paulo Figueira1-Dec-05 22:31 
GeneralRe: Seem it can NOT work on Windows Mobile 5.0 Pinmembersuper4861-Dec-05 23:14 
GeneralPls Help PinmemberPasha R14-Sep-05 19:56 
GeneralHelp me Pinmemberthanhthuyvn_vtajkhskjakjd22-Jul-05 14:48 
GeneralRe: Help me PinmemberJoão Paulo Figueira20-Aug-05 0:16 
GeneralMenu pops up 3 times...! PinmemberJor-El24-Feb-05 2:41 
GeneralRe: Menu pops up 3 times...! PinmemberJoão Paulo Figueira24-Feb-05 2:50 
GeneralRe: Menu pops up 3 times...! PinmemberJor-El24-Feb-05 3:30 
GeneralAre you wrong or am I missing something? TRUST MFC PinsussJeremie Weldin8-Jul-04 15:27 
GeneralRe: Are you wrong or am I missing something? TRUST MFC PinmemberJoão Paulo Figueira8-Jul-04 22:43 
GeneralRe: Are you wrong or am I missing something? TRUST MFC Pinmemberjweldin16-Jul-04 18:37 
GeneralProblem with Handling TAP and HOLD Pinsussmurali krishna prasad28-Feb-04 2:05 
GeneralRe: Problem with Handling TAP and HOLD PinmemberJoão Paulo Figueira28-Feb-04 5:55 
GeneralProblem After Menu Selection PinmemberHGREEN327-Nov-03 11:28 
GeneralRe: Problem After Menu Selection PinmemberJoão Paulo Figueira30-Nov-03 6:00 
GeneralRe: Problem After Menu Selection PinmemberHGREEN37-Dec-03 2:47 
GeneralRe: Problem After Menu Selection PinmemberJoão Paulo Figueira7-Dec-03 4:12 
GeneralRe: Problem After Menu Selection PinmemberHGREEN38-Dec-03 0:47 
GeneralPopup menu processing PinmemberLymeric22-Oct-03 16:44 
GeneralRe: Popup menu processing PinmemberJoão Paulo Figueira22-Oct-03 22:44 
GeneralRe: Popup menu processing PinmemberLymeric23-Oct-03 10:03 
GeneralRe: Popup menu processing PinmemberLymeric23-Oct-03 10:50 
QuestionHow can I disable the red circles? PinmemberRafael Leonhardt10-Oct-03 10:16 
AnswerRe: How can I disable the red circles? PinmemberRafael Leonhardt10-Oct-03 10:24 
GeneralPlain Windows CE 3.0 PinsussFiras1-Jun-03 20:39 

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

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

| Advertise | Privacy | Mobile
Web01 | 2.8.140814.1 | Last Updated 1 Mar 2003
Article Copyright 2003 by João Paulo Figueira
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid