Vous êtes sur la page 1sur 140

Dmarrer avec les MFC 9.

0
Par Patrick OTTAVI MVP Visual C++ (Site) (Blog) Version pour Visual Studio 2008

Sommaire
Introduction : .......................................................................................................................... 3
Pr requis :......................................................................................................................................... 3 Conventions dcritures pour les classes et variables : .................................................................. 4 Interactions avec lditeur : ............................................................................................................. 5 Lapprentissage : ............................................................................................................................... 5

Le FrameWork : ...................................................................................................................... 6
Gnration dun projet avec lassistant cration de projets : ....................................................... 6 La Classe dapplication CWinAppEx : ......................................................................................... 14 Lobjet gestionnaire de fentre MFC : .......................................................................................... 17 La classe Document : ...................................................................................................................... 18 Initialisation de la fentre principale de lapplication CMainFrame : ...................................... 20 La classe Fentre Principale CMainFrame .................................................................................. 23 Le traitement des Messages Windows : ........................................................................................ 24
Longlet proprits : .................................................................................................................................... 28 Les diffrentes catgories de messages : ..................................................................................................... 31 Spy++ lespion qui vous veut du bien : ....................................................................................................... 33 Les messages explications complmentaires: .............................................................................................. 35

Unicode et internationalisation du logiciel : ......................................................................... 36


Rappels : .................................................................................................................................................... 36 Quelles sont les incidences sur notre code ? ....................................................................................... 36 Utilisation de la classe string de la stl : .................................................................................................. 38 Conclusion : ............................................................................................................................................... 38

La fentre vue de notre application CSampleSDIView : ............................................................. 39


Lditeur de ressources et les contrles disponibles : .................................................................................. 40 Mise en place des contrles :.................................................................................................................. 42 Gestion de lalignement des contrles : ....................................................................................................... 43 Comment travailler avec les contrles ? ...................................................................................................... 47

La fonction dinitialisation de la vue OnInitialUpdate :.............................................................. 55 Intercepter les vnements de linterface : ................................................................................... 56
Intercepter le clic sur un bouton : ................................................................................................................ 56 Intercepter un message sur un contrle : ..................................................................................................... 58 Implmenter une commande dune barre doutils ....................................................................................... 60 Mise en place du message : ......................................................................................................................... 61

Complments dinformations sur les contrles ............................................................................ 63


Les Edits : .................................................................................................................................................... 63 Les ComboBox : .......................................................................................................................................... 67 Les Listbox .................................................................................................................................................. 71

Le Traitement des couleurs ............................................................................................................ 73 Les Botes de dialogue ..................................................................................................................... 77


Dfinition : .................................................................................................................................................. 77

Application : ................................................................................................................................................ 77 Cration des variables attaches aux contrles : .......................................................................................... 82

Gnralits sur les botes de dialogues .......................................................................................... 87


La gestion des couleurs : ............................................................................................................................. 89

La srialisation des donnes ........................................................................................................... 93


La lecture des donnes :............................................................................................................................... 93 La sauvegarde des donnes: ........................................................................................................................ 94 Srialisation de collection dobjets en mmoire : ........................................................................................ 95 Enregistrement de lextension de fichier dans la base de registre : ............................................................. 99 Mise en place du code de lecture criture de nos donnes : ...................................................................... 100

Les Nouvelles classes MFC ............................................................................................ 103


La Programmation du ruban (Ribbon bar) .......................................................................... 103
Deux classes essentielles : ................................................................................................................... 103 Ajout dune Catgorie :........................................................................................................................... 103 Ajout dlments dans le panneau : ..................................................................................................... 106 Communiquer avec les lments du ruban : ...................................................................................... 107 Ajout dun Combo box : .......................................................................................................................... 111 Ajout dun bouton de raccourci : ........................................................................................................... 113

La barre dtats : ........................................................................................................................ 115


Rajout dun bouton de lien: .................................................................................................................... 115 Modifications des textes dans les panneaux : .................................................................................... 117 Ajout dune animation : .......................................................................................................................... 120 Rajout dune barre doutils :................................................................................................................... 122

La barre OutLook : ..................................................................................................................... 125

Le dbogage dune application ..................................................................................... 132


Tour dhorizon des fonctionnalits : ........................................................................................... 132

Conclusions:....................................................................................................................... 140 Remerciements: ................................................................................................................. 140

Introduction :
Ce document est une rvision du tutoriel dmarrer les MFC avec Visual 6.0. Les MFC ont rcemment volues avec lajout dun feature pack dans Visual 2008 ou plus rcemment par le service pack 1 de Visual studio. Il est maintenant tout fait possible davoir une interface imitant Word 2007 avec son ruban ou encore linterface de Visual studio 2008. Il ma sembl normal et important de dmarrer lapprentissage en intgrant tout de suite les nouvelles fonctionnalits des MFC.

Pr requis :
Le niveau requis en C++ pour dmarrer est plutt moyen, il faut connatre les bases du langage. Les MFC proposent un ensemble de classes couvrant lensemble des besoins pour dvelopper une application Windows. Elles fournissent des modles darchitecture pour lapplication sur lesquels celle-ci va tre construite, savoir le modle document - vue dclin en trois options : applications SDI (single document interface : une seule fentre), MDI (multiple document interface : plusieurs fentres), et une application de type boite de dialogue. Lensemble des classes fentres est une encapsulation des contrles Windows de base. Les MFC utilisent lhritage simple (pas dhritage multiple), lensemble des classes forme une hirarchie. Les nouvelles classes MFC sont identifies par le prfixe CMFC ou par lajout de Ex comme pour la classe CPrintDialogEx. Ce graphique (sur le site de MSDN) donne une ide des classes disponibles :

http://msdn.microsoft.com/en-us/library/ws8s10w4.aspx
Il montre bien les diffrents sujets couverts par les MFC: Un ensemble de classes pour : Larchitecture de lapplication. La gestion des fentres La gestion des exceptions. La gestion des fichiers La gestion des dessins La gestion des menus Le support des bases de donnes. Les objets de synchronisation Les Sockets Les objets de stockage. Les services Internet.

Toutes les classes hritent de la classe CObject sauf les classes lies aux services Internet et quelques classes utilitaires. Toutes les classes fentres ou contrles hritent de la classe CWnd qui est la classe de base de toutes les fentres et comprend tous les traitements de base effectus sur une fentre : changement de titre, dplacement de la fentre etc.. Les noms des mthodes restent trs proches de leur quivalent win32.

Conventions dcritures pour les classes et variables :


Pour bien comprendre les classes MFC, il faut comprendre la manire dont les mthodes et variables sont nommes. Les classes MFC utilisent la notation Hongroise. Cest une convention qui sapplique la cration des noms des variables et des fonctions. Elle est utilise par la majorit des programmeurs Windows, bien utilise elle facilite la relecture et la maintenance des programmes. Cette notation est adopte par Microsoft. Un nom de variable est compos dun court prfixe significatif plac devant un nom plus long, de prfrence explicite. Le prfixe dcrit le type de donne rfrence par la variable. Dans certains cas, ce prfixe peut aussi dcrire la faon dont la variable est utilise. Le prfixe peut lui-mme servir de nom de variable. Voici quelques exemples demploi de la notation Hongroise : char ch ; // char char achFile[128] ;// tableau de caractres Int cbName ;

LPCTSTR lpszString // long pointeur constante string :


Int ich ; Prfixe a ch cb dw h hdc hWnd I d f L Lp n np sz w // index un tableau de caractres Type de donnes tableau (type compos) Caractre [char] Comptage doctets Non sign long unsigned long (DWORD) Handle (identificateur) handle pour un priphrique. handle pour une fentre Index (type compos) Double Float Entier long [long] Pointeur long (far) Entier [int] pointeur court (near) chane de caractre termine par un zro binaire. entier non sign [unsigned int] (WORD)

On utilise aussi la notation Hongroise pour les noms de fonctions. La mthode la plus courante combine un verbe et un nom pour dcrire une fonction. Exemple, dans Windows, on trouve trois routines qui sappellent : CreateWindow, DrawText, LoadIcon. On peut trouver aussi un nom tout seul : DialogBox. Les routines qui convertissent un type en un autre sont souvent de la forme XtoY, comme pour la fonction DptoLP qui convertit les coordonnes physiques en coordonnes logiques. Pour finir, les variables membres dune classe sont prfixs de m_ voulant dire membre.

Interactions avec lditeur :


Visual C++ nest pas un RAD (rapid aid dveloppent), mais il gnre nanmoins le squelette de lapplication suivant le modle choisi. Il permet entre autres la gnration de code li une nouvelle fentre, aux notifications de messages issus dun contrle, dun menu etc. Le dessin dune interface base de contrles Lditeur dispose aussi dassistants pour gnrer du code, ceux-ci sont disponibles de manire contextuelle.

Lapprentissage :
Apprendre programmer avec les MFC ncessite de comprendre larchitecture gnre par loutil Visual et dappliquer des mthodes rfrences pour rsoudre un problme darchitecture, comme par exemple comment mettre en place un splitter, avoir deux vues sur le mme document, mettre en place une barre doutils (toolbar) etc.. Cest sans doute cet aspect qui est le plus difficile apprhender pour un dbutant, car ces mthodes ne sont pas fournies directement par lenvironnement, il faut les implmenter soit mme.

Cet apprentissage se dcompose en plusieurs parties :


Le FrameWork : tout ce qui touche lorganisation des fentres. Les contrles de bases dit ,static ,listbox etc.. , avec les diffrents traitements et personnalisations possibles. La persistance des donnes : gestion de fichier du plus simple la base de donnes en passant par le mcanisme de srialisation propos par les MFC. Le GDI : les classes de dessin, bitmaps etc..

Nous allons effleurer lessentiel pour dmarrer une application.

Le FrameWork :

Gnration dun projet avec lassistant cration de projets :


Pour commencer notre initiation nous allons gnrer une application de type SDI : Dans Visual slectionner le menu fichier et loption nouveau projet :

Slectionnez loption MFC et renseignez le nom du projet comme ci-dessus.

Ltape suivante permet de dcider le type dapplication dsire, Slectionnez loption Mono document et Standard MFC pour le style de projet. Puis cliquez sur le bouton suivant.

Indiquez lextension du document li lapplication dans la zone extension de fichier. 7

Puis cliquez sur la zone Fonctionnalits interface utilisateur.

Cliquez sur la zone utiliser un ruban. Puis cliquez sur le bouton suivant.

Cette tape permet de dfinir lallure gnrale de lapplication

De nouvelles possibilits de gnration soffrent nous dans la partie Fentre frames avances. Dans mon exemple jai coch loption volet de navigation et barre de lgende. Puis cliquez sur le bouton suivant.

Le dernier volet concerne les classes gnres automatiquement, Visual montre le nom des classes qui seront gnres Au total quatre classes : Une classe fentre utilisateur CSampleSDIView Une classe dapplication CSampleSDIApp Une classe fentre principale de lapplication CMainFrame Une classe Document CSampleSDIDoc Vous remarquerez qu part la classe CMainFrame les classes principales utilisent le nom de lapplication comme prfixe. Revenons sur la classe CSampleSDIView. Nous allons slectionner dans la boite de slection (combo box) classe de base la classe CFormView comme sur lcran ci-dessus. Cette classe de base permet lutilisation de contrles Windows sur son espace de travail. Cest donc ce moment de la gnration quil faudra choisir le type de fentre correspondant linterface souhaite :

10

Classes Disponibles :
CEditView : toute la fentre est une zone de saisie multi lignes. CFormView : fentre avec contrles Windows. CHtmlview : fentre pour laffichage de page html. ClistView : fentre sous forme de liste ; par exemple : lexplorateur Windows (partie droite de lcran). CRichEditView : saisie de texte au format RTF comme wordpad. CScrollView : fentre de dessin avec une surface logique pouvant tre suprieur lcran physique. CTreeView : fentre permettant davoir un arbre multi niveaux comme lexplorateur Windows (partie gauche de lcran). CView : fentre de dessin simple.

11

Cliquez ensuite sur le bouton Terminer. Visual gnre alors le projet . Aprs validation :

Avant de modifier et dexpliquer le contenu de ce qui a t gnr, nous allons compiler et lier lapplication. Pour cela utiliser le menu Gnrer et loption gnrer sampleSDI.EXE ou touche F7 . Une fois ceci termin, excutez le programme en mode de dbogage avec la touche F5 ou la touche CTRL+F5 sans dbogage ou slectionner ces options dans le menu dboguer Vous obtenez ceci :

12

En comparaison jai gard lquivalent de la gnration effectue avec Visual 6.0 pour visualiser le chemin parcouru entre les deux versions.

Lapplication est compose des lments suivants : La barre de ruban Un volet de navigation pr rempli dun calendrier Notre fentre de contrles Une barre de lgende Une barre de statut qui affiche de manire standard ltat du clavier ainsi que les libells daide lors de la slection de menu ou lors du dplacement de la souris sur les boutons de la barre doutils.

13

Etudions le code gnr automatiquement :

La Classe dapplication CWinAppEx :


Une application MFC comporte obligatoirement une classe dapplication, drive de la classe de base CWinAppEx., cette classe est une extension de la classe CWinApp qui est utilise pour les anciennes applications MFC. Elle contrle lapplication, cest elle qui va la dmarrer avec la mthode InitInstance, mais aussi la terminer avec la mthode ExitInstance. Elle va mettre en place lapplication grce lunique objet instanci de cette classe. Cest aussi elle qui va router les diffrents messages aux diffrentes fentres de lapplication. Elle offre en outre dautres services comme la gestion des fichiers .ini. Dans notre exemple les fichiers dfinissant cette classe se nomme SampleSDI.H et SampleSDI.cpp : le mme nom que celui donn lapplication.
/ CSampleSDIApp: // Consultez SampleSDI.cpp pour l'implmentation de cette classe // class CSampleSDIApp : public CWinAppEx { public: CSampleSDIApp(); // Substitutions public: virtual BOOL InitInstance(); // Implmentation UINT m_nAppLook; BOOL m_bHiColorIcons; virtual void PreLoadState(); virtual void LoadCustomState(); virtual void SaveCustomState(); afx_msg void OnAppAbout(); DECLARE_MESSAGE_MAP() };

Ouvrez le source CSampleSDiApp.cpp : Vous pouvez utiliser le raccourci suivant : Placez la souris sur le constructeur de la classe et faite clic droit et slectionnez loption Atteindre la dfinition.

14

Slectionner la ligne correspond au source puis cliquez sur OK. Vous remarquerez le seul objet de la classe CSampleSDiApp qui va dmarrer lapplication :
// SampleSDI.cpp : Dfinit les comportements de classe pour l'application. // #include "stdafx.h" #include "afxwinappex.h" #include "SampleSDI.h" #include "MainFrm.h" #include "SampleSDIDoc.h" #include "SampleSDIView.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // CSampleSDIApp BEGIN_MESSAGE_MAP(CSampleSDIApp, CWinAppEx) ON_COMMAND(ID_APP_ABOUT, &CSampleSDIApp::OnAppAbout) // Commandes de fichier standard ON_COMMAND(ID_FILE_NEW, &CWinAppEx::OnFileNew) ON_COMMAND(ID_FILE_OPEN, &CWinAppEx::OnFileOpen) END_MESSAGE_MAP() // construction CSampleSDIApp CSampleSDIApp::CSampleSDIApp() { m_bHiColorIcons = TRUE; // TODO : ajoutez ici du code de construction, // Placez toutes les initialisations significatives dans InitInstance } CSampleSDIApp theApp; // initialisation de CSampleSDIApp BOOL CSampleSDIApp::InitInstance() {

La fonction InitInstance() va initialiser lapplication ,cest donc la premire fonction utile lapplication. On peut considrer en faisant le parallle avec un programme DOS que cest le main de lapplication.

15

BOOL CSampleSDIApp::InitInstance() { // InitCommonControlsEx() est requis sur Windows XP si le manifeste de l'application // spcifie l'utilisation de ComCtl32.dll version 6 ou ultrieure pour activer les // styles visuels. Dans le cas contraire, la cration de fentres chouera. INITCOMMONCONTROLSEX InitCtrls; InitCtrls.dwSize = sizeof(InitCtrls); // dfinir pour inclure toutes les classes de contrles communs utiliser // dans votre application. InitCtrls.dwICC = ICC_WIN95_CLASSES; InitCommonControlsEx(&InitCtrls); CWinAppEx::InitInstance(); // Initialiser les bibliothques OLE if (!AfxOleInit()) { AfxMessageBox(IDP_OLE_INIT_FAILED); return FALSE; } AfxEnableControlContainer(); // Initialisation standard // Si vous n'utilisez pas ces fonctionnalits et que vous souhaitez rduire la taille // de votre excutable final, vous devez supprimer ci-dessous // les routines d'initialisation spcifiques dont vous n'avez pas besoin. // Changez la cl de Registre sous laquelle nos paramtres sont enregistrs // TODO : modifiez cette chane avec des informations appropries, // telles que le nom de votre socit ou organisation SetRegistryKey(_T("Applications locales gnres par AppWizard")); LoadStdProfileSettings(0); // Charger les options de fichier INI standard InitContextMenuManager(); InitShellManager(); InitKeyboardManager(); InitTooltipManager(); CMFCToolTipInfo ttParams; ttParams.m_bVislManagerTheme = TRUE; theApp.GetTooltipManager()->SetTooltipParams(AFX_TOOLTIP_TYPE_ALL, RUNTIME_CLASS(CMFCToolTipCtrl), &ttParams); // Inscrire les modles de document de l'application. Ces modles // lient les documents, fentres frame et vues entre eux CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CSampleSDIDoc), RUNTIME_CLASS(CMainFrame), // fentre frame SDI principale RUNTIME_CLASS(CSampleSDIView)); if (!pDocTemplate) return FALSE; AddDocTemplate(pDocTemplate); // Activer les ouvertures d'excution DDE EnableShellOpen(); RegisterShellFileTypes(TRUE); // Analyser la ligne de commande pour les commandes shell standard, DDE, ouverture de fichiers CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo);

16

// Commandes de dispatch spcifies sur la ligne de commande. Retournent FALSE si // l'application a t lance avec /RegServer, /Register, /Unregserver ou /Unregister. if (!ProcessShellCommand(cmdInfo)) return FALSE; // La seule fentre a t initialise et peut donc tre affiche et mise jour m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow(); // appelle DragAcceptFiles uniquement s'il y a un suffixe // Dans une application SDI, cet appel doit avoir lieu juste aprs ProcessShellCommand // Activer les ouvertures via glisser-dplacer m_pMainWnd->DragAcceptFiles(); return TRUE; }

Une application Windows a la possibilit dcrire des donnes dans un fichier ini qui porte son nom (hritage de Windows 3.xx). Depuis Windows 95 ces mmes donnes peuvent tre stockes dans la base de registre. Pour utiliser ces fonctionnalits nous disposons de mthodes dfinies dans la classe dapplication CWinAppEx permettant leurs critures et lectures (par exemple WriteProfileString et GetProfileString pour une chane de caractres). La ligne SetRegistyKey spcifie la clef qui sera dfinie dans la base de registre pour stocker ces donnes. Si on veut stocker les donnes dans un fichier .ini plutt de dans la base de registres, il suffira de supprimer cette ligne, rendant inoprant du mme coup la ligne suivante qui permet la sauvegarde des derniers fichiers utiliss par lapplication.

Lobjet gestionnaire de fentre MFC :


Avec le bloc de lignes suivant on rentre dans le vif du sujet. Il y est fait rfrence linitialisation dun objet de la classe CSingleDocTemplate avec diffrentes classes entoures de la macro RUNTIME_CLASS. En fait pour disposer dune fentre de type Vue, il faut lui dfinir un environnement de travail quil faut renseigner au niveau de la classe dapplication qui en sera le gestionnaire. La dfinition dune vue se dcompose comme suit : Une fentre mre ici la classe : CMainFrame , La classe fentre elle-mme : CSampleSDIView Dune classe Document : CSampleSDIDoc Dun identifiant de menu : IDR_MAINFRAME correspondant au menu associ la fentre.

17

Une fois lobjet renseign la fonction AddocTemplate est appele avec lobjet dcrivant lunique fentre de notre application. Revenons sur cette description, pourquoi une fentre mre ? Les MFC dcomposent la fentre en deux parties : le contexte de travail (fentre parent appele la frame) qui comprendra les barres doutils et la barre de statuts. Dans le cas dun projet SDI cest la fentre principale MainFrame qui remplit ce rle. Dans le cas dun projet MDI (que nous verrons plus tard) une autre classe apparat la classe CMDIChildWnd, cest elle qui jouera ce rle, la deuxime partie tant la fentre elle-mme.

La fentre vue est donc comprise dans la fentre principale tablissant ainsi une hirarchie parent / enfant. Larchitecture se charge de grer les interactions entre la fentre principale et ses diffrentes fentres filles.

La classe Document :
Ce modle de construction est appel document-vue car pour chaque fentre vue on disposera dun objet document. Celui-ci na pas de partie graphique visible, il est destin enregistrer et lire les donnes relatives la fentre. Il est en relation directe avec la vue, celle-ci disposant dun accs rapide pour y accder la mthode GetDocument() . La liaison document vue est effective dans les deux sens (vue document). Bien que gnre automatiquement il nest pas indispensable de lutiliser.

18

Pour ne pas trop compliquer tout de suite les choses je dcrirai cette classe un peu plus loin dans la deuxime partie consacre au framework pour le contexte MDI o elle prendra un peu plus dimportance pour des raisons darchitecture. La macro RUNTIME_CLASS : Autre point nbuleux et difficile apprhender pour le dbutant la macro RUNTIME_CLASS. Cette macro permet dobtenir la signature dune classe. En fait chaque classe drive de la classe CObject possde une structure CRuntimeClass qui peut tre utilise pour obtenir des informations en temps rel sur lobjet manipul comme : Le nom de la classe contenue dans une chane de caractres. Une fonction CreateObject( ); qui permet de crer un objet correspondant la signature.

Cest justement cette fonctionnalit qui est utilise en interne par larchitecture MFC pour crer les objets fentres. Vous remarquerez par la suite que pour les classes fentres gnres par Visual le constructeur est protg. Et pour finir la fonction BOOL IsDerivedFrom( const CRuntimeClass* pBaseClass) qui permet de savoir si la signature en cours hrite de la classe reprsente par sa signature pBaseClass. Exemple dcriture possible: MyClass ::OneFunc(CWnd *pWnd) { if(pWnd ->GetRuntimeClass()->IsDerivedFrom(RUNTIME_CLASS(CEdit))) { // cet objet est bien driv de la classe CEdit . } } Il est noter que ce mcanisme est indpendant de celui du RTTI du C++. Nous reviendrons plus loin sur ces fonctionnalits. Nous avons vu les diffrentes initialisations dans la fonction InitInstance, mais rien qui montre comment est cre la fentre.

19

En fait cest le groupe de ligne :


// Activer les ouvertures d'excution DDE EnableShellOpen(); RegisterShellFileTypes(TRUE); // Analyser la ligne de commande pour les commandes shell standard, DDE, ouverture de fichiers CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo); // Commandes de dispatch spcifies sur la ligne de commande. Retournent FALSE si // l'application a t lance avec /RegServer, /Register, /Unregserver ou /Unregister. if (!ProcessShellCommand(cmdInfo)) return FALSE;

Qui sen charge, et qui soccupe de relayer les commandes par dfaut du Shell, permettant par exemple louverture de lapplication sur un double clic sur le fichier de donnes enregistr pour lapplication (voir RegisterShellFileTypes). Le nom sera utilis par la classe document pour lire les informations.

Initialisation de la fentre principale de lapplication CMainFrame :


Les deux lignes suivantes correspondent la mise en place de la fentre principale. La premire ligne rend visible la fentre et la deuxime permet son rafrachissement.
// La seule fentre a t initialise et peut donc tre affiche et mise jour m_pMainWnd->ShowWindow(SW_SHOW); m_pMainWnd->UpdateWindow();

m_pMainWnd correspond donc un pointeur sur la classe CMainFrame . En fin de traitement la fonction InitInstance renvoie true, lexcution de la boucle de messages au niveau de lapplication est lance par la fonction :

virtual int CWinApp::Run( );


Renvoyer FALSE abandonnera le lancement de lapplication, faisant de cette fonction lendroit idal pour y grer son accs ou son enregistrement.

Synthse :
Une application MFC possde une classe dapplication drive de la classe CWinApp(Ex) . Un objet unique de cette classe est instanci et permet par lintermdiaire de sa mthode virtuelle InitInstance : dinitialiser lapplication : couche rseau, objets ol (ActiveX) etc.. de dfinir les diffrentes vues disponibles dans lapplication (une seule en SDI) . La mise en place finale de la fentre principale correspondant la classe CMainFrame.

20

Quelques donnes membres et fonctions utiles de la classe dapplication : ::m_pszAppName ::m_hInstance ::m_lpCmdLine ::m_nCmdShow Contient le nom de lapplication Identifie linstance de lapplication Pointe vers une chane termine par un \0 qui contient la ligne de commande de lapplication. Spcifie comment la fentre va tre initialise. Appele lorsque lapplication est ferme, redfinir pour faire un travail de libration mmoire par exemple. Permet de rgler globalement pour les botes de dialogue : la couleur de fond et/ou le texte dans les contrles

Virtual int CWinApp::ExitInstance( ) void CWinApp::SetDialogBkColor( COLORREF clrCtlBk = RGB(192, 192, 192), COLORREF clrCtlText = RGB(0, 0, 0) );

Une fonction globale extrmement utile : CWinApp *AfxGetApp() ; Cette fonction permet quelque soit lendroit du programme dobtenir un pointeur sur la classe dapplication. Exemple appliqu notre projet dexemple :
CSampleSDIApp *pTheApp=static_cast< CSampleSDIApp *>(AfxGetApp()) ;

Il est tout fait possible de disposer de lobjet dapplication dans tout le programme, en utilisant directement la variable globale theApp gnre par lassistant.

Les fonctions de traitement des fichiers .ini :


CWinApp::GetProfileInt UINT GetProfileInt( LPCTSTR lpszSection, LPCTSTR lpszEntry, int nDefault ); CWinApp::WriteProfileInt BOOL WriteProfileInt( LPCTSTR lpszSection, LPCTSTR lpszEntry, int nValue ); CWinApp::GetProfileString CString GetProfileString( LPCTSTR lpszSection, LPCTSTR lpszEntry, LPCTSTR lpszDefault = NULL ); CWinApp::WriteProfileString BOOL WriteProfileString( LPCTSTR lpszSection, LPCTSTR lpszEntry, LPCTSTR lpszValue ); CWinApp::WriteProfileBinary BOOL WriteProfileBinary( LPCTSTR lpszSection, LPCTSTR lpszEntry, LPBYTE pData, UINT nBytes ); CWinApp::GetProfileBinary BOOL GetProfileBinary(LPCTSTR lpszSection,LPCTSTR lpszEntry,LPBYTE* ppData, UINT* pBytes );

21

Ces fonctions permettent de stocker dans le .ini de lapplication ou la base de registres des informations utilisateur. Dans le cas dun .ini celui-ci est stock dans le rpertoire Windows et porte le nom de lapplication. Les fonctions de lecture admettent un argument pour spcifier une valeur par dfaut quand la clef nexiste pas dans le .ini.

Exemples :
// criture dans le fichier ini AfxGetApp()->WriteProfileString("Parametres", "UserDefault", "Farscape"); // lecture dans le fichier ini CString str = AfxGetApp()->GetProfileString("Parametres", "UserDefault") ; AfxMessageBox(str) ;

Donnera le rsultat suivant :

[Parametres] UserDefault=Farscape

22

La classe Fentre Principale CMainFrame


Continuons notre tude de code avec la classe CMainFrame .
Rappels : Visual C++ a gnr pour notre exemple une classe dapplication CSampleSDIApp drive de CWinApp. Une classe fentre principale de lapplication CMainFrame drive de la classe CFrameWndEx. Note : La classe MFC dorigine est CFrameWnd, comme notre projet utilise les extensions MFC rajoutes par le service pack 1, cest la classe tendue qui est utilise. Voici sa dfinition :
// MainFrm.h : interface de la classe CMainFrame // #pragma once #include "CalendarBar.h" #include "Resource.h" class COutlookBar : public CMFCOutlookBar { virtual BOOL AllowShowOnPaneMenu() const { return TRUE; } virtual void GetPaneName(CString& strName) const { BOOL bNameValid = strName.LoadString(IDS_OUTLOOKBAR); ASSERT(bNameValid); } }; class CMainFrame : public CFrameWndEx { protected: // cration partir de la srialisation uniquement CMainFrame(); DECLARE_DYNCREATE(CMainFrame) // Attributs public: // Oprations public: // Substitutions public: virtual BOOL PreCreateWindow(CREATESTRUCT& cs); // Implmentation public: virtual ~CMainFrame(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif protected: // membres incorpors de la barre de contrle CMFCRibbonBar m_wndRibbonBar; CMFCRibbonApplicationButton m_MainButton; CMFCToolBarImages m_PanelImages; CMFCRibbonStatusBar m_wndStatusBar; COutlookBar m_wndNavigationBar; CMFCShellTreeCtrl m_wndTree; CCalendarBar m_wndCalendar; CMFCCaptionBar m_wndCaptionBar; // Fonctions gnres de la table des messages protected:

23

afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); afx_msg void OnApplicationLook(UINT id); afx_msg void OnUpdateApplicationLook(CCmdUI* pCmdUI); afx_msg void OnViewCaptionBar(); afx_msg void OnUpdateViewCaptionBar(CCmdUI* pCmdUI); DECLARE_MESSAGE_MAP() void InitializeRibbon(); BOOL CreateOutlookBar(CMFCOutlookBar& bar, UINT uiID, CMFCShellTreeCtrl& tree, CCalendarBar& calendar, int nInitialWidth); BOOL CreateCaptionBar(); int FindFocusedOutlookWnd(CMFCOutlookBarTabCtrl** ppOutlookWnd); CMFCOutlookBarTabCtrl* FindOutlookParent(CWnd* pWnd); CMFCOutlookBarTabCtrl* m_pCurrOutlookWnd; CMFCOutlookBarPane* m_pCurrOutlookPage; };

Ce code assez court demande beaucoup dexplications Le rle de cette classe cest dtre responsable de la fentre principale de lapplication. Elle va mettre en place lors de sa cration tout un lot dlments graphiques correspondant notre choix lors de la gnration du projet: La gestion de la barre ruban reprsente par la clase CMFCRibbonBar Le Boutton dapplication par la classe CMFCRibbonApplicationButton. Un conteneur pour stocker les images utilises dans le ruban avec la classe CMFCToolBarImages La barre de status du ruban represente par la classe CMFCRibbonStatusBar, dans une application MFC classique cest la classe CStatusBar qui est utilise. La gestion de notre objet de navigation est confie la classe CMFCOutlookBar. Un objet permettant de parcourir larborescence du poste de travail sous forme darbre avec la classe CMFCShellTreeCtrl . Un objet calendrier utilis par notre barre OutLook avec la classe CCalendarBar. La gestion de la barre dentte est confie la classe CMFCCaptionBar .

Aprs cette numration je pense que vous laurez compris, la classe CMainFrame est lendroit stratgique pour dclarer et initialiser les objets relatifs larchitecture de lapplication. Comme cest un lment important de lapplication, elle dispose elle aussi dune fonction globale permettant dy accder quel que soit lendroit du programme CWnd* AfxGetMainWnd( ); Exemple appliqu notre projet dexemple :
CMainFrame *pTheFrame=static_cast< CMainFrame *>( AfxGetMainWnd()) ;

Le traitement des Messages Windows :


Il nous faut maintenant parler de la gestion des messages et la manire dont ils sont implments dans le code.

24

Toutes les classes fentres hritent de la classe CWnd qui dispose dune fonction particulire la Windows procedure (CWnd ::WindowProc ) Pour chaque message Windows on aura une fonction commenant par On gnre. Dans notre exemple le message WM_CREATE a gnr la fonction : int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) Une boucle des messages (Message Map) implmente sous forme de macro maintient la liste des fonctions en rponse aux messages de linterface utilisateur dfinie, cest--dire les commandes issues des menus, des boutons etc.. Lutilisateur pourra aussi disposer de messages privs (cette notion sera dveloppe plus loin). A la rception dun message en destination de la fentre, la fonction correspondante est appele (ou ventuellement celle de la classe de base si aucune dfinition nest propose). Une question classique : Pourquoi ne pas avoir implment toutes les fonctions de rponses aux messages en mthodes virtuelles ? Plusieurs raisons : La liste des messages Windows nest pas finie et lutilisateur peut dfinir ses propres messages. D'autre part, dfinir un nombre trop important de mthodes virtuelles gaspillerait des ressources considrables, le compilateur devant maintenir dans une table (vtable) les pointeurs sur ces mthodes.

25

Ajouter un message avec lenvironnement Visual :


Heureusement pour nous ce travail de gnration de code est pris en charge par lenvironnement qui permet pour une classe donne de visualiser les messages disponibles et de gnrer la fonction correspondante. Pour appeler lassistant de gnration de message placez le curseur de votre souris sur la ligne END_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx) ON_WM_CREATE() ON_COMMAND_RANGE(ID_VIEW_APPLOOK_WIN_2000, ID_VIEW_APPLOOK_OFF_2007_AQUA, &CMainFrame::OnApplicationLook) ON_UPDATE_COMMAND_UI_RANGE(ID_VIEW_APPLOOK_WIN_2000, ID_VIEW_APPLOOK_OFF_2007_AQUA, &CMainFrame::OnUpdateApplicationLook) ON_COMMAND(ID_VIEW_CAPTION_BAR, &CMainFrame::OnViewCaptionBar) ON_UPDATE_COMMAND_UI(ID_VIEW_CAPTION_BAR, &CMainFrame::OnUpdateViewCaptionBar) END_MESSAGE_MAP()

Tapez ensuite sur la combinaison de touche ALT+entre pour faire afficher les proprits de la ligne slectionne. La fentre suivante apparait :

Longlet proprits apparait avec une srie de boutons.

26

Note : si la fentre proprits apparait flottante, vous pouvez lancrer sur le panneau contenant le projet Pour cela maintenez le clic gauche enfonc sur la fentre, dplacez la sur le panneau contenant le projet, les zones dencrages apparaissent, lchez le clic sur la partie centrale de la croix comme sur limage ci-dessus

La fentre proprits devient alors un onglet disponible tout instant. Vous pouvez rpter le procd pour les autres fentres comme celle des ressources.

27

Longlet proprits :
Le premier bouton de la barre doutils centrale donne les informations sur le source en question. Celui en forme dclair permet de gnrer une fonction de rponse aux identifiants de menu (commandes) :

Pour gnrer une mthode de rponse il suffit de cliquer en face de COMMAND ou de UPDATE_COMMAND_UI. Le message COMMAND permet de ragir une commande de menu ou de barre doutils slectionne par lutilisateur. Le message UPDATE_COMMAND_UI permet de grer la disponibilit de la commande pour lutilisateur en grisant la ressource si celle-ci ne peut tre utilise. Un exemple simple pour bien comprendre : Le bouton coller, doit tre inactif quand il ny a rien dans le presse papier. Le troisime bouton en forme de rectangle :

Permet de dfinir les messages associs la classe en cours. La liste des diffrents messages commenant par WM_ (Windows message) apparait,

28

Il suffira donc de slectionner le message dans la liste et de cliquer dans la partie droite de la liste pour gnrer le code de la fonction de rponse au message. Dans lexemple ci-dessus on voit bien que la mthode OnCreate a t gnre en rponse au message Windows WM_CREATE. Le dernier bouton permet de redfinir des fonctions virtuelles dfinies dans la classe de base :

Sur limage ci-dessous on retrouve la mthode virtuelle PreCreateWindow .

29

Une autre question : Comment Visual sy retrouve avec ces fonctions gnres dans le code ? Avec Visual 6.0 le code tait entour de commentaires permettant de reprer les blocs en question, avec Visual 2008 exit les commentaires tout est gr dynamiquement ce qui peut entrainer de lgers temps dattente ou danalyse. Passons maintenant la partie implmentation de ces fonctions :
// CMainFrame
IMPLEMENT_DYNCREATE(CMainFrame, CFrameWndEx) BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx) ON_WM_CREATE() ON_COMMAND_RANGE(ID_VIEW_APPLOOK_WIN_2000, ID_VIEW_APPLOOK_OFF_2007_AQUA, &CMainFrame::OnApplicationLook) ON_UPDATE_COMMAND_UI_RANGE(ID_VIEW_APPLOOK_WIN_2000, ID_VIEW_APPLOOK_OFF_2007_AQUA, &CMainFrame::OnUpdateApplicationLook) ON_COMMAND(ID_VIEW_CAPTION_BAR, &CMainFrame::OnViewCaptionBar) ON_UPDATE_COMMAND_UI(ID_VIEW_CAPTION_BAR, &CMainFrame::OnUpdateViewCaptionBar) END_MESSAGE_MAP()

Dans limplmentation de la classe Visual a gnr le bloc de rponse aux messages (message map). Il est dlimit par les macros BEGIN_MESSAGE_MAP et END_MESSAGE_MAP. La premire ligne IMPLEMENT_DYNCREATE avec le nom de la classe et le nom de la classe de base est ncessaire pour la cration dynamique dobjet par les MFC. Dans la macro BEGIN_MESSAGE_MAP il est prcis le nom de la classe o sappliquent les messages et le nom de la classe parent. Attention une erreur classique : en cas dhritage successif dune classe fentre il faut bien notifier le bon parent pour chaque classe drive, sinon les messages destins la classe parent ne seront pas traits. Un message rajout manuellement (message priv) doit tre plac juste au dessus de END_MESSAGE_MAP().

30

Les diffrentes catgories de messages :


Les messages Windows : Lidentifiant commence par ON_WM_ Voir documentation MSDN pour une liste exhaustive. Les Commandes du menu ou de barre doutils (ToolBar): Lidentifiant commence par ON_COMMAND(Identifiant,NomFonction) Exemple :
// CSampleSDIApp BEGIN_MESSAGE_MAP(CSampleSDIApp, CWinAppEx) ON_COMMAND(ID_APP_ABOUT, &CSampleSDIApp::OnAppAbout) // Commandes de fichier standard ON_COMMAND(ID_FILE_NEW, &CWinAppEx::OnFileNew) ON_COMMAND(ID_FILE_OPEN, &CWinAppEx::OnFileOpen) END_MESSAGE_MAP()

Note : Ces identifiants sont dfinis dans les ressources sur chaque ligne dun menu : Pour accder au panneau des ressources vous pouvez faire CTRL+Maj.+E et ancrer ensuite la fentre.

31

Les Messages privs : Ce sont des messages que vous dfinissez pour vos propres besoins, Ils sont envoys par lintermdiaire de la fonction : CWnd::PostMessage BOOL PostMessage( UINT message, WPARAM wParam = 0, LPARAM lParam = 0 ); Qui place le message dans la file dattente en cours pour la fentre concerne. Lappel est non bloquant, la fonction rend immdiatement la main. CWnd::SendMessage LRESULT SendMessage( UINT message, WPARAM wParam = 0, LPARAM lParam = 0 ); Envoie immdiat avec attente de rponse, donc bloquant Dans le message map on aura ON_MESSAGE(WM_USER+100 ,NomFonction) WM_USER tant le define de dpart pour les messages utilisateurs. La fonction de rponse aura le prototype suivant : LPARAM CMyWnd ::NomFonction(WPARAM wp,LPARAM lP) { return 0L; } Note: deux definitions sont disponibles pour les messages privs: WM_USER : voqu plus haut, est rserv pour les classes prives issues de contrles. WM_APP : est utilisable pour des communications entre classes fentres prives dans lapplication, sont utilisation est plus gnraliste que WM_USER. Les messages manant des contrles: Il est possible dintercepter au niveau de la classe fentre contenant les contrles certaines notifications. Nous verrons cette possibilit sur le chapitre consacr aux contrles.

Les Classes de fentres :


Chaque fentre sait se redessiner, mais quest ce qui diffrencie un contrle de type dit et un contrle de type static ? Rponse : sa classe de fentre, chaque fentre possde une classe permettant de dfinir son affichage et son comportement. Exemples : un contrle Edit aura pour classe Edit un contrle static static

32

Spy++ lespion qui vous veut du bien :


Il existe un utilitaire qui permet de visualiser et de se rendre compte des proprits des fentres, des changes etc. .. Cest Spy++ ,il est livr avec visual

Aprs lancement du programme slectionnez loption du menu spy++ : espionner, rechercher une fentre.. Placez la souris sur la zone en face du libell Outil recherche , puis maintenez le bouton gauche dessus et dplacez la souris sur les diffrentes parties de notre programme de test, par exemple sur le libell Bote de dialogue de notre fentre, et lchez le clic gauche dessus. Vous obtenez le rsultat visible sur la capture ci-dessus.

33

On retrouve bien tout ce qui t dcrit savoir le handle de fentre, le texte, la classe de fentre, son style sa positon etc. . Aprs validation de cette bote de dialogue, spy++ ouvre une fentre qui trace tous les messages reus par la fentre slectionne. Exercez-vous avec SPY qui est un bon outil pour comprendre comment se droulent les choses, les messages envoys suite une action etc. En gros espionner lapplication

34

Les messages explications complmentaires:


Le fonctionnement des fentres est bas sur lchange de messages, Ainsi nous aurons des messages en provenance de la fentre pour nous avertir dun changement. Quelques messages usuels et importants : WM_PAINT : il est envoy chaque fois quune fentre a besoin dtre redessine suite un recouvrement par une autre fentre par exemple ou par une action volontaire, WM_SIZE : il est envoy quand la fentre change de taille par une demande manuelle de lutilisateur ou par demande spcifique. WM_CREATE : il signale que la fentre va tre cre WM_CLOSE : il signale que la fentre va tre ferme. Il mest impossible de dcrire la liste des messages, elle est trop importante. Il suffit de savoir que le systme envoie des messages concernant la fentre, la gestion du clavier, la gestion de la souris etc.. Pour le reste cest un apprentissage progressif en fonction de ses besoins et on se reportera la documentation MSDN pour le dtail de fonctionnement. Nous enverrons des messages spcifiques pour changer les proprits dune fentre. Ou encore nous interrogerons une fentre pour rcuprer un tat ou un statut. Les deux dernires possibilits sont souvent encapsules par les classes MFC : Exemple : Communication avec le contrle de la classe CListCtrl : Affectation dune valeur : _AFXCMN_INLINE void CListCtrl::SetWorkAreas(int nWorkAreas, LPRECT lpRect) { ASSERT(::IsWindow(m_hWnd)); ::SendMessage(m_hWnd, LVM_SETWORKAREAS, nWorkAreas, (LPARAM) lpRect); } Rcupration dune valeur : _AFXCMN_INLINE DWORD CListCtrl::GetExtendedStyle() { ASSERT(::IsWindow(m_hWnd)); return (DWORD) ::SendMessage(m_hWnd, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); }La classe vue de lapplication CSampleSDIView Vous remarquerez dans les deux cas de lutilisation de la fonction SendMessage. De la variable m_hWnd qui est une donne membre de la classe CWnd et qui reprsente son handle (identifiant visible avec spy ) de fentre.

35

Unicode et internationalisation du logiciel :


Avant dattaquer la programmation des contrles et la manipulation de leurs donnes, il me semble important de parler de lUnicode. Lorsque vous gnrez un nouveau projet avec Visual Studio celui-ci rgle par dfaut le projet en Unicode :

Rappels : LUnicode permet le codage des pages de caractres pour les diffrentes langues. La norme Utf-8 la plus commune code un caractre sur 4 octets, alors que notre vieille table Ascii ne permet la codification que sur 1 octet, soit 256 valeurs diffrentes. Lutilisation de lUnicode pour dfinir nos ressources : bote de dialogue, menu etc. permettra dinternationaliser le projet en fournissant des dll de ressources correspondant la langue de distribution. Quelles sont les incidences sur notre code ? En premier lieu on vitera dutiliser des tableaux de char, sauf en connaissance de cause. En fait nous disposons dun ensemble de macros et de classes permettant de traiter le problme de lutilisation de lUnicode dans nos programmes. Avec les MFC, la classe CString est disponible en trois versions : La classe CString qui suivra le paramtrage du projet : UNICODE ou Multi-Byte. La classe CStringA pour travailler avec des chanes de caractres Multi-Byte (char *) . La classe CStringW pour travailler avec des chanes Unicode.

36

La conversion d'une chane UNICODE en Multi-Byte devient une chose aise :


// Rglage du projet en UNICODE CString strWide=_T("chaine de caractres"); CStringA strA(strWide); CString strWideDup(strA); // l'inverse VERIFY(strWideDup==strWide); char *sz=strA.GetBuffer(0);

L'exemple ci-dessus part d'une CString UNICODE et fournit une CString Multi-Byte et un pointeur sur char *. Et enfin, il fabrique une CString UNICODE partir d'une CString Multi-Byte. Le tableau qui suit prsente les macros disponibles :
Macro SBCS (_UNICODE, _MBCS non dfini ) _TCHAR _tfinddata_t _tfinddata64_t Char _finddata_t __finddata64_t char _finddata_t wchar_t _wfinddata_t _MBCS dfini _UNICODE dfini

__finddata64_t __wfinddata64_t _finddatai64_t int signed char unsigned char unsigned char Pas deffets _wfinddatai64_t wint_t wchar_t wchar_t wchar_t L (conversion du caractre ou de la chaine en Unicode)

_tfinddatai64_t _finddatai64_t _TINT _TSCHAR _TUCHAR _TXCHAR _T or _TEXT int signed char unsigned char char Pas deffets

Concrtement, lorsque vous devrez manipuler des chanes de caractres dans votre projet, il sera prfrable dutiliser la macro _TCHAR et les zones de texte seront dclares avec la macro _T. Exemples :
CString str=_T("chaine de caractres ") ; _TCHAR sz[10] ;

Les dclarations ci-dessus seront valides dans un projet Unicode ou Multi-bytes. Dans le cas de manipulations avec des _TCHAR, il faudra faire attention la taille du type manipul lors des copies mmoires dune chane une autre. Pour les comparaisons, manipulations et conversions de chanes, le fichier TCHAR.H dfinit un ensemble de define tenant compte du jeu de caractres rgls.

37

Exemple : au lieu dutiliser strncmp vous utiliserez _tcsnccmp :


TCHAR.H routine _tcsnccmp _UNICODE & _MBCS non dfini strncmp _MBCS dfini _mbsnbcmp _UNICODE dfini wcsncmp

Je vous conseille de parcourir ce fichier pour consulter lensemble des dfinitions disponibles. Pour les casts de chanes de caractres nous disposons aussi dune liste de dfinitions permettant de fonctionner dans les deux modes. Exemple : au lieu dutiliser un cast : const char * on utilisera LPCTSTR la place. Lensemble de ces dfinitions est disponible dans MSDN la rubrique : Windows data types. Utilisation de la classe string de la stl : En C++, nous disposons de deux classes pour manipuler les chanes de caractres : La classe string pour les char ( Multi-bytes) ou la classe wstring pour lUnicode. Lchange de donnes entre un objet de la classe string et un objet de la classe CString des MFC ne cause pas de problmes :
{ // string to CStringA std::string str= "chaine"; CStringA stra=str.c_str(); } { // wstring to CStringW std::wstring wstr= L"chaine"; CStringW strw=wstr.c_str(); } // CStringA to string { CStringA stra="chaine"; std::string str= stra; } // CStringW to wstring { CStringW strw=_T("chaine"); std::wstring wstr= strw; }

Conclusion : Dans vos projets, utilisez et manipulez le moins possible des chanes de caractres comme on le ferait en programmation C. A la place utilisez la classe CString des MFC pour stocker et manipuler vos chanes. Vous viterez ainsi bien des erreurs de dbordement mmoire et vous aurez une utilisation plus naturelle de lUnicode dans votre programme.

38

La fentre vue de notre application CSampleSDIView :


Volontairement jai choisi comme fentre de traitement une fentre de la classe de base CFormView . Ce qui va nous permettre de travailler avec des contrles et de mettre enfin en pratique les notions dveloppes prcdemment. Notre fentre vue dispose elle aussi dune boucle des messages permettant dintercepter des messages de la barre doutils ou des contrles que nous allons placer maintenant : Le Fichier des ressources graphiques de lapplication (SampleSDI.rc) Lensemble des ressources graphiques de lapplication est regroup dans un fichier qui porte le nom de lapplication plus lextension .RC Ce fichier apparat dans votre projet, cest un fichier au format texte ditable avec Notepad. Un simple double clic dessus ouvrira linterface de gestion des ressources de Visual

Dans le dossier dialogue nous voyons apparatre deux identifiants de fentres, le premier IDD_ABOUTBOX correspond la bote de dialogue propos de de notre application. Nous reviendrons plus tard sur les botes de dialogue. Le deuxime IDD_SAMPLESDI_FORM correspond notre fentre vue. Comment Visual fait il le lien entre la fentre dessine lcran et la classe associe CSampleSDIView ?

39

Par son identifiant qui apparat dans la dfinition de la classe vue :


class CSampleSDIView : public CFormView { protected: // cration partir de la srialisation uniquement CSampleSDIView(); DECLARE_DYNCREATE(CSampleSDIView) public: enum{ IDD = IDD_SAMPLESDI_FORM };

Ce mme identifiant est utilis sur le constructeur de la classe pour indiquer quelle ressource utiliser lors de la construction de la fentre :
// CSampleSDIView IMPLEMENT_DYNCREATE(CSampleSDIView, CFormView) BEGIN_MESSAGE_MAP(CSampleSDIView, CFormView) END_MESSAGE_MAP() // construction ou destruction de CSampleSDIView CSampleSDIView::CSampleSDIView() : CFormView(CSampleSDIView::IDD) { // TODO : ajoutez ici du code de construction }

Lditeur de ressources et les contrles disponibles :


Dans lditeur de ressources on trouve un panneau escamotable ancrable avec la liste des contrles disponibles. Cette liste correspond aux contrles de base de Windows, elle est extensible avec des contrles de type ActiveX.

Les contrles Usuels disponibles :


Nom du Contrle Picture Static Text Edit Box Group Box Button Check Box Radio button Combo Box List Box Horizontal scroll bar Vertical scroll bar Spin Progress Classe MFC associe CPicture CStatic CEdit CButton CButton CButton CComboBox CListBox CScrollBar CScrollBar Description Affichage dune image. Affichage dun libell fixe Saisie dune zone Permet de dfinir un groupe de contrles. Gestion dun bouton. Gestion dune case a coche. Gestion dun bouton radio (un choix parmi dautres). Boite de slection pouvant combiner un mode Edit et une liste droulante. Liste de slection mono colonne Gestion dun ascenseur horizontal Gestion dun ascenseur vertical

CSpinButtonCtrl Contrle dincrmentation dun dit. CProgressCtrl Barre de progression

40

Slider Hot Key ListControl Tree Control Tab Control Animate Rich Edit Date Time Picker Month calendar IP adress Custom Control Extanded combo box

CSliderCtrl CHotKeyCtrl CListCtrl CTreeCtrl CTabCtrl CRichEdit CDateTimeCtrl CMonthCalCtrl CIPAddressCtrl

Gestion dun curseur analogique Liste multi colonnes comme lexplorateur windows. Gestion dun arbre multi branches Gestion des onglets. Saisie dune zone de texte au format RTF (wordpad) Slection et saisie dune date avec calendrier ou de lheure. Gestion dun calendrier Saisie dune adresse IP Permet de faire un contrle personnalis Extension dune CComboBox pour le support dimages par item (3 possibles /ligne)

CComboBoxEx

41

Mise en place des contrles :


Nous allons placer sur notre fentre les lments suivants : Des zones dditions, des libells et deux boutons. Pour cela il suffit de cliquer sur la barre doutils sur le contrle en question, de positionner la souris sur la fentre et de cliquer nouveau pour faire apparatre le contrle. Pour notre exemple nous allons mettre en place une fiche permettant de saisir : Un nom Un Prnom Une adresse sur deux lignes. Un code Postal Une Ville. Placer les contrles sur la fentre, vous pouvez les dupliquer par les touches ou boutons habituels de copier/coller Windows. Chaque contrle dispose dun identifiant gnr automatiquement et modifiable. Note : Les proprits dun contrle sont modifiables par le panneau proprits (ALT+entre)

Renseignez les identifiant suivants : Le nom : IDC_EDITNOM Prnom : IDC_EDITPRENOM Adresse : IDC_EDITADRESSE Adresse : IDC_EDITADRESSE1 Code Postal : IDC_EDITCDP La ville. : IDC_EDITVILLE Un bouton enregistrer: IDC_BUTTONOK Un bouton RAZ : IDC_BUTTONRAZ

42

Ajustez la longueur des diffrentes zones dditions en tirant les contrles avec la souris.

Gestion de lalignement des contrles :


En bas de la zone ddition de la fentre vous disposez dune barre doutils pour grer lalignement des contrles :

Description de gauche droite des boutons : 1. permet de simuler le fonctionnement de la bote de dialogue en cours 2. alignement sur la gauche des contrles sur le contrle dominant (voir plus bas explications) 3. alignement sur la droite des contrles sur le contrle dominant. 4. alignement sur le bord suprieur des contrles sur le contrle dominant. 5. alignement sur le bord infrieur des contrles sur le contrle dominant. 6. centrage des contrles verticalement sur la bote de dialogue. 7. centrage des contrles horizontalement sur la bote de dialogue. 8. rgle les espaces horizontalement entre les contrles slectionns. 9. rgle les espaces verticalement entre les contrles slectionns. 10. donne la mme largeur aux contrles slectionns que le contrle dominant. 11. donne la mme hauteur aux contrles slectionns que le contrle dominant. 12. donne la mme taille aux contrles slectionns que le contrle dominant. 13. affichage dune grille dans la zone ddition. (les guides disparaissent) 14. enlve la grille La notion de contrle dominant : Nous allons aligner nos contrles, pour cela rglez correctement la position du premier Edit le nom. Ensuite slectionnez avec la souris Clic gauche + touche Ctrl les autres contrles Edit et en dernier le contrle Edit Nom, qui sera notre contrle dominant. Slectionnez ensuite le bouton dalignement vers la gauche (2). Le mme principe de slection est applicable pour les diffrents outils de la barre dalignement. Entraner-vous ! Agrandissez la zone adresse puis par copie affecter la mme taille sur la zone la deuxime zone dadresse. Alignez ensuite vos static.

43

Autres possibilits : Lajout de lignes de guide sur lequel les contrles sont placs pour faciliter le dplacement. Pour cela cliquez dans les rgles sur les cts de la fentre (voir ci-aprs). Le rglage des proprits de plusieurs contrles identiques en mme temps : Il suffit de slectionner les contrles en question et de faire clic droit proprits. La capture dun groupe de contrles : Il suffit de faire clic sur une zone libre de la fentre et de dplacer la souris pour dlimiter une surface de slection des contrles. Le dplacement dun groupe de contrles : Un groupe de contrle slectionn peut tre dplac simplement avec la souris Exemple dalignement du texte de tous les static vers la droite :

Notre fentre finale :

44

Gestion de lordre de saisie des contrles : Une fois les contrles placs il faut rgler lordre de saisie, qui correspond au dplacement avec la touche tabulation. Dans le menu layout slectionnez loption tab order ou CTRL+D.

Cliquez sur les dits dans lordre naturel de saisie : En premier le nom puis le prnom etc..

Pour finir le rglage de la tabulation cliquer sur une zone vierge de la fentre. Une petite prcision : Un contrle supporte la navigation par la touche tabulation si dans ses proprits loption tab stop est coche.

45

Il ne reste plus qu compiler et lier notre programme pour apprcier le rsultat :

Une question se pose maintenant : Comment communiquer avec les contrles, cest--dire comme rcuprer les valeurs saisies, ou comment placer une valeur dedans ? Cest lobjet du chapitre suivant.

46

Comment travailler avec les contrles ?


Il y a deux manires de travailler avec des contrles placs sur une fentre. La premire mthode consiste rcuprer un pointeur sur la fentre du contrle. Exemple : CListBox *pListBox =static_cast<CListBox *>(GetDlgItem(IDC_LISTBOX)) ; pListBox->AddString("coucou ") ; CEdit *pEdit=static_cast<CEdit *>(GetDlgItem(IDC_EDITNOM)) ; pEdit->SetWindowText("Robert") ; // etc On laura compris la mthode GetDlgItem ici applique la classe fentre CSampleSDIView permet de rcuprer un pointeur de type CWnd * sur lidentifiant du contrle pass en argument de la fonction. A partir de ce moment toutes les fonctionnalits de la classe CWnd sont accessibles comme par exemple affecter une nouvelle valeur ldit par la mthode SetWindowText. La deuxime consiste associer une variable au contrle, ce niveau on dispose encore de deux possibilits : la variable associe peut tre le contrle lui-mme un CEdit une CListBox etc., ou une variable pour manipuler le contenu du contrle. Exemple : dans le cas dun contrle CEdit il sera pratique de travailler avec une variable CString pour changer ou rcuprer le contenu du CEdit . La classe CString est une classe utilitaire permettant de travailler avec les chanes de caractres et ressemble sur certains points la classe string des STL. Cette association peut se faire directement partir de lditeur de ressources : Dans lditeur de ressources sur le contrle en question faire clic droit. Slectionnez loption Ajouter une variable.

47

Il ne reste plus qu renseigner le nom de la variable et indiquer la catgorie de variable : contrle ou valeur. Dans le cas dune valeur le type de variable CString ,int ,long cest suivant le type de contrle.

Exemple appliqu notre projet de test : Nous allons associer une variable CString tous les contrles ddition de notre fentre.

48

Dans le cas dune CString sur un contrle ddition (CEdit) on pourra limiter la zone de saisie un certain nombre de caractres. Exemple, ci-dessous je limite la saisie du code postal cinq caractres.

Pour bien comprendre le mcanisme dassociation de variables, nous allons associer une variable de classe CEdit sur le nom, et une variable de classe CButton sur lidentifiant IDC_BUTTONRAZ.

49

Examinons le code gnr par Visual :


Une variable est rajoute dans la classe fentre o est situ le contrle. Dans le .h de la classe on trouvera: Dans le code :
class CSampleSDIView : public CFormView { protected: // cration partir de la srialisation uniquement CSampleSDIView(); DECLARE_DYNCREATE(CSampleSDIView) public: enum{ IDD = IDD_SAMPLESDI_FORM }; //.. DECLARE_MESSAGE_MAP() public: afx_msg void OnBnClickedButtonok(); // variable associe au nom CString m_strNom; CString m_strPrenom; CString m_strAdresse; CString m_strAdresse2; CString m_strCdp; CString m_strVille; CEdit m_EditNom; CButton m_ButtonRaz; }; CSampleSDIView::CSampleSDIView() : CFormView(CSampleSDIView::IDD) , m_strNom(_T("")) , m_strPrenom(_T("")) , m_strAdresse(_T("")) , m_strAdresse2(_T("")) , m_strCdp(_T("")) , m_strVille(_T("")) { // TODO : ajoutez ici du code de construction }

50

La variable CString est initialise dans le constructeur.


void CSampleSDIView::DoDataExchange(CDataExchange* pDX) { CFormView::DoDataExchange(pDX); DDX_Text(pDX, IDC_EDITNOM, m_strNom); DDV_MaxChars(pDX, m_strNom, 30); DDX_Text(pDX, IDC_EDITPRENOM, m_strPrenom); DDV_MaxChars(pDX, m_strPrenom, 30); DDX_Text(pDX, IDC_EDITADRESSE, m_strAdresse); DDX_Text(pDX, IDC_EDITADRESSE1, m_strAdresse2); DDX_Text(pDX, IDC_EDITCDP, m_strCdp); DDV_MaxChars(pDX, m_strCdp, 5); DDX_Text(pDX, IDC_EDITVILLE, m_strVille); DDV_MaxChars(pDX, m_strVille, 30); DDX_Control(pDX, IDC_EDITNOM, m_EditNom); DDX_Control(pDX, IDC_BUTTONRAZ, m_ButtonRaz); DDX_Control(pDX, IDC_BUTTONOK, m_ButtonSave); }

Cest la fonction DoDataExchange qui tablit le lien entre le contrle Windows et les variables. Vous noterez les diffrentes macros utilises en fonction des associations que nous avons effectues. DDX_Control : Permet lassociation du contrle lidentifiant dsign. En gros cest ce contrle qui va se substituer au fonctionnement par dfaut du contrle Windows, on parle de subclassing. DDX_Text : tablit une relation dchange dans les deux sens entre une variable de classe CString et un contrle. DDV_MaxChars : Elle permet de prciser le nombre maxi de caractres saisissables pour la variable associe. Cette fonction est appele par la fonction UpdateData et le premier appel initialise les liens notamment pour la variable contrle (m_EditNom)) qui va subclasser le contrle windows. Note : jinsiste, le contrle sera graphiquement prt (donne membre m_hWnd diffrente de NULL) aprs le premier UpdateData(FALSE) et pas avant, celui tant fait par les MFC dans la fonction CFormView::OnInitialUpdate pour une CFormView ou CDialog::OnInitDialog pour une CDialog (nous verrons a plus loin).
void CFormView::OnInitialUpdate() { ASSERT_VALID(this); if (!UpdateData(FALSE)) TRACE(traceAppMsg, 0, "UpdateData failed during formview initial update.\n"); CScrollView::OnInitialUpdate(); } BOOL CDialog::OnInitDialog() { //. // transfer data into the dialog from member variables if (!UpdateData(FALSE)) { TRACE(traceAppMsg, 0, "Warning: UpdateData failed during dialog init.\n"); EndDialog(-1); return FALSE; } //. }

51

Consquences :
Toute tentative dutilisation dun contrle avant lexcution de ces fonctions se soldera par une assertion derreur. Exemple : faire m_EditNom.SetWindowText(_T("Farscape")) ; dans le constructeur. Liste des erreurs communes en relation avec la fonction DoDataExchange : Utiliser une variable dont le lien nexiste pas dans la fonction provoquera lerreur. ASSERT( IsWindow(m_hWnd) ); Ceci signifie que le handle de fentre nest pas initialis. Changer par mgarde lidentifiant dun contrle sans faire de mme dans la fonction provoquera aussi une erreur. Supprimer un contrle dans les ressources et garder le lien dans la fonction provoquera aussi une erreur.

Une fois ces initialisations faites on peut utiliser les deux formes de mise jour et rcupration de valeurs dans un contrle. Rcupration dune valeur sur un CEdit : Directement avec la fonction GetWindowText .

CString str ; m_EditNom.GetWindowText(str) ; // ou GetDlgItem(IDC_EDITNOM)->GetWindowText(str) ; Par la variable : UpdateData(TRUE) ; // mise jour des variables associes aux contrles

Affectation dune valeur un CEdit :


Directement avec la fonction SetWindowText : CString str = _T("Farscape") ; m_EditNom.SetWindowText(str) ; // ou GetDlgItem(IDC_EDITNOM)->SetWindowText(str) ; Par la variable : m_strNom=_T("Farscape") ; UpdateData(FALSE) ; // mise jour des contrles partir des variables associes.

52

Les lignes prcdentes ont mis en lumire une nouvelle fonction UpdateData. Rentrons un peu dans le dtail de cette fonction: Dfinition :
CWnd::UpdateData BOOL UpdateData( BOOL bSaveAndValidate = TRUE );

Cette fonction comme son nom lindique permet : La mise jour des variables attaches aux contrles lorsque bSaveAndValidate est gal TRUE . La mise jour des contrles depuis les variables lorsque bSaveAndValidate est gal FALSE. Jusque l rien dextraordinaire. Il faut quand mme savoir que ce mcanisme de mise jour dans les deux sens est assujetti une fonction essentielle dans la classe fentre o sont situs les contrles :
CWnd::DoDataExchange virtual void DoDataExchange( CDataExchange* pDX );

Cette fonction virtuelle sera gnre automatiquement par Visual lors de la gnration dune classe Fentre. Le code de conversion appropri sera plac automatiquement en fonction des types de variables que lutilisateur aura slectionns ; Les fichiers de dfinitions et sources tant bien sr mis jour automatiquement. Il existe d'autres formes d'changes : contrle vers entiers, string etc.. DoDataExchange sera appele chaque fois que UpdateData sera invoque, Lchange des donnes se fera par la fonction DDX_xxxx qui tablira le lien entre le numro didentit du contrle (IDC_) et sa variable. Dans le cas de la fonction DDX_Control dautres mcanismes sont mis en jeu.
// header afxdd_.h // for getting access to the actual controls void AFXAPI DDX_Control(CDataExchange* pDX, int nIDC, CWnd& rControl);

Explications complmentaires : Au premier UpdateData le contrle est subclass ,c'est--dire qu'on va intercepter les messages Windows destination du contrle (tous les processus par dfaut de Windows) pour lui donner un autre moteur de gestion des messages ,celui de lobjet mentionn dans la fonction DDX_Control . Ce travail est fait par la fonction CWnd ::SubclassWindow(HWND hWnd) qui fait appel elle-mme
LONG SetWindowLong( HWND hWnd, int nIndex, LONG dwNewLong);

Cest la fin de ce traitement dont je viens de rsumer les grandes lignes que la donne membre m_hWnd de la variable contrle sera affecte.

53

Conclusions : Toute variable contrle dclare dans une fentre dialogue (CDialog ou CFormView etc. ..) qui ne fera pas partie de la fonction DoDataExchange ne sera pas subclasse son handle de fentre sera gal NULL et son utilisation provoquera une assertion derreur. Toute variable contrle non prsente dans la fonction DoDataExchange ne sera pas affecte par laction de UpdateData. Pour subclasser un contrle manuellement on pourra utiliser la fonction SubClassDlgItem :
BOOL CAboutDlg::OnInitDialog() { CDialog::OnInitDialog(); // IDC_BUTTON1 is the ID for a button on the // dialog template used for CAboutDlg. m_myButton.SubclassDlgItem(IDC_BUTTON1, this); //

Pour placer dans la fonction DoDataExchange une variable dynamique utilisez la technique suivante :
void CBnqView::DoDataExchange(CDataExchange* pDX) { CMyFormView::DoDataExchange(pDX); DDX_Control(pDX, IDC_CEDITCDEBNQ, m_EditCdeBnq); // m_pEditDynamique mis null dans le constructeur et initialis dans OnInitialUpdate. if(m_pEditDynamique) { DDX_Control(pDX, IDC_CEDITDYN, *m_pEditDynamique); } DDX_Control(pDX, IDC_CEDITCDEGUICHET, m_EditCdeGuichet); DDX_Control(pDX, IDC_CEDITDOM, m_EditDom); //. }

L'appel la fonction DDX_Control se fera uniquement si le pointeur est diffrent de NULL.

54

La fonction dinitialisation de la vue OnInitialUpdate :


void CSampleSDIView::OnInitialUpdate() { CFormView::OnInitialUpdate(); GetParentFrame()->RecalcLayout(); ResizeParentToFit(); } Dans le cas dune vue cest la fonction virtuelle OnInitialUpdate qui est appele pour procder aux diffrentes mises jour utilisateur. La premire ligne appelle la mthode de la classe de base et procde donc aux initialisations des contrles (voir explications prcdentes). Les deux lignes qui suivent permettent dajuster la fentre au contenu. Pour mettre en pratique tout ce que nous avons dit, nous allons donner une valeur par dfaut certains contrles :
void CSampleSDIView::OnInitialUpdate() { CFormView::OnInitialUpdate(); GetParentFrame()->RecalcLayout(); ResizeParentToFit(); // initialisations manuelles: m_strVille=_T("Nice"); m_strCdp=_T("06000"); UpdateData(FALSE); //-> mise a jour des contrles depuis les variables. m_ButtonRaz.EnableWindow(FALSE);// dsactiver le bouton }

Rsultat aprs construction du programme :

55

Intercepter les vnements de linterface :


Je vais maintenant rajouter le code suivant : Permettre lenregistrement si tous les contrles sont renseigns. Ractivez le bouton RAZ si un des quatre premiers contrles est rempli Implmenter le traitement de la remise zro sur le bouton RAZ.

Intercepter le clic sur un bouton :


Dans lditeur de ressources sur le bouton Enregistrer faire clic droit option ajouter un gestionnaire dvnements

A gauche nous avons le type dvnement droite la classe ou sapplique lvnement. Validez le choix de la fonction propose.

56

Visual a gnr automatiquement la fonction de rponse dans la source :


BEGIN_MESSAGE_MAP(CSampleSDIView, CFormView) ON_BN_CLICKED(IDC_BUTTONOK, &CSampleSDIView::OnBnClickedButtonok) END_MESSAGE_MAP()
void CSampleSDIView::OnBnClickedButtonok() { // TODO : ajoutez ici le code de votre gestionnaire de notification de contrle UpdateData(TRUE); // mise a jour des donnes. // tableau des CString associes aux contrles CString *parString[]={&m_strNom,&m_strPrenom, &m_strAdresse2, &m_strAdresse, &m_strVille,&m_strCdp }; // tableau des identifiants correspondant UINT arnId[]={IDC_EDITNOM,IDC_EDITPRENOM, IDC_EDITADRESSE, IDC_EDITADRESSE1,IDC_EDITCDP,IDC_EDITVILLE }; for(int i=0;i<sizeof(parString)/sizeof(CString *);i++) { // si la chaine est vide on redonne la main en saisie au contrle en question. if(parString[i]->IsEmpty()) { GetDlgItem(arnId[i])->SetFocus(); return; } } if(AfxMessageBox(_T("confirmez l'enregistrement"),MB_YESNO|MB_ICONQUESTION)==IDYES) { // sauvegarde des donnes. } }

Reportez vous la documentation MSDN pour les classes et mthodes utilises:

57

Intercepter un message sur un contrle :


Nous allons maintenant intercepter un message gnr par le contrle dans la vue : Toujours dans les ressources, placez vous sur le contrle CEdit de saisie du nom et Faites clic droit Ajouter un gestionnaire dvnements.

Nous allons intercepter le message qui indique quun changement est arriv dans le contrle IDC_EDITNOM, validez le choix et procdez de mme pour les contrles : IDC_EDITPRENOM, IDC_EDITADRESSE, IDC_EDITADRESSE1 Pour effectuer ces modifications nous allons changez de mthode. Cliquez sur ALT+Entre pour faire apparaitre le panneau proprits, slectionnez le bouton vnements de contrle

58

Cliquez ensuite sur le message EN_CHANGE pour gnrer le message OnMajRAZButton. Pour gnrer le mme message pour les autres contrles il suffira de slectionner le contrle suivant dans la fentre des ressources. Visual va gnrer une fonction pour chaque contrle, cette notification je vais la rediriger sur une seule fonction de traitement OnMajRAZButton : La boucle des messages ressemblera donc :
BEGIN_MESSAGE_MAP(CSampleSDIView, CFormView) ON_BN_CLICKED(IDC_BUTTONOK, &CSampleSDIView::OnBnClickedButtonok) ON_EN_CHANGE(IDC_EDITNOM, &CSampleSDIView::OnMajRAZButton) ON_EN_CHANGE(IDC_EDITPRENOM, &CSampleSDIView::OnMajRAZButton) ON_EN_CHANGE(IDC_EDITADRESSE, &CSampleSDIView::OnMajRAZButton) ON_EN_CHANGE(IDC_EDITADRESSE1, &CSampleSDIView::OnMajRAZButton) END_MESSAGE_MAP()
void CSampleSDIView::OnMajRAZButton() { // TODO: S'il s'agit d'un contrle RICHEDIT, le contrle ne // envoyez cette notification sauf si vous substituez CFormView::OnInitDialog() // fonction et appelle CRichEditCtrl().SetEventMask() // avec l'indicateur ENM_CHANGE ajout au masque grce l'oprateur OR. // TODO: Ajoutez ici le code de votre gestionnaire de notification de contrle UINT arnId[]={IDC_EDITNOM,IDC_EDITPRENOM, IDC_EDITADRESSE, IDC_EDITADRESSE1,IDC_EDITCDP,IDC_EDITVILLE }; int nFull=0; CString str; for(int i=0;i<sizeof(arnId)/sizeof(UINT);i++) { GetDlgItem(arnId[i])->GetWindowText(str); //rcupration du texte dans le contrle nFull+=(!str.IsEmpty()); } // active ou dsactive le contrle si tous les contrles sont remplis ou vides. m_ButtonRaz.EnableWindow((nFull==sizeof(arnId)/sizeof(UINT))); }

Une question se pose, pourquoi ne pas avoir fait UpdateData(TRUE) ? UpdateData met jour tous les contrles et dans le contexte prsent, je suis dans le cas o pour chaque caractre saisi dans les dits concerns un message va tre envoy. On utilisera ce style dcriture chaque fois que lon dsire ne pas perturber les variables associes avec des valeurs intermdiaires. Continuons lexemple en implmentant la remise zro des contrles. Pour cela interceptez le message de clic sur le bouton RAZ

59

void CSampleSDIView::OnBnClickedButtonraz() { // TODO : ajoutez ici le code de votre gestionnaire de notification de contrle CString *parString[]={&m_strNom,&m_strPrenom, &m_strAdresse,&m_strAdresse2,&m_strVille,&m_strCdp }; for(int i=0;i<sizeof(parString)/sizeof(CString *);i++) parString[i]->Empty(); UpdateData(FALSE); // mise a jour des contrles. }

Implmenter une commande dune barre doutils


Pour continuer notre apprentissage sur la mise en place des traitements en rponse aux messages, nous allons voir comment traiter un clic sur un bouton de la barre doutils.

Celle-ci est dfinie dans lditeur de ressources dans la section Toolbar. Chaque bouton dispose dun identifiant. Double cliquez sur un des boutons pour voir le paramtrage. La ligne prompt correspond aux libells affichs respectivement sur la barre dtats (statusbar) et sur la bulle du bouton (tooltip). Les deux libells sont spars par un \n.

60

Mise en place du message :


Slectionnez longlet class view ou CTRL+Maj.+C, slectionnez ensuite la classe CSampleSDIView:

Je profite de loccasion pour vous indiquer quil est possible de retourner sur la fentre associe la classe partir du classview, il suffit de faire clic droit sur la classe et dappeler loption Go To Dialog

Tapez sur la combinaison de touche ALT+Entre pour faire apparaitre les proprits.

61

Et cliquez ensuite sur le bouton vnements:

Slectionnez ensuite lidentifiant ID_FILE_SAVE comme ci-dessus Dans la partie messages deux messages : COMMAND : traitement du clic UPDATE_COMMAND_UI : possibilit dautoriser ou dinterdire le traitement du message COMMANDE. Interceptez les deux messages. Je vais modifier mon code pour prendre en compte le traitement dj existant sur le bouton Enregistrer :
void CSampleSDIView::OnBnClickedButtonok() { // TODO : ajoutez ici le code de votre gestionnaire de notification de contrle if(CanSaveData(true) && AfxMessageBox(_T("confirmez l'enregistrement"),MB_YESNO|MB_ICONQUESTION)==IDYES) { // sauvegarde des donnes. } } bool CSampleSDIView::CanSaveData(bool bSetFocus/*=false*/) { UpdateData(TRUE); // mise a jour des donnes. // tableau des CString associes aux contrles CString *parString[]={&m_strNom,&m_strPrenom, &m_strAdresse,&m_strAdresse2,&m_strVille,&m_strCdp }; // tableau des identifiants correspondant UINT arnId[]={IDC_EDITNOM,IDC_EDITPRENOM, IDC_EDITADRESSE, IDC_EDITADRESSE1,IDC_EDITCDP,IDC_EDITVILLE }; for(int i=0;i<sizeof(parString)/sizeof(CString *);i++) { // si la chaine est vide if(parString[i]->IsEmpty()) { // on redonne la main en saisie au contrle en question. if(bSetFocus) GetDlgItem(arnId[i])->SetFocus(); return false; } } return true; }

62

void CSampleSDIView::OnMajRAZButton() { // TODO: Ajoutez ici le code de votre gestionnaire de notification de contrle UINT arnId[]={IDC_EDITNOM,IDC_EDITPRENOM, IDC_EDITADRESSE, IDC_EDITADRESSE1,IDC_EDITCDP,IDC_EDITVILLE }; int nFull=0; CString str; for(int i=0;i<sizeof(arnId)/sizeof(UINT);i++) { GetDlgItem(arnId[i])->GetWindowText(str); // rcupration du texte dans le contrle nFull+=(!str.IsEmpty()); } // active ou dsactive le contrle si tous les contrles sont remplis ou vides. m_ButtonRaz.EnableWindow((nFull==sizeof(arnId)/sizeof(UINT))); } void CSampleSDIView::OnBnClickedButtonraz() { // TODO : ajoutez ici le code de votre gestionnaire de notification de contrle CString *parString[]={&m_strNom,&m_strPrenom, &m_strAdresse,&m_strAdresse2,&m_strVille,&m_strCdp }; for(int i=0;i<sizeof(parString)/sizeof(CString *);i++) parString[i]->Empty(); UpdateData(FALSE); // mise a jour des contrles. } void CSampleSDIView::OnFileSave() { if(AfxMessageBox(_T("confirmez l'enregistrement"),MB_YESNO |MB_ICONQUESTION)==IDYES) { // sauvegarde des donnes. } } void CSampleSDIView::OnUpdateFileSave(CCmdUI *pCmdUI) { // TODO: Add your command update UI handler code here pCmdUI->Enable(CanSaveData()); }

pCmdUi->Enable permet dactiver ou de dsactiver le bouton en fonction du rsultat de la Mthode CanSaveData(). Ce code montre bien la convergence de traitements en fonction dlments dinterface diffrents mais reprsentant le mme traitement. Ici le bouton sur ma vue et le bouton sur la barre doutils. Il est noter que la ligne enregistrer du menu fichier est aussi traite, car partageant le mme Identifiant que le bouton de la barre doutils

Complments dinformations sur les contrles


Les Edits :
Un contrle CEdit peut tre multi lignes, il suffit pour cela de cocher dans les proprits du contrle loption multiline et want return pour la gestion du retour chariot. Les principales proprits dun dit sont rsumes ci-dessous :

63

64

On retrouve la notion dalignement, ainsi que : Le mode multi ligne. La saisie exclusive de nombre La gestion du scroll horizontal et vertical du texte. La saisie en mode mot de passe (avec des *). La mise en majuscule / minuscule. La possibilit de mettre ldit en lecture seule. Les boutons Radios : On utilisera les radios chaque fois que lon doit faire un choix unique parmi des options. Exemple : imaginons le traitement du rgime TVA dun client on aura par exemple France / Corse / Export / CEE . 1. tape : Dans lditeur de ressources sur la bote de dialogue concerne on placera le premier radio en spcifiant son nom : Dans mon exemple je le nommerai IDC_RADIOTVA son label France Ensuite ne pas oublier de cocher loption Tab Stop et surtout loption Group permettant de spcifier que lon commence un nouveau groupe et donc que les contrles suivants de mme nature en font partie. On place ensuite les autres radios et on laissera lditeur donner lID du radio quil incrmentera de manire automatique par rapport au premier radio. Si je dois placer un autre groupe de radios derrire il faudra juste spcifier sur le premier radio loption groupe pour distinguer les deux groupes. 2. tape : Pour connatre la valeur de la slection il faut attacher une variable uniquement sur le premier bouton radio du groupe. Pour cela on utilisera loption ajouter une variable disponible par un clic droit sur le contrle dans les ressources ou dans le panneau daffichage des classes (class view). La case cocher variable du contrle doit tre coche , lidentifiant du contrle (ici IDC_RADIOTVA) doit tre non gris. Spcifiez ensuite la catgorie du contrle value et le type de la variable int Il restera nommer la variable : Exemple m_nRegimeTVA . Et validez le choix par le bouton Terminer Lassistant : gnre automatiquement le code de dclaration dans la classe initialise la valeur dans le constructeur 1 place le code dchange des informations entre la variable et le contrle dans la fonction membre de la classe dialogue : DoDataExchange(CDataExchange* pDX). 3. tape : Pour donner une valeur de dpart laffichage de la fentre il suffira dinitialiser la variable dans la fonction InitDialog pour une boite de dialogue modale (CDialog) et OnInitialUpdate pour une classe CFormView :

65

m_nRegimeTVA=1 ; Ceci slectionnera le deuxime radio de la liste, lindexation commenant zro et finissant au nombre de bouton radio du groupe 1 dans mon exemple : 3. La valeur -1 voulant dire aucune valeur slectionne. Pour signifier sa valeur au contrle on utilisera la fonction UpdateData(FALSE) ; Pour la mise jour du contrle et UpdateData(TRUE) ; Pour rcuprer la valeur dans la variable. Note : UpdateData sapplique bien sur toutes les variables prsentes dans la fonction DoDataExchange. Autre cas: si la variable ne suffit pas et que lon dsire par exemple rcuprer le label du radio slectionn on peut procder comme suit. Toujours par rapport mon exemple : CString CMyDialog::GetRadioLabelForValue(UINT nId,int &rnValue) { CWnd *pRadio=NULL; UpdateData(TRUE); if(rnValue >=0) { pRadio=GetDlgItem(nId); for(int n=0;pRadio && n< rnValue;n++) pRadio=pRadio->GetWindow (GW_HWNDNEXT); } CString str; if(pRadio) pRadio->GetWindowText(str); return str; } CString str=GetRadioLabelForValue(IDC_RADIOTVA , m_nRegimeTVA);

66

Les ComboBox :
Leur utilisation est assez simple Mode de fonctionnement :

Elle supporte plusieurs modes de fonctionnement conditionns la valeur de la zone Type : Le mode Simple :

Une zone ddition et une zone fixe de choix rglable. Le mode Drop Down :

Une zone ddition et une liste droulante popup pour les options. Le mode Drop List :

67

Une liste droulante pour les options celles-ci ntant pas modifiables. Le rglage de la liste droulante : La taille par dfaut de la liste droulante correspond celle dun lment, donnant ainsi limpression quelle ne souvre pas. Son rglage se fait dans lditeur de ressources : Slectionner le contrle ComboBox et cliquer sur la flche de la liste, un rectangle montrant la hauteur de la liste apparat, il suffit alors de lui rgler sa taille avec la souris. Le remplissage dune CCombobox : Il peut se faire de deux manires : Les donnes sont fixes : On peut les rentrer directement au niveau des ressources :

Note : les valeurs doivent tre spares par des ;

68

Le remplissage est dynamique : Il suffira de remplir la CComboBox par sa mthode AddString . : Si elle na pas le style CBS_SORT la chane est insre en fin de liste. Cette fonction renvoie le numro ditem dans la liste box la premire dmarrant zro. CComboBox::AddString int AddString( LPCTSTR lpszString ); CString str; int nIndex; for (int i=0;i < 20;i++) { str.Format("Ma chane %d"), i); nIndex=MyComboBox.AddString( str ); }

69

Autre fonction utilisable: CComboBox::InsertString int InsertString( int nIndex, LPCTSTR lpszString ); Mme chose que AddString mais ici on prcise lemplacement avec l'argument nIndex. Si nIndex == -1 linsertion se fait en bout de liste. La slection dune ligne : CComboBox::SetCurSel int SetCurSel( int nSelect ); La valeur retourne est lindice de llment slectionn si la commande russit, Sinon elle renvoie CB_ERR pour indiquer une erreur. Si nSelect est gal -1 la slection en cours est supprime et le retour sera aussi CB_ERR. Exemple slection du dernier lment de la combo box. int nCount = MyComboBox.GetCount(); if (nCount > 0) MyComboBox.SetCurSel(nCount-1); Pour la slection en fonction dune chane de caractre utilisez SelectString qui est la combinaison de FindString et SetCurSel. Rcupration de la slection en cours : Rcupration de lindice de litem en slection : CComboBox::GetCurSel int GetCurSel( ) const; Rcupration dune chane pour un numro dindice : CComboBox::GetLBText int GetLBText( int nIndex, LPTSTR lpszText ) const; void GetLBText( int nIndex, CString& rString ) const; CString str ; int nIndex = MyComboBox.GetCurSel(); if(nIndex!=LB_ERR) MyComboBox.GetLBText(nIndex,str); AfxMessageBox(str); Suppression dune ligne : CComboBox::DeleteString int DeleteString( UINT nIndex ); for (int i=MyComboBox.GetCount()-1; i>=0 ;i--) { MyComboBox.DeleteString( i ); } // Ce mme code peut tre crit de la manire suivante : MyComboBox.ResetContent() ;

CComboBox::ResetContent void ResetContent( ); Supprime tous les lments de la combo box.

70

Les Listbox
Il est relativement ais de travailler avec une CListbox : Le plus simple tant de dclarer une variable de la classe CListBox attache au contrle et dutiliser directement les mthodes de cette classe. Traitements courants : Insrer des lments : CListBox::AddString int AddString( LPCTSTR lpszItem ); CString str; for (int i=0;i < 10;i++) { str.Format(_T("item string %d"), i); MyListBox.AddString( str ); } Rcuprer le texte dune ligne : On utilisera la mthode GetText : CListBox::GetText int GetText( int nIndex, LPTSTR lpszBuffer ) const; void GetText( int nIndex, CString& rString ) const; Exemple : rcuprer le texte de la ligne slectionne: CStrng str; int nIndex = myListBox.GetCurSel(); if((nIndex != LB_ERR)) myListBox.GetText( nindex,str ); AfxMessageBox(str); // affichage dans une bote de dialogue Supprimer une ligne : Si on ne dispose pas du numro index de la ligne il faudra la rechercher en utilisant la fonction FindString : CListBox::FindString int FindString( int nStartAfter, LPCTSTR lpszItem ) const; Exemple: nIndex = myListBox.FindString(0,"coucou"); if((nIndex != LB_ERR)) myListBox.DeleteString( nindex );

71

Autre exemple supprimer la ligne en cours de slection : En utilisant la fonction GetCurSel qui renvoie lindice courant slectionn. int nIndex = myListBox.GetCurSel(); if((nIndex != LB_ERR)) myListBox.DeleteString( nindex ); Slectionner le dernier lment : // Nombre d'lments dans la listbox int nCount = MyListBox.GetCount(); // si pas d'erreur listbox non vide , slection sur l'item nombre d'lments -1 // Puisque les indices commencent zro if (nCount > 0) MyListBox.SetCurSel(nCount-1); Dtruire tous les lments : On utilisera la mthode ResetContent( ) CListBox::ResetContent void ResetContent( ); // Dtruire tous les lments dune ClistBox MyListBox.ResetContent(); ASSERT(MyListBox.GetCount() == 0); Voila pour les principales fonctionnalits dune CListBox

72

Le Traitement des couleurs


Les contrles MFC ne grent pas les couleurs nativement dans lditeur de ressources comme a se fait en Visual Basic par exemple (qui utilise des Activex ). a ne veut pas dire pour autant que le traitement de la couleur est impossible, a ncessitera dintgrer du code en interceptant un message spcifique. Dans les MFC chaque contrle envoie un message au parent pour demander sa couleur. De ce fait si on veut grer les couleurs des contrles il suffit dintercepter le message WM_CTLCOLOR au niveau de la fentre parent, ce qui nest pas forcement pratique, puisque a oblige dfinir les couleurs au niveau de la fentre parent pour tous les contrles prsents. Avant les MFC 4.0 on navait pas le choix. Pour amliorer le traitement des couleurs Microsoft a ajout avec les MFC 4.0 la notion de message reflected qui permet de dfinir la couleur au niveau du contrle, ncessitant par la mme de faire une classe drive du contrle de base pour traiter le message. Cette technique permet la combinaison des deux mthodes, celle de la personnalisation au niveau du parent avec WM_CTLCOLOR et celle du message reflected au niveau du contrle, le message au niveau du contrle tant prfix dun = Pour la couleur on aura dans longlet proprits dans la catgorie messages = WM_CTLCOLOR sur une classe de type contrle Note : Au final il sera donc normal que le message WM_CTLCOLOR (simple sans le =) ne fonctionne pas au niveau du contrle. La fonction de rponse pour ce message sera : HBRUSH CtlColor(CDC* pDC, UINT nCtlColor)

73

Je propose ci-dessous un traitement plus volu car il permet grce la dfinition dune classe Template de grer les couleurs pour les contrles en une seule fois : Note : sa comprhension dpasse largement le niveau de connaissances acquises jusquici, il ne faut donc pas saffoler .
// include ///////////////////////////////////////////////////////////////////////////// // Classe Template attributs couleurs template <class GENERIC_CTRLCOLOR> class CTplCtrl : public GENERIC_CTRLCOLOR { // Construction public: CTplCtrl() { m_arClrCtlText[0]=::GetSysColor(COLOR_WINDOWTEXT); m_arClrCtlText[1]=RGB(0 ,0 ,255); // LtBlue m_arClrCtlText[2]=RGB(128,0,0); // Red. m_arClrCtlBkText[0]=::GetSysColor(COLOR_WINDOW); m_arClrCtlBkText[1]=::GetSysColor(COLOR_WINDOW); m_arClrCtlBkText[2]=::GetSysColor(COLOR_WINDOW); for(int i=0;i<3;i++) m_arHbrClrCtlBk[i]=::CreateSolidBrush(m_arClrCtlBkText[i]); } enum ModeColor { Normal, Disable, ReadOnly }; void SetBkColor(COLORREF clrCtlBk = RGB(192, 192, 192), // couleur de fond COLORREF clrCtlText = RGB(0, 0, 0), // couleur d'criture. ModeColor eMode=Normal) // mode actif/Inactif/lecture seule. { m_arClrCtlText[eMode]=clrCtlText; m_arClrCtlBkText[eMode]=clrCtlBk; if(m_arHbrClrCtlBk[eMode]) ::DeleteObject(m_arHbrClrCtlBk[eMode]); m_arHbrClrCtlBk[eMode] = ::CreateSolidBrush(clrCtlBk); if(m_hWnd) Invalidate(); } // Attributes public: HBRUSH m_arHbrClrCtlBk[3]; // brush de fond COLORREF m_arClrCtlBkText[3];// couleur du fond. COLORREF m_arClrCtlText[3]; // couleurs d'ecriture. // Operations public: virtual ~CTplCtrl() { for(int i=0;i<3;i++) if(m_arHbrClrCtlBk[i]) ::DeleteObject(m_arHbrClrCtlBk[i]);

74

}; afx_msg HBRUSH CtlColor(CDC* pDC, UINT nCtlColor) { bool bCEdit=IsKindOf(RUNTIME_CLASS(CEdit)); HBRUSH hbr=NULL; ModeColor eMode=Normal; if(GetStyle() & ES_READONLY) eMode=ReadOnly; if(!IsWindowEnabled()) eMode=Disable; // TODO: Change any attributes of the DC here pDC->SetTextColor(m_arClrCtlText[eMode]); // Fixe le fond en transparent pour le texte if(!bCEdit) pDC->SetBkMode(TRANSPARENT); else pDC->SetBkColor(m_arClrCtlBkText[eMode]); // retourne le handle de la brush pour le fond si il existe. if(m_arHbrClrCtlBk[eMode]) hbr = m_arHbrClrCtlBk[eMode]; // TODO: Return a different brush if the default is not desired return hbr; } virtual BOOL OnChildNotify( UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pLResult ) { // interception du message reflect if(message >= WM_CTLCOLORMSGBOX && message <= WM_CTLCOLORSTATIC) { UINT nCtlType = message - WM_CTLCOLORMSGBOX; ASSERT(nCtlType >= CTLCOLOR_MSGBOX); ASSERT(nCtlType <= CTLCOLOR_STATIC); CDC dcTemp; dcTemp.m_hDC = (HDC)wParam; HBRUSH hbr = CtlColor(&dcTemp, nCtlType); // fast detach of temporary objects dcTemp.m_hDC = NULL; *pLResult = (LRESULT)hbr; return TRUE; } return GENERIC_CTRLCOLOR::OnChildNotify( message,wParam, lParam,pLResult ); } };

75

Utilisation: class CSampleSDIView : public CFormView { protected: // create from serialization only CSampleSDIView(); DECLARE_DYNCREATE(CSampleSDIView) public: //{{AFX_DATA(CSampleSDIView) enum { IDD = IDD_SAMPLESDI_FORM }; CButton m_ButtonRAZ; CTplCtrl<CEdit> m_EditNom; On pourra aussi crire la chose suivante : Typedef CTplCtrl<CEdit> CEditEx; Et utiliser CEditEx comme nouvelle classe la place de CEdit. On pourra faire de mme pour un CStatic. Pour changer la couleur il suffira dappeler la fonction SetBkColor dans OnInitialUpdate, par exemple.

76

Les Botes de dialogue


Dfinition :
Toutes les applications Windows utilisent des boites de dialogues comme par exemple chaque fois que vous voulez sauvegarder, imprimer un document dans Word, une bote de dialogue apparat. Il existe deux types de bote de dialogue : les dialogues modales ou non modales. Une boite de dialogue modale empche lutilisateur de sortir ou daller sur une autre fentre tant quil na pas valid ou abandonn le traitement. Au contraire, une boite de dialogue non modale nest pas bloquante.

Application :
Nous allons dfinir une boite de dialogue modale avec un panel de contrles supplmentaire. Pour cela allez dans lditeur de ressources section dialogs et faire clic droit insert dialog. Je lai nomme IDD_DLGADVANCED

Les contrles utiliss pour lexemple sont : Trois cases cocher (Check Box) Deux boutons radio (radio button) Une Liste de slection (ListBox) Une boite de slection (Combo Box) Un curseur (Slider Ctrl)

77

Les cases cocher et les boutons radios sont entours dun contrle groupe (group box) Rglage de lordre de tabulation : (Ctrl+D)

Note : mettre loption groupe sur les boutons radio individuel et Anglais . Cration de la classe associe la bote de dialogue : Lappel de lassistant par un double clic sur une zone libre sur la bote de dialogue permet la cration de la classe associe :

78

Saisissez le nom de la classe : CDlgAdvanced puis valider sa cration par la touche Terminer . Continuons le paramtrage de notre classe en gnrant le message dinitialisation prvu pour une boite de dialogue : WM_INITDIALOG

79

Cliquez sur le slecteur pour rajouter la mthode OnInitDialog. Interceptez le clic sur le bouton IDOK

80

Interceptez le message BN_CLICKED sur le bouton radio :

Faites de mme avec le deuxime radio en donnant la mme fonction de rponse

OnBnClickedRadiosport.

81

Cration des variables attaches aux contrles :


Associez les variables suivantes leurs contrles respectifs
BOOL m_bCheckEnglish; BOOL m_bCheckItalien; BOOL m_bCheckEspagnol; CComboBox m_ComboSetSport; int m_nRadioSport; CSliderCtrl m_SliderPerf; CListBox m_ListBoxActivite;

Rajoutez dans le .h de la classe les variables :


CString CString int m_strSport; // pour le sport pratique m_strActivite; // pour l'activit m_nPerf; // note globale de 0 a 20

class CDlgAdvanced : public CDialog { DECLARE_DYNAMIC(CDlgAdvanced) public: CDlgAdvanced(CWnd* pParent = NULL); virtual ~CDlgAdvanced(); // constructeur standard

// Donnes de bote de dialogue enum { IDD = IDD_DLGADVANCED }; protected: virtual void DoDataExchange(CDataExchange* pDX); charge de DDX/DDV DECLARE_MESSAGE_MAP() public: virtual BOOL OnInitDialog(); afx_msg void OnBnClickedRadiosport(); void void combobox

// Prise en

InitComboSport(); // initialisation de la combobox SetComboSport(const TCHAR *szSport); // slection de la m_bCheckEnglish; m_bCheckItalien; m_bCheckEspagnol; m_ComboSetSport; m_nRadioSport; m_SliderPerf; m_ListBoxActivite; m_strSport; m_strActivite; m_nPerf; // pour le sport pratique // pour l'activit // note globale de 0 a 20

BOOL BOOL BOOL CComboBox int CSliderCtrl CListBox public: CString CString int };

82

Linitialisation des contrles :


// Gestionnaires de messages de CDlgAdvanced BOOL CDlgAdvanced::OnInitDialog() { CDialog::OnInitDialog(); // TODO: Ajoutez ici une initialisation supplmentaire TCHAR *aszActivite[]={_T("lycen"),_T("Etudiant"),_T("chmeur"),_T("Ouvrier"),_T("Cadre") ,_T("indpendant")}; m_SliderPerf.SetRange(0,20,TRUE); // fixer la plage de fonctionnement du sliderctrl for(int i=0;i<sizeof(aszActivite)/sizeof(TCHAR *);i++) m_ListBoxActivite.AddString(aszActivite[i]); int nIndex=m_ListBoxActivite.FindStringExact(0,m_strActivite); if(nIndex!=LB_ERR) m_ListBoxActivite.SetCurSel(nIndex); InitComboSport(); SetComboSport(m_strSport); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION : les pages de proprits OCX devraient retourner FALSE } void CDlgAdvanced::SetComboSport(const TCHAR *szSport) { int nIndex=m_ComboSetSport.FindStringExact(0,szSport); // recherche le libelle szSport if(nIndex!=LB_ERR) m_ComboSetSport.SetCurSel(nIndex); // slectionne la ligne dans la combobox } void CDlgAdvanced::InitComboSport() { UpdateData(TRUE); // mise a jour des datas m_ComboSetSport.ResetContent(); // efface le contenu de la combobox if(m_nRadioSport<=0) // chargement de la liste en fonction du radio. { TCHAR *aszSport[]={_T("Escrime"),_T("Natation"),_T("Course pied"),_T("Fitness"),_T("Autre")}; for(int i=0;i<sizeof(aszSport)/sizeof(TCHAR *);i++) m_ComboSetSport.AddString(aszSport[i]); } else { // sports collectifs ==1 TCHAR *aszSport[]={_T("Football"),_T("HandBall"),_T("VoleyBall"),_T("Fitness"),_T("Autre" )}; for(int i=0;i<sizeof(aszSport)/sizeof(TCHAR *);i++) m_ComboSetSport.AddString(aszSport[i]); } } void CDlgAdvanced::OnBnClickedRadiosport() { // TODO : ajoutez ici le code de votre gestionnaire de notification de contrle InitComboSport(); m_strSport=""; m_ComboSetSport.SetCurSel(0); }

83

Dans la fonction InitDialog jinitialise :

La listbox en insrant les libells et en slectionnant la ligne avec le libell affect m_strActivite. La combobox avec la fonction InitComboSport, puis la slection de la ligne est faite en fonction du contenu de la variable m_strSport avec la fonction SetComboSport

Le fait de cliquer sur un des boutons radios permet le changement dynamique de la combobox avec la fonction InitComboSport() (voir mthode OnBnClickedRadiosport). Traitement de lacceptation de la Bote de dialogue : Le traitement est effectu dans la mthode OnBnClickedOk : Il suffira dappeler UpdateData(TRUE) pour affecter les variables avec les diffrentes valeurs choisies.
void CDlgAdvanced::OnBnClickedOk() { // TODO : ajoutez ici le code de votre gestionnaire de notification de contrle UpdateData(TRUE); int nIndexCombo=m_ComboSetSport.GetCurSel(); if(nIndexCombo==LB_ERR) return ; // pas de selection == pas bon int nIndexList =m_ListBoxActivite.GetCurSel(); if(nIndexList==LB_ERR) return ; // pas de selection == pas bon // Rcupration des libells slectionns. m_ComboSetSport.GetLBText(nIndexCombo,m_strSport); m_ListBoxActivite.GetText(nIndexList,m_strActivite); OnOK(); }

On notera lutilisation des mthodes GetLBText et GetText pour la CComboBox et la CListBox afin de rcuprer le libell en cours de slection. Si une des deux slections nest pas valide le traitement dacceptation de la bote de dialogue est refus, empchant sa fermeture.

84

Implmentation de lappel de la bote de dialogue dans la vue : Rajouter un bouton proprits avances dans la vue et interceptez le message BN_CLICKED :

Rajoutez : #include "DlgAdvanced.h" dans le source de la classe CSampleSDIView. Appel de la boite de dialogue : Il ne reste plus qu implmenter lappel de notre bote de dialogue dans la vue:
void CSampleSDIView::OnBnClickedButtonadvanced() { // TODO: Add your control notification handler code here CDlgAdvanced Dlg; Dlg.m_nRadioSport=0; Dlg.m_strActivite="Etudiant"; Dlg.m_strSport="Course pied"; Dlg.m_nPerf=10; // 10/20 Dlg.m_bCheckEnglish=1; if(Dlg.DoModal()==IDOK) { // sauvegarde des lments ( faire) . afxDump << "Sport " << Dlg.m_strSport <<"\n"; afxDump << "Activite " << Dlg.m_strActivite << "\n"; afxDump << "anglais " << Dlg.m_bCheckEnglish <<"\n"; afxDump << "italien " << Dlg.m_bCheckItalien <<"\n"; afxDump << "Espagnol " << Dlg.m_bCheckEspagnol <<"\n"; afxDump << "Performance " << Dlg.m_nPerf <<"\n"; } }

85

Le rsultat :

La bote de dialogue est dclare localement la mthode OnBnClickedButtonadvanced Les diffrentes valeurs sont initialises avant lappel de la mthode DoModal qui lance la bote de dialogue. Cette mthode renverra en sortie de traitement soit la valeur IDOK ou IDCANCEL. En cas de succs il ne reste plus qu sauvegarder les valeurs rcupres grce la mthode OnBnClickedOk.

86

Gnralits sur les botes de dialogues


Une boite de dialogue modale pourra tre utilise chaque fois que lon dsire que le traitement propos soit exclusif c'est--dire que lutilisateur ne puisse faire autre chose dans le programme cet instant. Les premires oprations sur les contrles doivent tre faites dans la fonction OnInitDialog aprs la fonction qui appelle la mthode de la classe de base .CDialog :: OnInitDialog qui les initialisera graphiquement Toutes tentatives daccs avant cette initialisation se solderont par une assertion derreur. Aprs DoModal les contrles nexistent plus, une tentative daccs sur les contrles provoquera aussi une assertion derreur. La sauvegarde des informations dans des variables doit tre faite avant destruction de la bote de dialogue, la mthode virtuelle OnOk ou celle associe par lassistant au bouton IDOK est lendroit appropri pour ce traitement. Les boites de dialogues supportent mal la rutilisation du mme objet, il est prfrable dutiliser un objet unique comme dans notre exemple o lobjet tait local la fonction. Aspect qui peut tre gnant : lutilisation de la touche entre ou chappement provoque la fermeture de la bote de dialogue et la valeur IDOK ou IDCANCEL est envoye. Lors de lappui sur la touche entre le bouton disposant de la proprit : bouton par dfaut est appel :

87

Pour intercepter la touche entre ou chappement on peut procder comme suit : on surchargera la mthode OnCommand de la classe CWnd: CWnd::OnCommand virtual BOOL OnCommand(WPARAM wParam,LPARAM lParam );
BOOL CDlgAdvanced::OnCommand(WPARAM wParam, LPARAM lParam) { // TODO: Add your specialized code here and/or call the base class CWnd *pWnd = GetFocus(); switch(wParam) { case IDOK: if(pWnd!=GetDlgItem(IDOK)) { return FALSE; } break; case IDCANCEL:if(pWnd && pWnd!=GetDlgItem(IDCANCEL)) { return FALSE; } break; } return CDialog::OnCommand(wParam, lParam); } void CDlgAdvanced::OnSysCommand(UINT nID, LPARAM lParam) { // TODO: Add your message handler code here and/or call default if(nID==SC_CLOSE) { ::SetFocus(NULL); } CDialog::OnSysCommand(nID, lParam); }

Le code ci-dessus permet dinterdire la fermeture de la fentre par la touche entre si le contrle possdant le focus nest pas le bouton IDOK. Mme chose avec la touche Echappement, la fentre ne pourra pas se fermer si le bouton possdant le focus nest pas le bouton IDCANCEL. Notes : Lors de la fermeture de la fentre par la croix le message IDCANCEL sera gnr. Par rapport la version prcdente de mon tutoriel jai rajout le traitement avec OnSysCommand pour Windows Vista. En effet lors de la fermeture par la croix le focus nest pas donn au bouton dabandon. Mon traitement permet la dtection de la fermeture par la croix et de forcer le focus Null, ce qui donnera une valeur NULL pWnd dans OnCommand.

88

La gestion des couleurs :


Mme remarque que pour les contrles, la classe dialogue ne gre par la couleur de fond. Pour remdier ce problme il faut intercepter le message WM_ERASEBKGND
BOOL CDlgAdvanced::OnEraseBkgnd(CDC* pDC) { // TODO: Add your message handler code here and/or call default CBrush backBrush(m_crBackColor);//COLORREF CBrush *pOldBrush=pDC->SelectObject(&backBrush); CRect rect; pDC->GetClipBox(&rect); pDC->PatBlt(rect.left,rect.top,rect.Width(),rect.Height(),PATCOPY); pDC->SelectObject(pOldBrush); return TRUE; // return CDialog::OnEraseBkgnd(pDC); }

La variable m_crBackColor doit tre dclare dans la classe avec le type COLORREF. On initialisera sa valeur en utilisant la macro RBG dans la mthode OnInitDialog : Exemple : m_crBackColor=RGB(187,221,255); RGB voulant dire rouge, vert, bleu, un moyen pour visualiser les couleurs : utiliser dans MSPaint la fonction modification des couleurs. Le traitement de la couleur des contrles : Il est tout fait possible au niveau de la bote de dialogue de grer en une seule fois la couleur des contrles prsent. Il existe une fonction au niveau de la classe dapplication permettant de grer pour toute lapplication la couleur de fond et la couleur dcriture du texte des contrles : CWinApp::SetDialogBkColor void SetDialogBkColor( COLORREF clrCtlBk = RGB(192, 192, 192), COLORREF clrCtlText = RGB(0, 0, 0) );

89

Mais cette mthode ne permet pas un changement dynamique par bote de dialogue, donc voici une autre mthode. Il faut implmenter le message WM_CTLCOLOR sur la CDialog Appliquer notre exemple a donne le code suivant :
class CDlgAdvanced : public CDialog { DECLARE_DYNAMIC(CDlgAdvanced) public: CDlgAdvanced(CWnd* pParent = NULL); virtual ~CDlgAdvanced(); // Donnes de bote de dialogue enum { IDD = IDD_DLGADVANCED }; protected: virtual void DoDataExchange(CDataExchange* pDX); charge de DDX/DDV DECLARE_MESSAGE_MAP() public: virtual BOOL OnInitDialog(); afx_msg void OnBnClickedRadiosport(); void void combobox void InitComboSport(); // initialisation de la combobox SetComboSport(const TCHAR *szSport); // slection de la SetDialogBkColor(COLORREF clrCtlBk= RGB(192, 192, 192), COLORREF clrCtlText = RGB(0, 0, 0)); m_bCheckEnglish; m_bCheckItalien; m_bCheckEspagnol; m_ComboSetSport; m_nRadioSport; m_SliderPerf; m_ListBoxActivite; m_nPerf; // note globale de 0 a 20 m_strSport; m_strActivite; m_crBackColor, m_ClrCtlText; m_HbrClrCtlBk; void OnBnClickedOk(); BOOL OnCommand(WPARAM wParam, LPARAM lParam); void OnSysCommand(UINT nID, LPARAM lParam); BOOL OnEraseBkgnd(CDC* pDC); HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); // pour le sport pratique // pour l'activit // Prise en // constructeur standard

BOOL BOOL BOOL CComboBox int CSliderCtrl CListBox int public: CString CString COLORREF HBRUSH afx_msg protected: virtual public: afx_msg afx_msg afx_msg };

90

CDlgAdvanced::CDlgAdvanced(CWnd* pParent /*=NULL*/) : CDialog(CDlgAdvanced::IDD, pParent) , m_bCheckEnglish(FALSE) , m_bCheckItalien(FALSE) , m_bCheckEspagnol(FALSE) , m_nRadioSport(0) , m_nPerf(0) ,m_crBackColor(RGB(192, 192, 192)) ,m_HbrClrCtlBk(NULL) { } CDlgAdvanced::~CDlgAdvanced() { if(m_HbrClrCtlBk) ::DeleteObject(m_HbrClrCtlBk); } // Gestionnaires de messages de CDlgAdvanced BOOL CDlgAdvanced::OnInitDialog() { CDialog::OnInitDialog(); SetDialogBkColor(RGB(187,221,255)); // TODO: Ajoutez ici une initialisation supplmentaire TCHAR *aszActivite[]={_T("lycen"),_T("Etudiant"),_T("chmeur"),_T("Ouvrier"),_T("Cadre") ,_T("indpendant")}; //.. return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION : les pages de proprits OCX devraient retourner FALSE } void CDlgAdvanced::SetDialogBkColor(COLORREF clrCtlBk, COLORREF clrCtlText) { //m_HbrClrCtlBk est null dans le constructeur if(m_HbrClrCtlBk) ::DeleteObject(m_HbrClrCtlBk); m_HbrClrCtlBk = ::CreateSolidBrush(clrCtlBk); m_ClrCtlText = clrCtlText; m_crBackColor = clrCtlBk; if(m_hWnd) Invalidate(); } BOOL CDlgAdvanced::OnEraseBkgnd(CDC* pDC) { // TODO: Add your message handler code here and/or call default CBrush backBrush(m_crBackColor);//COLORREF CBrush *pOldBrush=pDC->SelectObject(&backBrush); CRect rect; pDC->GetClipBox(&rect); pDC->PatBlt(rect.left,rect.top,rect.Width(),rect.Height(),PATCOPY); pDC->SelectObject(pOldBrush); return TRUE; // return CDialog::OnEraseBkgnd(pDC); } HBRUSH CDlgAdvanced::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); /* // TODO: Change any attributes of the DC here CTLCOLOR_BTN button control CTLCOLOR_DLG dialog box CTLCOLOR_EDIT edit control CTLCOLOR_LISTBOX list box CTLCOLOR_MSGBOX message box

91

CTLCOLOR_SCROLLBAR scroll bar CTLCOLOR_STATIC static text, frame, or rectangle */ // TODO: Change any attributes of the DC here // par exemple en fonction de nCtlColor voir doc. switch(nCtlColor) { // Intercepte le message pour es statics //case CTLCOLOR_DLG: dj trait dans OnEraseBkgnd case CTLCOLOR_STATIC : // Fixe la couleur d'ecriture du texte pDC->SetTextColor(m_ClrCtlText); // enventuellement suivant les cas // pDC->pDC->SetBkColor(m_ClrCtlBk); // Fixe le fond en transparent pour le texte // ne pas faire pour un edit. pDC->SetBkMode(TRANSPARENT); // retourne le handle de la brush pour le fond si il existe. if(m_HbrClrCtlBk ) hbr = m_HbrClrCtlBk; break; } // TODO: Return a different brush if the default is not desired return hbr; }

Dans la mthode OnInitDialog on mettra la ligne suivante : SetDialogBkColor(RGB(187,221,255)); Le mme code fonctionne avec une CFormView Rsultat :

Note : Avec les Nouvelles MFC, on dispose dune classe CDialogEx qui prend en charge la gestion de la couleur du fond ou de laffichage dune image dans le fond. Ces mthodes se nomment respectivement : SetBackgroundColor et SetBackgroudImage

92

La srialisation des donnes


Dernire tape de notre projet : sauvegarder les donnes en utilisant le modle de lecture/sauvegarde propos par visual.

La lecture des donnes :


Lors de la gnration du projet visual a gnr automatiquement la commande douverture de fichier avec la mthode CWinApp(Ex)::OnFileOpen :
// CSampleSDIApp BEGIN_MESSAGE_MAP(CSampleSDIApp, CWinAppEx) ON_COMMAND(ID_APP_ABOUT, &CSampleSDIApp::OnAppAbout) // Commandes de fichier standard ON_COMMAND(ID_FILE_NEW, &CWinAppEx::OnFileNew) ON_COMMAND(ID_FILE_OPEN, &CWinAppEx::OnFileOpen) END_MESSAGE_MAP()

Cette fonction permet la slection dun fichier Elle appelle la fonction CDocument ::OnOpenDocument pour lobjet document
en cours . La fonction virtuelle CDocument ::DeleteContents est appele pour la libration dobjets du document en cours. Si vous devez librer des objets existants dans la vue ou le document cest dans cette fonction quil faudra le faire et non dans le destructeur de la vue. Celui-ci sera rserv pour la destruction dobjets non concerns par louverture dun fichier de donnes. Un objet de srialisation CArchive est cre en mode lecture et la fonction serialize du document est appele. La fonction OnInitialUpdade de la vue est appele.

93

La sauvegarde des donnes:


Il faudra intercepter la commande OnFileSave de la classe Document, qui appelle OnSaveDocument de CDocument qui appel son tour la fonction serialize du document avec un objet CArchive initialis pour la sauvegarde. La notification de modification de donnes : La classe document dispose dune variable indiquant si les donnes utilisateur ont t modifies. Lorsque lutilisateur ferme lapplication une bote de dialogue demande si on dsire sauvegarder les donnes. Cette variable est gre par les mthodes : CDocument::SetModifiedFlag void SetModifiedFlag( BOOL bModified = TRUE ); et la mthode : CDocument::IsModified BOOL IsModified( ); SetModifiedFlag permet de spcifier que les donnes sont modifies ou non. IsModified permet de connatre ltat des donnes. Avant de mettre ces notions en application dans notre projet quelques explications simposent : Les MFC fournissent des classes de traitement de fichier : La classe CFile qui utilise directement les apis 32 Windows et qui fournit les oprations de base pour la gestion dun fichier. La classe CStdioFile qui correspond la fonction du C fopen. Et enfin pour grer la srialisation de donnes nous avons la classe CArchive qui utilise avec un objet CFile permet larchivage de donnes et dobjets.

La notion darchivage dobjets est trs puissante et correspond la notion de marshalling de donnes, c'est--dire la facult de transformer ce qui nest pas directement enregistrable dans un fichier en une suite doctets, le mcanisme de lecture devant tre capable du processus inverse c'est--dire la reconstruction de lobjet. Pour bien comprendre la puissance de ce mcanisme je vous propose un petit dtour pour illustrer ce concept :

94

Srialisation de collection dobjets en mmoire :


Les classes MFC proposent des objets permettant de maintenir des collections dobjets en mmoire. Classiquement on retrouvera des objets grant des tableaux, des listes, et des maps. On trouve dans les MFC deux gnrations de ces objets : CObArray , CObList, CMapPtrToWord Dclins avec des string, word, int, etc.. CArray, CList ,CMap (avec des classes templates) il est prfrable dutiliser ces classes templates en remplacement des prcdentes. Lexemple qui suit : Dclare un objet srialiser Dfinit un tableau de cet objet. Effectue son remplissage, sa sauvegarde et sa lecture avec la classe CArchive. // header .................. // la classe qui contient les donnes class CItem : public CObject { public: DECLARE_SERIAL( CItem ) CItem(){Clear();} //----------------------------~CItem(){} //----------------------------// constructeur de copie CItem(const CItem &rItem) { CopyFrom(rItem); } //----------------------------// operateur d'affectation const CItem& operator=(const CItem& Src) { CopyFrom(Src); return *this; } //----------------------------// effacer les donnes. void Clear() { m_strNom=""; m_strPrenom=""; m_strCdp=""; m_strVille="";

95

} //----------------------------// la methode de serialisation. void Serialize(CArchive& ar) { if(ar.IsStoring()) ar << m_strNom << m_strPrenom << m_strCdp <<m_strVille; else ar >> m_strNom >> m_strPrenom >> m_strCdp >> m_strVille; } //----------------------------// copier les donnes d'une source. void CopyFrom(const CItem & Src ) { if(this==&Src) return; Clear(); // clear eventuel. m_strNom =Src.m_strNom; m_strPrenom =Src.m_strPrenom; m_strCdp =Src.m_strCdp; m_strVille =Src.m_strVille; } //----------------------------// dump pour test CString GetStrDump() { return ( m_strNom + "/" +m_strPrenom + "/" + m_strCdp +"/" + m_strVille); } // les donnes. CString m_strNom; CString m_strPrenom; CString m_strCdp; CString m_strVille; }; // la gestion d'un tableau de cette classe . typedef CArray<CItem,CItem&> CArrayItem; // Source......................... //--------------------------------------------------------------------------------------// dfinition de la mthode de srialisation de l'objet CItem pour le template CArray. template <> static void AFXAPI SerializeElements <CItem> ( CArchive& ar, CItem* pItem, int nCount ) { for ( int i = 0; i < nCount; i++, pItem++ ) pItem->Serialize( ar ); } IMPLEMENT_SERIAL( CItem, CObject, 0) void Test() { // un lment

96

CItem item; item.m_strCdp="06800"; item.m_strNom="farscape"; item.m_strPrenom="???"; item.m_strVille="Nice"; // un tableau de l'lment CArrayItem arItem; // remplissage. arItem.Add(item); for(int i=0;i<10;i++) { item.m_strNom.SetAt(0,'A'+i); arItem.Add(item); } // archivage. { CFile File; if(File.Open("MyArchive.arc", CFile::modeCreate | CFile::modeWrite )) { CArchive ar( &File, CArchive::store); arItem.Serialize(ar); } } // Lecture de l'archive. arItem.RemoveAll(); CFile File; if(File.Open("MyArchive.arc", CFile::modeRead )) { CArchive ar( &File, CArchive::load); arItem.Serialize(ar); } // verification du contenu. for(i=0;i<arItem.GetSize();i++) { AfxMessageBox(arItem[i].GetStrDump()); } }

97

Nous avons vu que les MFC fournissent une mthode de lecture/criture des donnes grce au modle document vue. Ce modle va jusqu' la description de lextension du fichier gr par lapplication. En fait, le type de fichier est gr par identifiant de mainframe :

Le paramtrage du type de fichier est renseigner au moment de la gnration du programme dans les proprits avances. A chaque nIDResource associe un document template on trouve dans lditeur de ressources dans la stringtable la chane douverture pour le document. Exemple: Pour lid IDR_MAINFRAME nous avons : SampleSDI\n\nSampleSDI\nSampleSDI Files (*.sdi)\n.sdi\nSampleSDI.Document\nSampleSDI.Document Description: IDR_MAINFRAME <windowTitle>\n<docName>\n<fileNewName>\n <filterName>\n <filterExt>\n<regFileTypeID>\n <regFileTypeName>\n <filterMacExt(filterWinExt)>\n <filterMacName(filterWinName)> Mon filtre fichier l'ouverture correspondra : Fichiers *.SDI

98

Enregistrement de lextension de fichier dans la base de registre :


Comment faire pour que lorsque lon double-clique sur le document gr par notre application, celle-ci sexcute ? Il faut enregistrer lextension du fichier dans la base de registre. Pour un programme MFC il y a une mthode qui fait ce travail : void RegisterShellFileTypes( BOOL bCompat = FALSE ); La valeur bCompat a TRUE permet linsertion des entres impression et impression sur dans la base de registre ainsi que le slectionner glisser lcher du document sur une imprimante. Cette fonction est insre derrire le dernier AddocTemplate dans la mthode InitInstance de la classe dapplication. La mthode EnableShellOpen doit tre appele.
// Inscrire les modles de document de l'application. Ces modles // lient les documents, fentres frame et vues entre eux CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate( IDR_MAINFRAME, RUNTIME_CLASS(CSampleSDIDoc), RUNTIME_CLASS(CMainFrame), // fentre frame SDI principale RUNTIME_CLASS(CSampleSDIView)); if (!pDocTemplate) return FALSE; AddDocTemplate(pDocTemplate); // Activer les ouvertures d'excution DDE EnableShellOpen(); RegisterShellFileTypes(TRUE) ;

99

Mise en place du code de lecture criture de nos donnes :


Nous allons commencer par mettre en place des donnes supplmentaires pour sauvegarder la valeur des contrles de notre bote de dialogue :
public: afx_msg void OnBnClickedButtonok(); // variable associe au nom CString m_strNom; CString m_strPrenom; CString m_strAdresse; CString m_strAdresse2; CString m_strCdp; CString m_strVille; CEdit m_EditNom; CButton m_ButtonRaz; CString m_strSport; // sport CString m_strActivite; //Activite BOOL m_bCheckEnglish; //anglais BOOL m_bCheckItalien; //italien BOOL m_bCheckEspagnol; //Espagnol int m_nPerf; //Performance void Serialize(CArchive& ar);

Ajouter une methode de sauvargarde dans notre vue :


void CSampleSDIView::Serialize(CArchive& ar) { // sauvegarde if (ar.IsStoring()) { ar << m_strNom << m_strPrenom << m_strVille << m_strCdp << m_strAdresse <<m_strAdresse2; ar << m_strSport << m_strActivite << m_bCheckEnglish << m_bCheckItalien << m_bCheckEspagnol << m_nPerf; } else { // lecture ar >> m_strNom >> m_strPrenom >> m_strVille >> m_strCdp >> m_strAdresse >> m_strAdresse2; ar >> m_strSport >> m_strActivite >> m_bCheckEnglish >> m_bCheckItalien >> m_bCheckEspagnol >> m_nPerf; } }

100

Modifier le code dappel de notre bote de dialogue :


void CSampleSDIView::OnBnClickedButtonadvanced() { // TODO: Add your control notification handler code here CDlgAdvanced Dlg; Dlg.m_strSport Dlg.m_strActivite Dlg.m_bCheckEnglish Dlg.m_bCheckItalien Dlg.m_bCheckEspagnol Dlg.m_nPerf =m_strSport; =m_strActivite; =m_bCheckEnglish; =m_bCheckItalien; =m_bCheckEspagnol; =m_nPerf;

if(Dlg.DoModal()==IDOK) { // sauvegarde des lments ( faire) . #ifdef _DEBUG afxDump << "Sport " << Dlg.m_strSport <<"\n"; afxDump << "Activite " << Dlg.m_strActivite << "\n"; afxDump << "anglais " << Dlg.m_bCheckEnglish <<"\n"; afxDump << "italien " << Dlg.m_bCheckItalien <<"\n"; afxDump << "Espagnol " << Dlg.m_bCheckEspagnol <<"\n"; afxDump << "Performance " << Dlg.m_nPerf <<"\n"; #endif m_strSport =Dlg.m_strSport; m_strActivite =Dlg.m_strActivite; m_bCheckEnglish =Dlg.m_bCheckEnglish; m_bCheckItalien =Dlg.m_bCheckItalien; m_bCheckEspagnol =Dlg.m_bCheckEspagnol; m_nPerf =Dlg.m_nPerf; GetDocument()->SetModifiedFlag(); // pour notifier qu'il y a eu une modifications des donnes. } }

101

Modifiez la mthode OnFileSave associe au bouton Enregistrer comme suit :


void CSampleSDIView::OnFileSave() { GetDocument()->SetModifiedFlag(); AfxGetApp()->SaveAllModified(); /* if(AfxMessageBox(_T("confirme l'enregistrement"),MB_YESNO |MB_ICONQUESTION)==IDYES) { // sauvegarde des donnes. }*/ }

Et enfin modifier la fonction de srialisation de la classe Document :


void CSampleSDIDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) { // TODO : ajoutez ici le code de stockage } else { // TODO : ajoutez ici le code de chargement } POSITION pos = GetFirstViewPosition(); CSampleSDIView* pView = static_cast<CSampleSDIView *>(GetNextView(pos)); if(pView) pView->Serialize(ar); }

Noubliez pas de rajouter #include "SampleSDIView.h" dans votre source. Jai redirig la srialisation du document sur la fonction dfinie dans notre vue. Il ne reste plus qu essayer. Autre test : Modifiez le chemin de recherche de Windows pour que votre excutable (en release) y soit notifi, et double cliquez sur un fichier gnr par notre application travers lexplorateur windows. Le programme doit souvrir Notes : La srialisation des donnes MFC se fait dans un format binaire, le fichier rsultant ne peut tre dit ou modifi par notepad. Dans le cas ou vous voudriez faire de la srialisation XML je vous invite consulter mon tutoriel Utilisation de Boost.serialize pour srialiser des classes MFC Ce chapitre clture lutilisation classique et basique des MFC, nous allons passer maintenant la dcouverte des nouvelles fonctionnalits apportes par le SP1 de Visual Studio 2008.

102

Les Nouvelles classes MFC


Comme je lai indiqu en prambule, avec Visual studio 2008 SP1 nous disposons de nouvelles fonctionnalits intgres aux MFC. Dans les pages qui suivent, je vous propose de dcouvrir quelques lments de cette nouvelle bibliothque.

La Programmation du ruban (Ribbon bar)


La programmation du ruban nest pas trs complique mais le nombre de lignes de code pourvoir est assez imposant. Pour se faire une ide du sujet, consultez la mthode InitializeRibbon() de la classe CMainFrame de notre application. Deux classes essentielles : CMFCRibbonCategory : pour la cration dun onglet. La catgorie permet de spcifier une liste de bitmap qui pourra tre utilise par les panneaux. Dans chaque onglet, on peut insrer des panneaux avec la classe : CMFCRibbonPanel. Une fois le panneau cr, on ajoute les diffrents composants. Ceux-ci doivent disposer dun identifiant unique comme une commande de menu. Dailleurs il faudra placer ces identifiants dans un menu afin de pouvoir gnrer un message de rponse, ce qui sera utile pour rcuprer les valeurs aprs slection. Un panneau peut avoir un bouton daccs aux proprits. Celui-ci doit tre associ un identifiant de menu. Dans mon exemple, tous les messages de rponses seront situs dans la FormView de notre application. Aprs ce bref rsum des fonctionnalits, passons la pratique. Ajout dune Catgorie : Avant dattaquer la programmation du ruban, je vais rajouter dans la vue principale un dit me permettant de renseigner lge. Ldit sera associ un contrle CSpinCtrl comme sur lcran ci-dessous :

103

Rglez bien les proprits du contrle de dfilement comme sur limage. Autre dtail, il faut rgler lordre de tabulation pour que le contrle soit plac juste aprs le contrle dit pour saisir lge. Ceci fait, passons au ruban : Notre application dispose dun onglet accueil, nous allons crer une nouvelle catgorie identit Dans la mthode InitializeRibbon() de la classe CMainFrame jai rajout les lignes suivantes:
bNameValid = strTemp.LoadString(IDS_RIBBON_STATUSBAR); ASSERT(bNameValid); CMFCRibbonButton* pBtnStatusBar = new CMFCRibbonCheckBox(ID_VIEW_STATUS_BAR, strTemp); pPanelView->Add(pBtnStatusBar); bNameValid = strTemp.LoadString(IDS_RIBBON_CAPTIONBAR); ASSERT(bNameValid); CMFCRibbonButton* pBtnCaptionBar = new CMFCRibbonCheckBox(ID_VIEW_CAPTION_BAR, strTemp); pPanelView->Add(pBtnCaptionBar); // Ajouter la catgorie "identit" : bNameValid = strTemp.LoadString(IDS_RIBBON_IDENTITY); ASSERT(bNameValid); CMFCRibbonCategory* pCategoryIdentity = m_wndRibbonBar.AddCategory(strTemp, IDB_WRITESMALL, IDB_WRITELARGE); // rajoute la panneau signaltique strTemp.LoadString(IDS_RIBBON_SIGNALETIQUE); CMFCRibbonPanel* pSignaletique = pCategoryIdentity->AddPanel(strTemp, m_PanelImages.ExtractIcon (7)); // specifie un label au panneau strTemp.LoadString(IDS_RIBBON_SIGNALETIQUE_LABEL); pSignaletique->Add(new CMFCRibbonLabel(strTemp)); // rajoute un edit sur le nom. strTemp.LoadString(IDS_RIBBON_SIGNALETIQUE_NAME); CMFCRibbonEdit* pEditTemp = new CMFCRibbonEdit(ID_IDENTITY_NAME, 72,strTemp);

104

pSignaletique->Add(pEditTemp); // le dernier argument 3, represente le numero de l'image specifie dans la creation de la category avec IDB_WRITESMALL, IDB_WRITELARGE strTemp.LoadString(IDS_RIBBON_SIGNALETIQUE_AGE); pEditTemp = new CMFCRibbonEdit(ID_IDENTITY_AGE, 72,strTemp, 3); // active le spinbutton pEditTemp->EnableSpinButtons(1, 100); // valeur initiale de l'edit pEditTemp->SetEditText(_T("20")); pSignaletique->Add(pEditTemp); // cette ligne est importante, quand elle est absente l'edit n'est pas actif... // en gros sans justification des colonnes ,certains controles sont inactifs. // c'est un constat je n'ai pas plus d'informations la dessus. pSignaletique->SetJustifyColumns();

Commencez par dfinir dans la table des chanes (stringtable) situe dans longlet ressources, les chanes pour les identifiants :
IDS_RIBBON_IDENTITY IDS_RIBBON_SIGNALETIQUE IDS_RIBBON_SIGNALETIQUE_LABEL IDS_RIBBON_SIGNALETIQUE_NAME IDS_RIBBON_SIGNALETIQUE_AGE

Avec les valeurs correspondantes visibles ci-dessous :

La table des chanes permet de dfinir lensemble des messages disponibles dans le logiciel. La classe CString est capable, avec lidentifiant, de charger le texte correspondant. Le fait de paramtrer ainsi lensemble des chanes permettra une internationalisation du logiciel. Il faudra dfinir une autre dll de ressource pour la langue distribue et la charger au lancement du programme. Description du code : On commence par rajouter une catgorie au ruban :
// Ajouter la catgorie "identit" : bNameValid = strTemp.LoadString(IDS_RIBBON_IDENTITY); ASSERT(bNameValid); CMFCRibbonCategory* pCategoryIdentity = m_wndRibbonBar.AddCategory(strTemp, IDB_WRITESMALL, IDB_WRITELARGE);

105

Le nom est lu dans la table des chanes (stringtable) La mthode AddCategory cre lobjet demand, IDB_WRITESMALL et IDB_WRITELARGE sont les bitmaps utiliss par la catgorie. Une fois la catgorie cre on lui rajoute un panneau :
// rajoute la panneau signaltique strTemp.LoadString(IDS_RIBBON_SIGNALETIQUE); CMFCRibbonPanel* pSignaletique = pCategoryIdentity->AddPanel(strTemp, m_PanelImages.ExtractIcon (7));

Licne spcifie apparat quand il manque de place pour afficher les diffrents lments. Ajout dlments dans le panneau : Dans le panneau nous allons pouvoir rajouter toute sorte de contrle :

CMFCRibbonEdit CMFCRibbonButton CMFCRibbonLabel CMFCRibbonCheckBox CMFCRibbonComboBox

: pour la gestion dun dit : Pour la gestion dun bouton : Pour la gestion dun label comme la classe CStatic : Pour la gestion dune case cocher : Pour la gestion dune combo box.

Je nai cit que les principaux contrles, vous pouvez consulter la documentation MSDN pour les autres contrles, la syntaxe gnrale tant CMFCRibbon suivi du type de contrle. Avant dcrire les lignes de codes relatives aux contrles, il va falloir dfinir leur identifiant dans le menu des ressources.

Jai rajout un menu identit correspondant ma nouvelle catgorie, puis deux items de menu : ge et nom, avec les identifiants ID_IDENTITY_AGE et ID_IDENTITY_NAME .

106

Dans mon extrait de code jai rajout un label et un dit comme suit :
// spcifie un label au panneau strTemp.LoadString(IDS_RIBBON_SIGNALETIQUE_LABEL); pSignaletique->Add(new CMFCRibbonLabel(strTemp)); // rajoute un edit sur le nom. strTemp.LoadString(IDS_RIBBON_SIGNALETIQUE_NAME); CMFCRibbonEdit* pEditTemp = new CMFCRibbonEdit(ID_IDENTITY_NAME, 72,strTemp); pSignaletique->Add(pEditTemp);

Le code est trs simple on donne un intitul au contrle et on lajoute au panneau dont il dpend. Dans le cas de ldit jai spcifi une largeur en pixels : 72 pour la taille de ldit. Jai rajout un peu plus doptions dans les lignes qui suivent :
// le dernier argument 3, represente le numero de l'image specifie dans la creation de la category avec IDB_WRITESMALL, IDB_WRITELARGE strTemp.LoadString(IDS_RIBBON_SIGNALETIQUE_AGE); pEditTemp = new CMFCRibbonEdit(ID_IDENTITY_AGE, 72,strTemp, 3); // active le spinbutton pEditTemp->EnableSpinButtons(1, 100); // valeur initiale de l'edit pEditTemp->SetEditText(_T("20")); pSignaletique->Add(pEditTemp); // cette ligne est importante, quand elle est absente l'edit n'est pas actif... // en gros sans justification des colonnes ,certains controles sont inactifs. // c'est un constat je n'ai pas plus d'informations la dessus. pSignaletique->SetJustifyColumns();

Ldit sur lge sera agrment dune icne dont lindice est spcifi dans le dernier argument et qui fait rfrence aux bitmaps associs la catgorie. Enfin comme dans la fentre de notre vue, jai activ le contrle de dfilement sur cet dit en lui spcifiant une plage de fonctionnement. Jai aussi affect pour lexemple une valeur par dfaut ldit. Voila notre nouvelle catgorie, son panneau et ses lments sont oprationnels, et comme laurez constat lajout dlments nest vraiment pas compliqu. Il sera beaucoup plus difficile mon avis de dcider de linterface que lon souhaitera donner au ruban que sa programmation. Communiquer avec les lments du ruban : Nous venons de mettre en place notre interface ruban, il va falloir maintenant ajouter le code de communication entre celui-ci et notre vue. Dans mon cas jai considr que les dits dans le ruban sont des zones de raccourcis de ceux de la vue principale. Lorsque je vais modifier leurs valeurs dans le ruban celles de la vue devront tre mis jour. Ajout des mthodes en rponse aux vnements du ruban : Comme je lai indiqu en prambule de ce chapitre chaque lment dispose dun identifiant de commande que nous avons rajout dans un menu fictif.

107

Placez vous sur la vue, puis sur la boucle de messages et demandez les proprits (ALT+ENTREE). Sur les identifiants ID_IDENTITY_NAME et ID_IDENTITY_AGE rajoutez une mthode de rponse comme sur limage ci-dessus.
void CSampleSDIView::OnIdentityAge() { // TODO : ajoutez ici le code de votre gestionnaire de commande CMainFrame *pFrame=static_cast<CMainFrame*>(GetTopLevelFrame()); CMFCRibbonBar* pRibbon =pFrame->GetRibbonBar(); ASSERT_VALID(pRibbon); CMFCRibbonEdit* pEdit = DYNAMIC_DOWNCAST(CMFCRibbonEdit, pRibbon>FindByID(ID_IDENTITY_AGE)); CString str=pEdit->GetEditText(); GetDlgItem(IDC_EDITAGE)->SetWindowText(str); } void CSampleSDIView::OnIdentityName() { // TODO : ajoutez ici le code de votre gestionnaire de commande CMainFrame *pFrame=static_cast<CMainFrame*>(GetTopLevelFrame()); CMFCRibbonBar* pRibbon =pFrame->GetRibbonBar(); ASSERT_VALID(pRibbon); CMFCRibbonEdit* pEdit = DYNAMIC_DOWNCAST(CMFCRibbonEdit, pRibbon>FindByID(ID_IDENTITY_NAME)); CString str=pEdit->GetEditText(); GetDlgItem(IDC_EDITNOM)->SetWindowText(str); }

Le but est de rcuprer les valeurs modifies du ruban pour mettre jour nos contrles de la vue principale. On commencera donc par rcuprer un pointeur sur la mainframe o est situ lobjet qui gre le ruban. Puis un pointeur sur lobjet ruban dlivr par la mthode GetRibbonBar de la classe CFrameWndEx La ligne qui suit :
CMFCRibbonEdit* pEdit = DYNAMIC_DOWNCAST(CMFCRibbonEdit, pRibbon>FindByID(ID_IDENTITY_NAME));

Permet de rcuprer le pointeur sur ldit en faisant une recherche par son identifiant.

108

La macro DYNAMIC_DOWNCAST permet de sassurer que lobjet rcupr est bien valide et que sa classe correspond bien CMFCRibbonEdit Enfin, il suffit dappeler la mthode GetEditText de ldit pour rcuprer le contenu du contrle. Attention ne pas confondre cette mthode avec GetText() qui permet de rcuprer le texte associ au contrle. Prenez le temps de consulter la classe de base des contrles CMFCRibbonBaseElement La dernire ligne procde la mise jour de ldit correspondant dans notre vue. Pour mettre jour les contrles du ruban en fonction des changements sur la vue il faudra en premier lieu intercepter les changements sur notre vue. Pour cela jai dcid de placer mon traitement en sortie de saisie de ldit. Le message WM_KILLFOCUS correspond cet vnement, il indique que le contrle a perdu le focus ddition:

Ldit relaie ce message sur la vue, il faudra appeler le gestionnaire dvnements, se placer sur ldit correspondant et gnrer le message sur la notification EN_KILLFOCUS
void CSampleSDIView::OnEnKillfocusEditnom() { // TODO: Add your control notification handler code here CMainFrame *pFrame=static_cast<CMainFrame*>(GetTopLevelFrame()); CMFCRibbonBar* pRibbon =pFrame->GetRibbonBar(); ASSERT_VALID(pRibbon); CMFCRibbonEdit* pEdit = DYNAMIC_DOWNCAST(CMFCRibbonEdit, pRibbon>FindByID(ID_IDENTITY_NAME)); CString str; GetDlgItem(IDC_EDITNOM)->GetWindowText(str); pEdit->SetEditText(str); } void CSampleSDIView::OnEnKillfocusEditage() { // TODO: Add your control notification handler code here CMainFrame *pFrame=static_cast<CMainFrame*>(GetTopLevelFrame()); CMFCRibbonBar* pRibbon =pFrame->GetRibbonBar(); ASSERT_VALID(pRibbon); CMFCRibbonEdit* pEdit = DYNAMIC_DOWNCAST(CMFCRibbonEdit, pRibbon>FindByID(ID_IDENTITY_AGE)); CString str; GetDlgItem(IDC_EDITAGE)->GetWindowText(str); pEdit->SetEditText(str); }

Le code est pratiquement similaire celui de la mise jour de la vue en fonction du ruban. Les diffrences sont visibles sur les trois dernires lignes :

109

On rcupre la valeur de ldit de la vue avec GetWindowText et on met jour ldit du ruban avec la mthode SetEditText. Le rsultat final :

110

Ajout dun Combo box : Pour cela, crer un deuxime panneau notre catgorie :
// rajoute le panneau Divers strTemp.LoadString(IDS_RIBBON_DIVERS); CMFCRibbonPanel* pDivers = pCategoryIdentity->AddPanel(strTemp, m_PanelImages.ExtractIcon (6));

Comme pour les autres contrles jai commenc par rajouter les identifiants dans les ressources :

Voici le prototype du constructeur de la classe CMFCRibbonComboBox :


public: CMFCRibbonComboBox( UINT nID, BOOL bHasEditBox=TRUE, Int nWidth=-1, LPCTSTR lpszLabel=NULL, Int nImage=-1 );

Le code final pour le ComboBox :


// Ajout d'un combobox. CMFCRibbonComboBox *pCombo = new CMFCRibbonComboBox(ID_DIVERS_COMBOBOX,0,1,_T("ComboBox")); pCombo->AddItem(_T("Item1")); pCombo->AddItem(_T("Item2")); pCombo->SelectItem(-1); pDivers->Add(pCombo);

Il reste ajouter le message de rponse dans notre vue :


void CSampleSDIView::OnDiversCombobox() { // TODO: Add your command handler code here CMainFrame *pFrame=static_cast<CMainFrame*>(GetTopLevelFrame()); CMFCRibbonBar* pRibbon =pFrame->GetRibbonBar(); ASSERT_VALID(pRibbon); CMFCRibbonComboBox* pCombo = DYNAMIC_DOWNCAST(CMFCRibbonComboBox, pRibbon->FindByID(ID_DIVERS_COMBOBOX)); CString str; int n=pCombo->GetCurSel(); if(n!=LB_ERR) str=pCombo->GetItem(n); AfxMessageBox(str);

111

Cest toujours la mthode qui est employe. Une fois le pointeur sur le contrle acquis, il suffit de retrouver lindex de litem slectionn avec GetCurSel() et rcuprer la valeur avec GetItem(). Le rsultat final :

112

Ajout dun bouton de raccourci : Continuons notre dcouverte du ruban en ajoutant un bouton de raccourci permettant dappeler notre bote de dialogue Proprits Avances directement dans notre panneau signaltique. Pour cela ajouter la ligne suivante :
// cette ligne est importante, quand elle est absente l'edit n'est pas actif... // en gros sans justification des colonnes ,certains controles sont inactifs. // c'est un constant je n'ai pas plus d'informations la dessus. pSignaletique->SetJustifyColumns(); // activation du bouton par dfaut dans le panneau, l'appui sur le bouton gnre la commande ID_IDENTITY_DEFAULT pSignaletique->EnableLaunchButton(ID_IDENTITY_DEFAULT);

Lidentifiant ID_IDENTITY_DEFAULT doit tre ajout dans le menu identit.

113

Ensuite, gnrez le message de rponse cette commande dans la vue :

void CSampleSDIView::OnIdentityDefault() { // TODO: Add your command handler code here OnBnClickedButtonadvanced(); }

Il ne me reste plus qu appeler la mthode existante qui se chargera de lancer notre dialogue.

Lajout de notre ligne de code a mis en place le bouton en bas droite de notre panneau.

114

La barre dtats :
La gestion des barres dtats est trs simple, on retrouve un peu la mme ide de conteneur dveloppe dans le ruban : On dispose de la classe CMFCRibbonStatusBar laquelle on va rajouter des lments, le plus souvent des objets de la classe CMFCRibbonStatusBarPane mais on peut aussi utiliser la classe CMFCRibbonLinkCtrl ou encore dfinir un groupe de boutons, mettre un slider, etc. En fait, si vous connaissez Word 2007 vous pouvez dfinir la mme barre dtat. Notre barre dtats est cre dans la mthode OnCreate de la classe CMainFrame.
if (!m_wndStatusBar.Create(this)) { TRACE0("Impossible de crer la barre d'tat\n"); return -1; // chec de la cration } CString strTitlePane1; CString strTitlePane2; bNameValid = strTitlePane1.LoadString(IDS_STATUS_PANE1); ASSERT(bNameValid); bNameValid = strTitlePane2.LoadString(IDS_STATUS_PANE2); ASSERT(bNameValid); m_wndStatusBar.AddElement(new CMFCRibbonStatusBarPane(ID_STATUSBAR_PANE1, strTitlePane1, TRUE), strTitlePane1); m_wndStatusBar.AddExtendedElement(new CMFCRibbonStatusBarPane(ID_STATUSBAR_PANE2, strTitlePane2, TRUE), strTitlePane2);

Rajout dun bouton de lien: Pour illustrer la programmation de la barre dtats je vais commencer par ajouter un bouton de lien (link Control). Ajoutez un nouveau menu Barre dtats dans les ressources puis crez un item de menu lien avec lidentifiant ID_STATUSBAR_LINK

Dans la zone Prompt jai renseign le libell suivant : Cliquez ici pour visiter le forum Visual C++ de dveloppez.com

115

Ajoutez la ligne dinsertion de llment juste aprs la cration de la barre dtats:


if (!m_wndStatusBar.Create(this)) { TRACE0("Impossible de crer la barre d'tat\n"); return -1; // chec de la cration } m_wndStatusBar.AddElement(new CMFCRibbonLinkCtrl(ID_STATUSBAR_LINK, _T("www.developpez.com"), _T("http://www.developpez.net/forums/f29/ccpp/outils-c-cpp-edi-compilateurs-etc/visual-cpp/")), _T("lien sur le forum Visual")); m_wndStatusBar.AddSeparator();

Il reste ajouter le message de rponse la commande ID_STATUSBAR_LINK directement sur la mainframe. Placez vous sur la zone END_MESSAGE_MAP() de la classe CMainFrame et appuyez sur la combinaison de touche ALT+ Entre

Sur le tableau des proprits dans la section vnements, ajoutez la commande sur lidentifiant du lien comme sur limage ci-dessus. Enfin une fois le pointeur sur lobjet boutonlink acquis il suffit dappeler la mthode OpenLink pour dclencher laffichage de la page Web dsigne.
void CMainFrame::OnLink() { CMFCRibbonLinkCtrl* pLink = static_cast<CMFCRibbonLinkCtrl*>(m_wndStatusBar.FindByID(ID_STATUSBAR_LINK) ); if (pLink != NULL) { pLink->OpenLink(); } }

116

Le rsultat final:

Modifications des textes dans les panneaux : Revenons un peu sur la mthode OnCreate de notre classe CMainFrame, les lignes qui suivent sont celles gnres par lassistant.
CString strTitlePane1; CString strTitlePane2; bNameValid = strTitlePane1.LoadString(IDS_STATUS_PANE1); ASSERT(bNameValid); bNameValid = strTitlePane2.LoadString(IDS_STATUS_PANE2); ASSERT(bNameValid); m_wndStatusBar.AddElement(new CMFCRibbonStatusBarPane(ID_STATUSBAR_PANE1, strTitlePane1, TRUE), strTitlePane1); m_wndStatusBar.AddExtendedElement(new CMFCRibbonStatusBarPane(ID_STATUSBAR_PANE2, strTitlePane2, TRUE), strTitlePane2);

Nous allons personnaliser le premier panneau pour quil indique lutilisateur ltat de sa saisie. Pour cela je vais lgrement modifier la ligne dinsertion du panneau 1 pour indiquer un nom notre panneau et pour spcifier le texte le plus large quil devra afficher :
m_wndStatusBar.AddElement(new CMFCRibbonStatusBarPane(ID_STATUSBAR_PANE1, strTitlePane1, TRUE,NULL,_T("Saisie incomplte")),_T("Informations de saisie"));

117

Le dernier lment de la mthode AddElement permet de spcifier le nom du panneau. Voici la dfinition du constructeur utilis pour la classe CMFCRibbonStatusBarPane :
CMFCRibbonStatusBarPane( UINT nCmdID, LPCTSTR lpszText, BOOL bIsStatic=FALSE, HICON hIcon=NULL, LPCTSTR lpszAlmostLargeText=NULL );

Le paramtre lpszAlmostLargeText permet de spcifier le texte le plus large pour le panneau. Passons la mise jour du texte. Je vais notifier que la saisie est incomplte ou complte lorsque lutilisateur demande lenregistrement des donnes. Pour cela je vais modifier la mthode CanSaveData(bool bSetFocus/*=false*/) de notre classe vue CSampleSDIView.
bool CSampleSDIView::CanSaveData(bool bSetFocus/*=false*/) { UpdateData(TRUE); // mise a jour des donnes. // tableau des CString associes aux contrles CString *parString[]={&m_strNom,&m_strPrenom, &m_strAdresse,&m_strAdresse2,&m_strVille,&m_strCdp }; // tableau des identifiants correspondant UINT arnId[]={IDC_EDITNOM,IDC_EDITPRENOM, IDC_EDITADRESSE, IDC_EDITADRESSE1,IDC_EDITCDP,IDC_EDITVILLE }; CMainFrame *pFrame=static_cast<CMainFrame*>(GetTopLevelFrame()); CMFCRibbonStatusBar *pBar=pFrame->GetRibbonStatusBar(); CMFCRibbonStatusBarPane *pPane=DYNAMIC_DOWNCAST(CMFCRibbonStatusBarPane,pBar>FindByID(ID_STATUSBAR_PANE1)); for(int i=0;i<sizeof(parString)/sizeof(CString *);i++) { // si la chaine est vide if(parString[i]->IsEmpty()) { // on redonne la main en saisie au contrle en question. if(bSetFocus) GetDlgItem(arnId[i])->SetFocus(); if(pPane) pPane->SetText(_T("Saisie incomplte")); return false; } } if(pPane) pPane->SetText(_T("Saisie complte")); return true; }

118

Cest toujours le mme procd qui est employ, je rcupre le pointeur sur la mainframe puis un sur la barre de statuts.
CMainFrame *pFrame=static_cast<CMainFrame*>(GetTopLevelFrame()); CMFCRibbonStatusBar *pBar=pFrame->GetRibbonStatusBar();

Jai rajout une mthode dans la classe CMainframe pour renvoyer un pointeur sur la barre de statuts :
// Oprations public: CMFCRibbonStatusBar *GetRibbonStatusBar(){return &m_wndStatusBar;}

A partir de la barre de statuts je rcupre un pointeur sur le panneau grce son identifiant.
CMFCRibbonStatusBarPane *pPane=DYNAMIC_DOWNCAST(CMFCRibbonStatusBarPane,pBar>FindByID(ID_STATUSBAR_PANE1));

Il ne me reste plus qu modifier le texte du panneau.


if(pPane) pPane->SetText(_T("Saisie incomplte"));

Le rsultat final:

119

Ajout dune animation :


Pour ajouter une animation dans la barre dtats, il faudra disposer dun bitmap dune hauteur et dune largeur de 16 pixels multiplis par le nombre danimations. Au final, si mon bitmap doit reproduire quatre animations il aura une taille de 16 *64 pixels. La bonne nouvelle cest quavec Visual studio 2008 nous ne sommes plus limits 256 couleurs dans lditeur comme ctait le cas avec les versions prcdentes. Notre bitmap pourra tre au format 24 bits voir 32 bits (le maximum). Jai donc ajout mon projet un bitmap IDB_WRITEDATAS qui comporte un bitmap de 4 lments pour reprsenter la sauvegarde du document. Jai ajout dans la liste des icnes de lapplication une icne IDI_WRITEDATA qui reprsentera ltat initial. Ensuite, comme pour le bouton de lien, jai ajout une entre dans le menu barre dtats : Sauvegarde avec lidentifiant : ID_STATUSBAR_WRITEDATAS. Dans la zone prompt, jai mis : Cliquez ici pour sauvegarder les donnes. Jai ajout le code dinitialisation suivant dans la mainframe (mthode OnCreate):
m_wndStatusBar.AddElement(new CMFCRibbonStatusBarPane(ID_STATUSBAR_PANE1, strTitlePane1, TRUE,NULL,_T("Saisie incomplte")),_T("Informations de saisie")); m_wndStatusBar.AddSeparator(); HICON hIconWrite = (HICON) ::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_WRITEDATAS), IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), LR_SHARED); m_wndStatusBar.AddElement(new CMFCRibbonStatusBarPane(ID_STATUSBAR_WRITEDATAS, _T("Sauvegarde"), (UINT) IDB_WRITEDATAS, 16, RGB(192, 192, 192), hIconWrite), _T("Sauvegarde")); m_wndStatusBar.AddSeparator();

Comme pour le bouton de lien, jai rajout un message de rponse dans la vue avec lidentifiant de menu :ID _STATUSBAR_WRITEDATAS .
void CSampleSDIView::OnStatusbarWritedatas() { // TODO: Add your command handler code here CMainFrame *pFrame=static_cast<CMainFrame*>(GetTopLevelFrame()); CMFCRibbonStatusBar *pBar=pFrame->GetRibbonStatusBar(); CMFCRibbonStatusBarPane *pPane=DYNAMIC_DOWNCAST(CMFCRibbonStatusBarPane,pBar>FindByID(ID_STATUSBAR_WRITEDATAS)); if(CanSaveData()) { pPane->StartAnimation(); OnFileSave(); pPane->StopAnimation(); } }

Le code ne prsente aucune nouveaut si ce nest lappel des mthodes StartAnimation et StopAnimation.

120

Le rsultat final :

121

Rajout dune barre doutils : Pour ajouter une barre doutils dans notre barre dtats, nous avons besoin de prparer les lments suivants : Un bitmap dune hauteur de 14 pixels et dont les lments font 14 pixels de large. Dans mon exemple jai repris les trois premires icnes de la barre doutils standard pour crer mon bitmap que jai nomm : IDB_BITMAPOPENBAR

Un identifiant de menu ID_STATUSBAR_TOOLBAR que jai plac dans le menu IDR_MAINFRAME :

122

Passons maintenant au code : Dans la mthode : OnCreate de la classe CMainFrame Juste aprs le code prcdent sur le bouton de sauvegarde :
m_wndStatusBar.AddElement(new CMFCRibbonStatusBarPane(ID_STATUSBAR_WRITEDATAS, _T("Sauvegarde"), (UINT) IDB_WRITEDATAS, 16, RGB(192, 192, 192), hIconWrite), _T("Sauvegarde")); m_wndStatusBar.AddSeparator(); // mise en place de la barre d'outils CMFCRibbonButtonsGroup* pGroup = new CMFCRibbonButtonsGroup; CMFCToolBarImages ToolbarImages; ToolbarImages.SetImageSize(CSize(14, 14)); ToolbarImages.Load(IDB_BITMAPOPENBAR); pGroup->SetImages(&ToolbarImages, NULL, NULL); pGroup->SetID(ID_STATUSBAR_TOOLBAR); pGroup->AddButton(new CMFCRibbonButton(ID_FILE_NEW, _T(""), 0)); pGroup->AddButton(new CMFCRibbonButton(ID_FILE_OPEN, _T(""), 1)); pGroup->AddButton(new CMFCRibbonButton(ID_FILE_SAVE,_T(""), 2)); m_wndStatusBar.AddExtendedElement(pGroup, _T("Barre d'outils Gestion document"));

On commence par crer une instance de la classe CMFCRibbonButtonsGroup qui va servir de conteneur pour les boutons. On associe le bitmap des ressources un objet de la classe CMFCToolBarImages Cet objet qui lui-mme est associ au conteneur (pGroup). On arrive la phase finale : Comme pour tous les ajouts que nous avons effectus jusquici, il nous faut affecter lidentifiant ID_STATUSBAR_TOOLBAR ce groupe pour le gestionnaire de barre dtat.

123

Il ne nous reste plus qu ajouter les diffrents boutons au groupe laide de la classe CMFCRibbonButton Pour chaque bouton insr, il faudra indiquer la commande de menu associe. Dans mon exemple, les commandes ID_FILE_NEW, OPEN et SAVE existaient dj bien sr, vu que ma barre doutils est en quelque sorte un raccourci de celle affiche en haut de lapplication.

124

Le Rsultat final:

La barre OutLook :
Lors de la gnration de notre projet jai permis la cration dune barre OutLook par lassistant. La gnration par dfaut de cette barre met en place un Calendrier et un volet avec un mini explorateur Windows (CMFCShellTreeCtrl). La mise en place de ces lments est ralise dans la classe CMainFrame dans la mthode OnCreate avec lappel de la mthode CreateOutlookBar Je ne vais pas rentrer dans les dtails de la gestion par dfaut propose par lassistant, mais passer tout de suite la cration dun nouveau panneau semblable celui du courrier propos dans le logiciel Outlook. Pour cela jai besoin dun composant permettant dafficher verticalement une liste de bitmap de cliquer dessus et bien sr de grer le dfilement vertical de la liste. Le composant CListCtrl en mode Icne permet ce type daffichage, mais il faut lui rajouter pas mal de code pour grer correctement le dfilement et laffichage. Je vais donc prendre un raccourci sur ce composant et partir de celui existant dans la bibliothque Ultimate ToolBox propose sur le site CodeProjects. En effet, cette bibliothque implmente un composant de gestion de barre Outlook qui sappuie sur une CListCtrl.

125

Ce composant dont on peut voir limplmentation dans le lien cit se nomme COXShortcutBar. Il est utilis pour la gestion des pages la classe COXSHBListCtrl qui hrite justement dune CListCtrl et qui gre correctement laffichage des icnes. Jai donc ralis rapidement une version allge de cette classe en enlevant tous les liens avec le composant principal. Vous trouverez le code source de cette classe dans le projet dexemple. Passons maintenant la mise en place de mon composant. Jai ajout dans la classe CMainFrame une donne membre de la classe en question : COXSHBListCtrl m_ListShortCut;

Et modifi la mthode de mise en place de la barre Outlook : En premier lappel de la mthode comporte un nouvel argument : m_ListShortCut
// Crer et configurer la barre de navigation "Outlook" : if (!CreateOutlookBar(m_wndNavigationBar, ID_VIEW_NAVIGATION, m_wndTree, m_wndCalendar,m_ListShortCut,250)) { TRACE0("Impossible de crer le volet de navigation\n"); return -1; // chec de la cration }

Dtails dans la mthode CreateOutlookBar :


BOOL CMainFrame::CreateOutlookBar(CMFCOutlookBar& bar, UINT uiID, CMFCShellTreeCtrl& tree, CCalendarBar& calendar,COXSHBListCtrl &listCtrl, int nInitialWidth) { CWindowDC dc(NULL); bar.SetMode2003(); BOOL bNameValid; CString strTemp; bNameValid = strTemp.LoadString(IDS_SHORTCUTS); ASSERT(bNameValid); if (!bar.Create(strTemp, this, CRect(0, 0, nInitialWidth, 32000), uiID, WS_CHILD | WS_VISIBLE | CBRS_LEFT)) { return FALSE; // chec de la cration } DWORD dwStyle=WS_VISIBLE |LVS_SINGLESEL|LVS_SHAREIMAGELISTS|LVS_NOSCROLL|LVS_ICON; if(!listCtrl.Create(dwStyle ,CRect(0, 0, nInitialWidth, 32000), &bar,1203 )) { return FALSE; // chec de la cration } m_iListShortCut.Create(IDB_BITMAPLIST, 32, 11, RGB(255, 0, 255)); listCtrl.SetImageList(&m_iListShortCut,0); listCtrl.InsertItem(0,_T("Proprits"),0); listCtrl.InsertItem(1,_T("A propos de"),1); listCtrl.InsertItem(2,_T("Essai1"),2); listCtrl.InsertItem(3,_T("Essai2"),3); listCtrl.InsertItem(4,_T("Essai3"),4); listCtrl.InsertItem(5,_T("Essai4"),5); listCtrl.InsertItem(6,_T("Essai5"),6); listCtrl.InsertItem(7,_T("Essai6"),7); listCtrl.InsertItem(8,_T("Essai7"),8); listCtrl.InsertItem(9,_T("Essai8"),9); listCtrl.InsertItem(10,_T("Essai9"),10); CMFCOutlookBarTabCtrl* pOutlookBar = (CMFCOutlookBarTabCtrl*)bar.GetUnderlyingWindow(); if (pOutlookBar == NULL) { ASSERT(FALSE); return FALSE;

126

} listCtrl.SetBkColor(GetPaneBgrdColor()); pOutlookBar->EnableInPlaceEdit(TRUE); static UINT uiPageID = 1; DWORD dwPaneStyle = AFX_DEFAULT_TOOLBAR_STYLE | CBRS_FLOAT_MULTI; // peut flotter, peut se masquer automatiquement, peut se redimensionner, NE PEUT PAS SE FERMER dwStyle = AFX_CBRS_FLOAT | AFX_CBRS_AUTOHIDE | AFX_CBRS_RESIZE; CRect rectDummy(0, 0, 0, 0); const DWORD dwTreeStyle = WS_CHILD | WS_VISIBLE | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS; tree.Create(dwTreeStyle, rectDummy, &bar, 1200); bNameValid = strTemp.LoadString(IDS_FOLDERS); ASSERT(bNameValid); pOutlookBar->AddControl(&tree, strTemp, 2, TRUE, dwStyle); calendar.Create(rectDummy, &bar, 1201); bNameValid = strTemp.LoadString(IDS_CALENDAR); ASSERT(bNameValid); pOutlookBar->AddControl(&calendar, strTemp, 3, TRUE, dwStyle); bar.SetPaneStyle(bar.GetPaneStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC); bNameValid = strTemp.LoadString(IDS_SHORTCUTS); ASSERT(bNameValid); pOutlookBar->AddControl(&listCtrl,strTemp,4,TRUE,dwStyle); pOutlookBar->SetImageList(theApp.m_bHiColorIcons ? IDB_PAGES_HC : IDB_PAGES, 24); pOutlookBar->SetToolbarImageList(theApp.m_bHiColorIcons ? IDB_PAGES_SMALL_HC : IDB_PAGES_SMALL, 16); pOutlookBar->RecalcLayout(); BOOL bAnimation = theApp.GetInt(_T("OutlookAnimation"), TRUE); CMFCOutlookBarTabCtrl::EnableAnimation(bAnimation); bar.SetButtonsFont(&afxGlobalData.fontBold); return TRUE; }

Ma ListControl est cre par la ligne :


DWORD dwStyle=WS_VISIBLE |LVS_SINGLESEL|LVS_SHAREIMAGELISTS|LVS_NOSCROLL|LVS_ICON; if(!listCtrl.Create(dwStyle ,CRect(0, 0, nInitialWidth, 32000), &bar,1203 )) {

Jai ensuite associ une CImageList contenant la liste des bitmaps associer au contrle :
m_iListShortCut.Create(IDB_BITMAPLIST, 32, 11, RGB(255, 0, 255)); listCtrl.SetImageList(&m_iListShortCut,0);

127

Il me reste initialiser la liste des Items de ma liste :


listCtrl.InsertItem(0,_T("Proprits"),0); listCtrl.InsertItem(1,_T("A propos de"),1); listCtrl.InsertItem(2,_T("Essai1"),2); listCtrl.InsertItem(3,_T("Essai2"),3); listCtrl.InsertItem(4,_T("Essai3"),4); listCtrl.InsertItem(5,_T("Essai4"),5); listCtrl.InsertItem(6,_T("Essai5"),6); listCtrl.InsertItem(7,_T("Essai6"),7); listCtrl.InsertItem(8,_T("Essai7"),8); listCtrl.InsertItem(9,_T("Essai8"),9); listCtrl.InsertItem(10,_T("Essai9"),10);

Et enfin de rajouter le contrle au composant de la barre Outlook :


bNameValid = strTemp.LoadString(IDS_SHORTCUTS); ASSERT(bNameValid); pOutlookBar->AddControl(&listCtrl,strTemp,4,TRUE,dwStyle);

Vous noterez le numro 4 dans Addcontrol qui correspond au numro du bitmap utilis pour ce panneau. Ce numro fait rfrence aux bitmaps associs la barre Outlook dans ces lignes :
pOutlookBar->SetImageList(theApp.m_bHiColorIcons ? IDB_PAGES_HC : IDB_PAGES, 24); pOutlookBar->SetToolbarImageList(theApp.m_bHiColorIcons ? IDB_PAGES_SMALL_HC : IDB_PAGES_SMALL, 16);

jai donc modifi le bitmap dorigine pour rajouter celui de mon panneau :

128

Un premier aperu du rsultat simpose maintenant :

Sur lcran ci-dessus un nouveau volet raccourcis apparat avec la liste. Il ne me reste plus qu grer le clic dun item par lutilisateur. Pour cela je vais implmenter un message Notify dans mon contrle pour appeler directement une mthode de la CMainFrame.

129

Dans la mthode OnLButtonUp de la classe COXSHBListCtrl, jai implement le code suivant :


if(nItem!=-1 && nHitFlags!=SHBLC_ONITEM) { // if it is the selected item then activate it if(GetSelectedItem()==nItem) { int nOldItem=ActivateItem(nItem); if(nOldItem!=-1 && nOldItem!=nItem) // redraw old active item (only one item can be active at the moment) Update(nOldItem); struct COXSHBListCtrl_NOTIFY Notify; Notify.hdr.hwndFrom = GetSafeHwnd(); Notify.hdr.idFrom = GetDlgCtrlID(); Notify.hdr.code = OXLISTCTRL_LBUTTONUP; Notify.nItem=nItem; Notify.pCtrl=this; AfxGetMainWnd()->SendMessage(WM_NOTIFY, GetDlgCtrlID(), (LPARAM)&Notify); }

Jai cr une structure COXSHBListCtrl_NOTIFY qui englobe NMHDR hdr pour communiquer la classe CMainFrame les informations essentielles pour identifier lorigine du clic. Vous pouvez consulter la Faq Visual sur ce sujet : Comment implmenter un message supplmentaire partir d'un contrle ? Finalement, dans ma classe Mainframe jai ajout le message suivant :
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWndEx) //. ON_NOTIFY(OXLISTCTRL_LBUTTONUP,1203,OnSelOutLookBar) // le message rajouter manuellement. END_MESSAGE_MAP() //OXLISTCTRL_LBUTTONUP est dfinit dans OXSHBListCtrl.h #define OXLISTCTRL_LBUTTONUP 1 class COXSHBListCtrl; struct COXSHBListCtrl_NOTIFY { NMHDR hdr; int nItem; COXSHBListCtrl *pCtrl; };

1203 est le numro didentifiant donn notre contrle lors de sa cration.

130

Il ne nous reste plus qu voir la mthode de rponse au message OnSelOutLookBar


//------------------------------------------------------------------void CMainFrame::OnSelOutLookBar(NMHDR* pNMHDR, LRESULT* pResult) { COXSHBListCtrl_NOTIFY *pNotify=reinterpret_cast<COXSHBListCtrl_NOTIFY *>(pNMHDR); TRACE("\nId:%u Nitem:%d",pNMHDR->idFrom,pNotify->nItem); switch(pNotify->nItem) { case 0:static_cast<CSampleSDIView *>(GetActiveView())>OnBnClickedButtonadvanced(); break; case 1:theApp.OnAppAbout(); break; } }

Si lutilisateur appuie sur la premire icne, je lance la bote de dialogue proprits situe dans la view. La deuxime lancera la bote de dialogue propos de de lapplication. Voila, avec peu defforts nous avons complt le composant Barre Outlook des MFC (CMFCOutlookBar)

131

Le dbogage dune application


Visual dispose dun debugger intgr lenvironnement de dveloppement, Lorsque lapplication est compile en Debug nous pouvons tracer pas pas le code de notre application et : Inspecter les diffrentes variables Modifier les variables. Modifier le code de lapplication et continuer son excution Mettre des points darrts dans le code.

Tour dhorizon des fonctionnalits :


Compiler et linker notre projet dexemple en mode Debug : Nous allons placer un point darrt dans la fonction OnInitialUpdate de notre vue :

Placez le curseur sur la ligne CFormView ::OnInitialUpdate comme sur lcran ci-dessus . Cliquez sur la bordure grise gauche de la ligne concerne, ou utilisez la touche F9. Lancez lapplication en mode trace par la touche F5.

132

Sur lcran ci-dessus nous voyons les principales fonctionnalits du debugger : Linspection du contenu dun variable slectionne ici m_strCdp. Le panneau contexte permet de voir le contenu de lobjet en cours this (notre vue). Toutes les variables sont consultables. Nous pouvons aussi par un glisser dplacer placer un variable slectionne dans le panneau (watch) permettant ainsi de la surveiller tout au long de sa porte. Autre possibilit, consulter la pile dappel des fonctions partir du slecteur Stack Frame :

133

Cette fonctionnalit est trs importante et utile, car en cas derreur signale par une assertion MFC, Il suffit de remonter la pile dappels jusqu' trouver une fonction correspondant notre code pour trouver lorigine du problme. Exemples : Nous allons provoquer deux erreurs courantes des dbutants, une concernant lallocation mmoire, lautre concernant la gestion des contrles. Insrez le code suivant dans la fonction OnInitialUpdate de notre vue : CString *pString ; *pString="coucou" ; Lancez lexcution du programme par F5. On obtient le message

134

Pressez le bouton Break Vous obtenez :

Nous tombons donc directement dans du code MFC, comme trouver la portion de notre code causant le problme ? Il suffit de remonter la pile des appels :

pour arriver directement sur la premire fonction issue de notre code, quil suffit de slectionner :

135

Le curseur est plac sur la ligne provoquant lerreur. Un autre point significatif de lerreur : la valeur du pointeur pstring dans longlet Autos. Explications : Le compilateur suivant les cas lexcution peut affecter des valeurs spcifiques une variable pointeur. Connatre ces valeurs peut savrer fort utile en mode Debug : 0xFDFDFDFD : indique une adresse en dehors de lespace dadressage du processus. 0xDDDDDDDD: indique que la mmoire vient dtre libre. Attention a ne veut pas dire que linstruction delete va mettre cette valeur dans un pointeur. 0xCDCDCDCD : mmoire globale non initialise par exemple un pointeur membre dune classe. 0xCCCCCCCC : mmoire locale (pile /stack ) non initialise par exemple un pointeur dclar dans une fonction membre dune classe. On comprend lutilit de toujours initialiser les pointeurs NULL, et de leur affecter cette valeur aprs libration de la mmoire. Dans notre exemple pstring contient la valeur reconnaissable 0xcccccccc et le debugger met des ??? en face.

136

Autre exemple : Je supprime un contrle de notre vue, et ce contrle dispose dune variable membre associe. Je relance le programme : et jobtiens :

Je presse le bouton recommencer puis le bouton break pour debugger : Je tombe nouveau sur du code MFC :

Je procde de la mme manire en remontant la pile des appels, mme si la ligne TRACE me donne une bonne ide du problme Note cette trace est visible dans longlet Output (sortie).

Cette fois si je tombe dans notre fonction DodataExchange :

137

La variable causant le problme est m_EditNom. Note : Si vous regardez bien la pile des fonctions dans la zone contexte, on retrouve lenchanement des fonctions qui est dcrit pour lexplication de UpdateData (voir cran prcdent) Visual permet aussi de se dplacer pas pas dans le code en cours de dbogage :

Description dans lordre des icnes : Entre dans le code de la fonction o est positionn le curseur. Excute la fonction sans rentrer dans le code. Sort de la fonction en cours pour passer la prochaine ligne qui suit.

138

Autre possibilit intressante : utiliser la macro TRACE. TRACE fonctionne de la mme manire que printf du C, les messages apparaissent dans longlet output de Visual . Exemple tir de MSDN: int i = 1; char sz[] = "one"; TRACE( "Integer = %d, String = %s\n", i, sz ); Il existe une autre mthode pour laisser des traces dans le panneau debug : afxDump Exemple tir de MSDN : CPerson myPerson = new CPerson; // set some fields of the CPerson object... //.. // now dump the contents #ifdef _DEBUG afxDump << "Dumping myPerson:\n"; myPerson->Dump( afxDump ); afxDump << "\n"; #endif afxDump est actif uniquement en mode debug. Et enfin on peut aussi utiliser la fonction AfxMessageBox pour afficher des messages dans une bote de dialogue. Cest souvent cette mthode qui sera retenue en release pour traquer une erreur. Il suffira de mettre cette fonction dans les endroits clefs du programme pour se rapprocher au plus prs du code produisant lerreur. Exemple : int i = 1; char sz[] = "one"; CString str; Str.Format( "Integer = %d, String = %s", i, sz ); AfxMessageBox(str);

139

Conclusions:
A travers cet exemple nous avons vu les bases dune application MFC : Larchitecture SDI dun programme MFC Les messages Windows Lditeur de ressources La notion de contrles La communication avec les contrles Limplmentation dune bote de dialogue modale La srialisation de donnes et la reconnaissance de lextension fichier par Windows Un aperu des nouvelles classes MFC Lutilisation du debugger

Le lien sur le projet final : http://farscape.developpez.com/Samples/SampleSDI.zip

Remerciements:
Je remercie toute l'quipe du forum Visual C++ pour leur relecture attentive du document, notamment PetitPapaNol et Nico-pyright(c). Je remercie galement bigboomshakala et Anomaly pour la correction orthographique.

Les sources prsentes sur cette page sont libres de droits, et vous pouvez les utiliser votre convenance. Par contre, la page de prsentation constitue une uvre intellectuelle protge par les droits d'auteurs. Copyright 2009 farscape. Aucune reproduction, mme partielle, ne peut tre faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu' 3 ans de prison et jusqu' 300 000 E de dommages et intrts. Cette page est dpose la SACD.

140

Vous aimerez peut-être aussi