This post was published in french under Architecture Document-Vue, SDI.
Dans une application document-vue, les données de l’application sont représentées par un objet document et les visions de ces données sont representées par un ou plusieurs objets-vue. Les objets document et vue collaborent pour traiter les saisies utilisateur et dessinent des représentations textuelles et/ou graphiques des données qui en résultent. La classe MFC CDocument est la classe de base des objets document, alors que la classe CView et ses dérivées sont les classes de bases des objets vue.
La fenêtre principale de l’application, dont le comportement est décrit par les classes MFC CFrameWnd (ou CMDIFrameWnd) ne sert plus de point focal pour le traitement des messages. Elle sert principalement de conteneur pour des vues, des barres d’outils, des barres d’état et d’autres objets.
Avantages:
- modularité des objets et clarté dans la division des tâches logicielles
- fournit en standart certaines tâches, comme l’enregistrement et l’impression
- large gamme, en général, de fonctionnalités intégrées sans devoir écrire de code supplémentaire
Il y a deux types d’application document-vue. Les applications SDI (Single Document Interface) permettent d’ouvrir un seul document à la fois. Les applications MDI (Multiple Document Interface) permettent de travailler sur plusieurs documents à la fois.
Liaison d’un document avec ses vues
Les MFC intégrent un mécanisme permettant de lier les vues à un document. Pour ce faire, un objet document gére (automatiquement) une listes de pointeurs vers des vues qui le sont associées. De même, un objet vue possède un membre pointant vers le document associé. Par ailleurs, chaque fenêtre cadre possède un pointeur désignant la vue active.
Le but de ce tutorial est de vous expliquer le code que génére Visual C++6 dans le cas d’une application SDI. Ainsi nous créerons un projet MFC AppWizard exécutable nommé “sdi” avec les paramètres suivant dans AppWizard :
- Single document,Support de l’architecture Document-Vue (étape 1);
- pas de support de base de données (étape 2) ;
- pas de prise en charge d’OLE et d’Active X (étape 3) ;
- decocher les cases dans l’étape 4 ;
- étape 5, options par défaut
J’appellerai dans la suite les classes de l’application, CMyApp
, CmyView
, CMyDoc
.
La portée globale de l’objet application
L’objet application créé a une portée globale. Une fois l’objet theApp
créé, la fonction WinMain()
est appelée. Elle appelle à son tour deux fonctions membres de l’objet theApp
:
-
InitInstance()
qui assure toute initialisation necéssaire de l’application ; -
Run()
qui prend en charge la gestion initiale des messages Windows. Nous ne verrons pas cette fonction dans le WorkSpace puisqu’elle est virtuelle et n’est pas à être redéfini.
CMyApp
InitInstance
Utilité de InitInstance
Comme nous l’avons dit, l’objectif de InitInstance est de donner à l’application l’occasion de s’initialiser elle-même. La valeur retournée par InitInstance détermine ce que doit faire ensuite larchitecture d’application. Si InitInstance retourne FALSE, alors l’application s’arrête ; TRUE, elle continue. InitInstance est l’endroit idéal pour effectuer des initialisations devant intervenir au démarrage.
On a ainsi en partie :
Regardons d’abord :
Ces instructions créent un modèle de document SDI à partir de la classe CSingleDocTemplate. Ce modèle de document identifie la classe document employée por représenter les documents de l’application, la classe de fenêtre cadre englobant les vues des documents et la classe de la vue utilisée pour afficher les données du document.
Détail sur le constructeur CSingleDocTemplate
Le modèle de document contient un ID de ressource dont l’architecture d’application se sert pour charger l’interface utilisateur. Ici, c’est IDR_MAINFRAME
, l’ID de ressource utilisé par AppWizard. En regardant les ressources, notre application charge :
- l’icône de l’application ;
- les accélérateurs ;
- le menu.
La macro RUNTIME_CLASS
retourne un pointeur vers une structure CRuntimeClass
pour la classe spécifiée, ce qui permet à l’architecture d’application de créer des objets de cette classe en cours d’exécution. En effet, les trois arguments du constructeur de CDocTemplate` spécifient les classes fenêtre-cadre, document et vue que le document template va créer dynamiquement en réponse aux commandes de l’utilsateur comme New dans le menu File ou (New Window dans le menu Nouvelle fenetre dans une MDI).
Une fois le modèle de document créé, l’instruction :
l’ajoute à la liste des modéles de documents maintenue par l’objet application. Chaque modèle ainsi enregistré défini un type de document géré par l’application. Evidemment , les applications SDI ne gérent qu’un seul type de document, alors que les applications MDI peuvent en enregistrer plusieurs.
Traitement de la ligne de commande
utilisent CWinApp::ParseCommandLine
pour initialiser un objet CCommandLineInfo
avec des valeurs qui reflètent les paramètres saisis sur la ligne de commande.
Command-line argument | Command executed |
---|---|
app |
New file. |
app filename |
Open file. |
app /p filename |
Print file to default printer. |
app /pt filename printer driver port |
Print file to the specified printer. |
app /dde |
Start up and await DDE command. |
app /Automation |
Start up as an OLE automation server. |
app /Embedding |
Start up to edit an embedded OLE item. |
Création du document, de la fenêtre cadre et de la vue
Les instructions :
” traitent “ les paramètres de la ligne de commande. Entre autres choses, ProcessShellCommand appelle CWinApp::OnFileNew
pour démarrer l’application avec un document vide si aucun nom de fichier n’a été saisi sur la ligne de commmande ou bien appelle CWinApp::OpenDocumentFile
pour démarrer l’appliction en chargeant le fichier spécifié. C’est pendant cette phase que l’architecture d’application crée le document, la fenetre cadre et l’objet vu à l’aide des informations stockées dans le modèle de document. ProcessShellCommand renvoie TRUE si l’initialisation s’est bien passée, FALSE sinon. Un FALSE retournée oblige InitInstance
à renvoyer aussi FALSE, ce qui ferme l’application.
Affichage des fenêtres
Une fois les objets instanciés, InitInstance
affiche les fenêtres cadre et vue.en appelant ShowWindow
et UpdateWindow
via le pointeur m_pMainWnd
. m_pMainWnd
, membre de CMainWindow
, pointe vers l’objet fenêtre.
Une fenêtre reste invisible au début sauf si elle n’est pas créée avec l’attribut WS_VISIBLE. ShowWindow
ne prend qu’un seul paramètre : un entier spécifiant si la fenêtre doit être initialement affichée sous forme réduite, zoomée ou normale. ? UpdateWindow
complète le travail commencé par ShowWindow
en forçant un redessin immédiate.
Son travail fini, InitInstance retourne TrUE afin que l’application puisse continuer.
Commentaires sur les autres lignes de code de InitInstance
SetRegistryKey
dit à l’application d’utiliser la base de registre et non un fichier .INI, vestige des temps passés. De plus, SetRegistryKey
créé un dossier “SDI” dans la rubrique “Local AppWizard-Generated Applications”. Par défaut et comme Microsoft le conseille, le repertoire créé est rangé dans : HKEY_CURRENT_USER\Software\ Local AppWizard-Generated Applications\sdi
.
SetRegistryKey
ordonne à l’aplication de stocker les derniers fichiers utilisés dans le registre. Pour créer effectivement un repertoire dans la base de registre, enlevez la macro _T
.
L’appel à LoadProfileSettings
charge et conserve par défaut les 4 derniers fichiers sauvés. L’architecture d’application place alors dans le menu File les noms des quatres derniers fichiers utilisés. On peut charger de 0 à 16 fichiers avec LoadProfileSettings.
LoadProfileSettings(8);
conservera les huits derniers fichiers.
Objet Document
Qu’est ce qu’est un “ document “ au sens des MFC ? Dans une application document-vue, en l’occurrence une SDI ici, les données sont rangées dans un objet document appartenant à une classe dérivée de CDocument.
Le terme de “document “ ne désigne pas ici en exclusivité des productions de traitement de texte ou de tableurs. Le terme est beaucoup plus générique et ce document peut être une base de données, la position de pièces sur un jeu d’échec.
L’exemple classique est un logiciel de dessin. L’utilisateur, en tracant une ligne, stocke les données dans le document du type : quel sont les points extrémités de la ligne quel est la couleur de la ligne quel est l’epaisseur de la ligne La ligne déssinée par l’utilisateur sera représenté par la classe
CMyDoc sera :
Dans le contexte document-vue, le “document “ est une représentation abstraite des données d’une application.
Une classe dérivée de CDocument
hérite un certain nombre de fonction membres importantes:
-
SetModifiedFlag(bool bModified = TRUE );
doit être appelé après avoir fait une modifaication au ocument. En appelant cette fonction régulièrement, on s’assure que l’utilisateur sauvra avnt de fermer le document.En général, on utilise la valeur par défaut. Cette fonction active le flag de modification -
virtual POSITION GetFirstViewPosition( ) const;
Retourne une valeur POSITION que l’on veut passer à GetNextView afin d’énumérer les vues du document. -
virtual CView* GetNextView( POSITION& rPosition ) const;
Retourne un pointeur CView vers la vue suivante dans la liste des vues associées au document -
const CString& GetPathName( ) const;
Donne le nom et le chemin du fichier associé au document (chaîne nulle si le document n’a pas encore de nom -
const CString& GetTitle( ) const;
Donne le nom du fichier associé au doument (chaîne nulle si le document n’a pas encore de nom) -
void UpdateAllViews( CView* pSender, LPARAM lHint = 0L, CObject* pHint = NULL ); OnUpdate()
Met à jour toutes les vues associées au document, en appelant la fonction OnUpdate de chaque vue. -
BOOL IsModified( );
Retourne TRUE si le document contient des données non enregistrées
Dans une appliacation SDI, UpdateAllViews ne sert pas souvent : une vue pour un document.
Redéfinir des fonctions virtuelles avec VC++
Pour redéfinir certaines fonctions virtuelles héritées par exemple de CDocument, cliquez droit sur CMyApp(si votre nom de classe est CMyApp) dans l’espace de travail/onglet ClassView et choissisez “ Add Virtual Fonction… “
De plus, CDocument
admet plusieurs fonctions virtuelles qui dans la majorité des cas doivent être redéfinies pour personnaliser le comportement du document.
-
virtual BOOL OnNewDocument( );
Appelée par l’architecture d’application lors de la création d’un document. -
virtual BOOL OnOpenDocument( LPCTSTR lpszPathName );
Appélée par l’architecture d’application lors de l’ouverture d’un document -
virtual void DeleteContents( );
Appelée par l’architecture d’application pour effacer le contenu d’un document. A rédefinir si on veut libérer la memoire et d’autes ressources alloués au document avant sa fermeture. -
virtual void OnCloseDocument( );
appelée lors de la fermeture du document -
virtual BOOL OnSaveDocument( LPCTSTR lpszPathName );
appelée lors de l’enregistrement -
virtual BOOL SaveModified( );
appelée avant la fermeture d’un document pour permettre à l’utilisateur d’enregister les dernières modifications -
virtual void Serialize( CArchive& ar );
Appelée par l’architecture d’application pour sérialiser le document, vers ou depuis un fichier.
Importance de redefinir certaines fonctions virtuelles
Dans une application SDI, l’objet document n’est construit qu’une seule fois, et il est réutilisé chaque fois que l’on crée ou que l’on ouvre un document. Etant donné que le constructeur est exécuté une sule fois, le document doit : réaliser ces initialisation communes dans le constructeur realiser ses initialisations individuelles via OnNewDocument ou OnOpenDocument
Si vous redifinissez ces fonctions grâce au assistant de Visual C++, du code est génére en plus du type :
Appeler les versions de la classe de base permet d’effectuer des tâches vitales d’initialisation. De toute manière, il faut toujours respecter les manières d’implementer le code, manière indiquée par les commentaires de VC++.