| auteur : Farscape | 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.
|
| auteur : 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?
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) ;
}
}
|
|
| auteur : Farscape | En surchargeant la méthode OnCommand de la classe CWnd:
CWnd:: OnCommand
virtual BOOL OnCommand (WPARAM wParam,LPARAM lParam );
|
BOOL CAboutDlg:: OnCommand (WPARAM wParam, LPARAM lParam)
{
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 :
CWnd:: PreTranslateMessage
virtual BOOL PreTranslateMessage ( MSG* pMsg );
|
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 :
|
| auteur : 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 :
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.
|
| auteur : 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.
class CDlg1: public CDialog
{
public :
CDlg1 (CWnd* pParent = NULL );
virtual BOOL OnInitDialog ();
afx_msg void OnButton1 ();
DECLARE_MESSAGE_MAP ()
} ;
class CDlg2: public CDialog
{
public :
CDlg2 (CWnd* pParent = NULL );
virtual BOOL OnInitDialog ();
virtual void OnOK ();
DECLARE_MESSAGE_MAP ()
public :
} ;
CDlg1 dlg;
dlg.DoModal ();
void CDlg1:: OnButton1 ()
{
CDlg2 dlg (this );
dlg.DoModal ();
}
void CDlg2:: OnOK ()
{
CDlg1 * pDlg= ( CDlg1 * )GetParent ();
CDialog:: OnOK ();
}
|
|
| auteur : 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?
A l'appel de la boîte il suffit d'affecter les valeurs aux variables comme ci-dessus.
void CTestDlgDlg:: OnButton1 ()
{
CDialog1 dlg;
dlg.m_strEdit1= " coucou " ;
dlg.DoModal ();
}
|
Dans la fonction OnInitDialog il restera à faire un UpdateData(FALSE) comme dans l'exemple ci-dessus :
BOOL CDialog1:: OnInitDialog ()
{
CDialog:: OnInitDialog ();
UpdateData (FALSE);
return TRUE;
}
|
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 :
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.
class CMyListBox : public CListBox
{
public :
CMyListBox ();
public :
int AddString ( LPCTSTR lpszItem );
public :
CStringArray m_strArray;
public :
protected :
virtual void PreSubclassWindow ();
public :
virtual ~ CMyListBox ();
protected :
DECLARE_MESSAGE_MAP ()
} ;
int CMyListBox:: AddString (LPCTSTR lpszItem)
{
if (m_hWnd! = NULL ) return CListBox:: AddString (lpszItem);
m_strArray.Add (lpszItem);
return LB_ERR;
}
void CMyListBox:: PreSubclassWindow ()
{
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().
|
| auteur : Farscape | Lorsque l'on construit un objet boîte de dialogue le constructeur permet de spécifier la fenêtre parent de la dialogue
void CMyFormView:: OnButtonMachin ()
{
CMyDialog Dlg (this )
Dlg.DoModal ();
}
|
Ensuite dans la dialogue il suffira d'appeler la fonction GetParent() pour récupérer notre parent :
BOOL CMyDialog:: OnInitDialog ()
{
CDialog:: OnInitDialog ();
CMyFormView * pView= static_cast < CMyFormView * > (GetParent ());
|
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 :
BOOL CMyDialog:: OnInitDialog ()
{
CDialog:: OnInitDialog ();
CMyFormView * pView= static_cast < CMyFormView * > (m_pParentWnd);
|
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 .
|
| auteur : 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 :
CDialog * CSwitchDlgDlg:: CreatePage (UINT nPlaceCtrlId,UINT nDialogID,CRuntimeClass * pClass)
{
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:
pDlg- > ShowWindow (SW_HIDE);
pDlg- > EnableWindow (FALSE);
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: http://farscape.developpez.com/Samples/SwitchDlg.zip
|
| auteur : Farscape | Ceux qui se sont donnés la peine d'essayer de mettre un ShowWindow(SW_HIDE) dans la fonction OnInitDialog 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.
CMyDialog:: CMyDialog (CWnd* pParent )
: CDialog (CMyDialog:: IDD, pParent)
{
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.
|
| auteur : 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.
class CTestDlg : public CDialog
{
public :
CTestDlg (CWnd* pParent = NULL );
enum { IDD = IDD_DIALOGTEST } ;
public :
bool m_bFirstInit;
protected :
virtual void DoDataExchange (CDataExchange* pDX);
LRESULT OnRunTest (UINT wParam,LONG lParam);
protected :
afx_msg void OnPaint ();
DECLARE_MESSAGE_MAP ()
} ;
|
# define WM_RUNTEST WM_USER + 100
CTestDlg:: CTestDlg (CWnd* pParent )
: CDialog (CTestDlg:: IDD, pParent)
{
m_bFirstInit= true ;
}
void CTestDlg:: DoDataExchange (CDataExchange* pDX)
{
CDialog:: DoDataExchange (pDX);
}
BEGIN_MESSAGE_MAP (CTestDlg, CDialog)
ON_WM_PAINT ()
ON_MESSAGE (WM_RUNTEST,OnRunTest)
END_MESSAGE_MAP ()
void CTestDlg:: OnPaint ()
{
CPaintDC dc (this );
if (m_bFirstInit) PostMessage (WM_RUNTEST);
m_bFirstInit= false ;
}
LRESULT CTestDlg:: OnRunTest (UINT wParam, LONG lParam)
{
return 0L ;
}
|
|
| auteur : 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 :
int CMyDialog:: OnCreate (LPCREATESTRUCT lpCreateStruct)
{
if (CDialog:: OnCreate (lpCreateStruct) = = - 1 )
return - 1 ;
SetWindowLong (this - > m_hWnd,
GWL_STYLE,
GetWindowLong (this - > m_hWnd, GWL_STYLE) | WS_MINIMIZEBOX | WS_MAXIMIZEBOX);
return 0 ;
}
|
Et enfin à la demande :
ModifyStyle (0 ,WS_MINIMIZEBOX | WS_MAXIMIZEBOX);
|
|
| auteur : 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 CFrameWnd et sa fonction CFrameWnd::OnInitMenuPopup()
Extrait MSDN:
void CTestDlg:: OnInitMenuPopup (CMenu * pPopupMenu, UINT nIndex,BOOL bSysMenu)
{
ASSERT (pPopupMenu ! = NULL );
CCmdUI state;
state.m_pMenu = pPopupMenu;
ASSERT (state.m_pOther = = NULL );
ASSERT (state.m_pParentMenu = = NULL );
HMENU hParentMenu;
if (AfxGetThreadState ()- > m_hTrackingMenu = = pPopupMenu- > m_hMenu)
state.m_pParentMenu = pPopupMenu;
else if ((hParentMenu = :: GetMenu (m_hWnd)) ! = NULL )
{
CWnd* pParent = this ;
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)
{
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 ;
ASSERT (state.m_pOther = = NULL );
ASSERT (state.m_pMenu ! = NULL );
if (state.m_nID = = (UINT)- 1 )
{
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 ;
}
state.DoUpdate (this , TRUE);
}
else
{
state.m_pSubMenu = NULL ;
state.DoUpdate (this , FALSE);
}
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;
}
}
|
|
| auteur : 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 :
BOOL CAboutDlg:: OnInitDialog ()
{
CDialog:: OnInitDialog ();
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;
}
HBRUSH CAboutDlg:: OnCtlColor (CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
return static_cast < HBRUSH> (* m_pBrush);
}
|
Note:Avec visual 6.0 il faut disposer d'un SDK à jour .
|
| auteur : 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
class CTestsDiversDlg : public CDialog
{
public :
CTestsDiversDlg (CWnd* pParent= NULL );
CPicture m_Picture;
protected :
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);
DECLARE_MESSAGE_MAP ()
} ;
BEGIN_MESSAGE_MAP (CTestsDiversDlg, CDialog)
ON_WM_SYSCOMMAND ()
ON_WM_PAINT ()
ON_WM_QUERYDRAGICON ()
ON_WM_ERASEBKGND ()
END_MESSAGE_MAP ()
BOOL CTestsDiversDlg:: OnEraseBkgnd (CDC* pDC)
{
if (m_Picture.m_IPicture)
{
CRect rect;
GetClientRect (& rect);
m_Picture.UpdateSizeOnDC (pDC);
m_Picture.Show (pDC, CPoint (0 ,0 ), CPoint (m_Picture.m_Width,
m_Picture.m_Height), 0 ,0 );
m_Picture.Show (pDC,rect);
return FALSE;
}
return CDialog:: OnEraseBkgnd (pDC);
}
|
|
| auteur : Farscape | il suffit de rajouter la séquence suivante dans la fonction OnInitDialog :
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;
}
|
|
| auteur : Farscape | Méthode:
Générer un bitmap avec la portion d'écran souhaitée:
Note MSDN:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/bitmaps_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:
VERIFY (bm.Attach (:: LoadImage (:: AfxFindResourceHandle (
MAKEINTRESOURCE (inBitmapID), RT_BITMAP),
MAKEINTRESOURCE (inBitmapID), IMAGE_BITMAP, 0 , 0 ,
(LR_DEFAULTSIZE | LR_CREATEDIBSECTION))))
|
# 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)
{
CDC dc;
CPrintDialog printDlg (FALSE);
if (printDlg.DoModal () = = IDCANCEL) return ;
DEVMODE FAR * pDevMode= (DEVMODE FAR * ):: GlobalLock (printDlg.GetDevMode ());
pDevMode- > dmOrientation= DMORIENT_LANDSCAPE;
:: GlobalUnlock (printDlg.GetDevMode ());
HDC hdc = printDlg.CreatePrinterDC ();
dc.Attach (hdc);
dc.m_bPrinting = TRUE;
CString strTitle;
strTitle.LoadString (AFX_IDS_APP_TITLE);
DOCINFO di;
:: ZeroMemory (& di, sizeof (DOCINFO));
di.cbSize = sizeof (DOCINFO);
di.lpszDocName = strTitle;
if (dc.StartDoc ( & di ))
{
dc.StartPage ();
CRect rectDraw;
rectDraw.SetRect (0 , 0 ,
dc.GetDeviceCaps (HORZRES),
dc.GetDeviceCaps (VERTRES));
BITMAP bmpInfo;
pBmp- > GetBitmap (& bmpInfo);
CPictureHolder picture;
picture.CreateFromBitmap (pBmp);
int ncoefy= (rectDraw.Height ()/ bmpInfo.bmHeight);
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)));
picture.Render (& dc,rect,rect);
dc.EndPage ();
dc.EndDoc ();
}
else dc.AbortDoc ();
dc.Detach ();
DeleteObject (hdc);
}
CBitmap * pBmp= CopyWindowToBitmap (this ,true );
PrintBmp (pBmp);
pBmp- > DeleteObject ();
delete pBmp;
|
|
| auteur : Farscape | Le cas typique est l'utilisation d'une boite de dialogue non modale.
Exemple :
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 :
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.
void CMyDlg :: PostNcDestroy ( )
{
CDialog:: PostNcDestroy ();
delete this ;
}
|
|
| auteur : 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:
|
pixelX = (dialogunitX * baseunitX) / 4
pixelY = (dialogunitY * baseunitY) / 8
|
Exemple:
BOOL CAboutDlg:: OnInitDialog ()
{
CDialog:: OnInitDialog ();
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 );
TRACE (" PixelX = %d\n " , (baseUnitX * 300 )/ 4 );
TRACE (" PixelY = %d\n " , (baseUnitY * 300 )/ 8 );
return TRUE;
}
|
|
| auteur : matazz | 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'appeller AFX_MANAGE_STATE :
AFX_MANAGE_STATE (AfxGetStaticModuleState ());
CMyLocalDialog dlg;
dlg.DoModal ();
|
|
| auteur : Farscape | 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..
|
| auteur : Farscape |
class CDynamicDlg : public CDialog
{
public :
DECLARE_DYNCREATE (CDynamicDlg)
CDynamicDlg ();
CDynamicDlg (int x,int y,int cx,int cy,const char * szTitle,CWnd* pParent = NULL );
~ CDynamicDlg ();
void SetDynamicDlg (int x,int y,int cx,int cy,const char * szTitle,CWnd* pParent = NULL );
private :
HGLOBAL m_hgbl;
protected :
DECLARE_MESSAGE_MAP ()
} ;
IMPLEMENT_DYNCREATE (CDynamicDlg,CDialog)
CDynamicDlg:: CDynamicDlg ()
{
m_hgbl= NULL ;
}
CDynamicDlg:: CDynamicDlg (int x,int y,int cx,int cy,const char * szTitle,CWnd* pParent )
: CDialog ()
{
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 )
{
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)
END_MESSAGE_MAP ()
|
Utilisation:
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.
|
| auteur : 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.
|
| auteur : 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 :
# include <afxpriv.h>
int CDlgMyDlg:: DoModal (int x,int y)
{
CDialogTemplate dlgTemp;
int nResult;
if (! dlgTemp.Load (MAKEINTRESOURCE (IDD))) return - 1 ;
LPSTR pdata = reinterpret_cast < LPSTR> (GlobalLock (dlgTemp.m_hTemplate));
DLGTEMPLATE* pTemplate = reinterpret_cast < DLGTEMPLATE* > (pdata);
pTemplate- > x= x;
pTemplate- > y= y;
m_lpszTemplateName = NULL ;
InitModalIndirect (pdata);
nResult = CDialog:: DoModal ();
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:
CDlgMyDlg Dlg;
Dlg.DoModal (100 ,200 );
|
|
| auteur : 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 ;
Ensuite dans la fonction OnInitDialog on va lire la table d'accélérateurs souhaitée:
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.
BOOL CMyDialog:: PreTranslateMessage (MSG* pMsg)
{
if (m_hTable & & :: TranslateAccelerator (m_hWnd, m_hTable, pMsg))
return (TRUE);
return CDialog:: PreTranslateMessage (pMsg);
}
|
|
| auteur : 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.
BOOL
DlgOne:: OnInitDialog ()
{
CDialog:: OnInitDialog ();
ancre.ModifyStyleEx (0 , WS_EX_CONTROLPARENT);
DlgTwo.Create (IDD_DIAGTWO, & ancre);
}
|
|
| auteur : 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:
class CDlgTest : public CDialog
{
public :
DECLARE_DYNCREATE (CDlgTest)
CDlgTest (CWnd* pParent = NULL );
/ ....
|
Le .cpp:
IMPLEMENT_DYNCREATE (CDlgTest, CDialog)
CDlgTest:: CDlgTest (CWnd* pParent )
: CDialog (CDlgTest:: IDD, pParent)
{
}
|
L'appel de la dialogue:
CDlgTest * pDlg;
pDlg = new CDlgTest ();
pDlg- > Create (CDlgTest :: IDD,AfxGetMainWnd ());
pDlg- > ShowWindow (SW_SHOW);
|
Sa recherche:
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);
break ;
}
pChild = pChild- > GetWindow (GW_HWNDNEXT);
}
|
|
| auteur : Farscape | La méthode OnInitdialog doit renvoyer un BOOLEEN en sortie :
BOOL CSimpleDlg:: OnInitDialog ()
{
CDialog:: OnInitDialog ();
m_cMyEdit.SetWindowText (_T (" My Name " ));
m_cMyList.ShowWindow (SW_HIDE);
return TRUE;
}
|
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.
BOOL CSimpleDlg:: OnInitDialog ()
{
CDialog:: OnInitDialog ();
m_cMyEdit.SetWindowText (_T (" My Name " ));
m_cMyEdit.SetFocus ();
return FALSE;
}
|
|
Consultez les autres F.A.Q.
|
|