| auteur : Farscape | Il faut laisser à l'application le temps de traiter les messages sinon dés que l'on bouge la fenêtre ou si elle recouverte par une autre application il n'y a plus de reconstruction possible .
Solution intégrer dans sa boucle l'appel de la fonction suivante qui pourrait être une fonction static de la classe d'application par exemple :
void CMyApp :: PumpMessages ()
{
MSG msg;
while (PeekMessage (& msg, NULL , 0 , 0 , PM_REMOVE))
{
if (! AfxGetApp ()- > PreTranslateMessage (& msg))
{
:: TranslateMessage (& msg);
:: DispatchMessage (& msg);
}
AfxGetApp ()- > OnIdle (0 );
AfxGetApp ()- > OnIdle (1 );
}
}
|
|
| auteur : Farscape |
((CMDIFrameWnd * )AfxGetMainWnd ())- > GetActiveFrame ( )- > GetActiveView ();
|
Récupération de la mainFrame ,puis de la MDI Active .
Et enfin de la view dans la MDI active.
Le test doit être découpé pour éviter un problème si il n'y a pas MDI active !
CView* CFrameWnd:: GetActiveView ( ) const ;
|
|
| auteur : Farscape |
CWnd * FindInstanceView (CRuntimeClass * pViewClass)
{
CWinApp* pApp = AfxGetApp ();
CDocTemplate* pTemplate;
POSITION pos = pApp- > GetFirstDocTemplatePosition ();
while (pos ! = NULL )
{
pTemplate = pApp- > GetNextDocTemplate (pos);
ASSERT (pTemplate);
POSITION pos2 = pTemplate- > GetFirstDocPosition ();
while (pos2)
{
CDocument* pDoc = pTemplate- > GetNextDoc (pos2);
ASSERT (pDoc);
POSITION pos3 = pDoc- > GetFirstViewPosition ();
while (pos3 ! = NULL )
{
CView* pView = pDoc- > GetNextView (pos3);
ASSERT (pView);
if (:: IsWindow (pView- > GetSafeHwnd ()))
{
if (pView- > IsKindOf (pViewClass))
{
return pView;
}
}
}
}
}
return NULL ;
}
|
Utilisation: recherche d'une instance de la classe CTestMdiView .
si la fenêtre est trouvée on la refait passer en avant plan.
CTestMdiView * pView;
if ((pView= static_cast < CTestMdiView * > (FindClassView (RUNTIME_CLASS (CTestMdiView)))))
{
TRACE (" \nInstance de la classe trouvée " );
static_cast < CMDIChildWnd * > (pView- > GetParentFrame ())- > ActivateFrame (SW_RESTORE);
}
|
|
| auteur : Farscape | A chaque nIDResource associée à un document template on trouve dans l'éditeur de ressources dans la « stringtable » la chaîne d'ouverture pour le document.
Exemple:
Pour l'id IDR_TESTMDTYPE on pourra avoir :
\nExts\nExts\nFichiers (* .txt)\n.txt\nExts.Doc\nExts Doc
|
Description :
IDR_TESTMDTYPE < windowTitle> \n< docName> \n< fileNewName> \n
< filterName> \n < filterExt> \n< regFileTypeID> \n
< regFileTypeName> \n < filterMacExt (filterWinExt)> \n
< filterMacName (filterWinName)>
IDR_TESTMDTYPE \nExts\nExts\nFichiers (* .txt ;* .doc)\n.txt;
.doc\Exts.Document.1\nExts Document\nExts\nExts Fichiers
< windowTitle> Exts
< docName> Exts
< fileNewName> Exts
< filterName> Fichiers (* .txt)
< filterExt> . txt
< regFileTypeId> Exts.Document.1
< regFileTypeName> Exts Document
< filterMacExt> Exts
< filterMacName> Exts Fichiers
< item> voir Note MSDN:< / item>
< b> INFO: Format of the Document Template String
Q129095
< / b>
|
CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate (
IDR_TESTMDTYPE,
RUNTIME_CLASS (CTestMdiDoc),
RUNTIME_CLASS (CChildFrame),
RUNTIME_CLASS (CTestMdiView));
AddDocTemplate (pDocTemplate);
|
A l'ouverture j'aurai bien : Fichiers *.txt
Pour gérer le multi documents à l'ouverture c'est plus compliqué
voir note MSDN:
< b> HOWTO: How to Support Two File Extensions per MFC Document Type
Q141921< / b>
|
|
| auteur : Farscape | Dans un projet classique on a besoin de l'objet CMultiDocTemplate pour créer les fenêtres avec la commande suivante :
m_pTplEditView- > OpenDocumentFile (NULL ) ;
|
Comment faire pour éviter la déclaration de données membres dans la classe d'application ?
Je propose la solution suivante :
- Redéfinir une classe dérivée de CMultiDocTemplate.
- Déclarer une fonction d'ouverture d'une fenêtre en fonction de la signature de la classe fenêtre (runtime).
La création pourra s'écrire de la manière suivante:
void CTestMdiApp:: OnFileNew ()
{
OpenDocumentFile (RUNTIME_CLASS (CTestMdiView));
}
|
A la place de :
void CTestMdiApp:: OnFileNew ()
{
m_pTplEditView- > OpenDocumentFile (NULL ) ;
}
|
Implémentation:
class CExMultiDocTemplate : public CMultiDocTemplate
{
DECLARE_DYNAMIC (CExMultiDocTemplate)
public :
CExMultiDocTemplate (UINT nIDResource,
CRuntimeClass* pDocClass,
CRuntimeClass* pFrameClass,
CRuntimeClass* pViewClass):
CMultiDocTemplate (nIDResource,pDocClass,pFrameClass,pViewClass){ }
CRuntimeClass* GetDocClass () { return m_pDocClass;}
CRuntimeClass* GetFrameClass () { return m_pFrameClass;}
CRuntimeClass* GetViewClass () { return m_pViewClass;}
UINT GetnIDResource () { return m_nIDResource;}
} ;
IMPLEMENT_DYNAMIC (CExMultiDocTemplate,CMultiDocTemplate)
CDocument * CTestMdiApp:: OpenDocumentFile (CRuntimeClass * pClassView,
LPCTSTR lpszPathName,
BOOL bMakeVisible )
{
CDocTemplate* pTemplate;
POSITION pos = GetFirstDocTemplatePosition ();
while (pos ! = NULL )
{
pTemplate = GetNextDocTemplate (pos);
ASSERT (pTemplate);
if (pTemplate- > IsKindOf (RUNTIME_CLASS (CExMultiDocTemplate)))
{
if (((CExMultiDocTemplate * )pTemplate)- > GetViewClass ()= = pClassView)
return pTemplate- > OpenDocumentFile (lpszPathName,bMakeVisible);
}
}
return NULL ;
}
|
utilisation :
CExMultiDocTemplate * pTemplate;
pTemplate = new CExMultiDocTemplate (
IDR_TESTMDTYPE,
RUNTIME_CLASS (CTestMdiDoc),
RUNTIME_CLASS (CChildFrame),
RUNTIME_CLASS (CTestMdiView));
AddDocTemplate (pTemplate);
pTemplate= new CExMultiDocTemplate (
IDR_TESTMDTYPE,
RUNTIME_CLASS (CTestMdiDoc),
RUNTIME_CLASS (CChildFrame),
RUNTIME_CLASS (CEditView));
AddDocTemplate (pTemplate);
|
La classe CExMultiDocTemplate permet l'accès aux données membres de la classe de base, dans notre cas c'est le runtime de la classe fenêtre qui nous intéresse.
La fonction OpenDocumentFile parcourt tous les documents templates et recherche celui qui dispose de la signature fenêtre désirée pour appeler ensuite OpendocumentFile.
Donc plus besoin de variables pour stocker les documents templates et un appel simplifié par signature de classe pour créer la fenêtre.
|
| auteur : Farscape | En interceptant le message WM_GETMINMAXINFO.
Et en consultant les données de la structure MINMAXINFO,
Exemple limitation de la taille minimum de l'application à 320 pixels de large et 240 de haut.
void CMainFrame:: OnGetMinMaxInfo (MINMAXINFO FAR* lpMMI)
{
lpMMI- > ptMinTrackSize.x = 320 ;
lpMMI- > ptMinTrackSize.y = 240 ;
CMDIFrameWnd:: OnGetMinMaxInfo (lpMMI);
}
|
|
| auteur : Farscape | En utilisant la fonction SetCursorPos.
BOOL SetCursorPos (
int X,
int Y
);
|
Exemple en modifiant la position de la souris lors de son déplacement à la réception du message WM_MOUSEMOVE.
void CTestMdiView:: OnMouseMove (UINT nFlags, CPoint point)
{
if (! m_bMouseSetPos)
{
CPoint pt= point;
pt.x = 100 ;
ClientToScreen (& pt);
:: SetCursorPos (pt.x,pt.y);
m_bMouseSetPos= true ;
}
else m_bMouseSetPos= false ;
CFormView:: OnMouseMove (nFlags, point);
}
m_bMouseSetPos= false ;
|
|
| auteur : Farscape | Lorsqu'on a défini plusieurs objets de la classe CMultiDocTemplate dans InitInstance à l'appel la commande ID_FILE_NEW une fenêtre apparaît avec la liste des différents noms de documents disponibles.
Pour éviter cela il suffit de redéfinir la commande ID_FILE_NEW de la classe d'application et d'appeler l'ouverture de la fenêtre qui nous convient :
BEGIN_MESSAGE_MAP (CTestMdiApp, CWinApp)
ON_COMMAND (ID_APP_ABOUT, OnAppAbout)
ON_COMMAND (ID_FILE_NEW, OnFileNew)
ON_COMMAND (ID_FILE_OPEN, CWinApp:: OnFileOpen)
ON_COMMAND (ID_FILE_PRINT_SETUP, CWinApp:: OnFilePrintSetup)
END_MESSAGE_MAP ()
void CTestMdiApp:: OnFileNew ()
{
m_pTplEditView- > OpenDocumentFile (NULL ) ;
}
|
|
| auteur : Farscape | Il suffira de mettre en commentaire la séquence suivante contenue dans la fonction InitInstance de la classe d'application :
|
| auteur : Farscape |
Dans une application MFC la gestion des documents lecture sauvegarde etc.. est prise en charge par un objet dérivé de la classe CDocument pour le document template concerné (voir CDocTemplate ).
Il suffira donc d'intercepter le message ID_FILE_SAVE sur la classe document par l'intermédiaire de « ClassWizard »
Si d'aventure on souhaitait l'intercepter au niveau de la mainframe on récupérera la notification avec la fonction OnCommand :
BOOL CMainFrame:: OnCommand (WPARAM wParam, LPARAM lParam)
{
if (LOWORD (wParam)= = ID_FILE_SAVE)
{
AfxMessageBox (" Msg:ID_FILE_SAVE " );
}
return CMDIFrameWnd:: OnCommand (wParam, lParam);
}
|
Attention tout de même de savoir quel est le document concerné dans un contexte MDI.
Avec par exemple la fonction GetActiveDocument() :
CFrameWnd:: GetActiveDocument
virtual CDocument* GetActiveDocument ( );
|
|
| auteur : Farscape | En simulant l'appel des commandes :
ID_VIEW_TOOLBAR et ID_VIEW_STATUS_BAR comme sur le menu standard: "affichage" de l'application.
A partir de la view on récupère le pointeur sur la mainframe, puis on désactive la toolbar et la statusbar par l'envoi d'un message WM_COMMAND.
Les mêmes appels feront réapparaître les barres comme dans le menu...
CMainFrame * pMain= (CMainFrame * )AfxGetMainWnd ();
pMain- > SendMessage (WM_COMMAND, ID_VIEW_TOOLBAR, 0L );
pMain- > SendMessage (WM_COMMAND, ID_VIEW_STATUS_BAR,0L );
|
|
| auteur : Farscape | Dans le cas d'un projet SDI il faut faire le travail sur la mainframe.
Exemple sur l'action d'un bouton situé dans une CFormView:
void CTestSDIView:: OnButton1 ()
{
CRect RectFrame;
AfxGetMainWnd ()- > GetWindowRect (& RectFrame);
AfxGetMainWnd ()- > SetWindowPos (NULL ,0 ,0 ,
RectFrame.Width ()+ 100 ,
RectFrame.Height ()+ 100 ,
SWP_NOMOVE | SWP_NOZORDER);
}
|
|
| auteur : Farscape | Pour préciser le sujet:
Il arrive parfois que l'on ait besoin de créer dynamiquement des contrôles à l'initialisation de la CFormView ,
Dans ce contexte, il s'avère utile de connaître la surface disponible pour placer au mieux les contrôles.
L'endroit le plus approprié étant la fonction OnInitialUpdate .
Pour avoir un résultat correct avec la fonction GetClienRect, il faudra d'abord procéder à une demande de recalcule de la taille de la frame.
GetParentFrame ()- > RecalcLayout ();
ResizeParentToFit ();
CRect Rect ;
GetClientRect (& Rect) ;
|
ResizeParentToFit assure que la dimension de la vue soit bien réglée.
L'appel à RecalcLayout est indispensable avant ResizeParentToFit (risque d'assertion), il assure lors du dimensionnement de la frame le rafraîchissement des barres de contrôles.
|
| auteur : Nourdine Falola | En surchargeant la fonction membre virtuelle CWnd::PreCreateWindow pour modifier la structure CREATESTRUCT, qui définit les paramètres d'initialisation de la fenêtre (i.e. du cadre), avant la création de celle-ci.
typedef struct tagCREATESTRUCT {
LPVOID lpCreateParams;
HINSTANCE hInstance;
HMENU hMenu;
HWND hwndParent;
int cy;
int cx;
int y;
int x;
LONG style;
LPCTSTR lpszName;
LPCTSTR lpszClass;
DWORD dwExStyle;
} CREATESTRUCT, * LPCREATESTRUCT;
|
(cx,cy) : largeur et hauteur de la nouvelle fenêtre, en pixels
(x,y) : coordonnées du coin supérieur gauche de la fenêtre, en pixels.
style : style de la nouvelle fenêtre ( Available styles, Frame-window styles)
dwExStyle : style étendu de la nouvelle fenêtre
Exemple pour un projet SDI :
Le style par défaut est une combinaison de WS_OVERLAPPEDWINDOW et FWS_ADDTOTITLE. FWS_ADDTOTITLE indique au framework d'ajouter au titre de la fenêtre le titre du document.
BOOL CMainFrame:: PreCreateWindow (CREATESTRUCT& cs)
{
cs.style = WS_OVERLAPPED | WS_SYSMENU | WS_BORDER;
cs.cy = :: GetSystemMetrics (SM_CYSCREEN) / 3 ;
cs.cx = :: GetSystemMetrics (SM_CXSCREEN) / 3 ;
cs.y = ((cs.cy * 3 ) - cs.cy) / 2 ;
cs.x = ((cs.cx * 3 ) - cs.cx) / 2 ;
return CFrameWnd:: PreCreateWindow (cs);
}
|
Exemple pour un projet MDI :
Le style par défaut est une combinaison de WS_CHILD, WS_OVERLAPPEDWINDOW et FWS_ADDTOTITLE.
BOOL CChildFrame:: PreCreateWindow (CREATESTRUCT& cs)
{
cs.style & = ~ WS_MAXIMIZEBOX;
return CMDIChildWnd:: PreCreateWindow (cs);
}
|
|
| auteur : Farscape |
Il y a bien une différence dans la fonction AfxGetMainWnd() à partir de VC7 et les versions précédentes..
_AFXWIN_INLINE CWnd* AFXAPI AfxGetMainWnd ()
{ CWinThread* pThread = AfxGetThread ();
return pThread ! = NULL ? pThread- > GetMainWnd () : NULL ; }
|
Cette fonction appel la fonction AfxGetThread(), et cette fonction est implémentée différemment à partir de VC7...
en VC7 :
CWinThread* AFXAPI AfxGetThread ()
{
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState ();
CWinThread* pThread = pState- > m_pCurrentWinThread;
return pThread;
}
|
En VC6.0 :
CWinThread* AFXAPI AfxGetThread ()
{
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState ();
CWinThread* pThread = pState- > m_pCurrentWinThread;
if (pThread = = NULL )
pThread = AfxGetApp ();
return pThread;
}
|
donc on peux remplacer notre appel de AfxGetMainWnd() par :
AfxGetApp ()- > GetMainWnd ();
|
pourquoi ce code a été modifié reste un mystère ...
|
| auteur : Farscape | Pour empêcher dans un contexte MDI de déplacer une fenêtre fille dans la surface de travail de l'application on interceptera le message WM_WINDOWPOSCHANGING sur la MDIChild.
Exemple:
void CChildFrame:: OnWindowPosChanging (WINDOWPOS* lpwndpos)
{
if (! m_rect.IsRectEmpty ())
{
lpwndpos- > x= m_rect.left;
lpwndpos- > y= m_rect.top;
CRect rectBarPos;
static_cast < CMainFrame * > (AfxGetMainWnd ())- > m_wndToolBar.GetWindowRect (& rectBarPos);
lpwndpos- > y- = rectBarPos.Height ();
}
CMDIChildWnd:: OnWindowPosChanging (lpwndpos);
}
void CChildFrame:: OnSize (UINT nType, int cx, int cy)
{
CMDIChildWnd:: OnSize (nType, cx, cy);
if (m_rect.IsRectEmpty ())
{
GetWindowRect (& m_rect);
AfxGetMainWnd ()- > ScreenToClient (& m_rect);
}
}
|
Sur le premier WM_SIZE de la fenêtre on mémorise sa position et sur le message WM_WINDOWPOSCHANGING on la restitue.
|
| auteur : Farscape | L'espace de travaille alloué à la mainframe dans le mode maximisé dépend directement de la résolution de l'écran,
Pour disposer d'une surface plus grande on rajoutera les styles WS_VSCROLL | WS_HSCROLL à la création de la MainFrame.
Dés qu'une fenêtre sera poussée ou créer en dehors de la surface normale d'affichage, les ascenseurs apparaitront agrandissant du même coup notre espace de travail.
Comment procéder :
Surchargez avec l'assistant la fonction virtuelle PreCreateWindows et rajouter les styles pour les barres d'ascenseurs.
BOOL CMainFrame:: PreCreateWindow (CREATESTRUCT& cs)
{
cs.style | = (WS_VSCROLL | WS_HSCROLL);
return CMDIFrameWnd:: PreCreateWindow (cs);
}
|
|
| auteur : Farscape | On rajoutera le code suivant à la classe CMainFrame créée :
void CMainFrame:: ActiveFormView (int n)
{
VERIFY (n< m_arView.GetCount ());
CView* pActiveView = GetActiveView ();
CView * pNewView = m_arView[n];
UINT temp = :: GetWindowLong (pActiveView- > m_hWnd, GWL_ID);
:: SetWindowLong (pActiveView- > m_hWnd, GWL_ID,
:: GetWindowLong (pNewView- > m_hWnd, GWL_ID));
:: SetWindowLong (pNewView- > m_hWnd, GWL_ID, temp);
pActiveView- > ShowWindow (SW_HIDE);
pNewView- > ShowWindow (SW_SHOW);
SetActiveView (pNewView);
RecalcLayout ();
pNewView- > Invalidate ();
}
CView * CMainFrame:: AddView (CRuntimeClass * pClassView)
{
CView* pView = static_cast < CView * > (pClassView- > CreateObject ());
CView* pActiveView = GetActiveView ();
if (! m_arView.GetCount ()) m_arView.Add (pActiveView);
CDocument* pCurrentDoc = GetActiveDocument ();
CCreateContext newContext;
newContext.m_pNewViewClass = NULL ;
newContext.m_pNewDocTemplate = NULL ;
newContext.m_pLastView = NULL ;
newContext.m_pCurrentFrame = NULL ;
newContext.m_pCurrentDoc = pCurrentDoc;
CRect rect (0 , 0 , 0 , 0 );
pView- > Create (NULL , NULL ,(AFX_WS_DEFAULT_VIEW & ~ WS_VISIBLE),rect, this ,
AFX_IDW_PANE_FIRST + m_arView.GetCount (), & newContext);
m_arView.Add (pView);
pView- > OnInitialUpdate ();
return pView;
}
|
J'ai implémenté deux méthodes :
Une pour la création de la vue d'après un Runtime de classe fourni, l'autre pour activer la vue.
La vue 0 est la vue principale. L'ensemble des vues est stocké dans un CArray.
Utilisation:
dans InitInstance je demande la création d'une vue:
m_pMainWnd- > ShowWindow (SW_SHOW);
m_pMainWnd- > UpdateWindow ();
static_cast < CMainFrame* > (m_pMainWnd)- > AddView (RUNTIME_CLASS (CEditView));
|
Dans mon exemple j'ai ajouté une CEditView.
Pour procéder à l'activation on appellera ActiveFormView:
|
void CMainFrame:: OnFenetre1 ()
{
ActiveFormView (0 );
}
void CMainFrame:: OnFenetre2 ()
{
ActiveFormView (1 );
}
|
|
Consultez les autres F.A.Q.
|
|