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

FAQ VC++ et MFCConsultez toutes les FAQ

Nombre d'auteurs : 20, nombre de questions : 545, dernière mise à jour : 5 avril 2013  Ajouter une question

 

Cette faq a été réalisée pour répondre aux questions les plus fréquement posées sur le forum Développement Visual C++

Je tiens à souligner que cette faq ne garantit en aucun cas que les informations qu'elle contient sont correctes ; Les auteurs font le maximum, mais l'erreur est humaine. Si vous trouvez une erreur, ou si vous souhaitez devenir redacteur, lisez ceci.

Sur ce, je vous souhaite une bonne lecture. Farscape

SommaireClasses Fenêtres et FrameWorkBoite de DialogueCDialog (31)
précédent sommaire suivant
 

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.

Mis à jour le 20 mai 2006 farscape

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

Mis à jour le 5 avril 2013 farscape

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); 
}
Le code ci-dessus permet d'interdire la fermeture de la fenêtre par la touche entrée si le contrôle possédant le « focus » n'est pas le bouton IDOK.
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); 
}
Dans le code ci-dessus on interceptera uniquement les messages en provenance du clavier :

Mis à jour le 5 avril 2013 farscape

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();
À la sortie de la boîte de dialogue DoModal renverra donc soit IDOK ou IDCANCEL.
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.

Mis à jour le 20 mai 2006 farscape

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

Mis à jour le 5 avril 2013 farscape

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

Mis à jour le 5 avril 2013 farscape

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();  
}
Ensuite dans la dialogue il suffira d'appeler la fonction GetParent() pour récupérer notre parent :

Code c++ : Sélectionner tout
1
2
3
4
5
6
  
BOOL CMyDialog::OnInitDialog()  
{  
   CDialog::OnInitDialog();  
   CMyFormView *pView=static_cast< CMyFormView *>(GetParent());  
//&#8230;&#8230;&#8230;&#8230;&#8230;
Néanmoins il y a un cas où ce code ne fonctionne pas : l'appel d'une boîte de dialogue à partir d'un panneau d'un TabControl .

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);  
//&#8230;&#8230;&#8230;&#8230;&#8230;
m_pParentWnd est une donnée membre protected de la classe CDialog ,elle est affectée par la valeur passée en argument dans le constructeur .

Mis à jour le 27 novembre 2005 farscape

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

Mis à jour le 20 mai 2006 farscape

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); 
}
Noter la fonction SetShowWindow() qui permettra de faire apparaître le moment venu la boîte de dialogue.

Mis à jour le 5 avril 2013 farscape

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.

Mis à jour le 5 avril 2013 farscape

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; 
}
Et enfin à la demande :

Code c++ : Sélectionner tout
1
2
  
ModifyStyle(0,WS_MINIMIZEBOX | WS_MAXIMIZEBOX);

Mis à jour le 5 avril 2013 farscape

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 
}

Mis à jour le 5 avril 2013 farscape

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 .

Mis à jour le 5 avril 2013 farscape

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; 
    } 
}
La note MSDN: PRB: Update Command UI Handlers Do Not Work for Menu Attached to a Dialog Box

Mis à jour le 4 avril 2005 farscape

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; 
}
Note:Avec visual 6.0 il faut disposer d'un SDK à jour .

Mis à jour le 20 mai 2006 farscape

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

Mis à jour le 5 avril 2013 farscape

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 
}

Mis à jour le 5 avril 2013 farscape

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;

Mis à jour le 20 janvier 2005 farscape

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);
voir aussi  Comment libérer la mémoire sur une fenêtre dynamique ?

Mis à jour le 22 janvier 2007 farscape

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);
Dans ces conditions où placer le delete de pDlg ?
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( );
Cette fonction est appelée par la fonction membre OnNcDestroy après la destruction de la fenêtre.
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; 
}
Pour plus d'informations consulter la note MSDN : TN017: Destroying Window Objects

Mis à jour le 5 avril 2013 farscape

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 :
X pixels = 2 * unité boîte de dialogue

Pour l'exemple précédent d'une boîte de dialogue de 300 x 300 unités on obtiendra 600*600 pixels…

Mis à jour le 5 avril 2013 farscape

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/

Mis à jour le 27 novembre 2005 matazz

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

Mis à jour le 27 novembre 2005 farscape

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.

Mis à jour le 27 novembre 2005 farscape

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.

Mis à jour le 20 mai 2006 farscape

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 
}
Voilà je peux accéder à ma variable dans boîte de dialogue naturellement sans pointeur et interagir dessus comme le montre mon exemple ci-dessus.

Mis à jour le 22 janvier 2007 farscape

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;
La classe CDialogTemplate est définie dans le fichier afxpriv.h.

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

Mis à jour le 22 janvier 2007 farscape

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

Mis à jour le 22 janvier 2007 farscape

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

Mis à jour le 22 janvier 2007 farscape

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 
/....
Le .cpp:

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 
}
L'appel de la dialogue:

Code c++ : Sélectionner tout
1
2
3
4
5
  
    CDlgTest *pDlg; 
    pDlg = new CDlgTest(); 
    pDlg->Create(CDlgTest ::IDD,AfxGetMainWnd()); 
    pDlg->ShowWindow(SW_SHOW);
Sa recherche:

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

Mis à jour le 17 septembre 2007 farscape

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 
}
Par défaut celle-ci renvoie true.
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 
}

Mis à jour le 7 juillet 2008 farscape

Proposer une nouvelle réponse sur la FAQ

Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour ça


Réponse à la question

Liens sous la question
précédent sommaire suivant
 

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2024 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.