FAQ VC++ et MFCConsultez toutes les FAQ

Nombre d'auteurs : 20, nombre de questions : 546, dernière mise à jour : 5 avril 2013  Ajouter une question

 

Cette faq a été réalisée pour répondre aux questions les plus fréquement posées sur le forum Développement Visual C++

Je tiens à souligner que cette faq ne garantit en aucun cas que les informations qu'elle contient sont correctes ; Les auteurs font le maximum, mais l'erreur est humaine. Si vous trouvez une erreur, ou si vous souhaitez devenir redacteur, lisez ceci.

Sur ce, je vous souhaite une bonne lecture. Farscape


SommaireContrôlesNotifications ,Messages (13)
précédent sommaire suivant
 

En utilisant la macro ON_CONTROL_RANGE (voir documentation MSDN pour description) et en indiquant une plage d'indices consécutifs
Exemple d'implémentation :
Dans le .h

Code c++ : Sélectionner tout
1
2
3
4
5
  
DECLARE_MESSAGE_MAP()  
 virtual void OnRangeUpdateEdit(UINT nID);  
 virtual void OnRangeUpdateCheck(UINT nID);  
 virtual void OnRangeUpdateRadio(UINT nID);
Dans le .cpp

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  
BEGIN_MESSAGE_MAP(CMyDlg, CDialog)  
 //{{AFX_MSG_MAP(CMyDlg)  
 //}}AFX_MSG_MAP  
ON_CONTROL_RANGE(EN_CHANGE, IDC_EDIT1, IDC_EDIT3, OnRangeUpdateEdit)  
ON_CONTROL_RANGE(BN_KILLFOCUS , IDC_CHECK1, IDC_CHECK3, OnRangeUpdateCheck) 
ON_CONTROL_RANGE(BN_CLICKED , IDC_RADIO1, IDC_RADIO4, OnRangeUpdateRadio) 
// .................  
void CMyDlg::OnRangeUpdateEdit(UINT nID)  
{  
   if(SendDlgItemMessage(nID, EM_GETMODIFY, 0, 0))  
   {  
      // c'est modifier  
   }  
   else  
   {  
      // non  
   }  
} 
  
void CMyDlg::OnRangeUpdateCheck(UINT nID)  
{  
  // Récupération de l'état pour la check box nID.  
  LRESULT lResult = SendDlgItemMessage(nID, BM_GETSTATE, 0, 0);  
  
 if ( lResult == BST_CHECKED )  
 {  
// la check est cochée .  
 }  
  
}  
  
void CMyDlg::OnRangeUpdateRadio(UINT nID)  
{  
   // par exemple je decide uniquement une reaction sur IDC_RADIO2.  
   if ( nID == IDC_RADIO2 )  
  {  
 // action   
  }  
}

Mis à jour le 5 avril 2013 farscape

Comme on ne peut pas mettre à l'avance avec ClassWizard la notification du click sur le(s) bouton(s) il faudra le faire manuellement en utilisant la macro : ON_CONTROL_RANGE qui permet de récupérer les notifications de messages en provenance des contrôles pour une plage d'identificateurs.
Imaginons que la plage dynamique des Id des boutons soit de 1000 à 2000 on aura :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// .h 
// Generated message map functions 
protected: 
//{{AFX_MSG(CTestMDIView) 
//}}AFX_MSG 
void OnRangeClickButton(UINT nID); 
DECLARE_MESSAGE_MAP() 
}; 
  
// .cpp 
BEGIN_MESSAGE_MAP(CTestMDIView, CFormView) 
//{{AFX_MSG_MAP(CTestMDIView) 
//}}AFX_MSG_MAP 
ON_CONTROL_RANGE(BN_CLICKED , 1000,2000, OnRangeClickButton) 
END_MESSAGE_MAP() 
  
void CTestdlgBarMDIView::OnRangeClickButton(UINT nID) 
{    
       TRACE1("Click on button :%d\n\r",nID); 
}

Mis à jour le 5 avril 2013 farscape

Il ne faut pas oublier que l'on peut aussi appeler directement la fonction associée à la notification click du bouton ….
Mais si ce n'est pas possible il faudra envoyer un message directement au bouton :

Code c++ : Sélectionner tout
1
2
3
  
GetDlgItem(IDC_BUTTON1)->PostMessage(WM_KEYDOWN,' ');  
GetDlgItem(IDC_BUTTON1)->PostMessage(WM_KEYUP,' ');

Mis à jour le 5 avril 2013 farscape

Dans certaines situations, il est parfois nécessaire de pouvoir envoyer un message privé à une fenêtre .
L'exemple typique c'est la communication ou le rafraîchissement d'une fenêtre à partir d'un thread de travail.
Exemple :
Insertion d'un message privé WM_TESTdans la classe fenêtre CSdisamplesView.

Comment procéder :
Définir le message privé :

Code c++ : Sélectionner tout
1
2
  
#define WM_TEST WM_USER+100
WM_USER correspond à la constante de départ pour les messages privés.

Implémenter la fonction de réponse au message :
Celle-ci doit être insérée manuellement comme dans l'exemple ci-dessous :

Dans l'include de la classe fenêtre concernée :

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 
#define WM_TEST WM_USER+100 
 
class CSdisamplesView : public CFormView  
{ 
protected: // create from serialization only 
CSdisamplesView(); 
DECLARE_DYNCREATE(CSdisamplesView) 
 
public: 
//…………… 
// Generated message map functions 
protected: 
//{{AFX_MSG(CSdisamplesView) 
//}}AFX_MSG 
// fonction de réponse pour le message privé 
long OnReceiveTest(WPARAM wparam,LPARAM lparam);  
DECLARE_MESSAGE_MAP()
Dans l'include placer la définition de la fonction en dehors des balises AFX_MSG Dans le code:
rajouter la macro ON_MESSAGE comme ci-dessous :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  
BEGIN_MESSAGE_MAP(CSdisamplesView, CFormView) 
//{{AFX_MSG_MAP(CSdisamplesView) 
//}}AFX_MSG_MAP 
// Standard printing commands 
ON_COMMAND(ID_FILE_PRINT, CFormView::OnFilePrint) 
ON_COMMAND(ID_FILE_PRINT_DIRECT, CFormView::OnFilePrint) 
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CFormView::OnFilePrintPreview) 
ON_MESSAGE(WM_TEST, OnReceiveTest)  // message privé  
END_MESSAGE_MAP() 
  
long CSdisamplesView::OnReceiveTest(WPARAM wparam, LPARAM lparam) 
{ 
AfxMessageBox("coucou"); 
return 0L; 
}
pour appeler la fonction il suffira d'utiliser PostMessage :pas d'attente se place dans la file d'attente des messages.
ou SendMessage :traitement immédiat et attente d'une réponse.

Exemple: appel à partir de la classe où est défini le message:

Code c++ : Sélectionner tout
1
2
3
4
5
6
  
//------------------------------------------------------------------- 
void CSdisamplesView::OnButtonUp(NMHDR* pNMHDR, LRESULT* pResult) 
{ 
PostMessage(WM_TEST); 
}
Appel à partir d'un Thread de travail initié par la classe Fenêtre :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
  
void CSdisamplesView::OnButton1()  
{ 
// TODO: Add your control notification handler code here             
// le handle de fenetre est passé en arguement par GetSafeWnd(). 
AfxBeginThread(TheThread,GetSafeHwnd(),THREAD_PRIORITY_NORMAL) ; 
}
Le Thread:

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
  
UINT TheThread(LPVOID pParam) 
{ 
 HWND hWnd= reinterpret_cast< HWND >( pvParam) ; 
 ::PostMessage(hWnd,WM_TEST,0,0) ; 
  
return 0 ; 
}

Mis à jour le 19 septembre 2005 farscape

A l'instar de la librairie QT qui permet de définir dynamiquement une fonction de réponse à un message en provenance d'un contrôle, je propose la définition d'un mécanisme semblable avec les MFC.

Sous QT on peut trouver ce genre de syntaxe :

Code c++ : Sélectionner tout
1
2
  
connect(button,SIGNAL(clicked()),SLOT(action()));
Ou clicked est le type d'événement intercepté et action la fonction de réponse utilisateur.

Je propose la syntaxe suivante en MFC :

Code c++ : Sélectionner tout
1
2
  
connect(IDC_BUTTON1,SIGNAL(CTestQTMsgView::clicked),SLOT(CTestQTMsgView::action));
Où IDC_BUTTON1 est l'identifiant du bouton.
CTestQTMsgView::clicked et CTestQTMsgView::action correspondent à l'adresse de fonctions non statique à la classe.
En fait cette syntaxe repose sur la possibilité d'avoir un pointeur sur une fonction non statique à une classe.
Cette possibilité souvent méconnue est utilisée par les macros MFC des Messages Map.
Un exemple ci-dessous issu de MSDN :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
  
include <iostream.h> 
  
class Data 
{ 
private: 
   int y; 
   static int x; 
  
public: 
   void SetData(int value) {y = value; return;}; 
   int GetData() {return y;}; 
   static void SSetData(int value) {x = value; return;}; 
   static int SGetData() {return x;}; 
}; 
  
int Data::x = 0; 
  
void main(void) 
{ 
   Data mydata, mydata2; 
  
   // Initialize pointer. 
   void (Data::*pmfnP)(int) = &Data::SetData; // mydata.SetData; 
  
   // Initialize static pointer. 
   void (*psfnP)(int) = &Data::SSetData; 
  
   mydata.SetData(5); // Set initial value for private data. 
   cout << "mydata.data = " << mydata.GetData() << endl; 
  
   (mydata.*pmfnP)(20); // Call member function through pointer. 
   cout << "mydata.data = " << mydata.GetData() << endl; 
  
   (mydata2.*pmfnP)(10) ; // Call member function through pointer. 
   cout << "mydata2.data = " << mydata2.GetData() << endl; 
  
   (*psfnP)(30) ; // Call static member function through pointer. 
   cout << "static data = " << Data::SGetData() << endl ; 
}
Pour disposer de l'adresse de la fonction il suffit d'indiquer le nom de la classe suivis de :: .
L'appel de la fonction se fait par l'intermédiaire de l'objet.
Revenons à notre exemple qui donne ceci :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  
#if !defined(DynMsgMap) 
#define DynMsgMap 
  
#if _MSC_VER > 1000 
#pragma once 
#endif // _MSC_VER > 1000 
  
  
typedef bool (AFX_MSG_CALL CCmdTarget::*DYN_PMSGV)(UINT n); 
typedef int  (AFX_MSG_CALL CCmdTarget::*DYN_PMSGI)(UINT n); 
  
#define SIGNAL(fct) (DYN_PMSGI)& fct 
#define SLOT(fct) (DYN_PMSGV)& fct 
  
template <class DYN_MSG_MAP> 
class CTplDynMsgMap : public DYN_MSG_MAP 
{ 
    public: 
        CTplDynMsgMap( UINT nIDTemplate ):DYN_MSG_MAP(nIDTemplate){} 
  
        struct DynMSgMAP 
        { 
            UINT nMessage;   // windows message 
            UINT nCode;      // control code or WM_NOTIFY code 
            UINT nID;        // control ID (or 0 for windows messages) 
            UINT nLastID;    // used for entries specifying a range of control id's 
            DYN_PMSGV pfn;    // routine to call (or special value) 
        }; 
  
        //------------------------- 
        //etablit le lien entre la fonction de réponse et le contrôle. 
        bool  connect(UINT nIdCtrl,DYN_PMSGI pfnSignal,DYN_PMSGV pfnAction) 
        {       
            return _connect(nIdCtrl,nIdCtrl,pfnSignal,pfnAction);             
        } 
        //------------------------- 
        //etablit le lien entre la fonction de réponse et la plage de contrôles. 
        bool  connect(UINT nIdCtrl,UINT nIdCtrlEnd,DYN_PMSGI pfnSignal,DYN_PMSGV pfnAction) 
        {       
            return _connect(nIdCtrl,nIdCtrlEnd,pfnSignal,pfnAction);             
        } 
        //------------------------- 
        // clic milieu control ou fenetre 
        int     mclicked(UINT nIdCtrl){return WM_MBUTTONDOWN;} 
        //------------------------- 
        // double clic control ou fenetre 
        int     dclicked(UINT nIdCtrl) 
        { 
            return (IsButton(nIdCtrl)?BN_DBLCLK:WM_LBUTTONDBLCLK); 
        } 
        //-------------------------      
        // clic droit control ou fenetre 
        int      rclicked(UINT nIdCtrl){return WM_RBUTTONDOWN;} 
        //------------------------- 
        // clic control 
        int      clicked(UINT nIdCtrl) 
        { 
            return (IsButton(nIdCtrl)?BN_CLICKED:WM_LBUTTONDOWN);          
        } 
        //------------------------- 
        virtual BOOL PreTranslateMessage(MSG* pMsg) 
        {             
            UINT nID =0; 
            nID =::GetDlgCtrlID(pMsg->hwnd); 
            for(int i=0;i<m_aDynMsg.GetSize();i++) 
            {             
                if(m_aDynMsg[i].nCode==pMsg->message && 
                    nID>=m_aDynMsg[i].nID && nID<=m_aDynMsg[i].nLastID) 
                { 
                    if(pMsg->hwnd==m_hWnd) nID=0; 
                    if(!((*this).*m_aDynMsg[i].pfn)(nID)) return FALSE; 
                    break; 
                } 
            }                 
  
            return DYN_MSG_MAP::PreTranslateMessage(pMsg); 
        } 
        //------------------------- 
        virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam)            
        { 
            // crack message parameters 
            UINT nID = LOWORD(wParam);             
            UINT nCode = HIWORD(wParam); 
  
            if(nCode==WM_COMMAND) 
            for(int i=0;i<m_aDynMsg.GetSize();i++) 
            {             
                if(m_aDynMsg[i].nCode==WM_COMMAND && 
                   (nID>=m_aDynMsg[i].nID && nID <= m_aDynMsg[i].nLastID)) 
                { 
                    if(!((*this).*m_aDynMsg[i].pfn)(nID)) return FALSE; 
                    break; 
                } 
            } 
            return DYN_MSG_MAP::OnCommand(wParam,lParam); 
        } 
        //------------------------- 
        // renvoie si nIdCtrl represente un bouton. 
        bool IsButton(UINT nIdCtrl) 
        { 
            char szClassName[50]; 
            if(!nIdCtrl) return false; 
            CWnd *pWnd=GetDlgItem(nIdCtrl); 
            if(!pWnd) return false; 
            ::GetClassName(pWnd->GetSafeHwnd(),szClassName,sizeof(szClassName)); 
            return(!strcmp("Button",szClassName));             
        } 
        //------------------------- 
  
        private:         
            CArray < struct DynMSgMAP ,struct DynMSgMAP > m_aDynMsg; // memorisation des liens. 
  
        bool  _connect(UINT nIdCtrl,UINT nIdCtrlEnd,DYN_PMSGI pfnSignal,DYN_PMSGV pfnAction) 
        {       
            struct DynMSgMAP msg;    
            msg.nMessage=((*this).*pfnSignal)(nIdCtrl); 
  
            msg.nCode=(!IsButton(nIdCtrl)?msg.nMessage:WM_COMMAND); 
  
            if(!nIdCtrl) nIdCtrl=GetDlgCtrlID(); 
            msg.nID=nIdCtrl; 
            msg.nLastID=nIdCtrlEnd; 
            msg.pfn=pfnAction; 
            if(msg.nMessage!=-1) 
            { 
                m_aDynMsg.Add(msg); 
                return true; 
            } 
            return false; 
        } 
};  
#endif // !defined(DynMsgMap)
utilisation dans une CFormView:

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
  
class CTestQTMsgView : public CTplDynMsgMap<CFormView> 
{ 
protected: // create from serialization only 
CTestQTMsgView(); 
DECLARE_DYNCREATE(CTestQTMsgView) 
  
  
bool action(UINT nIdCtrl); // fonction de réponse  
  
//............
le code dans le source:

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  
CTestQTMsgView::CTestQTMsgView() 
: CTplDynMsgMap<CFormView>(CTestQTMsgView::IDD) 
{ 
//{{AFX_DATA_INIT(CTestQTMsgView) 
// NOTE: the ClassWizard will add member initialization here 
//}}AFX_DATA_INIT 
// TODO: add construction code here 
} 
  
void CTestQTMsgView::OnInitialUpdate() 
{ 
CFormView::OnInitialUpdate(); 
ResizeParentToFit(); 
connect(IDC_BUTTON1,SIGNAL(CTestQTMsgView::clicked),SLOT(CTestQTMsgView::action)); 
} 
///////////////////////////////////////////////////////////////////////////// 
void CTestQTMsgView::action(UINT nIdCtrl) 
{ 
AfxMessageBox("coucou dynamique"); 
}
dans la fonction OnInitialUpdate on établit le lien entre le click du bouton et la fonction de réponse local a la classe ..

Noter l'usage des defines SLOT et SIGNAL pour éviter le cast et de devoir écrire & .
la fonction clicked est à compléter pour les autres contrôles .
le système peut être étendu ...

Mis à jour le 19 septembre 2005 farscape

Avec une macro d'interception étendue :

La macro d'interception étendue ON_COMMAND_EX (par exemple) prend 2 paramètres, l'ID de la commande et le nom du gestionnaire. Si la fonction renvoie FALSE, l'architecture d'application recherche un autre gestionnaire de commandes.

Le ClassWizard ne gère pas les gestionnaires étendus. On doit tout faire à la main.

Exemple de gestionnaire interceptant 2 événements :

Dans la barre de menu, ajoutons le menu Test, et avec les options de menu "Item 1", "Item 2", "Item 3". Puis écrivons le code suivant dans la classe CMainFrame :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) 
//{{AFX_MSG_MAP(CMainFrame) 
// NOTE - the ClassWizard will add and remove mapping macros here. 
//    DO NOT EDIT what you see in these blocks of generated code ! 
ON_WM_CREATE() 
//}}AFX_MSG_MAP 
ON_COMMAND_EX(ID_ITEM1,OnItem) 
ON_COMMAND_EX(ID_ITEM2,OnItem) 
ON_COMMAND_EX(ID_ITEM3,OnItem) 
END_MESSAGE_MAP() 
  
BOOL CMainFrame::OnItem(UINT nID) 
{ 
CString msg; 
switch(nID) 
{ 
case ID_ITEM1: 
msg = "Item1"; 
break; 
case ID_ITEM2: 
msg = "Item 2"; 
break; 
default: 
MessageBeep(0xFFFFFFFF); 
return FALSE; 
} 
  
AfxMessageBox(msg); 
return TRUE; 
}
Code c++ : Sélectionner tout
1
2
3
4
5
6
protected: 
//{{AFX_MSG(CMainFrame) 
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); 
//}}AFX_MSG 
afx_msg BOOL OnItem(UINT nID); 
DECLARE_MESSAGE_MAP()
Avec une macro d'interception traitant un groupe de commandes :

Les macros suffixées _RANGE permettent de traiter des groupes de commandes dont les ID sont consécutifs.

Par exemple, si les items de tout à l'heure ont pour ID :

Code c++ : Sélectionner tout
1
2
3
#define ID_ITEM1                        32771 
#define ID_ITEM2                        32772 
#define ID_ITEM3                        32773
alors on pourrait écrire ainsi une fonction permettant d'afficher un message suivant l'item :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) 
//{{AFX_MSG_MAP(CMainFrame) 
// NOTE - the ClassWizard will add and remove mapping macros here. 
//    DO NOT EDIT what you see in these blocks of generated code ! 
ON_WM_CREATE() 
//}}AFX_MSG_MAP 
ON_COMMAND_RANGE(ID_ITEM1,ID_ITEM3,OnItemRange) 
END_MESSAGE_MAP() 
  
void CMainFrame::OnItemRange(UINT nID) 
{ 
   CString msg; 
   msg.Format("CMainFrame::OnItemRange, nID = %d",nID); 
  
   AfxMessageBox(msg); 
}
Code c++ : Sélectionner tout
1
2
3
4
5
6
protected: 
//{{AFX_MSG(CMainFrame) 
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); 
//}}AFX_MSG 
afx_msg void OnItemRange(UINT nID); 
DECLARE_MESSAGE_MAP()
Avec une macro d'interception étendue traitant un groupe de commandes:
Si on reprend le 1er exemple, alors il suffit de modifier la table d'interception :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) 
//{{AFX_MSG_MAP(CMainFrame) 
// NOTE - the ClassWizard will add and remove mapping macros here. 
//    DO NOT EDIT what you see in these blocks of generated code ! 
ON_WM_CREATE() 
//}}AFX_MSG_MAP 
ON_COMMAND_EX_RANGE(ID_ITEM1,ID_ITEM3,OnItem) 
END_MESSAGE_MAP()
Liste des macro d'interception permettant d'intercepter un groupe de commandes : WM_COMMAND

Code c++ : Sélectionner tout
1
2
3
4
rubrique dans la table d'interception          Prototype 
ON_COMMAND_EX(<id>,<FMembre>)                  afx_msg BOOL FMembre(UINT) 
ON_COMMAND_EX_RANGE(<id>,<idLast>,<FMembre>)   afx_msg BOOL FMembre(UINT) 
ON_COMMAND_RANGE(<id>,<idLast>,<FMembre>)      afx_msg void FMembre(UINT)
notification des contrôles

Code c++ : Sélectionner tout
1
2
3
4
5
rubrique dans la table d'interception                     Prototype 
ON_CONTROL_RANGE(<wCodNotif>,<id>,<idLast>,<FMembre>)     afx_msg void FMembre(UINT) 
ON_NOTIFY_EX(<wCodNotif>,<id>,<FMembre>)                  afx_msg BOOL FMembre(UINT,NMHDR*,LRESULT*) 
ON_NOTIFY_EX_RANGE(<wCodNotif>,<id>,<idLast>,<FMembre>)   afx_msg BOOL FMembre(UINT,NMHDR*,LRESULT*) 
ON_NOTIFY_RANGE(<wCodNotif>,<id>,<idLast>,<FMembre>)      afx_msg void FMembre(UINT,NMHDR*,LRESULT*)

Mis à jour le 27 novembre 2005 bigboomshakala

Le raccourci clavier donne la possibilité de relier une séquence de touches à une fonction.

Pour le mettre en place procéder comme suit :
Ouvrir l'éditeur de ressources, section : accélérateurs :
Double cliquer sur l'identifiant de la frame concernée.

Note dans le cas d'un projet SDI :IDR_MAINFRAME
Cliquer en bas de la liste pour rajouter une nouvelle séquence qui disposera d'un identifiant au même titre qu'un menu.
Au niveau de la vue de traitement il restera à intercepter l'identifiant clavier qui générera une fonction de réponse avec la macro ON_COMMAND

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
  
  
BEGIN_MESSAGE_MAP(CSampleSDIView, CFormView) 
//{{AFX_MSG_MAP(CSampleSDIView) 
ON_COMMAND(ID_MYHOTKEY, OnMyhotkey) 
//&#8230;&#8230;&#8230;&#8230;&#8230; 
void CSampleSDIView::OnMyhotkey()  
{ 
// TODO: Add your command handler code here 
    AfxMessageBox("coucou"); 
}

Mis à jour le 27 novembre 2005 farscape

Lorsque l'on intercepte des messages réfléchis sur un contrôle classwizard génère les macros ON_NOTIFY_REFLECT dans la boucle des messages.
Exemple sur un CTabCtrl avec les messages TCN_SELCHANGE.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
  
BEGIN_MESSAGE_MAP(CXTabCtrl, CTabCtrl) 
//{{AFX_MSG_MAP(CXTabCtrl) 
ON_NOTIFY_REFLECT(TCN_SELCHANGE, OnSelchange) 
ON_NOTIFY_REFLECT(TCN_SELCHANGING, OnSelchanging) 
//}}AFX_MSG_MAP 
END_MESSAGE_MAP()
Néanmoins on a parfois besoin de disposer de ces messages dans la fenêtre parent du contrôle.
Visual généra donc des messages de type ON_NOTIFY sur la fenêtre parent.
Exemple sur la fenêtre parent d'un CTabCtrl :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
  
BEGIN_MESSAGE_MAP(CTabctrlDlg, CDialog) 
//{{AFX_MSG_MAP(CTabctrlDlg) 
ON_NOTIFY(TCN_SELCHANGE, IDC_TAB, OnSelchangeTab) 
ON_NOTIFY(TCN_SELCHANGING, IDC_TAB, OnSelchangingTab) 
//}}AFX_MSG_MAP 
END_MESSAGE_MAP()
Le hic c'est que malgré l'adjonction des ces messages l'appel n'est pas effectué dans la fenêtre parent.
Pour en disposer il faudra modifier au niveau du contrôle la macro ON_NOTIFY_REFLECT et les remplacer par ON_NOTIFY_REFLECT_EX qui permet l'envoi du message au contrôle et au parent.

Mis à jour le 20 mai 2006 farscape

Quiconque a déplacé une fenêtre Windows avec la souris s'est demandé comment utiliser ce même mécanisme.
L'exemple qui suit montre comment modifier la taille d'un CEdit par la souris et l'appui sur la touche majuscule, ainsi que son déplacement dans la fenêtre parent avec la touche contrôle combinée avec la souris.

La technique utilisée permet de simuler le mécanisme Windows lorsqu'on se trouve sur le bandeau de la fenêtre.
Le message à envoyer est WM_SYSCOMMAND avec le paramètre SC_MOVE ou SC_SIZE auquel on rajoutera la valeur HTCAPTION pour simuler le message comme sur un bandeau de fenêtre.
Exemple appliqué à un CEdit:

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  
#define IsShiftDown()( (GetKeyState(VK_SHIFT) & (1 << (sizeof(SHORT)*8-1))) != 0   ) 
#define IsCtrlDown()( (GetKeyState(VK_CONTROL) & (1 << (sizeof(SHORT)*8-1))) != 0 ) 
  
void CMyEdit::OnLButtonDown(UINT nFlags, CPoint point) 
{ 
    // TODO : ajoutez ici le code de votre gestionnaire de messages et/ou les paramètres par défaut des appels 
    if(IsShiftDown()) 
    { 
        ReleaseCapture(); 
        SendMessage(WM_SYSCOMMAND, SC_SIZE |HTCAPTION , 0);  
    } 
    else if(IsCtrlDown()) 
    { 
        ReleaseCapture(); 
        SendMessage(WM_SYSCOMMAND, SC_MOVE | HTCAPTION, 0);  
    } 
    else CWnd::OnLButtonDown(nFlags, point); 
}
Note:Il faudra donc générer une classe dérivée de la classe CEdit et intercepter le message WM_LBUTTONDOWN
On associera ensuite une variable de type contrôle à l'édit dans la fenêtre de ressources en indiquant la nouvelle classe personnalisée.

Mis à jour le 22 janvier 2007 farscape

En utilisant la fonction GetAsyncKeyState.
si dans la valeur de retour le bit le plus fort est positionné ça voudra dire que la touche est enfoncée .
D'où le test à 0x8000 (32768) dans le code ci-dessous:

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
  
void GetMouseStatus(bool &bLeftButton,bool &bRightButton,bool &bMiddleButton) 
{ 
    bLeftButton = (GetAsyncKeyState(VK_LBUTTON) & 0x8000); 
    bRightButton = (GetAsyncKeyState(VK_RBUTTON) & 0x8000); 
    bMiddleButton = (GetAsyncKeyState(VK_MBUTTON) & 0x8000); 
  
} 
// utilisation: 
bool bLeftButton,bRightButton,bMiddleButton; 
GetMouseStatus(bLeftButton,bRightButton,bMiddleButton); 
    TRACE("\n bLeftButton:%d,bRightButton :%d,bMiddleButton :%d",bLeftButton,bRightButton,bMiddleButton);

Mis à jour le 22 janvier 2007 farscape

On procédera comme suit :
Au premier message WM_MOUSEMOVE sur la fenêtre on appellera la méthode SetCapture().
Dés lors tous les messages claviers et souris seront transmis à la fenêtre, même ceux en dehors.
Il restera à savoir si le curseur de la souris est à l'extérieur ou à l'intérieur de la fenêtre.
Si il est à l'extérieur il faudra relâcher la capture par la méthode ReleaseCapture().
Exemple d'implémentation:

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  
void CMDIMultiView::OnMouseMove(UINT nFlags, CPoint point)  
{ 
	// TODO: Add your message handler code here and/or call default 
  
	CFormView::OnMouseMove(nFlags, point); 
	// bool m_bIsCapture : variable membre initialisée a false dans le constructeur 
	if(!m_bIsCapture) SetCapture(); 
	m_bIsCapture=true; 
  
	CRect rect; 
	ClientToScreen(&point);	 
	GetWindowRect(&rect); 
  
	if(!rect.PtInRect(point)) 
	{ 
		ReleaseCapture(); 
		m_bIsCapture=false;	 
		AfxMessageBox("Attention vous sortez de la fenêtre",MB_ICONEXCLAMATION ); 
	} 
}

Mis à jour le 17 septembre 2007 farscape

Les nostalgiques des programmes consoles le savent bien, il est très pratique de naviguer dans des champs de saisies par les touches flèches haut et bas.
Sous Windows ce comportement n'est pas standard sauf quand le contrôle fait partie d'un group.
Dans ce contexte comment passer au contrôle suivant par la touche flèche bas ?
On procédera comme suit :
On interviendra directement au niveau la fonction virtuelle PreTranslateMessage de la fenêtre parent et on utilisera GetNextDlgTabItem voir qui donne le prochain contrôle dans l'ordre de tabulation.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  
BOOL CMyDlg::PreTranslateMessage(MSG* pMsg) 
{ 
   if ( pMsg->message == WM_KEYDOWN ) 
   { 
               if( pMsg->wParam == VK_DOWN  ) 
               { 
                     CWnd *pWnd=GetNextDlgTabItem(GetFocus()); 
                      if(pWnd) pWnd->SetFocus(); 
                      return TRUE; 
               } 
    } 
    return CDialog::PreTranslateMessage(pMsg); 
}

Mis à jour le 17 septembre 2007 farscape

Il est parfois utile de savoir quel est le premier contrôle dans l'ordre de tabulation pour une fenêtre, on utilisera la méthode :

Code c++ : Sélectionner tout
1
2
3
  
CWnd::GetNextDlgTabItem  
CWnd* GetNextDlgTabItem( CWnd* pWndCtl, BOOL bPrevious = FALSE ) const;
Code c++ : Sélectionner tout
1
2
  
CWnd *pFirstCtrl=this->GetNextDlgTabItem(NULL);

Mis à jour le 17 septembre 2007 farscape

Proposer une nouvelle réponse sur la FAQ

Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour ça


Réponse à la question

Liens sous la question
précédent sommaire suivant
 

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2017 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

 
Contacter le responsable de la rubrique C++