Nous voulons créer un bouton qui lorsque on le maintient enfoncé, il exécute une action jusqu'à ce que ce dernier soit relaché.
Pour y parvenir nous allons définir une classe personnalisée de CButton ou bien de CMFCButton (si nous voulons des fonctionnaliés en plus pour notre bouton). Mais CButton suffit pour ce que nous recherchons.

Voici la classe :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 
class CStayPressedButton : public CButton
{
	DECLARE_DYNAMIC(CStayPressedButton)
 
public:
	CStayPressedButton();
	virtual ~CStayPressedButton();
 
	LPVOID m_pThreadUserData;
	AFX_THREADPROC m_pUserActionThreadProc;
 
public:
	static UINT CALLBACK ThreadProc(LPVOID lParam);
	HANDLE m_hFinished;
 
	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
	afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
	afx_msg void OnMouseMove(UINT nFlags, CPoint point);
};
1. Lorsque l'on click sur le bouton la méthode OnLButtonDown() est appelée et dans cette dernière un thread exécutant la tâche en continue est automatique lancé.
2. Lorsque l'on relache le bouton la méthode OnLButtonUp() signale au thread qu'il doit stopper.
3. Lorsque la souris se trouvant à l'état enfoncé et sort du rectangle englobant du bouton, la méthode OnMouseMove() signale également que le thread doit stopper.
4. m_pThreadUserData contient les données que vous voulez passer au thread
5. m_pUserActionThreadProc pointe sur la méthode qui est le point d'entrée de votre thread
6. ThreadProc() est le thread lancé en interne par CStayPressedButton
7. m_hFinished est un handle pour signaler l'évènement d'arrêt du thread

Dans votre boite de dialogue vous ajoutez un bouton sur votre resource et vous lui mappez une variable CButton que vous remplacer en CStayPressedButton
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
 
class CMyDlg : public CDialog
{
// ...
             CStayPressedButton m_btnStayPressed;  // bouton resté pressé = action en continue
 
	int m_i; // déclaration de votre donnée personnelle à passer à pThreadUserData
	static UINT UserActionThreadProc(LPVOID pThreadUserData);  // votre méthode static s'exécutant en continue
//...
8. UserActionThreadProc est la méthode qui s'exécutera en boucle dans le thread de CStayPressedButton::ThreadProc()
9. m_i est un exemple d'une de vos variables qui sera passé à pThreadUserData dans votre méthode CMyDlg::UserActionThreadProc()
10. m_btnStayPressed est notre Stay Pressed Button.

11. Dans le CMyDlg::OnInitDialog() je prépare le CStayPressedButton
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
 
	m_i = 2;  // votre données personnelles
	m_btnStayPressed.m_pThreadUserData = (LPVOID) &m_i;
	m_btnStayPressed.m_pUserActionThreadProc = (AFX_THREADPROC)UserActionThreadProc;
12. Votre méthode en continue
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
 
UINT CMyDlg::UserActionThreadProc(LPVOID pThreadUserData)
{
 
             int i = *((int*)pThreadUserData);  // exemple d'utilisation
	// work, work, work still working on stay pressed button state...
	// put your code here in the thread loop...
	return 0;
}
Et maintenant le code du CStayPressedButton

13. Code d'initialisation :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
IMPLEMENT_DYNAMIC(CStayPressedButton, CButton)
 
CStayPressedButton::CStayPressedButton()
{
	m_pThreadUserData = NULL;
	m_pUserActionThreadProc = NULL;
	m_hFinished = ::CreateEvent(NULL, FALSE, FALSE, NULL);
}
 
CStayPressedButton::~CStayPressedButton()
{
	if(m_hFinished)
		::CloseHandle(m_hFinished);
}
14. Tables des messages
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
 
BEGIN_MESSAGE_MAP(CStayPressedButton, CButton)
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()
15. Quand le bouton gauche de la souris est enfoncé l'action est lancé :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
void CStayPressedButton::OnLButtonDown(UINT nFlags, CPoint point)
{ 
	if(m_pUserActionThreadProc == NULL)
	{
		CButton::OnLButtonDown(nFlags, point);
		return;
	}
 
	SetCapture();
 
	AfxBeginThread((AFX_THREADPROC)ThreadProc, (LPVOID) this);
 
	CButton::OnLButtonDown(nFlags, point);
}
16. Quand le bouton gauche de la souris est relaché l'action est stoppé:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
 
void CStayPressedButton::OnLButtonUp(UINT nFlags, CPoint point)
{
	if(GetCapture())
	{
		::SetEvent(m_hFinished);
		ReleaseCapture();
	}
 
	CButton::OnLButtonUp(nFlags, point);
}
17. Si la souris sort du rectangle du bouton l'action est aussi stoppé:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
void CStayPressedButton::OnMouseMove(UINT nFlags, CPoint point)
{
	if(GetCapture())
	{
		CRect rect;
		GetClientRect(&rect);
 
		if( !rect.PtInRect(point) )
		{
			::SetEvent(m_hFinished);
			ReleaseCapture();
		}
	}
	CButton::OnMouseMove(nFlags, point);
}
18. Le thread du bouton
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
UINT CALLBACK CStayPressedButton::ThreadProc(LPVOID pParam)
{
	CStayPressedButton* pBtn = (CStayPressedButton*)pParam;
	ASSERT_KINDOF(CStayPressedButton, pBtn);
 
	do
	{
		pBtn->m_pUserActionThreadProc(pBtn->m_pThreadUserData);
	}
	while(::WaitForSingleObject(pBtn->m_hFinished, 0) != WAIT_OBJECT_0);
 
	return 0;
}
Et Voilà