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
- 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 ?
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 ; |
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) ; |
Code c++ : | Sélectionner tout |
1 2 | UpdateData(TRUE) ; |
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); |
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); } |
Il suffit de mettre le caractère "\n" entre chaque ligne et de cocher l'option " multiligne" dans l'onglet style du bouton.
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); } |
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); |
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. |
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
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; } } |
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; } |
- 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(); ... } |
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. |
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 ); |
Code c++ : | Sélectionner tout |
1 2 | static_cast<CSpinButtonCtrl *>(GetDlgItem(IDC_SPIN1))->SetRange(0,100); |
Code c++ : | Sélectionner tout |
1 2 | static_cast<CSpinButtonCtrl *>(GetDlgItem(IDC_SPIN1))->SetRange(100,0); |
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 ); |
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(); .... |
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_) |
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 |
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()); } |
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..
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); |
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.
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
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_) |
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); } |
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()); } |
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.
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); |
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); } |
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); |
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"); } |
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é.
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.
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 çaLes 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.