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 donner le focus à un contrôle à partir d'OnInitDialog ?
- Comment traiter l'acceptation d'une boîte de dialogue ?
- Comment intercepter les touches entrée et échappement dans une boîte de dialogue ?
- Comment gérer la sortie d'une boîte de dialogue ?
- Comment accéder aux contrôles d'une boîte de dialogue à partir d'une autre boîte de dialogue ?
- Comment charger les contrôles d'une CDialog avant DoModal() ?
- Comment récupérer la fenêtre parent dans une boîte de dialogue ?
- Comment mettre des fenêtres de contrôles dynamiquement dans une boîte de dialogue ?
- Comment démarrer une boîte de dialogue en mode caché ?
- Comment lancer un traitement au démarrage d'une CDialog ?
- Comment rajouter un bouton agrandir et réduire sur une CDialog ?
- Comment faire une application boîte de dialogue sans bouton dans la barre des tâches Windows ?
- Comment rajouter un menu à une boîte de dialogue ?
- Pourquoi les notifications ON_UPDATE_COMMAND_UI ne fonctionnent pas dans une CDialog ?
- Comment rendre transparente une boîte de dialogue ?
- Comment mettre une image de fond dans une boîte de dialogue ?
- Comment faire pour qu'un projet boîte de dialogue soit toujours en avant plan ?
- Comment imprimer une boite de dialogue ?
- Comment créer une boîte de dialogue non modale ?
- Comment libérer la mémoire sur une fenêtre dynamique ?
- Comment est calculée l'unité de mesure d'une boîte de dialogue ?
- Comment afficher une CDialog depuis une DLL?
- Quand appeler la macro AFX_MANAGE_STATE(AfxGetStaticModuleState( )) ?
- Comment créer une boîte de dialogue dynamiquement sans ressources ?
- Comment copier une boîte de dialogue d'un projet à un autre ?
- Comment partager des données entre une CFormView et une CDialog ?
- Comment afficher une boîte de dialogue à des coordonnées choisies ?
- Comment mettre en place un raccourci clavier dans une boîte de dialogue ?
- Comment implémenter le tab stop entre deux boîtes de dialogue imbriquées ?
- Comment savoir si une boîte de dialogue non modale est active ?
- Comment donner le focus à un contrôle dans OnInitDialog ?
Par défaut la fonction OnInitDialog renvoie TRUE (valeur générée par visual) .
Ce qui sera interprété comme : donner le focus au premier contrôle suivant l'ordre de tabulation.
Si on souhaite donner le focus à un autre contrôle à partir de cette fonction il faudra retourner FALSE et utiliser la fonction SetFocus pour spécifier le contrôle de votre choix.
Lorsque l'utilisateur utilisera la touche entrée ou le bouton OK dans une boîte de dialogue celle-ci se fermera automatiquement.
Pour placer un traitement sur l'acception il suffit de générer la méthode OnOk sur le bouton IDOK de la boîte de dialogue avec l'aide de « ClassWizard ».
Classiquement si on veut récupérer les valeurs pour qu'elles soient accessibles après l'appel de la fonction DoModal() , on exécutera la fonction UpdateData(TRUE) pour mettre à jour l'ensemble des variables , et on procédera éventuellement à des traitements supplémentaires .
Par exemple la récupération de la sélection en cours dans une CListBox sachant qu'après c'est trop tard…
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | void CMyDlg::OnOK() { m_nIndiceSel = m_ListBox.GetCurSel(); if(m_nIndiceSel!=-1) m_ListBox.GetText( m_nIndiceSel,m_strSel ); CDialog::OnOK(); } void CMyView::OnShowDialog() { CMyDlg dlg ; if (dlg.DoModal()==IDOK) { CString str ; str.Format("%d:",dlg.m_nIndiceSel) ; AfxMessageBox(str+dlg.m_strSel) ; } } |
En surchargeant la méthode OnCommand de la classe CWnd:
Code c++ : | Sélectionner tout |
1 2 3 | CWnd::OnCommand virtual BOOL OnCommand(WPARAM wParam,LPARAM lParam ); |
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 | BOOL CAboutDlg::OnCommand(WPARAM wParam, LPARAM lParam) { // TODO: Add your specialized code here and/or call the base class CWnd *pWnd = GetFocus(); switch(wParam) { case IDOK: if(pWnd!=GetDlgItem(IDOK)) { return FALSE; } break; case IDCANCEL:if(pWnd!=GetDlgItem(IDCANCEL)) { return FALSE; } break; } return CDialog::OnCommand(wParam, lParam); } |
Même chose avec la touche Echappement , la fenêtre ne pourra pas se fermer si le bouton possédant le « focus » n'est pas le bouton IDCANCEL.
Une petite précision : lors de la femeture de la fenêtre par la croix le message IDCANCEL sera généré .
Ce qui ne sera pas le cas si pour récupérer les messages claviers on était passé par la fonction PreTranslateMessage de la classe Cwnd :
Code c++ : | Sélectionner tout |
1 2 3 | CWnd::PreTranslateMessage virtual BOOL PreTranslateMessage( MSG* pMsg ); |
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | BOOL CAboutDlg::PreTranslateMessage(MSG* pMsg) { if(pMsg->message == WM_KEYDOWN) { if(pMsg->wParam == VK_ESCAPE) return TRUE; if(pMsg->wParam == VK_RETURN) return TRUE; } return CDialog::PreTranslateMessage(pMsg); } |
Les boîtes de dialogues disposent de deux événements boutons pour se fermer :
Le bouton OK associé à l'identifiant IDOK et le bouton Abandon associé à l'identifiant IDCANCEL.
A ces deux boutons sont associées respectivement les fonctions virtuelles de la classe CDialog:
Code c++ : | Sélectionner tout |
1 2 3 | virtual void OnOK(); virtual void OnCancel(); |
Dans le cas où l'on souhaite avoir une autre valeur que celles prédéfinies on procédera comme suit.
Il suffira d'appeler la fonction EndDialog avec le numéro d'événement souhaité, exemple :
Si on doit fermer la boîte de dialogue sur le clic d'un autre bouton, il suffira de générer l'événement BN_CLICKED sur le bouton, et d'appeler la fonction EndDialog avec son identifiant.
Le cas typique est de récupérer des informations de la deuxième boîte de dialogue pour renseigner la boîte de dialogue parent qui l'a créée.
L'accès aux contrôles d'une boîte de dialogue n'est possible que si celle-ci est active,c'est-à-dire tant que la fonction DoModal() n'a pas renvoyé le code de retour IDOK ou IDCANCEL.
Après DoModal() la boîte de dialogue n'est plus valide graphiquement donc toute tentative d'accès aux contrôles déclenchera une assertion d'erreur.
L'exemple ci-dessous illustre le sujet :
Une première boîte est lancée CDlg1 sur un click bouton on exécute la boîte de Dialogue CDlg2 .
Celle-ci aura comme parent Window l'objet de la classe CDlg1 .
Sur le OnOk de Cdlg2 on récupère l'accès à l'objet parent ce qui permet un échange de données avant que l'objet (CDlg2) ne soit plus valide graphiquement.
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 | class CDlg1: public CDialog { public: CDlg1(CWnd* pParent = NULL);// standard constructor // Generated message map functions //{{AFX_MSG(CDlg1) virtual BOOL OnInitDialog(); afx_msg void OnButton1(); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; class CDlg2: public CDialog { public: CDlg2(CWnd* pParent = NULL);// standard constructor // Generated message map functions //{{AFX_MSG(CDlg2) virtual BOOL OnInitDialog(); virtual void OnOK(); //}}AFX_MSG DECLARE_MESSAGE_MAP() public: }; // appel dlg1 CDlg1 dlg; dlg.DoModal(); void CDlg1::OnButton1() { // TODO: Add your control notification handler code here CDlg2 dlg(this); // fournit comme parent CDlg1 par exemple // ou alors affectation à une variable interne. // par un accesseur etc... dlg.DoModal(); } void CDlg2::OnOK() { // TODO: Add extra validation here CDlg1 *pDlg=( CDlg1 *)GetParent(); // Maintenant j'ai accès à la boite de dialogue CDlg1. CDialog::OnOK(); } |
Rappels : Tant que la boîte de dialogue modale n'est pas initialisée graphiquement aucune mise à jour des contrôles n'est possible.
La boîte de dialogue modale est valide à partir de l'exécution de DoModal().
La première fonction disponible pour l'initialisation des contrôles : OnInitDialog
Les contrôles sont valides à partir du premier UpdateData voir post : Comment mettre à jour les contrôles depuis leurs variables et vice-versa ?
Plusieurs cas sont à étudier :
Les contrôles CEdit ou CStatic:
Avec l'aide de ClassWizard onglet member variables :
Attacher aux contrôles une variable de type CString ,int etc…
À l'appel de la boîte il suffit d'affecter les valeurs aux variables comme ci-dessus.
Code C++ : | Sélectionner tout |
1 2 3 4 5 6 7 | void CTestDlgDlg::OnButton1() { // TODO: Add your control notification handler code here CDialog1 dlg; dlg.m_strEdit1="coucou" ; dlg.DoModal(); } |
Dans la fonction OnInitDialog il restera à faire un UpdateData(FALSE) comme dans l'exemple ci-dessus :
Code C++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | BOOL CDialog1::OnInitDialog() { CDialog::OnInitDialog(); // TODO: Add extra initialization here UpdateData(FALSE); // mise à jour des contrôles return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } |
Les Listbox et CCombox et autres contrôles gérant des listes : On ne peut pas affecter un tableau d'éléments aux contrôles, il faudra stocker les différentes valeurs dans un tableau dynamique de type CStringArray ,CArray ou vector et procéder au chargement des contrôles dans la fonction OnInitDialog.
Néanmoins je propose une solution pour les CListbox et CComboBox permettant d'écrire ceci :
Code C++ : | Sélectionner tout |
1 2 3 4 | CDialog1 Dlg; Dlg.m_Listbox.AddString("essai"); Dlg.m_Listbox.AddString("essai1"); Dlg.DoModal(); |
La solution passe la définition d'une classe dérivée de CListBox et de la redéfinition de la fonction AddString :
Attention je précise que cette fonction n'est pas virtuelle dans la classe CListBox.
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 | class CMyListBox : public CListBox { // Construction public: CMyListBox(); // Attributes public: int AddString( LPCTSTR lpszItem ); // Operations public: CStringArray m_strArray; // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CMyListBox) public: protected: virtual void PreSubclassWindow(); //}}AFX_VIRTUAL // Implementation public: virtual ~CMyListBox(); // Generated message map functions protected: //{{AFX_MSG(CMyListBox) //}}AFX_MSG DECLARE_MESSAGE_MAP() }; int CMyListBox::AddString(LPCTSTR lpszItem) { // si la listbox est valide appel de la fonction d'origine. if(m_hWnd!=NULL) return CListBox::AddString(lpszItem); m_strArray.Add(lpszItem); return LB_ERR; } void CMyListBox::PreSubclassWindow() { // TODO: Add your specialized code here and/or call the base class CListBox::PreSubclassWindow(); for(int i=0; i<m_strArray.GetSize(); i++) AddString(m_strArray[i]); } |
Note : L'insertion du tableau dans le contrôle se fait dans la fonction PreSubclassWindow().
Lorsque l'on construit un objet boîte de dialogue le constructeur permet de spécifier la fenêtre parent de la dialogue
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 | void CMyFormView::OnButtonMachin() // c'est un exemple bien sur ... { CMyDialog Dlg(this) Dlg.DoModal(); } |
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 | BOOL CMyDialog::OnInitDialog() { CDialog::OnInitDialog(); CMyFormView *pView=static_cast< CMyFormView *>(GetParent()); //…………… |
Le pointeur retourné ne sera pas l'onglet mais la MainFrame .
On pourra contourner ce problème comme suit :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 | BOOL CMyDialog::OnInitDialog() { CDialog::OnInitDialog(); CMyFormView *pView=static_cast< CMyFormView *>(m_pParentWnd); //…………… |
On procédera de la manière suivante :
On créera autant de boîtes de dialogue devant apparaître dans la boîte de dialogue principale.
On réglera leurs options :
- style : child.
- Border : None
Pour créer dynamiquement la fenêtre :
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 | //----------------------------------------------------------- CDialog *CSwitchDlgDlg::CreatePage(UINT nPlaceCtrlId,UINT nDialogID,CRuntimeClass *pClass/*=NULL*/) { CDialog *pDlg=NULL; if(pClass) pDlg=reinterpret_cast<CDialog *>(pClass->CreateObject()); else pDlg= new CDialog; pDlg->Create(nDialogID,this); ASSERT(IsWindow(pDlg->m_hWnd)); CRect rect; CWnd *pWnd = GetDlgItem(nPlaceCtrlId); ASSERT(pWnd != NULL); ASSERT(IsWindow(pWnd->m_hWnd)); pWnd->GetWindowRect(&rect); ScreenToClient(&rect); pDlg->SetWindowPos(NULL, rect.left, rect.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE ); pDlg->EnableWindow(TRUE); return pDlg; } |
La fenêtre sera créée à l'emplacement d'un contrôle groupbox par exemple identifié par nPlaceCtrlId.
Pour basculer d'une boîte de dialogue à l'autre il suffira de faire :
Code C++ : | Sélectionner tout |
1 2 3 4 5 6 7 | // desactive pDlg->ShowWindow(SW_HIDE); pDlg->EnableWindow(FALSE); // active l'autre fenetre pDlg2->ShowWindow(SW_SHOW); pDlg2->EnableWindow(TRUE); |
On obtient ainsi un système similaire à une gestion par onglet ,mais sans le CTabCtrl…
Notes :
si un runtime de classe est passé en argument c'est un objet de cette classe qui sera créé, permettant ainsi d'avoir une gestion personnalisée de la fenêtre fille.
Cette classe devra donc avoir les macros :
DECLARE_DYNCREATE dans le .h
IMPLEMENT_DYNCREATE dans le .cpp
Comme avec une CFormView
Pour une mise en application voir cet exemple : SwitchDlg.zip
Ceux qui se sont donnés la peine d'essayer de mettre un ShowWindow(SW_HIDE) dans la fonctionOnInitDialog ont constaté que ça ne fonctionne pas.
Pourquoi ? En traçant le code source de CDialog on s'aperçoit qu'à la suite de OnInitDialog() il y a un appel de ShowWindow(SW_SHOW) qui réduit à néant nos efforts.
Pourtant il existe un moyen de contrer cela en interceptant le message WM_WINDOWPOSCHANGING et en enlevant le style SWP_SHOWWINDOW. Voir la fonction SetWindowPos dans MSDN.
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | CMyDialog::CMyDialog(CWnd* pParent /*=NULL*/) : CDialog(CMyDialog::IDD, pParent) { /*bool*/ m_bShow = false; } void CMyDialog::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos) { if(!m_bShow) lpwndpos->flags &= ~SWP_SHOWWINDOW; CDialog::OnWindowPosChanging(lpwndpos); } void CMyDialog::SetShowWindow() { m_bShow = true; ShowWindow(SW_SHOW); } |
Une précision ce post concerne le lancement d'un traitement au démarrage de la boite de dialogue, pouvant mettre à jour des éléments graphiques sans interactions clavier / souris.
Dans ce contexte placer le traitement dans OnInitDialog pose un problème la boîte de dialogue n'étant pas encore affichée le traitement risque de se dérouler mais sans afficher les interactions graphiques de la fenêtre et de ses contrôles (affichage partiel).
On continuera bien sûr à faire les initialisations graphiques simples dans la fonction OnInitDialog.
En conséquence le traitement doit être lancé quand la boite de dialogue est entièrement prête graphiquement, sinon le risque est de ne rien voir apparaître à l'écran.
La solution est de le déclencher au premier WM_PAINT de la fenêtre comme dans l'exemple ci-dessus.
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 | // le header class CTestDlg : public CDialog { // Construction public: CTestDlg(CWnd* pParent = NULL); // standard constructor // Dialog Data //{{AFX_DATA(CTestDlg) enum { IDD = IDD_DIALOGTEST }; // NOTE: the ClassWizard will add data members here //}}AFX_DATA public: bool m_bFirstInit; // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CTestDlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL LRESULT OnRunTest(UINT wParam,LONG lParam); // Implementation protected: // Generated message map functions //{{AFX_MSG(CTestDlg) afx_msg void OnPaint(); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; |
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 | // la partie .cpp #define WM_RUNTEST WM_USER+100 CTestDlg::CTestDlg(CWnd* pParent /*=NULL*/) : CDialog(CTestDlg::IDD, pParent) { //{{AFX_DATA_INIT(CTestDlg) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT m_bFirstInit=true; } void CTestDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CTestDlg) // NOTE: the ClassWizard will add DDX and DDV calls here //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CTestDlg, CDialog) //{{AFX_MSG_MAP(CTestDlg) ON_WM_PAINT() //}}AFX_MSG_MAP ON_MESSAGE(WM_RUNTEST,OnRunTest) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CTestDlg message handlers void CTestDlg::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: Add your message handler code here if(m_bFirstInit) PostMessage(WM_RUNTEST); m_bFirstInit=false; // Do not call CDialog::OnPaint() for painting messages } LRESULT CTestDlg::OnRunTest(UINT wParam, LONG lParam) { // votre Traitement // // return 0L; } |
Voilà après initialisation de la boîte de dialogue le message privé WM_RUNTEST est envoyé pour démarrer le traitement dans la fonction OnRunTest.
Note : Il ne pas faudra pas oublier d'insérer une fonction qui laisse passer les messages pour Windows.
Plusieurs possibilités:
Sur l'éditeur de ressources dans les propriétés de la boîte de dialogue.
A la création de la boîte de dialogue :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 | int CMyDialog::OnCreate(LPCREATESTRUCT lpCreateStruct) { if(CDialog::OnCreate(lpCreateStruct) == -1) return -1; // TODO: Add your specialized creation code here SetWindowLong(this->m_hWnd, GWL_STYLE, GetWindowLong(this->m_hWnd, GWL_STYLE) | WS_MINIMIZEBOX | WS_MAXIMIZEBOX); return 0; } |
Code c++ : | Sélectionner tout |
1 2 | ModifyStyle(0,WS_MINIMIZEBOX | WS_MAXIMIZEBOX); |
il suffit de donner le style ToolWindow à l'application.
Note :
On peut aussi donner ce style dans l'éditeur de ressources sur les propriétés de la boîte de dialogue:
onglet extended styles:cocher Tool Window.,
mais il faudra quand même enlever le style WS_EX_APPWINDOW.
Exemple d'application sur une application boîte de dialogue:
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | BOOL CTestToolDialogDlg::OnInitDialog() { CDialog::OnInitDialog(); ModifyStyleEx( WS_EX_APPWINDOW, WS_EX_TOOLWINDOW ); ........... return TRUE; // return TRUE unless you set the focus to a control } |
Pour rajouter un menu à boîte de dialogue il suffit de:
- Définir évidemment le menu dans les ressources.
- Sélectionner les propriétés de la boîte de dialogue.
- Dans la combobox menu sélectionner l'id du menu spécifique .
Dans le cas d'une boîte de dialogue les messages ON_UPDATE_COMMAND_UI ne sont pas effectifs.
Il faut rajouter une initialisation comme pour la classe CFrameWndet sa fonction CFrameWnd::OnInitMenuPopup()
Extrait MSDN:
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 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 | void CTestDlg::OnInitMenuPopup(CMenu *pPopupMenu, UINT nIndex,BOOL bSysMenu) { ASSERT(pPopupMenu != NULL); // Check the enabled state of various menu items. CCmdUI state; state.m_pMenu = pPopupMenu; ASSERT(state.m_pOther == NULL); ASSERT(state.m_pParentMenu == NULL); // Determine if menu is popup in top-level menu and set m_pOther to // it if so (m_pParentMenu == NULL indicates that it is secondary popup). HMENU hParentMenu; if (AfxGetThreadState()->m_hTrackingMenu == pPopupMenu->m_hMenu) state.m_pParentMenu = pPopupMenu; // Parent == child for tracking popup. else if ((hParentMenu = ::GetMenu(m_hWnd)) != NULL) { CWnd* pParent = this; // Child windows don't have menus--need to go to the top! if (pParent != NULL && (hParentMenu = ::GetMenu(pParent->m_hWnd)) != NULL) { int nIndexMax = ::GetMenuItemCount(hParentMenu); for (int nIndex = 0; nIndex < nIndexMax; nIndex++) { if (::GetSubMenu(hParentMenu, nIndex) == pPopupMenu->m_hMenu) { // When popup is found, m_pParentMenu is containing menu. state.m_pParentMenu = CMenu::FromHandle(hParentMenu); break; } } } } state.m_nIndexMax = pPopupMenu->GetMenuItemCount(); for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax; state.m_nIndex++) { state.m_nID = pPopupMenu->GetMenuItemID(state.m_nIndex); if (state.m_nID == 0) continue; // Menu separator or invalid cmd - ignore it. ASSERT(state.m_pOther == NULL); ASSERT(state.m_pMenu != NULL); if (state.m_nID == (UINT)-1) { // Possibly a popup menu, route to first item of that popup. state.m_pSubMenu = pPopupMenu->GetSubMenu(state.m_nIndex); if (state.m_pSubMenu == NULL || (state.m_nID = state.m_pSubMenu->GetMenuItemID(0)) == 0 || state.m_nID == (UINT)-1) { continue; // First item of popup can't be routed to. } state.DoUpdate(this, TRUE); // Popups are never auto disabled. } else { // Normal menu item. // Auto enable/disable if frame window has m_bAutoMenuEnable // set and command is _not_ a system command. state.m_pSubMenu = NULL; state.DoUpdate(this, FALSE); } // Adjust for menu deletions and additions. UINT nCount = pPopupMenu->GetMenuItemCount(); if (nCount < state.m_nIndexMax) { state.m_nIndex -= (state.m_nIndexMax - nCount); while (state.m_nIndex < nCount && pPopupMenu->GetMenuItemID(state.m_nIndex) == state.m_nID) { state.m_nIndex++; } } state.m_nIndexMax = nCount; } } |
Pour disposer du mode transparent disponible à partir de win2000 on fera comme suit:
- Définir #define _WIN32_WINNT 0x0500 dans stdafx.h
- Rajouter le style étendu WS_EX_LAYERED à la boîte de dialogue.
Intercepter le message CtlColor au niveau de la boîte de dialogue et fournir une brosse (solid brush) pour la couleur de transparence.
- Appeler l'api32 SetLayeredWindowAttributes dans OnInitDialog .
- consulter MSDN pour voir le réglage des options.
Exemple :
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 | BOOL CAboutDlg::OnInitDialog() { CDialog::OnInitDialog(); // TODO: Add extra initialization here m_pBrush= new CBrush; m_pBrush->CreateSolidBrush(RGB(187,221,255)); ModifyStyleEx(0,WS_EX_LAYERED); SetLayeredWindowAttributes(m_hWnd,RGB(187,221,255),180,LWA_ALPHA); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } HBRUSH CAboutDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { //HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); // TODO: Change any attributes of the DC here // TODO: Return a different brush if the default is not desired return static_cast<HBRUSH>(*m_pBrush); //return hbr; } |
Il faut intercepter le message WM_ERASEBKGND
Note : dans le cas d'une boîte de dialogue :
Avec Classwizard modifier le type de fenêtre dans l'onglet class info et sélectionner dans la combobox « message filter » l'option « child window » .
Sinon le message n'apparaîtra pas dans la liste…
Pour les besoins de l'exemple j'ai fait simple, je m'appuie sur un exemple de lecture d'image à partir de l'objet IPicture.
Le lien pour la classe CPicture : http://www.codeguru.com/bitmap/CPicture.html
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 | // la classe Dialog : class CTestsDiversDlg : public CDialog { // Construction public: CTestsDiversDlg(CWnd* pParent= NULL);// standard constructor // Dialog Data //................................................ CPicture m_Picture; // la variable picture // Implementation protected: // Generated message map functions //{{AFX_MSG(CTestsDiversDlg) virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); afx_msg BOOL OnEraseBkgnd(CDC* pDC); // le message //}}AFX_MSG DECLARE_MESSAGE_MAP() }; //----------------------------------------------------------------------------------- // le code BEGIN_MESSAGE_MAP(CTestsDiversDlg, CDialog) //{{AFX_MSG_MAP(CTestsDiversDlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_WM_ERASEBKGND() // le message rajoute par classwizard //}}AFX_MSG_MAP END_MESSAGE_MAP() BOOL CTestsDiversDlg::OnEraseBkgnd(CDC* pDC) { // TODO: Add your message handler code here and/or call default if(m_Picture.m_IPicture) { CRect rect; GetClientRect(&rect); m_Picture.UpdateSizeOnDC(pDC); // Get Picture Dimentions In Pixels m_Picture.Show(pDC, CPoint(0,0), CPoint(m_Picture.m_Width, m_Picture.m_Height), 0,0); m_Picture.Show(pDC,rect); // Change Original Dimentions return FALSE; } return CDialog::OnEraseBkgnd(pDC); } |
il suffit de rajouter la séquence suivante dans la fonction OnInitDialog :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | BOOL CTestDlg::OnInitDialog() { CDialog::OnInitDialog(); // .................................... //........................................ CRect rect; GetWindowRect( rect ); ::SetWindowPos(m_hWnd , HWND_TOPMOST, rect.left, rect.top, rect.Width(), rect.Height(), SWP_SHOWWINDOW); return TRUE; // return TRUE unless you set the focus to a control } |
Méthode :
Générer un bitmap avec la portion d'écran souhaitée :
Note MSDN : http://msdn.microsoft.com/library/de...tmaps_5a5h.asp
Imprimer le bitmap:
Un problème se pose:
Le bitmap généré représente un DDB : device-dependent bitmap . Et ce type de bitmap n'est pas imprimable directement ,il faudra le transformer en DIB device-independent bitmap.
d'où l'utilisation dans mon exemple de la classe CPictureHolder .
Le même problème sera rencontré avec l'utilisation de la fonction CBitmap::LoadBitmap, pour contourner le problème il faudra faire comme suit:
Code C++ : | Sélectionner tout |
1 2 3 4 | VERIFY (bm.Attach (::LoadImage (::AfxFindResourceHandle( MAKEINTRESOURCE (inBitmapID), RT_BITMAP), MAKEINTRESOURCE (inBitmapID), IMAGE_BITMAP, 0, 0, (LR_DEFAULTSIZE | LR_CREATEDIBSECTION)))) |
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 | #include <afxctl.h> CBitmap *CopyScreenToBitmap(LPRECT lpRect) { CDC SrcDC, MemDC; int nX, nY, nX2, nY2; int nWidth, nHeight; int xScrn, yScrn; CBitmap * pOldBitmap ,*pBitmap; if (IsRectEmpty(lpRect)) return NULL; SrcDC.CreateDC("DISPLAY", NULL, NULL, NULL); MemDC.CreateCompatibleDC(&SrcDC); nX = lpRect->left; nY = lpRect->top; nX2 = lpRect->right; nY2 = lpRect->bottom; xScrn = SrcDC.GetDeviceCaps(HORZRES); yScrn = SrcDC.GetDeviceCaps(VERTRES); if (nX < 0) nX = 0; if (nY < 0) nY = 0; if (nX2 > xScrn) nX2 = xScrn; if (nY2 > yScrn) nY2 = yScrn; nWidth = nX2 - nX; nHeight = nY2 - nY; pBitmap=new CBitmap; pBitmap->CreateCompatibleBitmap(&SrcDC,nWidth, nHeight); pOldBitmap =MemDC.SelectObject (pBitmap); MemDC.BitBlt(0, 0, nWidth, nHeight, &SrcDC, nX, nY, SRCCOPY); pBitmap = MemDC.SelectObject( pOldBitmap); MemDC.DeleteDC(); SrcDC.DeleteDC(); return (pBitmap); } CBitmap *CopyWindowToBitmap(CWnd* pWnd ,bool bWindowClient) { CBitmap *pBitmap=NULL; if(!pWnd->m_hWnd) return NULL; RECT rectWnd; pWnd->GetWindowRect(&rectWnd); if(!bWindowClient) return CopyScreenToBitmap(&rectWnd); RECT rectClient; POINT pt1, pt2; pWnd->GetClientRect(&rectClient); pt1.x = rectClient.left; pt1.y = rectClient.top; pt2.x = rectClient.right; pt2.y = rectClient.bottom; pWnd->ClientToScreen(&pt1); pWnd->ClientToScreen(&pt2); rectClient.left = pt1.x; rectClient.top = pt1.y; rectClient.right = pt2.x; rectClient.bottom = pt2.y; return CopyScreenToBitmap(&rectClient); } void PrintBmp(CBitmap *pBmp) { // mise a jour le 15/01/2005 :ajuste le bitmap sur la totalité de la feuille et edite en mode paysage. CDC dc; CPrintDialog printDlg(FALSE); // selection de l'imprimante. if (printDlg.DoModal() == IDCANCEL) return; DEVMODE FAR *pDevMode=(DEVMODE FAR *)::GlobalLock(printDlg.GetDevMode()); // set orientation to landscape pDevMode->dmOrientation=DMORIENT_LANDSCAPE; ::GlobalUnlock(printDlg.GetDevMode()); HDC hdc =printDlg.CreatePrinterDC(); //dc.Attach(printDlg.GetPrinterDC()); sinon le mode paysage ne fonctionne pas dc.Attach(hdc); dc.m_bPrinting = TRUE; // dc d'impression. // titre du document = titre application CString strTitle; strTitle.LoadString(AFX_IDS_APP_TITLE); DOCINFO di; ::ZeroMemory (&di, sizeof (DOCINFO)); di.cbSize = sizeof (DOCINFO); di.lpszDocName = strTitle; // debut d'impression if(dc.StartDoc( &di )) { // debut page dc.StartPage(); // surface d'impression CRect rectDraw; rectDraw.SetRect(0, 0, dc.GetDeviceCaps(HORZRES), dc.GetDeviceCaps(VERTRES)); // infos bitmap BITMAP bmpInfo; pBmp->GetBitmap(&bmpInfo); // creation d'un objet CPictureHolder pour avoir un dib. CPictureHolder picture; picture.CreateFromBitmap(pBmp); // rectangle d'impression int ncoefy=(rectDraw.Height()/bmpInfo.bmHeight); // centrer l'image sur la feuille int nX = rectDraw.left + (rectDraw.Width() - (bmpInfo.bmWidth*ncoefy)) / 2; int nY = rectDraw.top + (rectDraw.Height() - (bmpInfo.bmHeight*ncoefy)) / 2; CRect rect; rect.SetRect(CPoint(nX,nY),CPoint(nX+(bmpInfo.bmWidth*ncoefy),nY+(bmpInfo.bmHeight*ncoefy))); // affichage final picture.Render(&dc,rect,rect); dc.EndPage(); // fin de page dc.EndDoc(); // fin du document } else dc.AbortDoc(); // erreur d'impression dc.Detach(); // liberation dc d'impression. DeleteObject(hdc); } // contexte d'utilisation : dans une boite de dialogue ou view etc.. CBitmap *pBmp=CopyWindowToBitmap(this,true); PrintBmp(pBmp); pBmp->DeleteObject(); delete pBmp; |
On procédera comme avec une boîte de dialogue modale classique pour sa création dans les ressources.
Seul l'appel différera :
Code C++ : | Sélectionner tout |
1 2 3 | pDlg = new CMyDlg(this); pDlg->Create(CMyDlg ::IDD,this); pDlg->ShowWindow(SW_SHOW); |
Le cas typique est l'utilisation d'une boite de dialogue non modale.
Exemple :
Code c++ : | Sélectionner tout |
1 2 3 4 | pDlg = new CMyDlg(this); pDlg->Create(CMyDlg ::IDD,this); pDlg->ShowWindow(SW_SHOW); |
Les MFC disposent d'une fonction spécifique pour réaliser ce traitement :
Code c++ : | Sélectionner tout |
1 2 3 | CWnd::PostNcDestroy virtual void PostNcDestroy( ); |
Les classes dérivées peuvent utiliser cette fonction pour effectuer la suppression du pointeur this.
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 | void CMyDlg :: PostNcDestroy( ) { CDialog::PostNcDestroy(); delete this; } |
L'unité de base horizontale pour une boîte de dialogue est égale à la taille moyenne d'un caractère avec la fonte "Système".
Pour l'équivalence en pixels on utilise la formule suivante :
Code C++ : | Sélectionner tout |
1 2 | pixelX = (dialogunitX * baseunitX) / 4 pixelY = (dialogunitY * baseunitY) / 8 |
Exemple :
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 | BOOL CAboutDlg::OnInitDialog() { CDialog::OnInitDialog(); // TODO: Add extra initialization here CRect rc( 0, 0, 4, 8 ); MapDialogRect( &rc ); int baseUnitY = rc.bottom; int baseUnitX = rc.right; TRACE("baseUnitX = %d\n", baseUnitX ); TRACE("baseUnitY = %d\n", baseUnitY ); // si ma boîte de dialogue fait 300 x 300 unité on aura : TRACE("PixelX = %d\n", (baseUnitX *300)/4); TRACE("PixelY = %d\n", (baseUnitY *300)/8 ); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } |
Ceci est valable pour une fonte "Système" sinon se reporter à la note MSDN : http://support.microsoft.com/default...b;en-us;125681
Voir aussi : http://support.microsoft.com/default...b;en-us;145994
Note :
Dans le cas où on voudrait établir une correspondance directe à la conception avec le nombre de pixels,
On peut régler la fonte "Système" de la boîte de dialogue à 10 points,
Après on aura :
Pour afficher une boîte de dialogue depuis une DLL, il faut d'abord avoir choisi un projet DLL extension MFC ou avoir rajouté le support MFC dans votre DLL.
Après il suffit simplement d'appeler AFX_MANAGE_STATE :
Code C++ : | Sélectionner tout |
1 2 3 | AFX_MANAGE_STATE(AfxGetStaticModuleState()); CMyLocalDialog dlg; dlg.DoModal(); |
Lien d'origine : http://www.codeguru.com/Cpp/W-P/dll/article.php/c101/
La DLL MFC (MFC42.DLL) est chargée en tant qu'élément d'un processus et elle stocke des données dans des variables globales.
Si les fonctions MFC sont appelées depuis le programme ou d'une DLL d'extension la DLL MFC sait modifier ces variables pour le processus.
Par contre dans le cas d'une DLL classique cette macro est à appeler chaque fois que l'on appelle des fonctions de MFC42.dll.
Sinon les variables globales ne sont pas synchronisées et on a un comportement indéfini.
Note si les MFC sont liées statiquement la macro n'a aucun effet..
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 | // .h class CDynamicDlg : public CDialog { // Construction public: DECLARE_DYNCREATE(CDynamicDlg) CDynamicDlg(); CDynamicDlg(int x,int y,int cx,int cy,const char *szTitle,CWnd* pParent = NULL);// standard constructor ~CDynamicDlg(); // Dialog Data //{{AFX_DATA(CDynamicDlg) // NOTE: the ClassWizard will add data members here //}}AFX_DATA void SetDynamicDlg(int x,int y,int cx,int cy,const char *szTitle,CWnd* pParent = NULL); // Implementation private: HGLOBAL m_hgbl; protected: // Generated message map functions //{{AFX_MSG(CDynamicDlg) // NOTE: the ClassWizard will add member functions here //}}AFX_MSG DECLARE_MESSAGE_MAP() }; // .cpp ///////////////////////////////////////////////////////////////////////////// // CDynamicDlg dialog IMPLEMENT_DYNCREATE(CDynamicDlg,CDialog) CDynamicDlg::CDynamicDlg() { m_hgbl=NULL; } CDynamicDlg::CDynamicDlg(int x,int y,int cx,int cy,const char *szTitle,CWnd* pParent /*=NULL*/) : CDialog() { //{{AFX_DATA_INIT(CDynamicDlg) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT m_hgbl=NULL; SetDynamicDlg(x,y,cx,cy,szTitle,pParent); } void CDynamicDlg::SetDynamicDlg(int x,int y,int cx,int cy,const char *szTitle,CWnd* pParent /*=NULL*/) { // LPDLGTEMPLATE lpdt; LPWORD lpw; if(m_hgbl) GlobalFree(m_hgbl); m_hDialogTemplate=NULL; m_hgbl=GlobalAlloc(GMEM_ZEROINIT,1024); lpdt = (LPDLGTEMPLATE)GlobalLock(m_hgbl); lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION; lpdt->cdit = 0; lpdt->x = x; lpdt->y = y; lpdt->cx = cx; lpdt->cy = cy; lpw=(LPWORD) (lpdt+1); *lpw++=0; *lpw++=0; while(*szTitle) *lpw++=*szTitle++; *lpw++=0; GlobalUnlock(m_hgbl); InitModalIndirect(m_hgbl); } CDynamicDlg::~CDynamicDlg() { if(m_hgbl) GlobalFree(m_hgbl); } BEGIN_MESSAGE_MAP(CDynamicDlg, CDialog) //{{AFX_MSG_MAP(CDynamicDlg) // NOTE: the ClassWizard will add message map macros here //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CDynamicDlg message handlers |
Utilisation :
Code C++ : | Sélectionner tout |
1 2 3 | CDynamicDlg dlg; dlg.SetDynamicDlg(0,0,100,200,"essai",this); dlg.DoModal(); |
il ne reste plus qu'à l'habiller dynamiquement en créant les contrôles dans la fonction OnInitDialog initiée par le message WM_INITDIALOG.
A partir du projet de destination, ouvrir le fichier .RC du projet d'origine (file open avec VC6.0)
Sélectionnez la ou les boîtes de dialogues concernées, placez la sélection dans le presse-papiers.
(CTRL+INS ou ctrl+C ou clic droit copier) ,
Placez ensuite la souris dans le dossier dialogue du projet de destination pour insérer les dialogues par le bouton coller (ou Maj + INS ou CTRL+V)
On procédera de la même manière pour les barres d'outils (toolbars) et les bitmaps.
Si j'ai pris cet exemple c'est parce qu'il représente la majorité des cas pratiques rencontrés
Le problème :
Une boîte de dialogue est lancée à partir d'une CFormView .
Dans cette boîte de dialogue j'ai besoin d'accéder et de partager l'accès à une variable quelconque située dans ma CFormView .
Comment faire ? , On peut envisager de passer un pointeur et de manipuler celui-ci dans la dialogue.
Pourquoi ne pas passer l'argument directement comme une référence ?
Exemple:
Je vais partager une variable conteneur de type CStringList entre ma CFormView et ma CDialog.
Ma classe dialogue:
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class CTestDlg : public CDialog { // Construction public: // le constructeur d'origine est modifié pour le passage de la variable a partager. CTestDlg(CStringList &rStringList,CWnd* pParent = NULL); // standard constructor // Dialog Data //{{AFX_DATA(CTestDlg) enum { IDD = IDD_DIALOG1 }; // NOTE: the ClassWizard will add data members here //}}AFX_DATA CStringList &m_rStringList; // la donnée membre dans la dialogue. |
Le code de ma boîte de dialogue regardez bien le constructeur :
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 | CTestDlg::CTestDlg(CStringList &rStringList,CWnd* pParent /*=NULL*/) : CDialog(CTestDlg::IDD, pParent),m_rStringList(rStringList) { //{{AFX_DATA_INIT(CTestDlg) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT } BOOL CTestDlg::OnInitDialog() { CDialog::OnInitDialog(); // TODO: Add extra initialization here // j'affiche en debug le contenu de mon containeur #ifdef _DEBUG for(POSITION pos = m_rStringList.GetHeadPosition(); pos != NULL; ) { afxDump << m_rStringList.GetNext( pos ) << "\n"; } #endif // je rajoute un élément m_rStringList.AddTail("essai dlg"); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } |
Le code d'appel dans ma 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 | void CTestToolBarView::OnInitialUpdate() { CFormView::OnInitialUpdate(); ResizeParentToFit(); CStringList rStringList; rStringList.AddTail("essai"); rStringList.AddTail("essai2"); rStringList.AddTail("essai3"); CTestDlg Dlg(rStringList);// passage de la variable dans le constructeur. Dlg.DoModal(); // affichage du containeur #ifdef _DEBUG for(POSITION pos = rStringList.GetHeadPosition(); pos != NULL; ) { afxDump << rStringList.GetNext( pos ) << "\n"; } #endif } |
Le lancement d'une boîte de dialogue ne permet pas de spécifier l'emplacement de celle-ci à l'écran.
On peut tout au plus spécifier que la boîte de dialogue sera centrée en réglant l'option sur la propriété de la fenêtre dans le gestionnaire de ressources.
Il existe pourtant une possibilité méconnue: lire le template (DLGTEMPLATE) de la dialogue et modifier son contenu…
Pour cela j'utiliserai une classe MFC non documentée (CDialogTemplate) qui permet en outre de changer la police de la boîte de dialogue avant son affichage :
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 | #include <afxpriv.h> int CDlgMyDlg::DoModal(int x,int y) { CDialogTemplate dlgTemp; int nResult; // lecture du template d'origine if (!dlgTemp.Load(MAKEINTRESOURCE(IDD))) return -1; // fonte par defaut // dlgTemp.SetFont("MS Sans Serif", 8); eventuel changement de fonte // pointeur sur le dialogue template modifié LPSTR pdata = reinterpret_cast<LPSTR>(GlobalLock(dlgTemp.m_hTemplate)); DLGTEMPLATE* pTemplate = reinterpret_cast<DLGTEMPLATE*>(pdata); pTemplate->x=x; // changement des coordonnées x,y pTemplate->y=y; m_lpszTemplateName = NULL; InitModalIndirect(pdata); // appel domodal nResult = CDialog::DoModal(); // liberation du template modifié GlobalUnlock(dlgTemp.m_hTemplate); return nResult; |
Comment procéder :
rajouter à votre classe boîte de dialogue la fonction DoModal écrite ci-dessus.
Note: Celle-ci finit par rappeler la fonction DoModal de la classe CDialog
il ne reste plus qu'a appeler votre boîte de dialogue comme suit:
Code c++ : | Sélectionner tout |
1 2 3 | CDlgMyDlg Dlg; Dlg.DoModal(100,200); |
Le traitement des raccourcis claviers n'est pas pris en charge dans une boîte de dialogue.
Pour le rendre effectif on procédera comme suit :
Dans la classe CDialog héritée on commencera par déclarer une variable de type HACELL ;
Code c++ : | Sélectionner tout |
1 2 |
HACCEL m_hTable; |
Ensuite dans la fonction OnInitDialog on va lire la table d'accélérateurs souhaitée:
Code c++ : | Sélectionner tout |
1 2 | m_hTable = LoadAccelerators(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME)); |
Enfin il faut ajouter la fonction PreTranslateMessage avec l'assistant et mettre le code qui suit pour relayer les messages.
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 | BOOL CMyDialog::PreTranslateMessage(MSG* pMsg) { if (m_hTable && ::TranslateAccelerator(m_hWnd, m_hTable, pMsg)) return(TRUE); return CDialog::PreTranslateMessage(pMsg); } |
Dans le cas où on imbriquerait une autre boîte de dialogue sur une boîte de dialogue existante le problème de la gestion de l'ordre de tabulation entre les deux fenêtres apparaît.
Comment gérer le passage naturel par la touche tab stop entre les deux fenêtres ?
La fenêtre fille devra disposer du style WM_CHILD mais aussi du style DS_CONTROL.
ce dernier permettra à la boîte de dialogue de fonctionner comme une fenêtre enfant dans la boîte de dialogue principale permettant ainsi la gestion du tab stop à travers les fenêtres voir MSDN
Ce style est réglable dans les propriétés de la boîte de dialogue dans l'éditeur de ressources.
Note:Si le parent de la boîte de dialogue est un contrôle situé dans la boîte de dialogue principale, il faudra rajouter le style WS_EX_CONTROLPARENT à ce contrôle,
Exemple :
J'ai une boîte de dialogue DlgOne qui possède un contrôle static nommé ancre qui me permet d'afficher une autre boîte de dialogue DlgTwo dans DlgOne.
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | BOOL DlgOne::OnInitDialog() { CDialog::OnInitDialog(); ancre.ModifyStyleEx(0, WS_EX_CONTROLPARENT); DlgTwo.Create(IDD_DIAGTWO, &ancre); } |
Le problème :
Je dispose d'une boîte de dialogue non modale qui ne doit être lancée qu'une seule fois dans mon application.
Dans ce contexte comment savoir si la boîte de dialogue est déjà lancée et accessoirement comment récupérer un pointeur sur cette fenêtre ?
Réponse: En la cherchant dans les fenêtres de mon application.
Exemple pour une CDialog nommée CDlgTest:
il faudra rajouter manuellement à notre boîte de dialogue le couple de macros DECLARE_DYNCREATE et IMPLEMENT_DYNCREATE pour la reconnaissance de signature:
Le .h:
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 | class CDlgTest : public CDialog { // Construction public: DECLARE_DYNCREATE(CDlgTest) CDlgTest(CWnd* pParent = NULL); // standard constructor /.... |
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | ///////////////////////////////////////////////////////////////////////////// // CDlgTest dialog IMPLEMENT_DYNCREATE(CDlgTest, CDialog) CDlgTest::CDlgTest(CWnd* pParent /*=NULL*/) : CDialog(CDlgTest::IDD, pParent) { //{{AFX_DATA_INIT(CDlgTest) //}}AFX_DATA_INIT } |
Code c++ : | Sélectionner tout |
1 2 3 4 5 | CDlgTest *pDlg; pDlg = new CDlgTest(); pDlg->Create(CDlgTest ::IDD,AfxGetMainWnd()); pDlg->ShowWindow(SW_SHOW); |
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 | CWnd* pChild; pChild = AfxGetMainWnd()->GetWindow(GW_HWNDFIRST); while (pChild != NULL) { if(pChild->IsKindOf(RUNTIME_CLASS(CDlgTest))) { TRACE("\nCDialog Find:%x,%x",pChild,pChild->m_hWnd); // la dialogue est trouvée !!! break; } pChild = pChild->GetWindow(GW_HWNDNEXT); } |
La méthode OnInitdialog doit renvoyer un BOOLEEN en sortie :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 | BOOL CSimpleDlg::OnInitDialog() { CDialog::OnInitDialog(); // TODO: Add extra initialization here m_cMyEdit.SetWindowText(_T("My Name")); // Initialize control values m_cMyList.ShowWindow(SW_HIDE); // Show or hide a control, etc. return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } |
Dans le cas où on voudrait donner le focus à un contrôle spécifique il faudra appliquer SetFocus sur ce contrôle et renvoyer FALSE.
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 | BOOL CSimpleDlg::OnInitDialog() { CDialog::OnInitDialog(); // TODO: Add extra initialization here m_cMyEdit.SetWindowText(_T("My Name")); // Initialize control values m_cMyEdit.SetFocus(); return FALSE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } |
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.