IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
logo

FAQ VC++ et MFCConsultez toutes les FAQ

Nombre d'auteurs : 20, nombre de questions : 545, 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ôlesDivers (21)
précédent sommaire suivant
 

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 :

  • Le code de déclaration dans la classe.
  • Initialise la valeur dans le constructeur - 1.
  • 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 :

Code c++ : Sélectionner tout
1
2
  
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

Code c++ : Sélectionner tout
1
2
  
 UpdateData(FALSE) ;
pour la mise à jour du contrôle et

Code c++ : Sélectionner tout
1
2
  
 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 :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  
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);

Mis à jour le 5 avril 2013 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 :

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

Mis à jour le 17 juin 2004 farscape

Il suffit de mettre le caractère "\n" entre chaque ligne et de cocher l'option " multiligne" dans l'onglet style du bouton.

Mis à jour le 5 avril 2013 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 :

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

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
  
// 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);

Mis à jour le 5 avril 2013 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.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
  
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.

Mis à jour le 19 septembre 2005 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.

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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
// 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 :

Code C++ : Sélectionner tout
1
2
3
4
5
#include "TplLayout.hpp" 
  
class CTestMdILayoutView : public CTplLayout<CFormView> 
{ 
    ....
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
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:

Code C++ : Sélectionner tout
1
2
3
4
5
class CAboutDlg : public CTplLayout<CDialog> 
{ 
public: 
    CAboutDlg(); 
    //...............

code:

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 :

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
#include "TplLayout.hpp" 
///////////////////////////////////////////////////////////////////////////// 
// CMyDlgBar dialog 
class CMyDlgBar : public CTplLayout<CDialogBar> 
{ 
// Construction 
public: 
    CMyDlgBar();   // standard constructor 
  
    void InitDialog(); 
    //......

le code :

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
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:

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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: TestMdiLayout.zip

Mis à jour le 19 septembre 2005 farscape

Les notifications de message des CScrollbarss'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.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
  
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; 
} 
}

Mis à jour le 19 septembre 2005 farscape

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)

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
#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_)
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
// 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

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

Mis à jour le 27 novembre 2005 bigboomshakala

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 :

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
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 :

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
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 :

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
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.

Code C++ : Sélectionner tout
1
2
3
4
5
6
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: 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.

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

Code C++ : Sélectionner tout
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 .

Code C++ : Sélectionner tout
1
2
3
4
CString str ; 
m_EditCtrl.GetWindowText(str) ; 
// ou 
GetDlgItem(IDC_EDITTEST)->GetWindowText(str) ;

Par la variable :

Code C++ : Sélectionner tout
UpdateData(TRUE) ; // mise a jour des variables associées aux contrôles

Affectation d'une valeur à un CEdit :
Directement avec la fonction SetWindowText :

Code C++ : Sélectionner tout
1
2
3
4
CString str = "coucou"  ; 
m_EditCtrl.SetWindowText(str) ; 
// ou 
GetDlgItem(IDC_EDITTEST)->SetWindowText(str) ;

Par la variable :

Code C++ : Sélectionner tout
1
2
m_strForEdit="coucou" ; 
UpdateData(FALSE) ; // mise a jour des contrôles à partir des variables associées.

Mis à jour le 27 novembre 2005 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.

Code c++ : Sélectionner tout
1
2
3
4
  
CSpinButtonCtrl::SetRange 
void SetRange( int nLower, int nUpper ); 
void SetRange32( int nLower, int nUpper );
Exemple:

Code c++ : Sélectionner tout
1
2
  
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 .

Code c++ : Sélectionner tout
1
2
  
static_cast<CSpinButtonCtrl *>(GetDlgItem(IDC_SPIN1))->SetRange(100,0);

Mis à jour le 27 novembre 2005 farscape

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

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
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(...) :

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
  
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(); 
.... 
//où  
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(); 
....

Mis à jour le 27 novembre 2005 matazz

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 :

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

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

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
  
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..

Mis à jour le 20 mai 2006 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:

Code c++ : Sélectionner tout
1
2
3
4
5
  
 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.

Mis à jour le 20 mai 2006 farscape

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

Code C++ : Sélectionner tout
#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 :

Code C++ : Sélectionner tout
Microsoft::VisualC::MFC::CWinFormsControl< System::Windows::Forms::LinkLabel >m_linkLabelDotNet;

Ensuite, dans le DoDataExchange

Code C++ : Sélectionner tout
DDX_ManagedControl( pDX, IDC_DOTNET, m_linkLabelDotNet );

Rajouter ensuite un handler d'évènement

Code C++ : Sélectionner tout
void linkLabel_LinkClicked( System::Object^ sender, System::Windows::Forms::LinkLabelLinkClickedEventArgs^ e );

et utiliser la macro _DELEGATE_MAP pour câbler les méthodes

Code C++ : Sélectionner tout
1
2
3
BEGIN_DELEGATE_MAP( CMfcControlDotNetDlg ) 
    EVENT_DELEGATE_ENTRY( linkLabel_LinkClicked, System::Object ^, System::Windows::Forms::LinkLabelLinkClickedEventArgs^ ) 
END_DELEGATE_MAP()

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

Code C++ : Sélectionner tout
m_linkLabelDotNet->LinkClicked += MAKE_DELEGATE(System::Windows::Forms::LinkLabelLinkClickedEventHandler , linkLabel_LinkClicked);

Et vous pouvez ainsi désormais utiliser la fonction

Code C++ : Sélectionner tout
1
2
3
4
void CMfcControlDotNetDlg::linkLabel_LinkClicked(System::Object ^sender, System::Windows::Forms::LinkLabelLinkClickedEventArgs ^e) 
{ 
    System::Windows::Forms::MessageBox::Show(L"Coucou"); 
}

pour capter l'évènement du clic.

Téléchargez le programme d'exemple : MfcControlDotNet.rar
voir aussi Mixer du C++/CLI avec du code Win32 ou MFC

Mis à jour le 22 janvier 2007 nico-pyright(c)

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 desMFC.
Cette classe permet d'associer au bouton des bitmaps correspondant à leurs états.

Détails du composant:
le .h:

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

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

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
  
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.

Mis à jour le 22 janvier 2007 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 :

Code c++ : Sélectionner tout
1
2
3
  
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.

Mis à jour le 17 septembre 2007 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.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
  
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); 
}

Mis à jour le 17 septembre 2007 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.

Code c++ : Sélectionner tout
1
2
3
4
5
6
7
  
//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:

Mis à jour le 7 juillet 2008 farscape

Dans la Faq on trouve un post permettant d'implémenter un message privé 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.

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
8
#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 :

Code C++ : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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"); 
}

Mis à jour le 7 juillet 2008 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.

Code C++ : Sélectionner tout
MySlider.SetPos(MySlider.GetRangeMax()-x);

Où X représente la valeur initiale du déplacement souhaité.

Mis à jour le 7 juillet 2008 farscape

En utilisant la méthode SetMaxTipWidht comme suit :

Code C++ : Sélectionner tout
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.

Mis à jour le 7 juillet 2008 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 © 2024 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.