Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

Vous n'avez pas encore de compte Developpez.com ? L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Developpez.com

C++

Choisissez la catégorie, puis la rubrique :

logo
Sommaire > Contrôles > Notifications ,Messages
        Comment détecter les changements sur un groupe d'édit/checkbox/radio ?
        Comment gérer le click sur des boutons crées dynamiquement ?
        Comment simuler un click sur un CButton ?
        Comment définir un message privé ?
        Comment faire un message Map dynamique ?
        Comment intercepter plusieurs messages avec le même gestionnaire de commandes ?
        Comment mettre en place un raccourci clavier ?
        Comment faire pour que les messages réfléchis d'un contrôle soient disponibles sur la fenêtre parent ?
        Comment utiliser la méthode de Windows pour le redimensionnement d'une fenêtre ?
        Comment récupérer le statut des boutons de la souris ?
        Comment détecter que la souris sort de la fenêtre de travail ?
        Comment donner le focus au prochain contrôle par la touche flèche bas ?
        Comment trouver le premier contrôle dans l'ordre de tabulation ?



Comment détecter les changements sur un groupe d'édit/checkbox/radio ?
auteur : Farscape
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

DECLARE_MESSAGE_MAP() 
 virtual void OnRangeUpdateEdit(UINT nID); 
 virtual void OnRangeUpdateCheck(UINT nID); 
 virtual void OnRangeUpdateRadio(UINT nID); 
Dans le .cpp

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  
  } 
} 

Comment gérer le click sur des boutons crées dynamiquement ?
auteur : 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 :
// .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);
}

Comment simuler un click sur un CButton ?
auteur : 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 :

GetDlgItem(IDC_BUTTON1)->PostMessage(WM_KEYDOWN,' '); 
GetDlgItem(IDC_BUTTON1)->PostMessage(WM_KEYUP,' ');

Comment définir un message privé ?
Créé le 19/09/2005[haut]
auteur : 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_TEST dans la classe fenêtre CSdisamplesView.

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

#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 :

#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 :

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:

//-------------------------------------------------------------------
void CSdisamplesView::OnButtonUp(NMHDR* pNMHDR, LRESULT* pResult)
{
PostMessage(WM_TEST);
}
Appel à partir d'un Thread de travail initié par la classe Fenêtre :

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:

UINT TheThread(LPVOID pParam)
{
 HWND hWnd= reinterpret_cast< HWND >( pvParam) ;
 ::PostMessage(hWnd,WM_TEST,0,0) ;

return 0 ;
}

Comment faire un message Map dynamique ?
Créé le 19/09/2005[haut]
auteur : 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 :

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 :

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 :

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 :


#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:

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:

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 ...


Comment intercepter plusieurs messages avec le même gestionnaire de commandes ?
Créé le 27/11/2005[haut]
auteur : Nourdine Falola
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 :
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;
}
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 :
#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 :
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);
}
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 :
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
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
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*)

Comment mettre en place un raccourci clavier ?
Créé le 27/11/2005[haut]
auteur : Farscape
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


BEGIN_MESSAGE_MAP(CSampleSDIView, CFormView)
//{{AFX_MSG_MAP(CSampleSDIView)
ON_COMMAND(ID_MYHOTKEY, OnMyhotkey)
//?????
void CSampleSDIView::OnMyhotkey() 
{
// TODO: Add your command handler code here
    AfxMessageBox("coucou");
}

Comment faire pour que les messages réfléchis d'un contrôle soient disponibles sur la fenêtre parent ?
Créé le 20/05/2006[haut]
auteur : 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.

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 :

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.


Comment utiliser la méthode de Windows pour le redimensionnement d'une fenêtre ?
Créé le 22/01/2007[haut]
auteur : 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 :

#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.


Comment récupérer le statut des boutons de la souris ?
Créé le 22/01/2007[haut]
auteur : 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:

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);



Comment détecter que la souris sort de la fenêtre de travail ?
Créé le 17/09/2007[haut]
auteur : 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:

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 );
	}
}

Comment donner le focus au prochain contrôle par la touche flèche bas ?
Créé le 17/09/2007[haut]
auteur : 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 faq Comment trouver le premier contrôle dans l'ordre de tabulation ? qui donne le prochain contrôle dans l'ordre de tabulation.

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);
} 

Comment trouver le premier contrôle dans l'ordre de tabulation ?
Créé le 17/09/2007[haut]
auteur : 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 :

CWnd::GetNextDlgTabItem 
CWnd* GetNextDlgTabItem( CWnd* pWndCtl, BOOL bPrevious = FALSE ) const; 

CWnd *pFirstCtrl=this->GetNextDlgTabItem(NULL);


Consultez les autres F.A.Q.


Valid XHTML 1.0 TransitionalValid CSS!

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 © 2004 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site ni 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++

Partenaire : Hébergement Web