[FAQ][VC++]Comment créer un bouton exécutant une action en continue à l'état enfoncé
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
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à :D