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 > Divers
        Comment fonctionnent les boutons radios ?
        Comment savoir si la touche Alt ,contrôle ou majuscule est pressée dans une notification de message clavier ?
        Comment mettre le texte d'un bouton sur plusieurs lignes ?
        Comment implémenter un message supplémentaire à partir d'un contrôle ?
        Comment déplacer un contrôle dans une fenêtre ?
        Comment gérer le positionnement automatique des contrôles ?
        Comment intercepter les messages des CScrollBar ?
        Comment gérer des valeurs non-entières avec un Spin Control ?
        Comment travailler avec les contrôles ?
        Comment changer le sens d'incrémentation d'un CSpinButtonCtrl ?
        Comment associer des objets de classes personnalisées dans les contrôles Windows de type List, Combo et Tree sans gérer une liste d'objet en parallèle ?
        Comment grouper des contrôles dans une boîte de dialogue ?
        Comment régler l'ordre de tabulation sur un contrôle dynamique ?
        Comment utiliser un contrôle standard .Net dans une application MFC ?
        Comment réaliser un bouton interrupteur ?
        Comment insérer un contrôle dynamique dans l'ordre de tabulation ?
        Comment intercepter les déplacements d'un contrôle curseur ?
        Comment mettre en place un contrôle bouton de commande ?
        Comment implémenter un nouveau message ON_CONTROL sur un contrôle ?
        Comment inverser le déplacement d'un CSliderCtrl ?
        Comment faire un Tooltip multilignes ?



Comment fonctionnent les boutons radios ?
auteur : Farscape
On utilisera les radios chaque fois que l'on doit faire un choix unique parmi des options.
Exemple imaginons le traitement du régime TVA d'un client on aura par exemple France / Corse / Export / CEE .
1) étape :
Dans l'éditeur de ressources sur la dialogue concernée on placera le premier radio en spécifiant son nom :
Dans mon exemple je le nommerai IDC_RADIOTVA son label France
Ensuite ne pas oublier de cocher l'option Tab Stop et surtout l'option Group permettant de spécifier que l'on commence un nouveau groupe et donc que les contrôles suivants de même nature en font partie.
On place ensuite les autres radios et on laissera l'éditeur donner l'ID du radio qu'il incrémentera de manière automatique par rapport au premier radio.
Si je dois placer un autre groupe de radios derrière il faudra juste spécifier sur le premier radio l'option groupe pour distinguer les deux groupes.
2) étape :
Pour connaître la valeur de la sélection il faut attacher une variable uniquement sur le premier bouton radio du groupe.
Pour cela il faut lancer « ClassWizard » ,onglet « member variables » ,sélectionner dans la liste l'id du bouton radio ici IDC_RADIOTVA
Cliquer sur le bouton « Add Variable » spécifier le nom de la variable de type « int »
Exemple « m_nRegimeTVA ».
Et valider le choix par le bouton « Ok »
« ClassWizard » génère automatiquement :

  1. Le code de déclaration dans la classe.
  2. Initialise la valeur dans le constructeur - 1.
  3. Place le code d'échange des informations entre la variable et le contrôle dans la fonction membre de la classe dialogue : DoDataExchange(CDataExchange* pDX).
3)étape :
Pour donner une valeur de départ à l'affichage de la fenêtre il suffira d'initialiser la variable dans la fonction InitDialog pour une boite de dialogue modale (CDialog) et OnInitialUpdate pour une classe CFormView :

m_nRegimeTVA=1 ;
Ceci sélectionnera le deuxième radio de la liste,l'indexation commençant à zéro et finissant au nombre de bouton radio du groupe ? 1 dans mon exemple : 3.
La valeur à « -1 » voulant dire aucune valeur sélectionnée.
Pour signifier sa valeur au contrôle on utilisera la fonction

 UpdateData(FALSE) ;
pour la mise à jour du contrôle et

 UpdateData(TRUE) ; 
Pour récupérer la valeur dans la variable.

Autre cas: si la variable ne suffit pas et que l'on désire par exemple récupérer le label du radio sélectionné on peut procéder comme suit.
Toujours par rapport à mon exemple :

CString CMyDialog::GetRadioLabelForValue(UINT nId,int &rnValue)
{
    CWnd *pRadio=NULL;
    UpdateData(TRUE);
    if(rnValue >=0)
    {
        pRadio=GetDlgItem(nId);
        for(int n=0;pRadio && n< rnValue;n++)
            pRadio=pRadio->GetWindow (GW_HWNDNEXT);
    }
    CString str;
    if(pRadio) pRadio->GetWindowText(str);
    return str;
}
CString str=GetRadioLabelForValue(IDC_RADIOTVA , m_nRegimeTVA);

Comment savoir si la touche Alt ,contrôle ou majuscule est pressée dans une notification de message clavier ?
Mise à jour le 17/06/2004[haut]
auteur : Farscape
Exemple je traite le message OnKeyUp dans une fenêtre et je veux savoir si la touche contrôle ou majuscule est enfoncée :


#define IsShiftDown()( (GetKeyState(VK_SHIFT) & (1 << (sizeof(SHORT)*8-1))) != 0   )
#define IsCtrlDown()( (GetKeyState(VK_CONTROL) & (1 << (sizeof(SHORT)*8-1))) != 0 )
#define IsAltDown()( (GetKeyState(VK_LMENU) & (1 << (sizeof(SHORT)*8-1))) != 0   )

void CMyEdit::OnKeyUp(UINT nChar,
            UINT nRepCnt, UINT nFlags) 
{
    // TODO: Add your message handler code here and/or call default
    if(IsCtrlDown())
    {
        switch(nChar)
        {
            case VK_INSERT:
            case 'c':
            case 'C':Copy();
                    return;

            case 'z':
            case 'Z':Undo();
                    return;

            case 'x':
            case 'X':Cut();
                    return;
        }
    }
    if(IsShiftDown())
    {
        if(nChar==VK_INSERT) 
        {
            Paste();
            return;
        }
    }
    CEdit::OnKeyUp(nChar, nRepCnt, nFlags);
}
void CMyEdit::OnSysKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
// TODO: Add your message handler code here and/or call default
    if(IsAltDown()) 
    {
        TRACE("\nAlt");
    }
    CEdit::OnSysKeyDown(nChar, nRepCnt, nFlags);
}

Comment mettre le texte d'un bouton sur plusieurs lignes ?
auteur : Farscape
Il suffit de mettre le caractère "\n" entre chaque ligne et de cocher l'option " multiligne" dans l'onglet style du bouton.


Comment implémenter un message supplémentaire à partir d'un contrôle ?
auteur : Farscape
Exemple :
Je voudrais avoir un message m'indiquant que l'on a relâché le bouton gauche sur un CButton et avoir ce genre de message directement au niveau de la fenêtre de traitement.
Pour réaliser cette opération nous disposons du message WM_NOTIFY, permettant à un contrôle de communiquer avec son parent.

Méthode:
Avec l'aide de ClassWizard:
On générera une classe dérivée de la classe CButton.
Ensuite on interceptera le message WM_LBUTTONUP.
Enfin on enverra le message WM_NOTIFY au parent comme suit :

// ma classe bouton générée avec ClassWizard (en abrégé)

#define BN_LBUTTONUP 1 // evenement custom LBUTTONUP

CMyButton::CMyButton(){}
CMyButton::~CMyButton(){}

BEGIN_MESSAGE_MAP(CMyButton, CButton)
//{{AFX_MSG_MAP(CMyButton)
ON_WM_LBUTTONUP()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMyButton message handlers

void CMyButton::OnLButtonUp(UINT nFlags, CPoint point) 
{
// TODO: Add your message handler code here and/or call default
NMHDR hdr;
        hdr.hwndFrom = GetSafeHwnd();
        hdr.idFrom   = GetDlgCtrlID();
        hdr.code     = BN_LBUTTONUP;
GetParent()->SendMessage(WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&hdr);
CButton::OnLButtonUp(nFlags, point);
}
// fin classe bouton.

// application dans une CFormView:

BEGIN_MESSAGE_MAP(CSdisamplesView, CFormView)
//{{AFX_MSG_MAP(CSdisamplesView)
ON_BN_CLICKED(IDC_BUTTON1, OnButton1)
ON_NOTIFY(BN_LBUTTONUP,IDC_BUTTON1,OnButtonUp) // le message à rajouter manuellement.
END_MESSAGE_MAP()
//-------------------------------------------------------------------
void CSdisamplesView::OnButtonUp(NMHDR* pNMHDR, LRESULT* pResult)
{
   TRACE("\nId:%u mess:%d",pNMHDR->idFrom,pNMHDR->code);
}
La macro ON_NOTIFY est à rajouter manuellement dans BEGIN_MESSAGE_MAP.
Ainsi que la fonction en réponse au message OnButtonUp(NMHDR* pNMHDR, LRESULT* pResult)
Note :L'exemple ci-dessus utilise directement la structure NMHDR pour support du message, il est tout à fait possible de construire votre structure personnelle comme suit:

// exemple d'application à partir d'un CTreeCtrl
/*struct TREE_NOTIFY
{
     NMHDR hdr;
     HTREEITEM hItem;
};*/
struct TREE_NOTIFY Notify;
Notify.hdr.hwndFrom = GetSafeHwnd();
Notify.hdr.idFrom   = GetDlgCtrlID();
Notify.hdr.code     = TREE_INSERTITEM;
Notify.hItem=hNewItem;
GetParent()->SendMessage(WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&Notify); 

Comment déplacer un contrôle dans une fenêtre ?
Créé le 19/09/2005[haut]
auteur : Farscape
La technique :

On récupère les coordonnées du contrôle avec la fonction membre GetWindowRect.

Les coordonnées sont données en rapport à l'écran.

Pour déplacer un contrôle dans sa fenêtre on utilisera la fonction MoveWindow ou SetWindowPos, mais les coordonnées doivent être fournis en rapport à la fenêtre de placement (la view ou la dialogue) .

On utilisera pour la conversion la fonction ScreenToClient à partir de la fenêtre parent :

C'est-à-dire on demande de convertir des coordonnées écrans en coordonnées en rapport avec l'objet qui appel ScreenToClient .

Ensuite on applique la fonction de déplacement.

Note : dans le cas où on veut changer la taille on utilisera de préférence SetWindowPos à MoveWindow.

Dans l'exemple en dessus j'ai mis this->ScreenToClient pour bien montrer que c'est l'objet sur lequel on se trouve qui sert de référence pour la conversion.

CRect Rect; 
MyStatic.GetWindowRect(&Rect); // coordonnées screen 
this->ScreenToClient(&Rect); // screen -> to client ( view ou dialog) 
// X size 
Rect.InflateRect(Rect.Width(),Rect.Height()); 
MyStatic.SetWindowPos( NULL,0,0,Rect.Width(),Rect.Height(),SWP_NOMOVE | SWP_NOZORDER); // uniquement la taille
MyStatic.MoveWindow(&Rect) ; // les coordonnées completes.

Comment gérer le positionnement automatique des contrôles ?
Créé le 19/09/2005[haut]
auteur : Farscape
Il n'existe pas de mécanismes standard de gestion des positions (layout) sur les contrôles.
La classe Template suivante se propose de remédier à ce problème.

// TplLayout.hpp : interface of the CTplLayout class
//
/////////////////////////////////////////////////////////////////////////////
#if !defined(AFX_TPLLAYOUT_H__59DC7C67_79D6_4082_A38B_4CD17E8257C1__INCLUDED_)
#define AFX_TPLLAYOUT_H__59DC7C67_79D6_4082_A38B_4CD17E8257C1__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

// Adaptation de l'article:
// http://www.codeguru.com/Cpp/W-D/dislog/resizabledialogs/article.php/c1917/
// Farscape 23/02/2005 1.2
// 1.2 au 29/04/2005:enleve les warnings  sous VC6 et .net2003
// 1.1 au 23/02/2005: Mise a jour pour le Support des CDialogBar
// 1.0 au 22/02/2005: Support pour les CFormView et CDialog.
//

#include <algorithm>
#include <vector>

#ifndef OBM_SIZE
#define OBM_SIZE 32766
#endif

template <class GENERIC_LAYOUT>
class CTplLayout : public GENERIC_LAYOUT
{
public:
    CTplLayout( UINT nIDTemplate ):GENERIC_LAYOUT(nIDTemplate)
    {
        Init();
    }
    CTplLayout()
    {
        Init();    
    }

    void Init()
    {
        m_bModeReSizing=false;
        m_xAllow=sizeResize;
        m_yAllow=sizeResize;        
        m_bInited=false;    
    }
public:
    
    // renvoie la taille d'une ressource Dialog.
    CSize GetSizeDialog(UINT id);
    
    
    void SetModeResizeCtrl(bool bEnable=true){m_bModeReSizing=bEnable;}
    
    // possible sizing types
    boolm_bModeReSizing;
    
    
    
    // contained class to hold item state
    enum eSizeType
    {
        sizeNone,                           // do nothing
            sizeResize,                         // proportional expand/contract
            sizeRepos,                          // maintain distance from top/left
            sizeRelative                        // proportional distance from sides
    };
protected:
    class CItem
    {
    public:
        UINT      m_resID;                  // resource ID
        UINT      m_resIDChain;             // resource ID Chain
        eSizeType m_xSize;                  // x sizing option
        eSizeType m_ySize;                  // y sizing option
        CRect     m_rcControl;              // last size
        CRect  m_rcInitial;
        CRect  m_rcInitialChain;
        bool      m_bFlickerFree;           // flicker-free move?
        double    m_xRatio;                 // x ratio (for relative)
        double    m_yRatio;                 // y ratio (for relative)
        double  m_xPercen;
        double  m_yPercen;
        bool  m_bPercen;
        CSize  m_Space;
        
    protected:
        void Assign(const CItem& src)
        {
            m_rcControl=src.m_rcControl;
            m_rcInitial=src.m_rcControl;
            m_rcInitialChain=src.m_rcInitialChain;
            
            m_resID=src.m_resID;
            m_resIDChain=src.m_resIDChain;
            m_xSize=src.m_xSize;
            m_ySize=src.m_ySize;
            m_bFlickerFree=src.m_bFlickerFree;
            m_xRatio=src.m_xRatio;
            m_yRatio=src.m_yRatio;
            m_bPercen=src.m_bPercen;
            m_xPercen=src.m_xPercen;
            m_yPercen=src.m_yPercen;
            m_Space =src.m_Space;
        }
        
    public:
        CItem(){m_bPercen=false;}
        CItem(const CItem& src){Assign(src);}
        
        void OnSize(HDWP hdwp,
                    const CRect& rcParentOld,
                    const CRect& rcParentNew,CWnd *pDlg)
        {
            CSize diff;
            CRect rcControl;
            CWnd *pWnd;
            int   newpos,newsize;
            double x;
            bool bx=false,by=false;
            // get the control window
            
            pWnd=pDlg->GetDlgItem(m_resID);
            
            // get the size difference
            
            diff.cx=rcParentNew.Width()-rcParentOld.Width();
            diff.cy=rcParentNew.Height()-rcParentOld.Height();
            
            // preset for no change
            
            rcControl=m_rcControl;
            
            // process horizontal option
            
            switch(m_xSize)
            {
            case sizeResize:
                if(m_resIDChain)
                {
                    diff.cx/=2;
                    bx=true;
                }
                rcControl.right+=diff.cx;
                if(m_bPercen)
                {
                    x=m_rcInitial.Width()+(m_rcInitial.Width()*(m_xPercen/100.));
                    if(static_cast<double>(rcControl.Width())>x)
                        rcControl.right=rcControl.left+static_cast<long>(x);
                    if(rcControl.Width()<m_rcInitial.Width())
                        rcControl.right=m_rcInitial.right;
                }
                break;
                
            case sizeRepos:
                rcControl.left+=diff.cx;
                rcControl.right+=diff.cx;
                break;
                
            case sizeRelative:
                newpos=static_cast<int>((m_xRatio*(double)rcParentNew.Width())/(1.0+m_xRatio));
                newsize=rcControl.Width();
                rcControl.left=newpos;
                rcControl.right=newpos+newsize;
                break;
            }
            
            // process vertical option
            
            switch(m_ySize)
            {
            case sizeResize:  
                if(m_resIDChain)
                {
                    by=true;
                    diff.cy/=2;
                }
                rcControl.bottom+=diff.cy;
                if(m_bPercen)
                {
                    x=m_rcInitial.Height()+(m_rcInitial.Height()*(m_yPercen/100.));
                    if(rcControl.Height()>static_cast<long>(x))
                        rcControl.bottom=rcControl.top+static_cast<long>(x);
                    if(rcControl.Height()<m_rcInitial.Height())
                        rcControl.bottom=m_rcInitial.bottom;
                }
                break;
                
            case sizeRepos:
                rcControl.top+=diff.cy;
                rcControl.bottom+=diff.cy;
                break;
                
            case sizeRelative:
                newpos=static_cast<int>((m_yRatio*(double)rcParentNew.Height())/(1.0+m_yRatio));
                newsize=rcControl.Height();
                rcControl.top=newpos;
                rcControl.bottom=newpos+newsize;
                break;
            }
            
            // set new size
            
            if((rcControl!=m_rcControl) || (m_xSize!=m_ySize))
            {
                if(m_resID<=2)
                {
                    pWnd->MoveWindow(&rcControl);
                }
                else 
                    DeferWindowPos(hdwp,*pWnd,
                                    NULL,
                                    rcControl.left,
                                    rcControl.top,
                                    rcControl.Width(),
                                    rcControl.Height(),
                                    SWP_NOZORDER);
                m_rcControl=rcControl;
                if(m_resIDChain && (bx || by))
                {
                    CRect rect;
                    pWnd=pDlg->GetDlgItem(m_resIDChain);
                    
                    pWnd->GetWindowRect(rect);
                    pDlg->ScreenToClient(rect);
                    if(bx)
                    {
                        x=rect.Width();
                        rect.left=rcControl.right+m_Space.cx;
                        rect.right=rect.left+static_cast<int>(x)+diff.cx;
                    }
                    if(by)
                    {
                        x=rect.Height();
                        rect.top=rcControl.bottom+m_Space.cy;
                        rect.bottom=rect.top+static_cast<int>(x)+diff.cy;
                    }
                    DeferWindowPos(hdwp,*pWnd,
                                    NULL,
                                    rect.left,
                                    rect.top,
                                    rect.Width(),
                                    rect.Height(),
                                    SWP_NOZORDER);
                }
            }
        }

        CItem& operator=(const CItem& src)
        {
            Assign(src);
            return *this;
        }
};
// id for the size icon - change if you get a clash with any of your controls

enum { m_idSizeIcon=0x4545 };



// data members

std::vector<CItem> m_Items;           // array of controlled items
CRect              m_rcDialog;        // last dialog size
CPoint             m_MinSize;         // smallest size allowed
eSizeType          m_xAllow;          // horizontal sizing allowed
eSizeType          m_yAllow;          // vertical sizing allowed
CBitmap            m_bmSizeIcon;      // size icon bitmap
CStatic            m_wndSizeIcon;     // size icon window
bool               m_bInited;         // set after initialize  

public:
    // initialisation CFormView et CDialog.
    // mettre bSetIcon=FALSE pour une CFormView
    void  InitDialog(CRect &Rect,BOOL bSetIcon=TRUE); 
    
    // initialisation CDialogBar
    void  InitDialogBar(UINT uResId);
    
    void SetPercenMaxSize(const UINT resID,double dysize,double dxsize);
    void AddChainControl(const UINT resIDSrc,const UINT resIDChain);
    void AddControl(const UINT resID,
                    const eSizeType xsize,
                    const eSizeType ysize,
                    const bool bFlickerFree=true);
    
    void AllowSizing(const eSizeType xsize,const eSizeType ysize);
    void HideSizeIcon(void);
    
    void _OnSize(UINT nType, int cx, int cy);
    BOOL _OnGetMinMaxInfo(MINMAXINFO *lpMMI);
    virtual LRESULT DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
    {        
        if(nMsg==WM_SIZE )
        {
            _OnSize(wParam,LOWORD(lParam),HIWORD(lParam));
        }
        if(nMsg==WM_GETMINMAXINFO)
        {
            _OnGetMinMaxInfo(reinterpret_cast<MINMAXINFO *>(lParam));
        }
        return GENERIC_LAYOUT::DefWindowProc( nMsg, wParam,lParam);
    }
};
// ------------------------------------------------------------------
template <class GENERIC_LAYOUT>
void CTplLayout<GENERIC_LAYOUT>::SetPercenMaxSize(const UINT resID,double dysize,double dxsize)
{
    std::vector<CItem>::iterator it;
    if(m_Items.size())
    {
        for(it=m_Items.begin();it!=m_Items.end();it++)
        {
            if(it->m_resID==resID)
            {
                it->m_bPercen=true;
                it->m_xPercen=dxsize;
                it->m_yPercen=dysize;
                return;
            }
        }
    }
}
// ------------------------------------------------------------------
template <class GENERIC_LAYOUT>
void CTplLayout<GENERIC_LAYOUT>::AddChainControl(const UINT resIDSrc,const UINT resIDChain)
{
    std::vector<CItem>::iterator it;
    if(m_Items.size())
    {
        for(it=m_Items.begin();it!=m_Items.end();it++)
        {
            if(it->m_resID==resIDSrc)
            {
                it->m_resIDChain=resIDChain;
                GetDlgItem(resIDChain)->GetWindowRect(it->m_rcInitialChain);
                ScreenToClient(it->m_rcInitialChain);
                it->m_Space.cx=it->m_rcInitialChain.left-it->m_rcControl.right;
                it->m_Space.cy=it->m_rcInitialChain.top-it->m_rcControl.bottom;
                return;
            }
        }
    }
}
// ------------------------------------------------------------------
template <class GENERIC_LAYOUT>
CSize CTplLayout<GENERIC_LAYOUT>::GetSizeDialog(UINT id)
{
    int cy=0,cx=0;
    LPCTSTR lpszTemplateName;
    lpszTemplateName = MAKEINTRESOURCE(id);
    
    LPDLGTEMPLATE lpDialogTemplate = 0;
    HGLOBAL hDialogTemplate   =0;
    HINSTANCE hInst = AfxGetResourceHandle();
    if (lpszTemplateName != NULL)
    {
        hInst = AfxFindResourceHandle(lpszTemplateName, RT_DIALOG);
        HRSRC hResource = ::FindResource(hInst, lpszTemplateName, RT_DIALOG);
        hDialogTemplate = LoadResource(hInst, hResource);
    }
    if (hDialogTemplate != NULL)
        lpDialogTemplate = (LPDLGTEMPLATE)LockResource(hDialogTemplate);
    if(hDialogTemplate != NULL)
    {
        LOGFONT lf;
        CFont Font;
        
        memset(&lf,0,sizeof(lf));
        lf.lfHeight=-11;
        lf.lfWeight=400;
        strcpy(lf.lfFaceName,"MS Sans Serif");
        Font.CreateFontIndirect( &lf );
        CWnd *pWnd=AfxGetMainWnd();
        CDC* pDC =pWnd->GetDC();
        CFont* oldFont = pDC->SelectObject(&Font);
        TEXTMETRIC tm;
        pDC->GetTextMetrics( &tm );
        int baseUnitY = tm.tmHeight;
        CSize size;
        size = pDC->GetTextExtent(
            "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52);
        int baseUnitX = (size.cx / 26 + 1) / 2;    
        pDC->SelectObject(oldFont);
        Font.DeleteObject();
        
        cy=(lpDialogTemplate->cy*baseUnitY)/8;
        cx=(lpDialogTemplate->cx*baseUnitX)/4;
    }
    if (lpszTemplateName != NULL || hDialogTemplate != NULL) 
        UnlockResource(hDialogTemplate);
    if (lpszTemplateName != NULL)
        FreeResource(hDialogTemplate);
    
    return CSize(cx,cy);
}
// ------------------------------------------------------------------
template <class GENERIC_LAYOUT>
void CTplLayout<GENERIC_LAYOUT>::AddControl(const UINT resID,
                                            const eSizeType xsize,
                                            const eSizeType ysize,
                                            const bool bFlickerFree)
{
    CItem item;
    
    // create the new item
    CWnd *pWnd=GetDlgItem(resID);
    pWnd->GetWindowRect(item.m_rcControl);
    
    ScreenToClient(item.m_rcControl);
    item.m_rcInitial=item.m_rcControl;
    item.m_bFlickerFree=bFlickerFree;
    item.m_resID=resID;
    item.m_resIDChain=0;
    item.m_xSize=xsize;
    item.m_ySize=ysize;
    item.m_bPercen=false;
    item.m_rcInitialChain=CRect(0,0,0,0);
    if(xsize==sizeRelative)
        item.m_xRatio=(double)item.m_rcControl.left/((double)m_rcDialog.Width()-(double)item.m_rcControl.left);
    if(ysize==sizeRelative)
        item.m_yRatio=(double)item.m_rcControl.top/((double)m_rcDialog.Height()-(double)item.m_rcControl.top);
    
    // add to the array
    m_Items.push_back(item);
}
// ------------------------------------------------------------------
template <class GENERIC_LAYOUT>
void CTplLayout<GENERIC_LAYOUT>::AllowSizing(const eSizeType xsize,const eSizeType ysize)
{
    m_xAllow=xsize;
    m_yAllow=ysize;
}
// ------------------------------------------------------------------
template <class GENERIC_LAYOUT>
void CTplLayout<GENERIC_LAYOUT>::HideSizeIcon(void)
{
    m_wndSizeIcon.ShowWindow(SW_HIDE);
}
// ------------------------------------------------------------------
template <class GENERIC_LAYOUT>
void CTplLayout<GENERIC_LAYOUT>::_OnSize(UINT nType,int cx,int cy)
{
    if(!m_bModeReSizing) return;
    CRect rect;
    HDWP hdwp;
    std::vector<CItem>::iterator it;
    
    
    if(m_Items.size())
    {
        if(IsKindOf(RUNTIME_CLASS(CFormView)))
        {
            if(m_MinSize.x && m_MinSize.y)
            {
                if(cx<m_MinSize.x || cy<m_MinSize.y) return;
            }
        }
        // get the new size
        GetWindowRect(rect);
        
        // start deferring window pos        
        hdwp=BeginDeferWindowPos(20);
        
        // children can resize themselves        
        for(it=m_Items.begin();it!=m_Items.end();it++)
            it->OnSize(hdwp,m_rcDialog,rect,this);
        
        // do the deferred window position change        
        EndDeferWindowPos(hdwp);
    }
    
    // remember new size
    m_rcDialog=rect;
}
// ------------------------------------------------------------------
template <class GENERIC_LAYOUT>
BOOL CTplLayout<GENERIC_LAYOUT>::_OnGetMinMaxInfo(MINMAXINFO *lpMMI)
{
    if(!m_bModeReSizing) return FALSE;
    if(m_bInited)
    {
        lpMMI->ptMinTrackSize=m_MinSize;
        if(m_xAllow==sizeNone)
            lpMMI->ptMaxTrackSize.x=lpMMI->ptMaxSize.x=m_MinSize.x;
        
        if(m_yAllow==sizeNone)
            lpMMI->ptMaxTrackSize.y=lpMMI->ptMaxSize.y=m_MinSize.y;
        return TRUE;
    }
    return FALSE;
}
//-----------------------------------------
template <class GENERIC_LAYOUT>
void CTplLayout<GENERIC_LAYOUT>::InitDialogBar(UINT uResId)
{
    CSize size=GetSizeDialog(uResId);
    InitDialog(CRect(0,0,size.cx,size.cy),FALSE);
}
//-----------------------------------------
template <class GENERIC_LAYOUT>
void CTplLayout<GENERIC_LAYOUT>::InitDialog(CRect &Rect,BOOL bSetIcon) 
{
    
    SetModeResizeCtrl();    
    
    CRect rcIcon,rcDialogClient,rcCurrent;
    m_rcDialog=Rect;
    // get the dialog size
    if(!Rect.left   && 
        !Rect.top    &&
        !Rect.bottom &&
        !Rect.right)
    {
        GetWindowRect(m_rcDialog);  
        Rect= m_rcDialog;
    }
    GetWindowRect(rcCurrent);
    
    m_MinSize.x=m_rcDialog.Width();
    m_MinSize.y=m_rcDialog.Height();
    
    if(bSetIcon)
    {
        // set up the size icon
        m_bmSizeIcon.LoadOEMBitmap(OBM_SIZE);
        m_wndSizeIcon.Create(NULL,
            WS_CHILD | WS_VISIBLE | SS_BITMAP,CRect(0,0,10,10),
            this,m_idSizeIcon);
        
        m_wndSizeIcon.SetBitmap(m_bmSizeIcon);
        
        // move the icon to the bottom-right corner
        
        GetClientRect(rcDialogClient);
        m_wndSizeIcon.GetWindowRect(rcIcon);
        ScreenToClient(rcIcon);
        m_wndSizeIcon.SetWindowPos(NULL,rcDialogClient.right-rcIcon.Width(),
            rcDialogClient.bottom-rcIcon.Height(),
            0,0,SWP_NOZORDER | SWP_NOSIZE);
        
        // make it auto-position
        AddControl(m_idSizeIcon,sizeRepos,sizeRepos);
    }
    
    // all done
    m_bInited=true;  
    if(Rect!=rcCurrent)  _OnSize(0,rcCurrent.Width(),rcCurrent.Height());
}
#endif // !defined(AFX_TPLLAYOUT_H__59DC7C67_79D6_4082_A38B_4CD17E8257C1__INCLUDED_)
utilisation avec une CFormView :

#include "TplLayout.hpp"

class CTestMdILayoutView : public CTplLayout<CFormView>
{
....

CTestMdILayoutView::CTestMdILayoutView()
: CTplLayout<CFormView>(CTestMdILayoutView::IDD)
{
//{{AFX_DATA_INIT(CTestMdILayoutView)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// TODO: add construction code here

}
void CTestMdILayoutView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
    
    CChildFrame *pChild=static_cast<CChildFrame *>(GetParentFrame());
   // si on utilise la classe template de gestion des tailles 
    if(pChild->GetInitialRectFrame().IsRectNull()) 
    {
        ResizeParentToFit();        
    }            
// sinon a faire systematiquement
//    ResizeParentToFit();        
AddControl(IDC_STATICLIB,eSizeType::sizeResize,eSizeType::sizeRepos);
AddControl(IDOK,eSizeType::sizeRepos,eSizeType::sizeNone);
AddControl(IDCANCEL,eSizeType::sizeRepos,eSizeType::sizeNone);

InitDialog(pChild->GetInitialRectFrame(),FALSE);
// CRect Rect(0,0,0,0); 
// InitDialog(Rect,FALSE);
}
pour une boîte de Dialogue (CDialog)
Déclaration:

class CAboutDlg : public CTplLayout<CDialog>
{
public:
CAboutDlg();
//...............
code:

CAboutDlg::CAboutDlg() : CTplLayout<CDialog>(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
BOOL CAboutDlg::OnInitDialog() 
{
CDialog::OnInitDialog();

// TODO: Add extra initialization here
    AddControl(IDOK,eSizeType::sizeRepos,eSizeType::sizeNone);

    CRect Rect(0,0,0,0);
    InitDialog(Rect,TRUE);

return TRUE;  // return TRUE unless you set the focus to a control
              // EXCEPTION: OCX Property Pages should return FALSE
}
Note: le style resizing doit être activé sur la dialogue dans l'éditeur de ressources.

pour une CDialogBar :
la déclaration :

#include "TplLayout.hpp"
/////////////////////////////////////////////////////////////////////////////
// CMyDlgBar dialog
class CMyDlgBar : public CTplLayout<CDialogBar>
{
// Construction
public:
CMyDlgBar();   // standard constructor

void InitDialog();
//......
le code :

CMyDlgBar::CMyDlgBar(): CTplLayout<CDialogBar>()
{
//{{AFX_DATA_INIT(CMyDlgBar)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
}
void CMyDlgBar::InitDialog()
{
AddControl(IDC_BUTTON,eSizeType::sizeRepos,eSizeType::sizeNone);
AddControl(IDC_STATIC1,eSizeType::sizeRepos,eSizeType::sizeNone);
AddControl(IDC_EDIT1,eSizeType::sizeResize,eSizeType::sizeNone);
InitDialogBar(IDD_DIALOGBAR);
}
Initialisation de la CDialogBar à partir de la view:

void CTestMdILayoutView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();    
    CChildFrame *pChild=static_cast<CChildFrame *>(GetParentFrame());
    if(pChild->GetInitialRectFrame().IsRectNull()) 
    {
        ResizeParentToFit();        
    }            

    AddControl(IDC_STATICLIB,eSizeType::sizeResize,eSizeType::sizeRepos);
AddControl(IDOK,eSizeType::sizeRepos,eSizeType::sizeNone);
AddControl(IDCANCEL,eSizeType::sizeRepos,eSizeType::sizeNone);

    InitDialog(pChild->GetInitialRectFrame(),FALSE);

// initialisation de la DialogBar
pChild->m_DlgBar.InitDialog();
}
le positionnement est géré sur l'axe des X et Y :

sizeNone : ne fait rien
sizeResize : dimensionnement du contrôle proportionel
sizeRepos : maintient la distance en fonction du point haut /gauche
sizeRelative : distance proportionelle en fonction des côtés
Les Fonctions:
void SetPercenMaxSize(const UINT resID,double dysize,double dxsize):
Permet de spécifier la taille maximum de d'étirement sur la largeur et hauteur du contrôle identifié par resID
void AddChainControl(const UINT resIDSrc,const UINT resIDChain);
Permet de lier au déplacement d'un contrôle resIDSrc un contrôle secondaire resIDChain L'espace initial entre les deux est maintenu.

void AddControl(const UINT resID,const eSizeType xsize,const eSizeType ysize,const bool bFlickerFree=true);
Fixe les modalités d'étirements du contrôle sur la largeur et ou hauteur .

void InitDialog(CRect &Rect,BOOL bSetIcon=TRUE);
Initialisation pour une classe CFormView et CDialog.
mettre bSetIcon=FALSE pour une CFormView et TRUE pour une CDialog permettant d'afficher l'icone d'étirement en bas à droite.

void InitDialogBar(UINT uResId);
Initialisation d'une CDialogBar.
uResId et l'identifiant de la CDialogBar permettant de calculer sa taille initiale de référence

Un exemple complet:
http://farscape.developpez.com/Samples/TestMdiLayout.zip


Comment intercepter les messages des CScrollBar ?
Créé le 19/09/2005[haut]
auteur : Farscape
Les notifications de message des CScrollbars s'interceptent sur la fenêtre avec les messages :
WM_VSCROLL (vertical) et WM_HSCROLL (horizontal) que l'on générera avec l'aide de classwizard sous VC6 ou avec .NET en sélectionnant la classe fenêtre puis avec le bouton messages.
Le message génère une fonction qui contient le pointeur sur la scrollbar le type d'événement dans nSBCode, et la position dans nPos.
Exemple sur une CScrollBar verticale.

void CMyForm::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
switch(nSBCode)
{
case SB_THUMBPOSITION:break;
case SB_THUMBTRACK   :break;
case SB_PAGEDOWN     :break;
case SB_PAGEUP     :break;
case SB_LINEDOWN:break;
case SB_LINEUP   :break;
}
}

Comment gérer des valeurs non-entières avec un Spin Control ?
Créé le 27/11/2005[haut]
auteur : Nourdine Falola
En désactivant la propriété "Set buddy integer" du controle Spin et en interceptant le message UDN_DELTAPOS :

  • soit dans la classe view ou dialog ayant pour donnée membre le spin
  • soit dans une classe spin perso dérivée de CSpinButtonCtrl

Je propose la classe CSpinBtnCtrl, dérivant de CSpinButtonCtrl, qui prend en charge les double.
Cette classe redéfinit une partie de l'interface de CSpinButtonCtrl (en fait les fonstions de CSpinButtonCtrl n'étant pas virtuelles, on les masque en implémentant des fonctions de même nom public ou privé suivant que l'on veuille laisser à l'utilisateur la possibilité de les utiliser)
#if !defined(AFX_SPINBTNCTRL_H__98DE1443_58B6_43DA_ABBB_F9A2CA4417C7__INCLUDED_)
#define AFX_SPINBTNCTRL_H__98DE1443_58B6_43DA_ABBB_F9A2CA4417C7__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// SpinBtnCtrl.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CSpinBtnCtrl window

class CSpinBtnCtrl : public CSpinButtonCtrl
{
// Construction
public:
CSpinBtnCtrl();

// Attributes
public:
double m_min;
double m_max;
double m_inc;
double m_pos;
//int m_nbDigits;
CString m_strFormat;

// Operations
public:
void SetRange(double lower,double upper);
void GetRange(double & lower,double & upper) const;

double SetPos(double pos);
double GetPos() const;

double GetInc();
void SetInc(double inc);

//void SetFormat(int nbDigits);
void SetFormat(CString strFormat);

void UpdateBuddy();

// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSpinBtnCtrl)
//}}AFX_VIRTUAL

// Implementation
public:
virtual ~CSpinBtnCtrl();

// Generated message map functions
protected:
//{{AFX_MSG(CSpinBtnCtrl)
afx_msg void OnDeltapos(NMHDR* pNMHDR, LRESULT* pResult);
//}}AFX_MSG

DECLARE_MESSAGE_MAP()

private:

// on masque les fonctions héritées de CSpinButtonCtrl

//void SetRange(int lower,int upper) {};
void GetRange(int & lower,int & upper) const {};

//int SetPos(int pos) {};

DWORD GetRange() const {};
void GetRange32(int & lower,int &upper) const {};
void SetRange32(int lower,int upper) {};

int SetBase(int nBase) {};
UINT GetBase() const {};

BOOL SetAccel(int nAccel,UDACCEL* pAccel) {};
UINT GetAccel(int nAccel,UDACCEL* pAccel) const {};
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_SPINBTNCTRL_H__98DE1443_58B6_43DA_ABBB_F9A2CA4417C7__INCLUDED_)
// SpinBtnCtrl.cpp : implementation file
//

#include "stdafx.h"
#include "SpinBtnCtrl.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CSpinBtnCtrl

CSpinBtnCtrl::CSpinBtnCtrl()
{
m_min = 0;
m_max = 100;
m_inc = 1;
m_pos = 0;
//m_nbDigits = 1;
m_strFormat = "%.1f";
}

CSpinBtnCtrl::~CSpinBtnCtrl()
{
}


BEGIN_MESSAGE_MAP(CSpinBtnCtrl, CSpinButtonCtrl)
//{{AFX_MSG_MAP(CSpinBtnCtrl)
ON_NOTIFY_REFLECT(UDN_DELTAPOS, OnDeltapos)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSpinBtnCtrl message handlers

void CSpinBtnCtrl::SetRange(double lower,double upper)
{
m_min = lower;
m_max = upper;
}
void CSpinBtnCtrl::GetRange(double & lower,double & upper) const
{
lower = m_min;
upper = m_max;
}
double CSpinBtnCtrl::SetPos(double pos)
{
if ((pos >= m_min)&&(pos <= m_max))
{
double oldPos = m_pos;
m_pos = pos;

return oldPos;
}
return m_pos;
}


double CSpinBtnCtrl::GetPos() const
{
return m_pos;
}
double CSpinBtnCtrl::GetInc()
{
return m_inc;
}
void CSpinBtnCtrl::SetInc(double inc)
{
m_inc = inc;
}

/*
void CSpinBtnCtrl::SetFormat(int nbDigits)
{
m_nbDigits = nbDigits > 0 ? nbDigits : 1;
}
*/

void CSpinBtnCtrl::SetFormat(CString strFormat)
{
m_strFormat = strFormat;
}

void CSpinBtnCtrl::UpdateBuddy()
{
CString strPos;
//strPos.Format("%0.*f",m_nbDigits,m_pos);
strPos.Format(m_strFormat,m_pos);

CWnd* pWnd = GetBuddy();

if (pWnd)
pWnd->SetWindowText(strPos);
}

void CSpinBtnCtrl::OnDeltapos(NMHDR* pNMHDR, LRESULT* pResult) 
{
    NM_UPDOWN* pNMUpDown = (NM_UPDOWN*)pNMHDR;     
    HWND pWnd = ::GetWindow(pNMUpDown->hdr.hwndFrom, GW_HWNDPREV);

    if (pWnd != NULL)
{   
char cValue[20];
        ::GetWindowText(pWnd, cValue, 20);
       
m_pos = atof(cValue);
   
        if (pNMUpDown->iDelta > 0)
m_pos += m_inc;
        else
m_pos -= m_inc;

        if (m_pos > m_max) m_pos = m_max;
        if (m_pos < m_min) m_pos = m_min;
       
CString strValue;
//strValue.Format("%.*f",m_nbDigits,m_pos);
strValue.Format(m_strFormat,m_pos);
::SetWindowText(pWnd, strValue);
    }   
    *pResult = 0; 
}
Un exemple d'utilisation (avec le classwizard on a associé 2 controles Spin à 2 variables CSpinBtnCtrl) :

  • Le 1er spin se comporte comme un spin classique (affiche des double à valeur entière, sans virgule)
  • Le 2è spin affiche des nombres à virgule
BOOL CMyDialog::::OnInitDialog()
{
   ...

   // intervalle de valeurs
   m_spin1.SetRange(-160,220);
   m_spin2.SetRange(-2.4,63.2);

   // valeur de départ
   m_spin1.SetPos(-160);
   m_spin2.SetPos(13.6);

   // incrément/décrément
   m_spin1.SetInc(1);
   m_spin2.SetInc(0.1);

   // format de l'affichage du compagnon
   m_spin1.SetFormat("%g");
   m_spin2.SetFormat("%.1f");

   // mise à jour de l'affichage du compagnon
   m_spin1.UpdateBuddy();
   m_spin2.UpdateBuddy();

   ...
}
On pourrait ajouter la fonction SetDec pour définir la valeur de décrémentation si celle-ci doit être différente de la valeur d'incrémentation. Il faudra alors ajouter une donnée membre supplémentaire à la classe et mettre à jour la fonction OnDeltapos.


Comment travailler avec les contrôles ?
Créé le 27/11/2005[haut]
auteur : Farscape
Il y a deux manières de travailler avec des contrôles placés sur une fenêtre :
La première méthode consiste à récupérer un pointeur sur la fenêtre du contrôle Exemple :

CListBox *pListBox =static_cast<CListBox *>(GetDlgItem(IDC_LISTBOX)) ;
pListBox->AddString("coucou ") ;

CEdit *pEdit=static_cast<CEdit *>(GetDlgItem(IDC_EDIT)) ;
pEdit->SetWindowText("essai") ;

// etc ?
La deuxième consiste à associer une variable au contrôle, à ce niveau on dispose encore de deux possibilités,
La variable associée peut être le contrôle lui-même un CEdit une CListBox etc, ou une variable pour manipuler le contenu du contrôle exemple :

Dans le cas d'un contrôle CEdit il sera pratique de travailler avec une variable CString pour changer ou récupérer le contenu du CEdit .

Cette association peut se faire directement à partir de l'éditeur de ressources :

Dans l'éditeur de ressources sur le contrôle en question faire clic droit

Avec Visual 6.0 :
Sélectionner l'option ClassWizard ,puis l'onglet member variables et enfin le bouton Add Variable.
Il ne reste plus qu'a renseigner le nom de la variable, indiquer le type de variable : contrôle ou valeur.
Dans le cas de valeur le type de variable CString ,int ,long c'est suivant le type de contrôle .

Avec Visual .net :
Sélectionner l'option ajouter une variable.
L'assistant d'ajout de variables apparaît, il ne reste plus qu'à sélectionner les différentes options : contrôle ou valeur .

Examinons le code généré par Visual :
Une variable est rajoutée dans la classe fenêtre ou est situé le contrôle :
Exemple avec une variable de type CEdit et une variable CString attachée à ce même contrôle :
Dans le .h de la classe :

protected:

// Fonctions générées de la table des messages
protected:
DECLARE_MESSAGE_MAP()

public:
CEdit m_EditCtrl;
CString m_strForEdit;
};
dans le code :

CsamplenetView::CsamplenetView()
: CFormView(CsamplenetView::IDD)
, m_strForEdit(_T(""))
{
// TODO : ajoutez ici le code d'une construction

}
La variable CString est initialisée dans le constructeur.

void CsamplenetView::DoDataExchange(CDataExchange* pDX)
{
CFormView::DoDataExchange(pDX);
DDX_Control(pDX, IDC_EDITTEST, m_EditCtrl);
DDX_Text(pDX, IDC_EDITTEST, m_strForEdit);
}
C'est la fonction DodaExchange qui établit le lien entre le contrôle Windows et les variables.

Cette fonction est appelée par la fonction UpdateData et le premier appel initialise les liens notamment pour la variable contrôle (m_EditCtrl) qui va subclasser le contrôle windows.

Voir: faq Comment mettre à jour les contrôles depuis leurs variables et vice-versa ?

Note : j'insiste ,le contrôle sera graphiquement prêt après le premier UpdateData(FALSE) et pas avant, celui étant fait par les MFC dans la fonction CFormView::OnInitialUpdate pour une CFormView ou CDialog::OnInitDialog pour une CDialog.

void CFormView::OnInitialUpdate()
{
ASSERT_VALID(this);

if (!UpdateData(FALSE))
TRACE(traceAppMsg, 0, "UpdateData failed during formview initial update.\n");

CScrollView::OnInitialUpdate();
}

BOOL CDialog::OnInitDialog()
{
//??????.
// transfer data into the dialog from member variables
if (!UpdateData(FALSE))
{
TRACE(traceAppMsg, 0, "Warning: UpdateData failed during dialog init.\n");
EndDialog(-1);
return FALSE;
}
//??????.
}
Conséquences :
Toutes tentatives d'utilisations d'un contrôle avant l'exécution de ces fonctions se solderont par une assertion d'erreur.
Exemple : faire m_EditCtrl.SetWindowText("coucou") ; dans le constructeur.

Liste des erreurs communes en relation avec la fonction DodataExchange :
Utiliser une variable dont le lien n'existe pas dans la fonction provoquera l'erreur.

ASSERT( IsWindow(m_hWnd) );
Signifiant que le handle de fenêtre n'est pas initialisé.

Changer par mégarde l'identifiant d'un contrôle sans faire de même dans la fonction provoquera aussi une erreur.
Supprimer un contrôle dans les ressources et garder le lien dans la fonction provoquera aussi une erreur.

Une fois ces initialisations faites on peut utiliser les deux formes de mise à jour et récupération de valeurs dans un contrôle .
Récupération d'une valeur sur un CEdit :

Directement avec la fonction GetWindowText .

CString str ;
m_EditCtrl.GetWindowText(str) ;
// ou
GetDlgItem(IDC_EDITTEST)->GetWindowText(str) ;
Par la variable :

UpdateData(TRUE) ; // mise a jour des variables associées aux contrôles 
Affectation d'une valeur à un CEdit :
Directement avec la fonction SetWindowText :

CString str = "coucou"  ;
m_EditCtrl.SetWindowText(str) ;
// ou
GetDlgItem(IDC_EDITTEST)->SetWindowText(str) ;
Par la variable :

m_strForEdit="coucou" ;
UpdateData(FALSE) ; // mise a jour des contrôles à partir des variables associées.

Comment changer le sens d'incrémentation d'un CSpinButtonCtrl ?
Créé le 27/11/2005[haut]
auteur : Farscape
Par défaut le sens de défilement est le suivant:
La flèche vers le haut décrémente la valeur.
La flèche vers le bas incrémente la valeur.

Ce qui n'est pas très logique.
L'appel de la fonction GetRange montre que par défaut les bornes de parcours sont inversées.

Pour y remédier il suffit d'appeler la fonction SetRange en spécifiant les bornes souhaitées.

CSpinButtonCtrl::SetRange
void SetRange( int nLower, int nUpper );
void SetRange32( int nLower, int nUpper );
Exemple:

static_cast<CSpinButtonCtrl *>(GetDlgItem(IDC_SPIN1))->SetRange(0,100);
Dans le cas où l'on souhaiterait inverser le sens, il suffit d'inverser les bornes de SetRange .

static_cast<CSpinButtonCtrl *>(GetDlgItem(IDC_SPIN1))->SetRange(100,0);

Comment associer des objets de classes personnalisées dans les contrôles Windows de type List, Combo et Tree sans gérer une liste d'objet en parallèle ?
Créé le 27/11/2005[haut]
auteur : matazz
J'ai vérifié pour les classes suivantes CListCtrl, CListBox, CTreeCtrl, et CComboBox, et cette astuce marche.
Il se peut qu'il y ait d'autres contrôles qui le gèrent, à voir...

En fait lors de la construction de la Liste (ou du Tree, ou du Combo), il est possible d'associer l'adresse d'un pointeur d'un objet personnalisé à chaque Item ajouté.

Soit par exemple une de vos classes ClsMyData, avec des variables et des méthodes, un pointeur de cette classe sera stocké sur 32 bits soit un DWORD.
Il suffit de créer une nouvelle instance de votre classe et de la stocker dans le Contrôle grâce à la méthode SetItemData

ClsMyData *MyObj  = new ClsMyData();
...
//Initilisation de votre objet 

//stockage dans le contrôle grâce à SetItemData
CTreeControl::SetItemData(HTREEITEM hItem, DWORD dwItemData );
CListBox::SetItemData(int nIndex, DWORD dwItemData ); 
CListCtrl::SetItemData( int nItem, DWORD dwData );
CComboBox::SetItemData( int nIndex, DWORD dwItemData );
Ensuite sur les évènement de clic (CTreeCtrl) ou de sélection (List et Combo) vous pouvez récupérer l'objet de votre classe tout simplement en utilisant GetItemData(...) :

ClsMyData *MyObj  = (ClsMyData *)MyList.GetItemData(MyList.GetCurSel());
// et après il est possible d'utilser les méthodes et les membres de l'objet...
MyObj->AfficheCaractéristiques();
....
// 
ClsMyData *MyObj  = (ClsMyData *)MyTree.GetItemData(MyTree.GetSelectedItem( ));
// et après il est possible d'utilser les méthodes et les membres de l'objet...
MyObj->AfficheCaractéristiques();
....

Comment grouper des contrôles dans une boîte de dialogue ?
Créé le 20/05/2006[haut]
auteur : Farscape
L'idée de base est d'utiliser le composant graphique groupbox pour définir l'ensemble des composants à grouper.
Ensuite ce composant par l'intermédiaire d'une classe spécialisée permettrait des manipulations sur l'ensemble des composants inclus dans sa surface.

Exemple d'implémentation:
l'include :

#if !defined(AFX_GROUP_H__AD9C45E9_1C19_44A4_B708_698ED49E178B__INCLUDED_)
#define AFX_GROUP_H__AD9C45E9_1C19_44A4_B708_698ED49E178B__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// Group.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CGroup window

class CGroup : public CButton
{
    // Construction
public:
    CGroup();
    
    // Attributes
public:
    
    // Operations
public:
    // gestion de l'affichage du groupe
    boolShowGroup(int nCmdShow=SW_SHOW);
    
    // active /desactive un group
    boolEnableGroup(BOOL bEnable=TRUE);
    
    // groupe caché ?
    boolIsHide();
    
    // group actif ?
    boolIsEnable();
    
    // Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CGroup)
    //}}AFX_VIRTUAL
    
    // Implementation
public:
    virtual ~CGroup();
    
    // Generated message map functions
protected:
    //{{AFX_MSG(CGroup)
    // NOTE - the ClassWizard will add and remove member functions here.
    //}}AFX_MSG
    
    DECLARE_MESSAGE_MAP()
private:
    // recupere les identifiants des controles d'un group.
    boolGetCtrlOnGroup();
    
private:
    CArray<int,int> m_arIdCtrl;
    CArray<CRect,CRect> m_arRectCtrl;
    
};
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_GROUP_H__AD9C45E9_1C19_44A4_B708_698ED49E178B__INCLUDED_)
le .cpp

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CGroup

CGroup::CGroup()
{
}

CGroup::~CGroup()
{
}
//----------------------------------------------------------------------
bool CGroup::ShowGroup(int nCmdShow/*=SW_SHOW   */)
{
    if(!GetCtrlOnGroup()) return false;
    CWnd *pDialog=GetParent();
    
    for(int i=0;i<m_arIdCtrl.GetSize();i++) pDialog->GetDlgItem(m_arIdCtrl[i])->ShowWindow(nCmdShow);
    
    return true;
}
//----------------------------------------------------------------------
bool CGroup::EnableGroup(BOOL bEnable/*=TRUE*/)
{
    if(!GetCtrlOnGroup()) return false;
    CWnd *pDialog=GetParent();
    for(int i=0;i<m_arIdCtrl.GetSize();i++) pDialog->GetDlgItem(m_arIdCtrl[i])->EnableWindow(bEnable);
    return true;
}
//----------------------------------------------------------------------
bool CGroup::GetCtrlOnGroup()
{
    ASSERT(m_hWnd);
    
    if(m_arIdCtrl.GetSize()) return true;
    
    CWnd *pDialog=GetParent();
    
    CRect rectGrp;
    GetWindowRect(&rectGrp);
    
    CWnd *pCtrl=pDialog->GetNextDlgTabItem(this);
    
    CRect rectCtrl,rectUnion;
    if(!pCtrl) return false;
    
    do
    {
        pCtrl->GetWindowRect(&rectCtrl);
        
        if(rectGrp.PtInRect(CPoint(rectCtrl.left,rectCtrl.top)) &&
            rectGrp.PtInRect(CPoint(rectCtrl.right,rectCtrl.top)) &&
            rectGrp.PtInRect(CPoint(rectCtrl.left,rectCtrl.bottom)) &&
            rectGrp.PtInRect(CPoint(rectCtrl.right,rectCtrl.bottom)))
        {
            m_arIdCtrl.Add(pCtrl->GetDlgCtrlID());
            m_arRectCtrl.Add(rectCtrl);
        }
        else break;
        pCtrl=pDialog->GetNextDlgTabItem(pCtrl);
    }
    while(pCtrl );
    
    return (m_arIdCtrl.GetSize()>0);
}
//---------------------------
bool CGroup::IsHide()
{
    // 
    if(!GetCtrlOnGroup()) return false;
    
    int nct=0;
    CWnd *pDialog=GetParent();
    for(int i=0;i<m_arIdCtrl.GetSize();i++) 
        nct+=!(pDialog->GetDlgItem(m_arIdCtrl[i])->IsWindowVisible());
    
    return(nct==m_arIdCtrl.GetSize());
}
//---------------------------
bool CGroup::IsEnable()
{
    // 
    if(!GetCtrlOnGroup()) return false;
    
    int nct=0;
    CWnd *pDialog=GetParent();
    for(int i=0;i<m_arIdCtrl.GetSize();i++) 
        nct+=pDialog->GetDlgItem(m_arIdCtrl[i])->IsWindowEnabled();
    
    return(nct==m_arIdCtrl.GetSize());
}
//---------------------------

BEGIN_MESSAGE_MAP(CGroup, CButton)
//{{AFX_MSG_MAP(CGroup)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CGroup message handlers
l'utilisation dans une CFormView:

void CSampleSDIView::OnButton1() 
{
    // TODO: Add your control notification handler code here
    //m_group.ShowGroup(!m_group.IsHide()?SW_HIDE:SW_SHOW);
    m_group.EnableGroup(!m_group.IsEnable());
}
Il suffit de mettre un groupbox dans la ressource, d'enlever la propriété visible et de déclarer une variable contrôle dessus en choisissant la classe CGroup.

Attention aux conditions pour qu'un contrôle fasse partie d'un groupe :
Le premier contrôle possède le style group et tab stop.
L'ordre des tab stop doit être réglé au niveau des ressources (tab order).

A adapter selon vos besoins..


Comment régler l'ordre de tabulation sur un contrôle dynamique ?
Créé le 20/05/2006[haut]
auteur : Farscape
En utilisant la fonction SetWindowPos qui permet de spécifier l'ordre des fenêtres (Z order)
Le contrôle dynamique devra néanmoins disposer du style WS_TAB.

Exemple avec un CEdit:

 CEdit* pEdit = new CEdit;
   pEdit->Create(ES_MULTILINE | WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER,
      CRect(10, 10, 100, 100), this, 1);
pEdit->SetWindowPos(pWndAfter,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE);
pWndAfter représente la fenêtre d'insertion dans l'ordre des fenêtres.

On dispose d'autres modes voir MSDN, parmi les plus utiles:

wndBottom: Place la fenêtre en bas du Z-order
wndTop: Place la fenêtre au sommet du Z-order.


Comment utiliser un contrôle standard .Net dans une application MFC ?
Créé le 22/01/2007[haut]
auteur : nico-pyright(c)
Il s'agit tout d'abord de transformer son application MFC en application managée, pour ceci, il faut ajouter le support du CLR.
Bouton droit sur le projet -> Common properties -> general -> Common language runtime support ; Mettre à /clr (common language runtime support).

Ensuite, pour ajouter un contrôle .Net, il vous faut d'abord ajouter un static MFC classique où vous voudrez positionner votre contrôle .Net

Ajouter ensuite l'include dans stdafx.h
#include <afxWinForms.h>

On utilise ensuite l'objet CWinFormsControl pour instancier une donnée membre de notre contrôle.
Par exemple, si je veux utiliser le contrôle LinkLabel :
Microsoft::VisualC::MFC::CWinFormsControl< System::Windows::Forms::LinkLabel >m_linkLabelDotNet;

Ensuite, dans le DoDataExchange
DDX_ManagedControl( pDX, IDC_DOTNET, m_linkLabelDotNet );

Rajouter ensuite un handler d'évènement
void linkLabel_LinkClicked( System::Object^ sender, System::Windows::Forms::LinkLabelLinkClickedEventArgs^ e );
et utiliser la macro _DELEGATE_MAP pour câbler les méthodes
BEGIN_DELEGATE_MAP( CMfcControlDotNetDlg )
    EVENT_DELEGATE_ENTRY( linkLabel_LinkClicked, System::Object ^, System::Windows::Forms::LinkLabelLinkClickedEventArgs^ )
END_DELEGATE_MAP()

On crée le délégate à l'initialisation du composant :

m_linkLabelDotNet->LinkClicked += MAKE_DELEGATE(System::Windows::Forms::LinkLabelLinkClickedEventHandler , linkLabel_LinkClicked);

Et vous pouvez ainsi désormais utiliser la fonction
void CMfcControlDotNetDlg::linkLabel_LinkClicked(System::Object ^sender, System::Windows::Forms::LinkLabelLinkClickedEventArgs ^e)
{
    System::Windows::Forms::MessageBox::Show(L"Coucou");
}

pour capter l'évenement du click

Téléchargez le programme d'exemple : ftp://ftp-developpez.com/nico-pyright/faq/MfcControlDotNet.rar
voir aussi Mixer du C++/CLI avec du code Win32 ou MFC


Comment réaliser un bouton interrupteur ?
Créé le 22/01/2007[haut]
auteur : Farscape
Je vous propose de créer un petit contrôle bouton très simple : le bouton Interrupteur
Définition:
Un bouton interrupteur est un bouton qui lorsqu'il est activé reste dans l'état déclenché.
Cet état est caractérisé par un aspect visuel différent de celui du repos.
Pour réaliser ce contrôle je vais utiliser la classe CBitmapButton des MFC.
Cette classe permet d'associer au bouton des bitmaps correspondant à leurs états.

Détails du composant:
le .h:

#if !defined(AFX_SWITCHBUTTON_H__E61BA43E_E769_4651_84F1_661092D276CE__INCLUDED_)
#define AFX_SWITCHBUTTON_H__E61BA43E_E769_4651_84F1_661092D276CE__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// SwitchButton.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CSwitchButton window

class CSwitchButton : public CBitmapButton
{
// Construction
public:
    CSwitchButton();

// Attributes
private:
    UINT m_nIDBitmapResource, 
         m_nIDBitmapResourceSel;
    bool m_bSwitch;
// Operations
public:

    bool IsSwitch(){return m_bSwitch;}

    void SetSwitch()
    {        
        m_bSwitch=!m_bSwitch;
        SetBitmapButton(m_nIDBitmapResourceSel,m_nIDBitmapResource);
    }
    void SetBitmapButton(UINT nIDBitmapResource, UINT nIDBitmapResourceSel = 0)
    {
        m_nIDBitmapResource    =nIDBitmapResource;
        m_nIDBitmapResourceSel    =nIDBitmapResourceSel;
        LoadBitmaps(nIDBitmapResource,nIDBitmapResourceSel);
        Invalidate();
    }

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CSwitchButton)
    //}}AFX_VIRTUAL

// Implementation
public:
    virtual ~CSwitchButton();

    // Generated message map functions
protected:
    //{{AFX_MSG(CSwitchButton)
    afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
    //}}AFX_MSG

    DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_SWITCHBUTTON_H__E61BA43E_E769_4651_84F1_661092D276CE__INCLUDED_)
le .cpp:

// SwitchButton.cpp : implementation file
//

#include "stdafx.h"
#include "visualpaye.h"
#include "SwitchButton.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CSwitchButton

CSwitchButton::CSwitchButton()
{
    m_bSwitch=false;
}

CSwitchButton::~CSwitchButton()
{
}

BEGIN_MESSAGE_MAP(CSwitchButton, CBitmapButton)
    //{{AFX_MSG_MAP(CSwitchButton)
    ON_WM_LBUTTONUP()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSwitchButton message handlers

void CSwitchButton::OnLButtonUp(UINT nFlags, CPoint point) 
{
    // TODO: Add your message handler code here and/or call default
    SetSwitch();
    CBitmapButton::OnLButtonUp(nFlags, point);
}
Le code est très simple, mon nouveau contrôle hérite bien de la classe CBitmapButton
Pour initialiser le bouton il suffira d'appeler la fonction SetBitmapButton
Avec les deux bitmaps correspondant à l'état repos et activé.
Sur le message WM_LBUTTONUP l'état est permuté et est consultable avec la fonction
IsSwitch().

Implémentation du bouton dans votre projet :

Pour implémenter ce bouton il suffira de mettre un bouton classique sur la fenêtre de dialogue et d'activer la propriété Owner Draw.
Ensuite il faudra lui attacher une variable contrôle, par défaut la classe CButton.
A remplacer si possible avec la classe CSwitchButton (suivant les versions de Visual ).
Sinon il suffira de changer sa définition dans le .h de votre classe fenêtre en CSwitchButton.
Dans la fonction OnInitDialog ou OnInitialUpdate de votre fenêtre on activera le bouton avec la fonction SetBitmapButton

void CMyFormView::OnInitialUpdate() 
{
       CFormView::OnInitialUpdate();
       m_Bouton.SetBitmapButton(IDB_BITMAPSWTCH, IDB_BITMAPSWTCH2);
//....
void CMyFormView::OnButtonSwitch() 
{
    // TODO: Add your control notification handler code here    
    TRACE("\nSwitch:%d",m_BoutonSwitch.IsSwitch());
}
Le résultat en images :

au repos :


Activé :


Note: j'ai utilisé la classe CBitmapButton à la place de la classe CButton parce qu'elle permet plus de possibilités sur la gestion des états par bitmaps.


Comment insérer un contrôle dynamique dans l'ordre de tabulation ?
Créé le 17/09/2007[haut]
auteur : Farscape
Lors de la création dynamique d'un contrôle disposant du style WS_TABSTOP, il devient nécessaire de spécifier son placement dans l'ordre de tabulation (tab order) de la fenêtre.
On Utilisera la méthode SetWindowPos comme suit :

CWnd *pWndAfter=GetNextDlgTabItem(NULL);
pWndDynCtrl->SetWindowPos(pWndAfter,0,0,0,0,SWP_NOMOVE | SWP_NOSIZE);
dans mon exemple pWndDynCtrl sera placé dans l'ordre de tabulation aprés pWndAfter, qui est ici le premier contrôle dans l'ordre de tabulation.


Comment intercepter les déplacements d'un contrôle curseur ?
Créé le 17/09/2007[haut]
auteur : Farscape
Il faut intercepter le message WM_HSCROLL ou WM_VSCROLL sur la fenêtre parent du CSliderCtrl
le contrôle appelle l'un ou l'autre message, ça dépendra de son orientation horizontale ou verticale dans la fenêtre.
Il faut ensuite tester le pointeur pScrollBar fournit dans la fonction de réponse pour s'assurer que l'on est bien en présence du contrôle CSliderCtrl.

void CMyFormView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
    // TODO: Add your message handler code here and/or call default
    if(GetDlgItem(IDC_SLIDER1)==pScrollBar)
    {
        TRACE("\nSlider");
    }
    CFormView::OnHScroll(nSBCode, nPos, pScrollBar);
}

Comment mettre en place un contrôle bouton de commande ?
Créé le 07/07/2008[haut]
auteur : Farscape
Parmi les nouveaux contrôles ajoutés aux MFC on trouve le "Command bouton control"
L'éditeur de ressources de Visual 2008 permet l'ajout de ce composant directement dans la form .
En fait un "command button control" est un bouton avec le style BS_COMMANDLINK ou BS_DEFCOMMANDLINK.
L'association du contrôle à une variable contrôle sera donc du type CButton qui est la classe MFC de base pour gérer les boutons.

Comment procéder :
On spécifie le libellé du bouton dans l'éditeur de ressources ou par un classique SetWindowText dans le code.
Une nouvelle méthode SetNote permet de définir le libellé de description.
Enfin la méthode SetShield permet de changer l'icône pour celle du bouclier utilisée par Vista pour l'elévation des droits.

//CButton m_Btn;
//CButton m_BtnAcces;

CString str=_T("ceci est un commentaire\nsur deux lignes");
m_Btn.SetNote(str);
m_BtnAcces.SetShield(TRUE);
l'exemple en image:


Comment implémenter un nouveau message ON_CONTROL sur un contrôle ?
Créé le 07/07/2008[haut]
auteur : Farscape
Dans la Faq on trouve un post permettant d'implémenter un message privé faq Comment implémenter un message supplémentaire à partir d'un contrôle ? sur un contrôle à destination du parent.
Un message ON_CONTROL appelle une fonction dans la classe parent sans argument particulier
Pour illustrer le sujet je vais rajouter une notification double clic sur un contrôle Edit.
On commencera par créer avec l'assistant une classe héritée de CEdit , puis on implémentera le message à intercepter et à relayer à la fenêtre parent.

#define EDIT_LBUTTONDBLCLK 1 // evenement custom

void CTestEdit::OnLButtonDblClk(UINT nFlags, CPoint point)
{
    // TODO: Add your message handler code here and/or call default
    GetParent()->SendMessage(WM_COMMAND,MAKEWPARAM( GetDlgCtrlID(), EDIT_LBUTTONDBLCLK),(LPARAM)GetSafeHwnd());
    CEdit::OnLButtonDblClk(nFlags, point);
}
L'utilisation dans la classe parent :

BEGIN_MESSAGE_MAP(CTESTONCONTROLDlg, CDialog)
    //{{AFX_MSG_MAP(CTESTONCONTROLDlg)
    ON_WM_SYSCOMMAND()
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    //}}AFX_MSG_MAP
    ON_CONTROL(EDIT_LBUTTONDBLCLK,IDC_EDITTEST,OnDblClickEdit)
END_MESSAGE_MAP()
 
void CTESTONCONTROLDlg::OnDblClickEdit()
{
    //
    AfxMessageBox("coucou");
}
lien : faq Comment implémenter un message supplémentaire à partir d'un contrôle ?

Comment inverser le déplacement d'un CSliderCtrl ?
Créé le 07/07/2008[haut]
auteur : Farscape
Après lecture de l'aide sur MSDN on pourrait penser que le style TBS_DOWNISLEFT puisse faire l'affaire, il n'en est rien.
Le moyen le plus simple à ce jour est de faire un appel spécifique à la méthode SetPos comme suit.

MySlider.SetPos(MySlider.GetRangeMax()-x);
Où X représente la valeur initiale du déplacement souhaité.


Comment faire un Tooltip multilignes ?
Créé le 07/07/2008[haut]
auteur : Farscape
En utilisant la méthode SetMaxTipWidht comme suit :

m_tooltip.SetMaxTipWidth(500);
SetMaxTipWidht permet de spécifier la taille maximum du texte du tooltip , en précisant une taille et en fournissant une chaîne de caractères avec des lignes séparées par des "/r/n" la bulle tient compte des lignes.
Voir aussi faq Comment gérer un Tooltip (bulle) sur un contrôle CStatic ou un CEdit ?

lien : faq Comment gérer un Tooltip (bulle) sur un contrôle CStatic ou un CEdit ?


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