Soyez le premier à donner votre avis sur cette source.
Vue 10 896 fois - Téléchargée 718 fois
I) Création du Squelette Créer un projet MFC SimpleDocument (SDI) de nom "serialisation" à l'étape 4, lui donner une extension de fichier (.PTS) générer le squelette d'application II) Dessin simple Faire en sorte qu'à chaque clic de souris, un nouveau point soit dessiné à l'écran à la position du clic on pourra représenter le point par ses coordonnées sous la forme [x, y] Pour le débutant, voici la démarche pas à pas : il suffit de faire un clic droit dans l'onglet ClassView sur la classe CSerialisationView et de demander la génération d'un gestionnaire de message pour windows "Windows Message Handler" On choisira le WM_LBUTTONDOWN (clic gauche) et add&edit on rajoutera le code suivant dans le gestionnaire : void CSerialisationView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CString s; s.Format("[%d,%d]", point.x, point.y); CClientDC dc(this); dc.TextOut(point.x, point.y,s); CView::OnLButtonDown(nFlags, point); } fin de la démarche pas à pas : Tester l'application en faisant quelques clics sur le fond de la fenêtre Et vérifier le mauvais comportement de l'application lors des redimensionnements de la fenêtre ou de minimisation-restauration. III) Dessin et Mémorisation On notera que si les points sont simplement dessinés et non archivés dans la classe CDocument tout rafraîchissement de la fenêtre (un redimensionnement par exemple) fait disparaître tous les points A) On a donc besoin à chaque clic de souris d'ajouter le point à une collection du document et de le dessiner ATTENTION ! la classe CPoint n'est pas dérivée de CObject Le but de cet article étant de traiter la sérialisation (à la MFC) et le PREMIER PRINCIPE à respecter pour être une classe sérialisable étant de dériver de CObject : => Il faut donc créer notre propre classe de Point, on pourra la nommer CObjectPoint et lui donner un constructeur acceptant un CPoint et une donnée membre de type CPoint (réutilisation par composition) Prendre bien soin de conserver le constructeur par défaut car il est exigé en vertu du DEUXIEME PRINCIPE de la sérialisation. B) De plus, dans l'événement WM_PAINT de la vue, il faut redessiner tous les points. Il faut donc être capable d'itérer sur la collection privée et nous fournirons en prenant exemple sur la conception (le Design) de la classe CObArray les méthodes GetSize et GetAt. Pour le débutant, voici la démarche pas à pas : Créer une nouvelle classe CObjectPoint Clic droit dans ClassView sur le projet (serialisation classes) puis new class On doit prendre une classe générique (non MFC) car la classe CObject n'est pas vue de l'assistant nommer la classe CObjectPoint (les fichiers "objectPoint.h et .cpp" sont automatiquement créés) On modifiera le constructeur Modification du document : Comme on utilisera le type CObjectPoint, ajouter la ligne suivante en tête de "serialisationDoc.h" #include "objectPoint.h" Doter la classe dérivée de CDocument (CSerialisationDoc) d'un attribut privé de type CObArray (tableau dynamique d'objets) Modifier le code du gestionnaire OnLButtonDown pour ajouter un point dans la collection à chaque clic. private: CObArray m_points; Parce que notre collection est privée, on doit fournir une méthode d'encapsulation Add(CPoint* pPoint) Elle sera donc publique et son contenu : void CSerialisationDoc::Add(CObjectPoint *pPoint) { m_points.Add(pPoint); } Modifier le gestionnaire OnLButtonDown en lui rajoutant la ligne suivante GetDocument()->Add(new CObjectPoint(point)); Si vous vous demandez pourquoi faire un new (alloc dynamique)... réfléchissez! Ajout de GetSize et GetAt (ne pas oublier les déclarations dans le .h) int CSerialisationDoc::GetSize() { return m_points.GetSize(); } CObjectPoint* CSerialisationDoc::GetAt(int index) { return (CObjectPoint*)m_points.GetAt(index); } Gérer le rafraîchissement de la vue en traitant le WM_PAINT Pour cela, il faut parcourir la collection de points et dessiner chacun d'eux. Il est préférable de demander à chaque point de se dessiner lui-même et nous créons une méthode Draw(CDC*) pour notre classe CObjectPoint (elle recevra le Device Context de dessin): void CObjectPoint::Draw(CDC* pDC) { CString s; s.Format("[%d,%d]", m_point.x, m_point.y); pDC->TextOut(m_point.x, m_point.y,s); } Modification de la méthode OnDraw de la vue (c'est elle qui est appelée lors des WM_PAINT) void CSerialisationView::OnDraw(CDC* pDC) { CSerialisationDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here for (int i=0; i<pDoc->GetSize(); i++) pDoc->GetAt(i)->Draw(pDC); } fin de la démarche pas à pas On peut alors test l'application : elle fonctionne correctement même lors des redimensionnements... ou presque ... Avez vous essayé le menu Fichier/Nouveau ! Les points ne disparaissent jamais et d'ailleurs ils ne sont jamais détruits (pas de delete => fuite mémoire) La bonne méthode (officielle MFC) est de redéfinir la méthode virtuelle DeleteContent Clic droit sur CSerialisationDoc / Add Virtual fonction / DeleteContents / Add & Edit void CSerialisationDoc::DeleteContents() { // TODO: Add your specialized code here and/or call the base class for (int i=0; i<m_points.GetSize(); i++) delete m_points[i]; //destruction des CObjectPoint (libération du tas) m_points.RemoveAll(); //libération du conteneur CDocument::DeleteContents(); } Encore un point nécessaire ! Démarrer l'application, cliquer pour générer quelques points, puis fermer l'application. Rien d'anormal ? Elle a oublié de nous proposer de sauvegarder notre document... Pour cela, nous devons passer à l'étape suivante de sérialisation mais on peut tout de même la préparer en appelant SetModifiedFlag() à chaque fois que nous ajoutons un point au document Donc dans la méthode Add. IV) Sérialisation de base Si l'on referme l'application, les points étant simplement mis en mémoire vive, ils sont perdus: Il faut donc les archiver sur disque, le plus simple étant d'utiliser le mécanisme de sérialisation du framework Le TROISIEME PRINCIPE de la sérialisation est de posséder une méthode Serialize Nous pouvons vérifier que notre classe document le vérifie déjà ... mais pas notre CObjectPoint On doit donc l'ajouter à la classe : Clic droit sur CObjectPoint dans ClassView => ajouter une méthode... void CObjectPoint::Serialize(CArchive &ar) { if (ar.IsStoring()) { // TODO: add storing code here ar << m_point; } else { // TODO: add loading code here ar >> m_point; } } Quand et Comment cette méthode est elle appelée ? Lorsque le document a besoin de se sauvegarder... par l'appui sur fichier/Enregistrer par exemple. C'est donc via le document que l'appel a lieu On doit modifier le contenu de la méthode Serialize du document ainsi : void CSerialisationDoc::Serialize(CArchive& ar) { m_points.Serialize(ar); } C'est presque fini pour le code ... on teste - d'abord l'enregistrement d'un document vide ... aucun clic => aucun point => OK on peut vérifier qu'un fichier à l'extension .PTS a bien été créé - ensuite l'enreristrement d'un ducument non vide ! et un message d'erreur apparaît Il nous faut respecter le QUATRIEME PRINCIPE de la sérialisation : utiliser les MACROS DECLARE_SERIAL et IMPLEMENT_SERIAL rajouter à la fin de la définition de la classe CObjectPoint (fichier "objectPoint.h") DECLARE_SERIAL(); et au début du fichier objectPoint.cpp (mais après les includes) IMPLEMENT_SERIAL(CObjectPoint, CObject, 1) Retester ... V) Sérialisation avec gestion de version On voit bien l'intérêt de la sérialisation ... conserver de façon persistante (archivée) les informations qui resteraient volatiles sinon. Le problème est que les logiciels évoluent ... ils changent de version Que se passe t-il par exemple lorsque l'on veut traiter des points colorés dans une version 2 ? Généralement, on ne change pas l'extension de fichier pour autant. La sérialisation d'un document en version 1 suivi de la désérialisation en version 2 devrait bien se passer ! Pour cette raison, lors de la sérialisation, un numéro de version est archivé avec le contenu et il peut être récupéré lors de la désérialisation pour savoir si l'on ne lit que des CObjectPoint ou bien des CObjectPointCouleur ! Voyons ce que nous offre le framework MFC pour traiter la question : La MACRO IMPLEMENT_SERIAL(CObjectPoint, CObject, 1) contient en troisième paramètre le numéro de version pour faire évoluer le logiciel, il faut donc le modifier dans la version 2. Pour le débutant, voici la démarche pas à pas : Partie traitant les points en couleur Parsque cet article se focalise sur la sérialisation et non sur les bonnes pratiques d'interfaces utilisateur, on gèrera la couleur courante dans la vue et un clic droit pour passer de rouge à bleu et réciproquement un menu déroulant/menu contextuel/barre d'outil/boite de dialogue serait plus adapté mais plus long à décrire Ajouter une donnée membre privée de type COLORREF appelée m_couleur à la classe Vue (CSerialisationView) private: COLORREF m_couleur; Initialiser le à "bleu" dans le constructeur m_couleur = RGB(0,0,255); Gérer le clic droit qui change la couleur courante: void CSerialisationView::OnRButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default if (m_couleur==RGB(0,0,255)) m_couleur=RGB(255,0,0); else m_couleur=RGB(0,0,255); CView::OnRButtonDown(nFlags, point); } Modifier la classe CObjectPoint pour suppporter la couleur ajouter une donnée membre m_couleur, ajouter un Constructeur capturant la couleur et initialiser. Modifier la méthode Draw du point pour tenir compte de la couleur pDC->SetTextColor(m_couleur); Modifier la méthode Serialize du CObjectPoint if (ar.IsStoring()) { ar << m_point << m_couleur; } else { ar >> m_point << m_couleur; } Modifier la création des points dans la méthode OnLButtonDown void CSerialisationView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CString s; s.Format("[%d,%d]", point.x, point.y); CClientDC dc(this); dc.SetTextColor(m_couleur); dc.TextOut(point.x, point.y,s); GetDocument()->Add(new CObjectPoint(point, m_couleur)); CView::OnLButtonDown(nFlags, point); } Vérifier qu'il existe alors un problème lors de la relecture des fichiers sauvegardés en Version 1 On utilise pour régler cela la technique suivante : modifier la macro pour lui passer un numéro de version égal à 2 IMPLEMENT_SERIAL(CObjectPoint, CObject, VERSIONABLE_SCHEMA|2) Modifier la sérialisation du point ainsi : void CObjectPoint::Serialize(CArchive &ar) { if (ar.IsStoring()) { ar << m_point << m_couleur;//on décide de toujours re-stocker en version 2 } else { int nVersion = ar.GetObjectSchema(); switch(nVersion) { case 1: // lire en version 1 (sans la couleur) ar >> m_point ; break; case 2: // lire en version courante (2) ar >> m_point >> m_couleur; break; default: // version inconnue break; } } } On pourra tester en ouvrant un fichier sauvegardé en version 1 (les points sont noirs) Modifier ensuite en rajoutant des points rouges et bleus puis sauvegarder. Ouvrir ensuite le fichier résultat. Notez que la compatibilité ascendante est contrôlée en particulier car le constructeur des CObjectPoint prend une valeur par défaut à 0 pour la couleur (Noire) correspondant à la couleur de la version 1
moi je suis debutant cet article ma servis beaucoup.
Tres bon article monsieur et merci !
SARIH MOHAMED sarihmohamed@yahoo.fr
merci
je vais tester ca:)
Vous n'êtes pas encore membre ?
inscrivez-vous, c'est gratuit et ça prend moins d'une minute !
Les membres obtiennent plus de réponses que les utilisateurs anonymes.
Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.
Le fait d'être membre vous permet d'avoir des options supplémentaires.