1
• La gestion des fichiers
• Architecture non Document/Vue
• Boîte de dialogue non modale Stockage des documents
Boîtes de dialogue
2
Les fichiers
La majorité des fonctions de bas niveau (API) de manipulations des fichiers sont encapsulées dans les MFC dans la classe CFile.
CFile propose les méthodes classiques d'ouverture, fermeture, écriture et lecture dans des fichiers.
Cependant les MFC proposent aussi une autre classe CArchive pour la gestion des fichiers largement employée dans les applications.
Ces deux classes sont liées à différents niveaux.
3
Les fichiers – La classe CFile
La classe CFile encapsule les fonctionnalités de l'API Win32 dédiées à la gestion des entrées/sorties (fichiers).
Elle comporte plus de 25 méthodes pour manipuler les fichiers :
ouverture, fermeture,
écriture, lecture
création, renommage, recherche d'informations,
autres …
La classe CFile possède
un attribut (protégé) : m_StrFileName qui contient le nom du fichier
un attribut (public) : m_hFile qui contient le numéro de handle associé au fichier.
Certaines fonctions comme :
GetFilePath, GetFileName, GetFileTitle
permettent de connaître le nom du complet du fichier
(z:\users\ihm\cours.doc) ou simplement le nom du fichier avec son extension (cours.doc) ou sans son extension (cours).
4
Les fichiers – La classe CFile
Ouvrir, fermer et créer des fichiers
Ouverture
Un fichier peut être ouvert
soit par appel explicite de la méthode Open.
soit de façon implicite par le constructeur de la classe.
1 ère méthode : déclaration d'un objet de type CFile et utilisation de la méthode Open.
// déclaration d'un objet de type CFile CFile fich;
// ouverture du fichier
fich.Open("exemple.dat", CFile :: modeReadWrite);
La méthode Open retourne un booléen (BOOL) qui indique si l'opération d'ouverture s'est bien déroulée.
// ouverture avec test du résultat if (fich.Open("exemple.dat", CFile :: modeReadWrite)) { // tout est correct
………
} else
{// une erreur s'est produite
……..
}
5
Les fichiers – La classe CFile
Ouvrir, fermer et créer des fichiers
Ouverture
Lorsqu'une erreur se produit, une exception de type CFileException est déclenchée. Un objet de ce type peut être donné en 3 ème paramètre à la méthode Open.
CFileException e;
if (fich.Open("exemple.dat", CFile :: modeReadWrite, &e) { // tout est correct
}………
else{// une erreur s'est produite e.ReportError();
}
La classe CFileException dispose d'un attribut m_cause (public) qui décrit la cause de l'erreur. La méthode ReportError affiche le message d'erreur correspondant.
6
Les fichiers – La classe CFile
Ouvrir, fermer et créer des fichiers
2 ème méthode d'ouverture d'un fichier : par le constructeur
CFile fich("exemple.dat",CFile::modeReadWrite);
Si une erreur se produit, une exception de type CFileException est déclenchée.
C'est au programmeur de prévoir un gestionnaire approprié pour récupérer cette exception.
Aussi la partie de programme qui effectue l'ouverture d'un fichier doit être
contrôlée (par try)
et suivie de la définition du gestionnaire pour les
exceptions CFileException.
7
Les fichiers – La classe CFile
Ouvrir, fermer et créer des fichiers void sauvegarde(void)
{ try {
CFile fich("exemple.dat",CFile::modeReadWrite);
….
}
catch (CFileException *e) { e->ReportError();
e->Delete ();
} }
L'exception générée doit être « désallouée » par le gestionnaire.
8
Les fichiers – La classe CFile
Ouvrir, fermer et créer des fichiers
Les modes d'ouverture
CFile::modeCreate
création d'un nouveau fichier avec écrasement si fichier existant.
CFile::modeNoTruncate
mode à combiner avec la valeur modeCreate.
Si le fichier existe déjà, il n'est pas écrasé. Le fichier est donc soit créé s'il n'existe pas soit ouvert seulement s'il existe déjà.
CFile f("exemple.dat", CFile::modeReadWrite
|CFile::modeCreate | CFile :: modeNoTruncate).
création du fichier si inexistant, sinon ouverture sans écrasement.
9
Les fichiers – La classe CFile
Ouvrir, fermer et créer des fichiers
Les modes d'accès :
CFile::modeRead
accès en lecture seulement
CFile::modeReadWrite
accès en lecture/écriture
CFile::modeWrite
accès en écriture seulement
10
Les fichiers – La classe CFile
Ouvrir, fermer et créer des fichiers
Fermeture
La fermeture s'effectue simplement par la méthode Close.
fich.Close();
Cette fermeture peut aussi être faite par le destructeur.
Le destructeur de la classe CFile appelle Close si le fichier est encore ouvert.
11
Les fichiers – La classe CFile
Lecture, Ecriture
Lecture
La lecture se fait par la méthode Read.
virtual UINT Read( void*lpBuf, UINTnCount);
throw( CFileException );
où lpBuf donne est un pointeur du buffer destiné à récupérer les données lues et nCount est le nombre (maximum) d'octets à lire.
exemple : void lire_fich();
{ CFile fich("exemple.dat", CFile::modeRead);
char texte[50];
// lecture de 10 caractères à stocker dans texte fich.Read(texte,10);
}…
La méthode GetLength permet de connaître la taille du fichier :
taille = fich.GetLength();
La méthode Read retourne le nombre d'octets effectivement lus. 12
Les fichiers – La classe CFile
Lecture, Ecriture
Lecture
Si une erreur se produit durant la lecture,
une exception de type CFileException est déclenchée.
La gestion de ces exceptions doit être faite
en contrôlant la zone d'appel de Read (par un bloc Try)
et en définissant un gestionnaire de traitement.
De nouveau
la variable (public) de CFile, m_cause est affectée au code d'erreur
et la méthode ReportError peut être utilisée dans le
gestionnaire pour connaître l'erreur.
13
Les fichiers – La classe CFile
Lecture, Ecriture
Ecriture
L'écriture dans un fichier est réalisée par ma méthode Write.
virtual void Write( const void*lpBuf, UINTnCount);
throw( CFileException );
Son utilisation est similaire à Read mais permet l'écriture.
Elle retourne le nombre d'octets effectivement écrits et déclenche une exception CFileException si problème.
Le pointeur donné en paramètre indique l'adresse de la zone contenant les données à écrire.
void ecrire_fich();
{ CFile fich("exemple.dat", CFile::modeWrite |CFile::modeCreate);
char texte[50]="Je suis le texte à sauvegarder";
// écriture des 10 premiers caractères fich.Write(texte,10);
}
14
Les fichiers – La classe CFile
Se positionner
Positionnement
Le positionnement direct dans le fichier peut être fait par la méthode Seek.
virtual LONG Seek( LONGlOff, UINTnFrom);
throw( CFileException );
lOff indique le nombre d'octets de déplacement du pointeur de fichier.
nFrom indique le mode de mouvement, il doit être l'une des trois valeurs suivantes :
CFile::begin bouge le pointeur de nOff octets à partir du début du fichier.
CFile::current bouge le pointeur de nOff octets à partir de la position courant du pointeur du fichier.
CFile::end bouge le pointeur de nOff octets à partir de la fin fichier (la valeur de déplacement doit dans ce cas être négative).
15
Les fichiers – La classe CFile
Se positionner
La méthode GetPosition peut être utilisée pour connaître la position courante.
virtual DWORD GetPosition( ) const;
throw( CFileException );
On peut aussi fixer la position courante du pointeur de fichier par les méthodes suivantes :
soit au début :
void SeekToBegin( ); throw( CFileException );
soit à la fin :
DWORD SeekToEnd( ); throw( CFileException );
On peut aussi changer la taille d'un fichier par :
virtual void SetLength( DWORD dwNewLen );
throw( CFileException );
16
Les fichiers – La classe CFile
Renommer, Supprimer
Renommage
CFile comporte une méthode Rename qui permet de renommer un fichier.
static void PASCAL Rename( LPCTSTR lpszOldName , LPCTSTR lpszNewName ); throw(
CFileException );
où pszOldName indique l'ancien nom
lpszNewName indique le nouveau nom
Cette méthode renomme le fichier spécifié, les répertoires ne peuvent pas être modifiés.
Cette méthode est équivalente à la commande REN.
17
Les fichiers – La classe CFile
Renommer, Supprimer
Renommage : exemple
pOldName et pNewName sont de type char *
L'appel de la méthode Rename est contrôlé car il peut produire une exception.
Un exemple de gestionnaire est décrit après le bloc Try,
il affiche en utilisant m_cause, les raisons du déclenchement de l'erreur.
try
{ CFile::Rename( pOldName, pNewName );
}
catch( CFileException, e ) { #ifdef _DEBUG
//afxDump envoi un message vers la fenêtre de Debug de Visual afxDump << "File " << pOldName << " not found, cause = "
<< e->m_cause << "\n";
#endif }
18
Les fichiers – La classe CFile
Renommer, Supprimer
Suppression
static void PASCAL Remove( LPCTSTR lpszFileName ); throw( CFileException );
où lpszFileName indique le nom du fichier à supprimer
La méthode Remove déclenche une exception si le fichier ne peut pas être détruit (ou est ouvert).
Cette méthode est équivalente à la commande DEL.
19
Les fichiers – La classe CFile
Renommer, Supprimer
Suppression : exemple
char* pFileName = "test.dat";
try {
CFile::Remove( pFileName );
}
catch ( CFileException e ) {
#ifdef _DEBUG
afxDump << "File " << pFileName << " cannot be removed\n";
#endif }
20
Les fichiers – La classe CArchive
Bien que la classe CFile fournissent toutes les méthodes nécessaires à la gestion des fichiers,
la majorité des applications MFC
n'utilisent pas directement la cette classe
mais réalisent les opérations d'écriture et de lecture dans les fichiers
à l'aide de la classe CArchive.
CArchive utilise les méthodes de la classe CFile mais MFC assure la surcharge des opérateurs de flots << et >> avec CArchive.
Dans les architectures de type Document/Vues des MFC,
des méthodes de gestion des archives sont proposées dans la classe Document
pour permettre une sauvegarde simple des données.
21
Les fichiers – La classe CArchive
Exemple de sauvegarde d'entiers à l'aide de CArchive.
CFile fich("exemple.dat",CFile :: modeWrite|CFile ::modeCreate);
int a=5,b=6;
L'écriture :
fich.Write(&a, sizeof((a));
fich.Write
(&b, sizeof((b));
peut être faite de façon équivalente à l'aide de la classe CArchive par
CArchive ar(&fich, CArchive::store);
ar << a << b;
22
Les fichiers – La classe CArchive
Exemple de Lecture d'entiers à l'aide de CArchive.
CFile fich("exemple.dat",CFile :: modeRead);
int a,b;
La lecture :
fich.Read(&a, sizeof((a));
fich.Read(&b, sizeof((b));
peut être faite de façon équivalente à l'aide de la classe CArchive par
CArchive ar(&fich, CArchive::load);
ar >> a >> b;
MFC permet la "sérialisation" d'un grand nombre de types y compris les classes CString, CTime, CTimeSpan, …, CSize, CPoint, CRect.
23
Les fichiers
La "sérialisation" des classes définies par le programmeur
L'aspect le plus puissant du mécanisme de sérialisation proposé par les MFC est
le fait que l'on puisse créer des classes "sérialisables"
qui travaillent avec les opérateurs d'écriture et de lecture de la classe CArchive
sans avoir à surcharger les opérateurs de flots << et
>>.
Pour cela, il faut que la classe définie
soit une classe descendante directement ou indirectement de la classe CObject
car dans ce cas les MFC surchargent les opérateurs de flots pour les pointeurs vers des instances de classes dérivées de CObject.
24
Les fichiers
La "sérialisation" des classes définies par le programmeur
Si une erreur se produit lors de la sérialisation une exception est générée.
Le type de l'exception produite dépend de la nature de l'erreur.
CMemoryException indique un problème d'allocation mémoire,
CFileException indique une erreur d'entrée/sortie,
CArchiveException dans les autres cas.
25
Les fichiers
La "sérialisation" des classes définies par le programmeur
La définition d'une classe « sérialisable » peut être faite en 5 étapes :
Etape 1 : Dériver la classe à construire de CObject (ou d'une héritière de CObject).
class CLine : public CObject { protected :
CPoint m_Start;
CPoint m_End;
public :
…….
}
26
Les fichiers
La "sérialisation" des classes définies par le programmeur
Etape 2 : Inclure la déclaration de la macro instruction DECLARE_SERIAL dans la classe.
class CLine : public CObject { protected :
CPoint m_Start;
CPoint m_End;
public :
…….
DECLARE_SERIAL(CLine) }
27
Les fichiers
La "sérialisation" des classes définies par le programmeur
Etape 3 : Surcharger la fonction « Serialize » de la classe de base et sérialiser les membres données de la classe dérivée.
class CLine : public CObject { protected :
CPoint m_Start;
CPoint m_End;
public :
void Serialize(CArchive& ar);
…….
}
void CLine :: Serialize(CArchive& ar) { CObject :: Serialize();
if (ar.storing ()) ar << m_Start << m_End;
else
ar >> m_Start >> m_End;
}
28
Les fichiers
La "sérialisation" des classes définies par le programmeur
Etape 4 : Définition d'un constructeur par défaut
Si la classe dérivée ne possède pas de constructeur par défaut, il est nécessaire d'en définir un.
Cette étape est nécessaire car lorsqu'un objet est lu à partir d'une archive,
MFC crée un nouvel objet et l'initialise avec les valeurs lues.
Le constructeur par défaut est utilisé à lors de cette étape.
class CLine : public CObject { protected :
CPoint m_Start;
CPoint m_End;
public :
CLine() { }; //nécessaire
CLine (CPoint p1, Cpoint p2) {m_Start=p1; m_End=p2;}
void Serialize(CArchive& ar);
…….
}
29
Les fichiers
La "sérialisation" des classes définies par le programmeur
Etape 5 : Inclure la macro IMPLEMENT_SERIAL dans l'implémentation de la classe
La macro instruction IMPLEMENT_SERIAL admet trois paramètres :
le nom de la classe,
le nom de la classe ancêtre,
un numéro relatif à la version (pour gérer différent formats de sauvegarde).
dans Line.cpp
IMPLEMENT_SERIAL (CLine, CObject, 1)
dans une version ultérieure, on pourrait avoir :
IMPLEMENT_SERIAL(CLine, CObject, 2|VERSIONABLE_SCHEMA)
30
Les fichiers
La "sérialisation" des classes définies par le programmeur
Stockage des documents dans l'application IHM
Dans le cas de l'application IHM
les documents à sauvegarder sont les objets de type CElement (via les classes dérivées CLine, CCercle, CRectangle).
Pour qu'une classe soit sérialisable,
il faut que toutes les classes utilisées dans ses données membres soient aussi sérialisables.
Les gestionnaires définis dans la classe CIHMDoc associés aux commandes :
Fichier | Ouvrir,
Fichier | Enregistrer,
Fichier | Enregistrer Sous
comportent du code pour la sérialisation des documents.
31
Les fichiers
La "sérialisation" des classes définies par le programmeur
Stockage des documents dans l'application IHM
CIHMDoc dérive de CDocument qui dérive elle-même de CObject.
La classe CIHMDoc comporte un constructeur par défaut, utilise la macro instruction DECLARE_DYNCREATE et surcharge la méthode Serialize.
class CIHMDoc : public CDocument {
protected: // create from serialization only CIHMDoc();
DECLARE_DYNCREATE(CIHMDoc)
………
// Operations public:
………
// Overrides
// ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CIHMDoc)
public:
virtual BOOL OnNewDocument();
virtual void Serialize(CArchive& ar);
//}}AFX_VIRTUAL
………
} 32
Les fichiers
La "sérialisation" des classes définies par le programmeur
Stockage des documents dans l'application IHM
Trois niveaux de fonctionnalités sont disponibles dans les classes dérivées de CObject.
DECLARE_DYNAMIC(nom_classe) qui assure la prise en charge des informations de classe à l'exécution.
DECLARE_DYNCREATE(nom_classe) qui assure la création dynamique des objets et des informations de classe à l'exécution.
DECLARE_SERIAL(nom_classe) qui assure la prise en charge de la sérialisation des objets, de leur création dynamique et des informations de classe à l'exécution.
33
Les fichiers
La "sérialisation" des classes définies par le programmeur
Stockage des documents dans l'application IHM
Chaque niveau exige l'insertion de la macro correspondante dans le fichier contenant l'implémentation de la classe.
IMPLEMENT_DYNAMIC(nom_classe, nom_classe_ancêtre)
IMPLEMENT_DYNCREATE(nom_classe, nom_classe_ancêtre)
IMPLEMENT_SERIAL(nom_classe, nom_classe_ancêtre, numero)
La classe CIHMDoc n'a pas besoin de sérialisation mais les données membres doivent être sérialisables.
La macro IMPLEMENT_SERIAL a comme dernier argument un numéro qui est une numéro de version (appelé aussi numéro de schéma).
34
Les fichiers
La "sérialisation" des classes définies par le programmeur
La classe CIHMDoc contient le code suivant :
IMPLEMENT_DYNCREATE(CIHMDoc, CDocument) void CIHMDoc :: Serialize (CArchive& ar) { if (ar.IsStoring()){ // TODO : add storing code here } else { // TODO : add loading code here }
La méthode IsStroring() retourne
vraie si l'archive a été déclarée (par le constructeur) en mode stockage/écriture des données
et faux sinon.
De façon similaire, il existe une méthode IsLoading() de la classe CArchive qui teste si une archive est créée en mode chargement/lecture ou non.
35
Les fichiers
La "sérialisation" des classes définies par le programmeur
Donc la méthode « Serialize » permet soit la sauvegarde soit la restauration des données.
La classe CArchive surcharge les opérateurs de flots << et
>> sur les types :
float, double, BYTE, int, LONG, WORD, DWORD,
CObject *,
CString,
SIZE et CSize,
POINT et CPoint, RECT et CRect,
CTime et CTimeSpan (contient un intervalle en secondes).
On peut noter en particulier la surcharge sur CObjet * qui assure la surcharge sur les objets de CObjet * mais aussi de toutes ses classes dérivées.
36
Les fichiers
La "sérialisation" des classes définies par le programmeur
Sérialisation de la classe CElement
Définition dans CElement.h de la déclaration de la macro :
DECLARE_SERIAL(CElement)
Ne pas mettre cette macro dans une zone gérée par AppWizard.
Il est préférable de la mettre en 1 ère ligne pour éviter de l'oublier.
Définition de la macro dans CElement.cpp
IMPLEMENT_SERIAL(CElement, CObject,
VERSION_NUMBER)
37
Les fichiers
La "sérialisation" des classes définies par le programmeur
Définition de la méthode Serialize dans CElement.h
virtual void Serialize(CArchive& ar);
Implémentation de la fonction Serialize dans CElement.cpp void CElement::Serialize(CArchive& ar)
{ // Appel de la méthode Serialize de la classe de base CObject CObject::Serialize(ar);
if (ar.IsStoring())
{ /* Sauvegarde de la couleuret de l'épaisseur du crayon */
ar << m_Color << m_Pen;
}else
{ /* Restauration de la couleur et de l'épaisseur du crayon */
ar >> m_Color >>m_Pen;
}}
38
Les fichiers
La "sérialisation" des classes définies par le programmeur
Mise en œuvre de la sérialisation dans les classes CLine, CRectangle, CCercle.
Dans les constantes du programmes : // Numéro de version pour la sérialisation const UINT VERSION_NUMBER 1
Dans les déclarations des respectives des classes (.h) DECLARE_SERIAL (CLine)
virtual void Serialize(CArchive& ar);
DECLARE_SERIAL (CRectangle) virtual void Serialize(CArchive& ar);
DECLARE_SERIAL (CCercle) virtual void Serialize(CArchive& ar);
Dans les implémentations respectives des classes : IMPLEMENT_SERIAL(CLine, CElement, VERSION_NUMBER) IMPLEMENT_SERIAL(CRectangle, CElement, VERSION_NUMBER) IMPLEMENT_SERIAL(CCercle, CElement, VERSION_NUMBER)
39
Les fichiers
La "sérialisation" des classes définies par le programmeur
Implémentations des méthodes Serialize dans les classes respectives
void CLine::Serialize(CArchive& ar) {
// Appel de la méthode de la classe mère CElement::Serialize(ar);
//
Sauvegarde ou restaure les coordonnées des points extrémitésif (ar.IsStoring())
{ ar << m_Start << m_End; } else
{ ar >> m_Start >> m_End; } }
40
Les fichiers
La "sérialisation" des classes définies par le programmeur
void CRectangle::Serialize(CArchive& ar) {
// Appel de la méthode de la classe mèreCElement::Serialize(ar);
if (ar.IsStoring())
{ ar << m_TopLeft << m_BottomDown; } else
{ ar >> m_TopLeft >> m_BottomDown; } }
Il en est de même pour la classe CCercle avec les attributs m_center et m_r.
41
Les fichiers
La "sérialisation" des classes définies par le programmeur
Sérialisation de la classe CIHMDoc
Pour gérer la sauvegarde et la restauration des données,
il est nécessaire de savoir si des modifications ont été apportées au document ou non.
On va utiliser la fonction SetModifiedFlag héritée de la classe CDocument
void CDocument :: SetModifiedFlag( BOOL bModified = TRUE );
Cette méthode permet d'affecter un indicateur de modification du document en fonction de la valeur du paramètre (True ou False)
42
Les fichiers
La "sérialisation" des classes définies par le programmeur
Dans la classe CIHMDoc, l'ajout d'un élément et donc la modification du document est fait dans la méthode :
void CIHMDoc :: AddElement(CElement *pElement);
Nous allons donc insérer dans cette méthode un appel à la méthode SetModifiedFlag.
void CIHMDoc :: AddElement(CElement* pElement) {
m_ElementList.AddTail(pElement);
SetModifiedFlag();
}
43
Les fichiers
La "sérialisation" des classes définies par le programmeur
On peut maintenant terminer la mise en place de la sérialisation dans la classe CIHMDoc en implémentant la méthode virtuelle Serialize
void CIHMDoc::Serialize(CArchive& ar) {
// Appel de la méthode Serialize sur la liste d'élement // CTypedPtrList<CObList, CElement*> m_ElementList;
m_ElementList.Serialize(ar); // Serialize the element list
// Sauvegarde ou restauration de la couleur courante, de l'élément courant, de l'épaisseur de la plume courante et de la taille du document.
if (ar.IsStoring()) {
ar << m_Color << m_Element << m_Pen << m_DocSize;
} else {
ar >> m_Color >> m_Element >> m_Pen >> m_DocSize;
}
} 44
Les fichiers
La "sérialisation" des classes définies par le programmeur
Les gestionnaires OnNewDocument, OnOpenDocument, On CloseDocument, OnSaveDocument
Ces gestionnaires ne sont pas surchargés dans CIHMDoc, ce sont les gestionnaires par défaut de la classe CDocument qui sont utilisés.
Le gestionnaire OnNewDocument.
Si l'on souhaite surcharger ce gestionnaire, on peut écrire : BOOL CIHMDoc ::OnNewDocument()
{ if (!CDocument::OnNewDocument()) return FALSE;
// TODO : Ajouter les initialisations souhaitées return TRUE;
}
45
Les fichiers
La "sérialisation" des classes définies par le programmeur
Le gestionnaire OnOpenDocument.
virtual BOOL OnOpenDocument( LPCTSTRlpszPathName);
Cette méthode retourne une valeur non nulle si l'opération s'est bien déroulée ou 0 sinon.
Le paramètre lpszPathName est le nom du document à ouvrir.
L'implémentation par défaut de ce gestionnaire :
ouvre le fichier spécifié,
appelle la méthode DeleteContents de la classe CDocument pour s'assurer que le document est bien 'vide',
appelle la méthode CObject::Serialize pour lire le document
et marque le document comme non modifié.
On peut surcharger ce gestionnaire si on souhaite mettre en œuvre un autre mécanisme que le principe de sérialisation proposé
46
Les fichiers
La "sérialisation" des classes définies par le programmeur
Le gestionnaire OnCloseDocument
virtual CDocument :: void OnCloseDocument( );
Appelé lors du choix Fichier -> Fermer.
L'implémentation par défaut :
détruit toutes les structures utilisées pour visualiser le document,
ferme les vues associées au document,
réinitialise le contenu du document,
appelle la méthode DeleteContent pour détruire les données du document.
47
Les fichiers
La "sérialisation" des classes définies par le programmeur
Le gestionnaire OnSave Document
virtual BOOL OnSaveDocument( LPCTSTRlpszPathName);
retourne une valeur non nulle si la sauvegarde s'est bien passée et 0 sinon.
Le paramètre lpszPathName désigne le nom du ficher de sauvegarde.
Appelé comme partie du traitement des commandes
Fichier -> Enregistrer
Fichier -> Enregistrer Sous
L'implémentation par défaut :
ouvre le fichier spécifié,
appelle la fonction CObject:: Serialize pour écrire le document dans le fichier,
marque le document comme non modifié.
On peut surcharger cette méthode pour réaliser des traitements spécifiques lors des opérations de sauvegarde.
48
Les fichiers
Conclusion
MFC fournit des nombreuses fonctionnalités pour la gestion des fichiers à travers les classes
CFile qui encapsule les méthodes de lecture et d’écriture bas niveaux, sur des fichiers
CArchive qui propose des mécanismes de sauvegarde
et restauration d’objets haut niveaux (sérialization)
49
Application SDI – Non Document/Vue Boîte de dialogue non modale
On construit une application à l’aide de AppWizard,
Simple Document Interface,
sans utiliser l’architecture Document/Vue,
appelée DlgExemple
Le menu principal comporte par défaut :
Fichier -> Quitter
Edition -> Annuler, Couper, Copier, Coller
Affichage -> Barre d’outils, Barre d’état
? -> A propos de ...
Le squelette généré comporte
4 classes :
CMainFrame // objet fenêtre cadre
CDlgExempleApp // objet application
CChildView // objet vue
CAboutDlg // boîte de dialogue gérée par // ? A propos de …
50
Application SDI – Non Document/Vue Boîte de dialogue non modale
L'application comporte en plus du squelette généré, un sous-menu : Fichier -> Options
Le choix du sous-menu Options fait apparaître la fenêtre de dialogue
Le programme affiche simplement un rectangle dans le coin haut droit de la fenêtre en respect des
spécifications données dans le menu Options.
Par défaut, la largeur et la longueur sont de 2 pouces.
51
Application SDI – Non Document/Vue Boîte de dialogue non modale
La boîte de dialogue Options permet
de paramétrer la taille du rectangle
et d'appliquer ces modifications directement sans fermer la boîte de dialogue par le choix Appliquer.
Les options
Fermer ferme la boîte de dialogue et
Défaut réinitialise les paramètres à leurs valeurs initiales.
La boîte de dialogue ouverte est non modale i.e.
on peut effectuer d'autres opérations en conservant la boîte de dialogue ouverte.
52
Application SDI – Non Document/Vue Boîte de dialogue non modale
Etape 1 : Création du projet à l'aide AppWizard
Les classes créées sont :
CAboutDlg, CChildView, CMainFrame et CDlgExempleApp.
La classe CChildView comporte une surcharge de la méthode OnPaint
void CChildView::OnPaint()
{ CPaintDC dc(this); // device context for painting // TODO: Add your message handler code here }
53
Application SDI – Non Document/Vue Boîte de dialogue non modale
Etape 2 : Ajout de nouveaux attributs dans la classe CChildView.
Trois options de dessin sont gérées dans le programme :
la largeur et la longueur du rectangle et l'unité de mesure.
On ajoute donc les trois variables :
m_Largeur, m_Longueur et m_Unite
Ces attributs sont initialisés dans le constructeur de la classe CChildView.
CChildView::CChildView() {
m_Largeur = 2;
m_Longueur =2;
m_Unite =0; // l’unité est le pouce }
54
Application SDI – Non Document/Vue Boîte de dialogue non modale
Etape 3 : Surcharge de la méthode OnPaint de la classe CChildView.
La méthode OnPaint affiche le rectangle dans la vue en fonction de l'unité choisie.
void CChildView::OnPaint() {CPaintDC dc(this);
CBrush brush (RGB (255, 0, 255));
dc.SelectObject (&brush);
switch (m_Unite) {
case 0: // Pouces
dc.SetMapMode (MM_LOENGLISH);
dc.Rectangle (0, 0, m_Largeur * 100, -m_Longueur * 100); break;
case 1: // Centimètres dc.SetMapMode (MM_LOMETRIC);
dc.Rectangle (0, 0, m_Largeur * 100, -m_Longueur * 100); break;
case 2: // Pixels dc.SetMapMode (MM_TEXT);
dc.Rectangle (0, 0, m_Largeur, m_Longueur); break;
} }
55
Application SDI – Non Document/Vue Boîte de dialogue non modale
Etape 4 : Ecrire un gestionnaire de message (OnCreate) pour le message WM_CREATE.
Dans cette architecture, la création de la fenêtre vue peut être faite par le gestionnaire OnCreate dans la classe CMainFrame.
La création est faite en utilisant la méthode Create de la classe CWnd.
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
return -1;
if (!m_wndView.Create(NULL, NULL, AFX_WS_DEFAULT_VIEW, CRect(0, 0, 0, 0), this, AFX_IDW_PANE_FIRST, NULL)) return -1;
return 0;
}(cf Documentation CWnd :: Create) – Cette création est générée dans le
squelette de l’application 56
Application SDI – Non Document/Vue Boîte de dialogue non modale
Etape 5 : Création de la ressource boîte de dialogue
La boîte de dialogue est créée à l'aide de l'éditeur de ressources de AppWizard
La boîte de dialogue comporte :
2 contrôles STATIC pour les messages Largeur, Longueur.
2 contrôles EDIT pour la saisie de ces valeurs.
3 contrôles BUTTON pour Appliquer, Fermer et Défaut.
3 boutons radio groupés : Pouces, Centimètres et Pixels dans un contrôle STATIC (Unités).
57
Application SDI – Non Document/Vue Boîte de dialogue non modale
L'option WS_GROUP permet de grouper certains contrôles.
Les contrôles groupés sont ceux compris entre deux WS_GROUP. Ici, les trois boutons radio.
Ceci permet d'avoir automatiquement un seul choix possible parmi les boutons groupés.
Le bouton OK est celui défini par défaut lorsque l'utilisateur utilise la touche entrée.
On peut aussi définir l'ordre de parcours de contrôles avec la touche tab en utilisant le menu Format -> Ordre de
tabulation. 58
Application SDI – Non Document/Vue Boîte de dialogue non modale
On peut vérifier que des contrôles sont groupés en utilisant le menu
Format -> Tester la boîte de dialogue
Cliquer sur le premier contrôle et à l'aide des flèches haut, bas on peut se déplacer en boucle sur les contrôles groupés.
59
Application SDI – Non Document/Vue Boîte de dialogue non modale
Etape 6 : Création de la classe COptionsDialog
On définit dans la classe COptionsDialog
3 attributs pour gérer les options :
m_Largeur, m_Longueur et m_Unite.
Lors de la création de la classe COptionsDialog par ClassWizard sur la boîte dialogue
IDD_OPTIONS,
Le constructeur et la méthode DoDataExchange sont automatiquement générés.
On ajoute ensuite les variables.
60
Application SDI – Non Document/Vue Boîte de dialogue non modale
Ces attributs/variables sont associés aux 3 contrôles de la boîte de dialogue:
Pour la longueur (IDC_LONGUEUR)
Pour la largueur (IDC_LARGEUR)
Pour les 3 boutons radio groupés (1er bouton)
(IDC_POUCES)
61
Application SDI – Non Document/Vue Boîte de dialogue non modale
Etape 7 : Mise à jour du menu principal et création du gestionnaire
pour le menu Fichier -> Options
La boîte de dialogue est gérée en mode non modal.
Le gestionnaire OnFichierOptions va donc prendre en charge :
l'allocation d’un pointeur sur un objet de type COptionsDialog, pour gérer la boîte de dialogue
l'initialisation des champs,
la création de la boîte (lien avec la ressource IDD_OPTIONS) et l'affichage de cette boîte.
Un pointeur sur cette boîte est défini dans la classe CChildView.
62
Application SDI – Non Document/Vue Boîte de dialogue non modale
void CChildView::OnFichierOptions()
{ // Si la boîte existe, on l'affiche.
if (m_pDlg != NULL) m_pDlg->SetFocus ();
// Si la boîte n'existe pas, on la crée.
else {
m_pDlg = new COptionsDialog;
m_pDlg->m_Largeur = m_Largeur;
m_pDlg->m_Longueur = m_Longueur;
m_pDlg->m_Unite = m_Unite;
m_pDlg->Create (IDD_OPTIONS);
m_pDlg->ShowWindow (SW_SHOW);
} }
63
Application SDI – Non Document/Vue Boîte de dialogue non modale
Etape 8 : Création des gestionnaires pour les contrôles : Appliquer et Fermer.
Une différence fondamentale entre la gestion des boîtes modales et non modales est la manière dont les gestionnaires OnOk et OnCancel sont définis.
Pour les boîtes modales,
La méthode OnCancel est rarement redéfinie car son implémentation dans la classe CDialog appelle EndDilaog pour fermer la boîte et retourne IDCANCEL.
De même, la méthode OnOK de CDialog appelle OnUpdateData pour mettre à jour les attributs de la boîte de dialogue avant de fermer la boîte.
Si les contrôles de la boîte et les attributs sont liés par les mécanismes DDX et DDV, l'action par défaut CDialog::OnOk est en général suffisante.
64
Application SDI – Non Document/Vue Boîte de dialogue non modale
Pour les boîtes non modales,
Les méthodes OnOK et OnCancel doivent presque toujours être surchargées.
Il faut donc éviter que les méthodes :
CDialog::OnOK et CDialog::OnCancel soient appelées.
Les boîtes de dialogue non modales doivent être fermées par DestroyWindow.
Dans l'exemple, les deux boutons
Appliquer (IDOK) et Fermer (IDCANCEL) génèrent des appels à OnOK et OnCancel,
il faut donc surcharger ces méthodes.
65
Application SDI – Non Document/Vue Boîte de dialogue non modale
Principe de gestion des messages pour les boutons Appliquer et Fermer
Les méthodes OnOK et OnCancel sont surchargées dans la classe COptionsDialog.
Ces méthodes émettent un message vers la fenêtre principale qui le traite et le retransmet vers la vue qui termine le traitement.
COptionsDialog OnOK, OnCancel
CMainFrame OnAppliquer, OnDialogDestroyed WM_USER_APPLY
WM_USER_DIALOG _DESTROYED
CChildView OnAppliquer, OnDialogDestroyed
WM_USER_APPLY
WM_USER_DIALOG_D ESTROYED
66
Application SDI – Non Document/Vue Boîte de dialogue non modale
Définitions des gestionnaires dans la classe COptionsDialog.
void COptionsDialog::OnOK () {
UpdateData (TRUE);
RECTPROP rp;
rp.Largeur = m_Largeur;
rp.Longueur = m_Longueur;
rp.Unite = m_Unite;
AfxGetMainWnd ()->SendMessage (WM_USER_APPLY_, 0, (LPARAM)
&rp);
}
UpdateData(TRUE) permet la mise à jour des données de la boîte de dialogue.
Ces valeurs sont stockées dans une structure RECTPROP pour être transmises en paramètres vers la fenêtre principale à l'aide d'un message utilisateur WM_USER_APPLY (correspondant au choix de l'option Appliquer).
67
Application SDI – Non Document/Vue Boîte de dialogue non modale
La définition de la structure RECTPROP est définie de la façon suivante:
typedef struct tagRECTPROP { int Largeur;
int Longueur;
int Unite;
} RECTPROP;
Elle peut être incluse dans le fichier stdafx.h
WM_USER_ sont des messages utilisateur définis sous la forme suivante dans stdafx.h.
#define WM_USER_APPLY WM_USER+0x100
#define WM_USER_DIALOG_DESTROYED WM_USER+0x101 WM_USER qui est défini à l'adresse 0x400 dans le fichier Winuser.h
spécifie la borne inférieure des adresses utilisables pour des messages utilisateurs sans provoquer de conflits avec les messages standard de Windows.
WM_USER_APPLY est arbitrairement défini à cette adresse + 0x100.
68
Application SDI – Non Document/Vue Boîte de dialogue non modale
Le gestionnaire OnCancel détruit la fenêtre en utilisant la méthode DestroyWindow de CDialog.
void COptionsDialog::OnCancel () {
DestroyWindow ();
}
La méthode OnOK émet un message
WM_USER_APPLY vers la fenêtre principale. Ce message doit donc être géré par cette classe.
69
Application SDI – Non Document/Vue Boîte de dialogue non modale
Gestion des messages dans la classe CMainFrame
Déclaration du gestionnaire dans la table des messages de CMainFrame :
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //{{AFX_MSG_MAP(CMainFrame)
ON_WM_SETFOCUS() ON_WM_CREATE()
ON_MESSAGE(WM_USER_APPLY,OnAppliquer) //}}AFX_MSG_MAP
END_MESSAGE_MAP()
70
Application SDI – Non Document/Vue Boîte de dialogue non modale
Définition du gestionnaire :
LRESULT CMainFrame::OnAppliquer (WPARAM wParam, LPARAM lParam) { // m_wndView désigne la vue de type CChildView
m_wndView.SendMessage (WM_USER_APPLY, wParam, lParam);
return 0;
}
La méthode OnAppliquer de la fenêtre principale envoie un message vers la vue.
On doit donc définir un gestionnaire pour ce message (WM_USER_APPLY) dans la classe CChildView.
71
Application SDI – Non Document/Vue Boîte de dialogue non modale
Gestionnaires de la classe CChildView
Déclaration du gestionnaire dans la table des messages de CChildView :
BEGIN_MESSAGE_MAP(CChildView,CWnd ) //{{AFX_MSG_MAP(CChildView) ON_WM_PAINT()
ON_COMMAND(ID_FICHIER_OPTIONS, OnFichierOptions) ON_MESSAGE(WM_USER_APPLY, OnAppliquer);
//}}AFX_MSG_MAP END_MESSAGE_MAP()
72
Application SDI – Non Document/Vue Boîte de dialogue non modale
Définition du gestionnaire :
LRESULT CChildView::OnAppliquer (WPARAM wParam, LPARAM lParam) {
RECTPROP* prp = (RECTPROP*) lParam;
m_Largeur = prp->Largeur;
m_Longueur = prp->Longueur;
m_Unite = prp->Unite;
Invalidate ();
return 0;
}
Le gestionnaire récupère en paramètre les valeurs des
attributs à appliquer et les affecte aux attributs de la vue,
qui est invalidée pour être repeinte par OnPaint.
73
Application SDI – Non Document/Vue Boîte de dialogue non modale
Gestionnaire OnCancel dans la classe COptionsDialog,
OnCancel appelle la méthode DestroyWindow.
Cette fonction appelle pour terminer la méthode COptionsDialog::PostNcDestroy qui doit donc être définie.
void COptionsDialog::PostNcDestroy () { CDialog::PostNcDestroy ();
AfxGetMainWnd()->
SendMessage(WM_USER_DIALOG_DESTROYED, 0, 0);
delete this;
}
Cette méthode envoie un message à la fenêtre principale pour signaler la terminaison de la boîte de dialogue.
Ce message doit donc être récupéré par la fenêtre principale qui pourra ainsi transmettre ce message à la vue et la vue réinitialisera l'attribut m_pDlg à NULL.
74
Application SDI – Non Document/Vue Boîte de dialogue non modale
Dans la classe CMainFrame :
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd) //{{AFX_MSG_MAP(CMainFrame) ON_WM_SETFOCUS() ON_WM_CREATE()
ON_MESSAGE(WM_USER_APPLY,OnAppliquer)
ON_MESSAGE(WM_USER_DIALOG_DESTROYED,OnDialogDestroyed) //}}AFX_MSG_MAP
END_MESSAGE_MAP()
Définition du gestionnaire :
LRESULT CMainFrame::OnDialogDestroyed (WPARAM wParam, LPARAM lParam) {
m_wndView.SendMessage (WM_USER_DIALOG_DESTROYED, wParam, lParam);
return 0;
}
75
Application SDI – Non Document/Vue Boîte de dialogue non modale
Dans la classe CChildView :
BEGIN_MESSAGE_MAP(CChildView,CWnd ) //{{AFX_MSG_MAP(CChildView) ON_WM_PAINT()
ON_COMMAND(ID_FICHIER_OPTIONS, OnFichierOptions) ON_MESSAGE(WM_USER_APPLY, OnAppliquer);
ON_MESSAGE(WM_USER_DIALOG_DESTROYED, OnDialogDestroyed) //}}AFX_MSG_MAP
END_MESSAGE_MAP()
Définition du gestionnaire :
LRESULT CChildView::OnDialogDestroyed (WPARAM wParam, LPARAM lParam)
{
m_pDlg = NULL;
return 0;
}
76
Application SDI – Non Document/Vue Boîte de dialogue non modale
Etape 9 : Définition du gestionnaire pour le bouton Défaut
Le bouton Défaut provoque la réinitialisation des valeurs de largeur, longueur et unité aux valeurs initiales.
Le gestionnaire est défini dans la classe COptionsDialog.
Il affecte les valeurs aux attributs et demande une mise à jour des informations.
BEGIN_MESSAGE_MAP(COptionsDialog, CDialog) //{{AFX_MSG_MAP(COptionsDialog) ON_BN_CLICKED(IDC_DEFAUT, OnDefaut) //}}AFX_MSG_MAP
END_MESSAGE_MAP()
Le message géré est de type ON_BN_CLICKED , message lié à un clic sur un bouton.
77
Application SDI – Non Document/Vue Boîte de dialogue non modale
void COptionsDialog::OnDefaut() {
m_Largeur = 4;
m_Longueur = 2;
m_Unite = 0;
UpdateData (FALSE);
}
La méthode UpdateData(FALSE) demande le transfert des variables de la classe (m_Largeur,
…) vers les contrôles de la boîte de dialogue.
78
Application SDI – Non Document/Vue
Boîte de dialogue non modale
79
Application SDI – Non Document/Vue Boîte de dialogue non modale
Conclusion
L’environnement intégré de Visual Studio 2005 permet la création de différents squelettes d’applications C++
Document/vue (SDI ou MDI)
SDI sans Document/Vue
Basés sur des boîte de dialogues