2. Programguiden

 

1.        MFC AppWizard.

2.        Project Workspace.

3.        Menyredigeraren.

4.        Dialogredigeraren.

5.        Koppla menykommando.

6.        Programmets beståndsdelar.

7.        ClassWizard.

8.        AFX meddelandehantering.

9.        Variablernas plats.

 

1. MFC AppWizard.

                       [File  - New...]

 

I första kapitlet gjorde vi ett Windowsprogram helt själva. All ingående kod skrev vi. Det var ganska mycket att skriva, trots att vi gjorde ett enklast möjliga exemp­el. Skulle man vilja skriva ett mera avancerat program är det lätt att inse att det blir mycket komplicerat att skriva. Det som gör saken komplicerad är all Win­dows­specifik hantering. Vi kan inte längre skriva 'cin' och 'cout' när vi vill 'prata' med användaren. Vi måste även göra alla de inställningar som beskriver program­fönstrets utseende, och skulle vi behöva andra fönster, dokumentfönst­er, dialog­rutor etcetera, så blir det lika mycket extra jobb med var och ett av dessa fönster. Meddelandehanteringen ska skrivas för vart och ett av fönstren. Det finns massor av andra exempel på saker man måste göra i Windows, och allt innebär merar­be­te, men visst blir det fint?

 

För att underlätta detta arbete har vi två hjälp­medel: MFC och AppWizard. MFC står för Microsoft Foundation Classes, och AppWizard är en program-guide. App står för Applicaton, vilket betyder tillämpning, en vanlig benämning på ett pro­g­ram.

 

·     MFC innehåller färdiga klasser för det mesta man behöver i Win­dows­program, till exempel CWin, en klass för fönster, CDialog, en klass för dialogrutor, CDocument, en klass för dokument etcete­ra.

 

·     AppWizard skapar ett projekt åt oss, och skriver de filer vi behöver för att få ett program baserat på MFC. Det blir naturligtvis mycket olikt det program vi skrev i föregående exempel, eftersom det baseras på klasserna i stället för att ska­pa programfönstret som vi gjorde i Win­Main(). Många filer är helt standar­dis­erade, så vi behöver inte ens källkoden till dem, de bara länkas in från objekts-filer. Själva Win­Main() ligger i en sådan fil. Vi ska titta mer på allt detta senare.

 

Nu ska vi se hur man kan utnyttja detta för att skapa ett enkelt exempel. Vi ska börja med att göra ett program som har ett programfönster, en egen ikon och en meny. Dessutom har det en 'About'-dialog, och faktiskt även ett dokument med tillhörande vy. Vad dokument och vy är ska vi titta på senare. Det enda vi ska lägga till manuellt är en MessageBox, liksom vi gjorde i förra kapitlet.

 

När vi tidigare skapade våra projekt valde vi nästan alltid 'Console Application' i dialogrutan 'New Project Workspace'. Enda undantaget var i förra kapitlet, när vi gjorde ett Windows-program. Då valde vi i stället ‘Application’. Detta val resulterade i huvudsak i tre skillnader i våra projekt:

 

1.   ‘Console Application’ skapade en programfil som märkts för att köras under DOS, medan ‘Application’ skapade en programfil som märkts för att köras under Windows.

2.   ‘Console Application’ länkade in filen ‘winstub.obj’ i program­filen. Den innehåller den funktion som skriver ut texten ‘Detta program är avsett att köras under Windows’.

3.   Developer Studio sparade undan information om att programmet skulle startas via MS-DOS-prompt respektive som Windows­pro­gram, så att Ctrl+F5 kunde starta programmet på rätt sätt.

 

Nu vill vi skapa ett Windowsprogram igen, men denna gången kommer vi att använda Microsoft Foundation Classes (MFC). Välj därför ‘MFC Application (exe) i listrutan ‘Type’:

 

 

Som synes följer vi den gamla traditionen att börja med ett exempel som skriver ut ‘Hello World’ på skärmen. Jag har här skapat en ny katalog för våra Win­dows­projekt: C:\WinMFC\. I den kan vi lägga alla övningar i detta häfte.

 

Som sagt innehåller MFC en massa färdiga klasser att bygga programmet på. Dessa måste sättas samman enligt våra önskemål, och därför vill program­gen­eratorn (programguiden) samla litet information innan den kan sätta samman programmet åt oss. Guiden AppWizard består av ett antal sidor, och så här ser första sidan ut:

 

 

Här kan man välja mellan tre olika huvudtyper av program:

 

1.   Single document’, ett program som hanterar endast ett dokument i taget, som t.ex. Anteckningar och Paint. Vill man bearbeta flera dokument samtidigt måste man starta flera kopior av programmet.

2.   Multiple document’, ett program som kan hantera flera doku-ment samtidigt, som till exempel Word och Developer Studio. Detta alternativ är förvalt.

3.   Dialog based’, ett program som har en dialogruta som program­fönster. Här kan man ha kontroller, det vill säga tryckknappar, listrutor med mera i programfönstret utan att behöva använda verktygsfält.

 

Observera att den sista typen av projekt, Dialog based, inte har något dokument över huvud taget. Vad det får för konsekvenser inser vi när vi senare lär oss mer om vad dokumentklassen, CDocument, innehåller. Det finns inte heller någon vyklass, CView, och samma sak gäller här.

 

Vi väljer ‘Single document’ och observerar hur bildexemplet till vänster ändras för att återspegla det vi valt. Vi ser också att ett verktygsfält finns med i exemp­let, men det är en inställning som kommer på en senare sida, och vi har inte gjort den inställningen ännu. I själva verket kan AppWizard läg­ga till många fler finesser i vårt program. Det finns också en möjlighet att välja på vilket språk som felmeddelanden, menyval etc. ska skrivas. Tyvärr har man inte hun­nit till svenska än, så vi får själva översätta de texter som blir aktuella.

 

Var kan man göra resten av inställningarna då? I titelraden står att detta är första sidan. Guiden har olika antal sidor beroende på vilka val man gör. I detta fall blir det 6 sidor vilka man kan bläddra bland med hjälp av knapparna ‘<Back’ och ‘Next>’. Det finns också en knapp ‘Finish’, som man kan trycka på vilken sida man än befinner sig på. Den startar själva programgenereringen.

 

Vi trycker på ‘Next>’, eftersom vi har fler inställningar att göra:

 

 

Denna sida handlar om databashantering, och vi kommer inte att behöva det denna gång.

 

Vi går till nästa sida i stället. Här har vi inställningar för objektlänkning och inbakning av objekt, Object Linking and Embedding, förkortas OLE. Inte heller här gör vi någon inställning.

 

 

Nästa sida däremot handlar om diverse komponenter som kan väljas alternativt väljas bort:

 

 

Vi ska göra ett enkelt exempel, så vi klickar bort de bockar vi hittar, och lämnar alla 'optioner' avstängda. Den som vill kan låta 3D controls vara kvar. Titta på exempelbilden till vänster när du markerar/avmarkerar rutan, så ser du vad den gör.

 

Den näst sista sidan behandlar två helt olika saker, huruvida man vill ha kom­mentarer till den genererade koden eller ej, samt om man vill länka MFC dyn­amiskt eller statiskt:

 

 

Man ska vara rätt bra driven innan man kan hitta i all den kod som genereras innan man klarar sig utan kommentarerna, så det valet lämnar vi 'på'. Där­emot ändrar vi det andra valet. Vi kan generera en programfil som dynamiskt länkas mot en dll som är gemensam för flera programfiler. Detta hokus-pokus betyder att vissa delar som kommer från MFC kan stå i en separat fil vilken kan delas av flera MFC-baserade program. Vårt projekt ska dock endast resultera i en pro­gram­fil, och då kan vi lika gärna länka in MFC på vanligt sätt, d.v.s. statiskt. Därigenom blir det lättare att leverera programfilen eftersom man inte behöver komma ihåg att dll-filen ska följa med. Alla övningar i detta häfte ska genereras med statiskt länkad MFC.

 

Nu är det bara sista sidan kvar:

 

 

Här kan man ändra viss information. En del klasser tillåter att man använder andra namn på de filer som ska genereras, och åter andra tillåter att man ändrar vilken klass man ärver från. Vi ska inte ändra något i detta exempel. Detta är sista sidan, och ‘Next>’-knappen är passiv, så om allt är rätt ifyllt klickar vi i stället på ‘Finish’.

Innan projektet genereras får vi en sammanställning över vad vi har ‘beställt’:

 

 

Vi klickar naturligtvis på ‘OK’. Nu skapas projektet med alla källkodsfiler som behövs för att vi ska kunna kompilera, länka och testa.

 

Det vi just nu gjort kommer inte att diskuteras i detalj i kommande exempel. Endast inställningar som avviker från ovanstående kommer att beröras, och endast första gången en sådan avvikelse förekommer. Meningen är att du ska kunna göra inställningarna på egen hand, och inte bara läsa och klicka.

 

Innan vi gör det ska vi ta och titta på hur vi kan få en överblick av allt vi fått. Nästa avsnitt diskuterar Info View litet djupare än vi gjort hittills.

 

2. Project Workspace.

 

Visste du att man inte är bunden till den layout vi sett med ‘Info View’ till vänst­er, redigering och hjälptexter till höger samt listningar i nederkanten? Förutom att man kan ändra inbördes storlek genom att dra i kanterna och att man kan stänga ett fönster (höger mustangent ger snabbmeny) så kan man visa det i anpassat läge. Dubbelklicka på kanten respektive titelraden, så växlar fönstret läge mellan ‘dockat’ läge och anpassat dito. Det samma gäller för verktygsfälten. Huruvida ett fönster överhuvud taget 'dockar' mot en kant kan anges i [Tools - Options] under fliken 'Workspace'.

 

Om vi nu anpassar ‘InfoView’ upptäcker vi att det egentligen heter ‘Project Workspace’, eftersom det står så i fönstrets titelrad. Titelraden syns ju inte när fönstret är dockat. Det är egentligen bara den flik som innehåller böckerna som heter ‘InfoView’. Vi har dessutom fått fyra flikar nu när vi genererat ett MFC-projekt. Observera att flikarnas namn bara syns om fönstret är tillräckligt brett. Vi sdka titta litet närmre på vad som finns under de olika flikarna. Första fliken heter ‘ClassView’:

 

 

När vi expanderar mappen ‘Hello classes’ ser vi projektets ingående klasser. Det var de vi beställde förut, plus en extra för dialogrutan ‘About…’ (svenska ‘Om…’). Det finns också en mapp ‘Globals’. Expanderar man den finner man alla globalt deklarerade objekt, och där finns just nu endast ett: själva applika­tionen skapas som sådant, och det heter alltid 'theApp'.

 

Expanderar man en klass finner man ingående medlemsmetoder. De metoder som inte är publika markeras med en nyckel om de är deklarerade som ‘protected:’ och ett lås om de är deklarerade som ‘private:’. (Det sistnämnda förekommer inte i bilden ovan.)

 

Dubbelklick på en funktion resulterar i att motsvarande fil öppnas för redigering i redigeringsfönstret (om den inte redan är öppen), och att markören positioner­as på funktionen.

 

Höger mustangent ger en snabbmeny där man bland annat kan lägga till metoder i en klass, söka upp definitioner och referenser och så vidare.

 

Nästa flik heter ‘ResourceView’. Här kan vi se de resurser som ingår i projektet. Det vi ser finns deklarerat i en fil som heter hello.rc i detta exempel. Om man öppnar en annan fil med tillägget ‘.rc’ får vi upp ett motsvarande ‘mappträd’ i redigeringsfönstret.

 

 

Expanderar man ‘Hello resources’ ser man ett antal mappar, en för varje typ av resurs som ingår i projektet. Expanderar man en sådan mapp får man en lista på ingående resurser av angiven typ. Ovan ser vi att AppWizard lagt till en dialog för ‘About…’.

 

Vi har också en meny. Det är den som fått identifikationen ‘IDR_MAINFRA­ME’. Om man dubbelklickar på en resurs öppnas resursredigeraren  i rediger­ings­fönst­ret och resursen läses in för redigering. Vi ska strax göra detta, när vi är klara med ‘Project Workspace’.

 

Den tredje fliken informerar oss om vilka filer som ingår i projektet. Vi expanderar mappen ‘Hello files’ och mappen dependencies som dyker upp:

 

 

Mappen ‘Hello files’ innehåller de filer som ingår i projektet. AppWizard har själv tagit hand om detaljen med ‘Insert Files into Project’. För vår bekvämlig­hets skull finns där även en fil som inte ska kompileras, den ska läsas - av oss. Filen heter ReadMe.Txt. I den filen beskrivs de filer som ingår i projektet. Dubbel­klicka och läs.

 

Mappen ‘Dependencies’ innehåller en lista på de filer som inte ingår direkt i projektet, utan som kommer med via #include eller motsvarande. Filen Hel­lo.ico t.ex. innehåller den bitmap som beskriver programmets ikon. Den ingår genom en instruktion i filen Hello.rc. Den är skriven i ett speciellt språk som läses av en speciell resurskompilator. Om man öppnar den filen i Anteck­ningar kan man finna följande rad, vilken anger ikonen och dess bitmapp:

 

IDR_MAINFRAME ICON DISCARDABLE "res\\Hello.ico"

 

Resursredigeraren klarar inte alla sorters resurser, t.ex. fonter. De som den inte klarar får inte ligga i Hello.rc. Därför läggs sådana separat i en fil som i vårt fall heter Hello.rc2. Den kommer med genom att man sist i Hello.rc lagt in raden:

 

#include "res\\Hello.rc2" // non-Microsoft Visual C++ edited

                          // resources

 

Den fjärde fliken innehåller fortfarande våra böcker. Glöm inte att läsa på egen hand om olika saker där! Det viktigaste du har att lära dig är hur du finner infor­mation du behöver. Behoven kan variera, informationen kan variera, men ing­en­ting kan förändra det faktum att den som kan finna informationen kan lösa uppgiften.

 

3. Menyredigeraren.

 

Nu väljer vi ‘ResourceView’ och dubbelklickar på ‘IDR_MAINFRAME’ i map­pen ‘Menu’. Därigenom startar resursredigeraren för den resursen, vilken är den meny vi får i programfönstret. Så här ser det ut:

 

 

Här ser vi de ‘pop-up’-menyer som ingår i menyn. Det finns tre stycken, samt en tilläggsruta. I tilläggsrutan kan man lägga till en ny ‘pop-up’-meny eller ett meny­val, men det be­höver vi inte nu. Man kan flytta om menyerna genom att dra dem med musen. Först markerar vi ‘Edit’ och tar bort den genom att trycka på 'Delete'-knappen.

 

Därefter öppnar vi 'File'-menyn genom att klicka på den, och tittar vad som finns därunder:

 

 

Observera följande:

 

·     Vi har fått menyval för hantering av dokument. Dessa är kopplade till funktioner i dokumentklassen, så man tar inte bort dem om de kommer att användas, eftersom man måste skapa dessa koppling­ar när man lägger till menyvalen.

·     Vi har ett passivt menyval 'Recent File'. Denna text ska aldrig synas, den ska bytas ut mot senast använda filnamn.

·     Vi har fått avdelare. Dessa betraktas som menyval som inte fungerar. Dubbelklicka på en och betrakta egenskapsrutan. Det finns en egenskap som heter 'Separator', och den är förbockad för avdelare.

·     Även här finns en tom ruta, denna är till för att lägga in menyval eller 'pop-up'-menyer.

 

Vi tar bort alla menyval i 'File'-menyn utom en avdelare och menyvalet 'Exit'. Därefter dubbelklickar vi på den tomma rutan och skriver in '&Hello' i fältet 'Caption' i dialogrutan 'Menu Item Properties' som nu dyker upp:

 

 

Observera ‘&’-tecknet. Det är det som ger oss Alt-snabbtangenter. Titta på menyn, så ser du att ‘H’ i ‘Hello’ har blivit understruket:

 

 

Tryck på ‘Enter’ så stängs dialogrutan ‘Menu Item Properties’. Ett ID-begrepp skapas samtidigt för menyvalet. Det komponeras samman av ‘ID’, menyns och menyvalets namn: ID_FILE_HELLO. Dubbelklicka på ‘Hello’ igen så ser du det i fältet ID. Detta ID-begrepp motsvarar ett numeriskt värde. Det definieras i filen resource.h, vilken inte syns i ‘FileView’.

 

Dra nu upp menyvalet ‘Hello’ till positionen överst i ‘File’-menyn:

 

 

Nu är vi klara med menyerna. Spara och stäng resursredigeraren, så ska vi ta och titta på var ID_FILE_HELLO hamnade. Öppna filen resource.h efter kom-pilering, så finner du raden:

 

#define ID_FILE_HELLO                   32771

 

Vi behöver naturligtvis inte själva hålla reda på detta nummer. Det är meningen att vi ska använda ordet ID_FILE_HELLO i stället. Hur vi använder det i detta fallet ska vi se i ett senare avsnitt, där vi kopplar samman menyvalet med en funk­tion, den funktion vi vill ska utföras när användaren väljer [File - Hello]. Det är naturligtvis i den funktionen vi ska använda MessageBox() för att säga 'Hello Windows'. Det är den funktionen vi redan i förra kapitlet valde att kalla en 'kommandohanterare', eller på engalske: 'command handler'.

 

Men först ska vi testa dialogredigeraren. För att finna en anledning till detta kan vi ändra litet i 'About'-dialogen, och lägga till vårt eget namn i Copyright­klaus­u­len. Detta gör vi i nästa avsnitt.

 

4. Dialogredigeraren.

 

Starta redigering av IDD_ABOUTBOX på motsvarande sätt som du startade redigeringen av menyerna. Så här ska det se ut när den är igång:

 

 

Här syns dialogrutan och ett verktygsfält med diverse kontroller att använda i dialogrutor. Vi ska bara redigera 'Copyright'-texten, vilken finns i en kontroll av typen 'Static text', ett så kallat rubrikobjekt. Dubbelklicka på texten så öppnas dia­log­­rutan för egenskaper igen. Lägg till ditt namn efter texten i fältet 'Caption':

 

 

Tryck 'Enter', stäng och spara resursen. Vi kommer att se effekten av det vi gjort i slutet av nästa avsnitt.

 

5. Koppla menykommando.

 

Nu ska vi se till så att någonting händer när vi väljer [File - Hello]. Vi väljer att lägga in en funktion i klassen CMainFrame. Den funktionen kan göra det sam­ma som vi gjorde i förra kapitlet, nämligen använda en MessageBox() för att skri­va ut texten 'Hello Windows'. Så här gör vi för att lägga till en meddelande­han­terare, 'Message handler', vilket man kallar sådana funktioner:

 

1.   Klicka på fliken 'ClassView' i 'Project Workspace'-fönstret.

2.   Dubbelklicka på någon av funktionerna i klassen CMainFrame, så öppnas filen mainfrm.cpp för redigering. (Man kan också klicka på detta filnamn under fliken 'FileView' eller öppna som vanligt.)

3.   I och med att redigeringsfönstret innehåller en källkodsfil i ett MFC-projekt dyker det upp två kombinationsrutor i fönstrets överkant, ett för objekt och ett för meddelanden, samt en knapp som öppnar motsvarande headerfil. Välj vårt nya objekt:



4.   Under listan Messages väljer vi ‘COMMAND’. I och med att det inte finns någon funktion kopplat till detta kommando visas dialog­rutan ‘Add Member Function:



Observera namnkonstruktionen! Objektets beteckning föregått av ordet ‘On’. Allt enligt standarden med inledande versal på varje nytt ord. Följ denna standard!

5.   Tryck på 'OK' för att acceptera det angivna namnet och skapa funktionen. Ändra inte namnet, standarden är konsekvent och bra.

 

Nu har vi vår funktion. I den finns en kommentar som talar om var vi kan skriva vår kod. Lägg till en MessageBox() efter den kommentaren. Så här ska det se ut:

 

///////////////////////////////////////////////////////////////

// CMainFrame message handlers

 

void CMainFrame::OnFileHello()

{

 // TODO: Add your command handler code here

    MessageBox("Hello Windows!");

}

 

Nu kan vi kompilera, länka och testa vårt program. Så här ska det se ut när man väljer [About - Hello…]:

 

 

Vi kan även prova vårt menyval [File - Hello]:

 

 

Och naturligtvis fungerar [File - Exit] samt alla programfönstrets standardfunk­tioner.

 

Övningsuppgift 2.5.1:

·      Ovanstående moment kommer att ingå i valda delar i alla kommande övningar och kommer då inte att förklaras närmre. Repetera och utför därför allt i dessa fem första avsnitt tills du känner att du behärskar det till fullo.

 

? Läs om resursredigeraren. Testa Help-knappen i egenskapsrutan medan den visar  egenskaperna för olika typer av objekt. Testa Help-knappen i AppWiz­ards olika dialogrutor. Läs i böckerna om AppWizard.

 

6. Programmets beståndsdelar.

 

AppWizard har komponerat samman ett program bestående av objekt som byg­ger på vissa klasser. Dessutom länkas en del färdig kod in i progammet, kod som är likadan i alla MFC-program. WinMain() ingår i sådan kod.

 

MFC innehåller ett meddelandehanteringssystem kallat AFX, Application Framework Exchange. Detta fungerar i klasser som direkt eller indirekt ärver från klassen CCmdTarget, vilket står för Class Command Target, måltavla för kommandoklass. Grundklassen för alla fönster, CWnd, är en klass som ärver direkt av CCmdTarget. Alla fönster ska ju ha en fönsterprocedur, och på detta sätt får de det. För att det hela ska fungera behöver klasserna utnyttja några makro, vilket AppWizard redan lagt in till oss.

 

Innan vi går närmre in på det ska vi se vilka klasser programmet består av:

 

·     CHelloApp. Denna klass beskriver själva huvudprogrammet. Det mesta kommer att länkas in från färdigkompilerade filer, så det finns inte så mycket i denna klass. Klassen ärver av CWinApp, och via CWinThread även av CCmdTarget. Klassen beskrivs i filerna hello.h och hello.cpp.

·     CMainFrame. Sköter programfönstrets ram inklusive meny, verk­tygsfält om det finns samt eventuellt statusfält. Ärver av CFrameWnd som i sin tur ärver av CWnd, vilken ärver av CCmdTarget. Detta faller sig naturligt eftersom programfönstrets ram hanterar meddelanden till programmet i allmänhet, och behöver innehålla kommandohantering. Klassen står beskriven i mainframe.h och mainframe.cpp.

·     CHelloDoc. Denna klass ärver från CDocument och är avsedd att sköta om det data programmet hanterar. Om programmet är en ord­behandlare är dokumentet den text vi skriver, inklusive information om textformat och dylikt. Hanteringen innefattar även att spara på disk, öppna etcetera. För att kunna göra detta krävs även ett par makro, och även dessa har AppWizard lagt in. Klasshuvudet står beskrivet i filen hellodoc.h medan metoderna står i hellodoc.cpp.

·     CHelloView. Windowsprogram presenterar sina dokument i fönster. Detta innefattar en hel del hantering, och dokumentets visuella presentation sköts därför i denna separata klass, vilken ärver från CView. Ett dokument kan visas i fler än en vy, medan en vy alltid hör till ett visst dokument. Dokument och vy hör således intimt samman. Vyn presenterar sitt data i ett fönster vilket antingen visas i anpassat läge i programfönstrets arbetsyta, client area, eller i max­i­merat läge, varvid man uppfattar vyfönstret som om det vore själva arbetsytan i programfönstret.

·     CAboutDlg. Denna klass beskriver 'About...'-dialogrutan. Vi kom­mer att läsa mer om dialogrutor senare, och då får vi veta mer om vad dess implementeringsfil har för syfte. Klassen ärver från CDialog, som är förälderklass till alla dialoger. CDialog ärver i sin tur av CWnd, och har alltså en egen kommandohantering. CAboutDlg beskrivs i implementeringsfilen till CHelloApp, näm­ligen hello.cpp. Denna placering är litet udda, och bryter mot den standard man är van att se om man är erfaren inom objektorienter­ad programmering.

 

Nu skulle det inte vara helt fel att skriva ut dessa filer på papper. Det kan vara bra att ha när man behöver en överblick över hur programmet är gjort, även om vi inte just nu besitter kunskaper nog att förstå allt som står i dem.

 

Övningsuppgift 2.6.1:

·      Skriv ut ovan nämnda filer och häfta samman för framtida referens.

 

Till hjälp har vi nu även ett verktyg som heter ClassWizard. Detta verktyg hjälp­te oss att lägga in den kod som kopplade menyvalet [File - Hello] till funktionen OnFileHello(), samt att skapa denna funktion. ClassWizard 'vet' vad vårt pro­gram har för klasser och hur de ska användas genom sin databas, vilken finns i filen hello.clw.

 

ClassWizard kan användas från flera olika verktyg, och vi har provat ett sätt. Vi kommer att se ett annat sätt när vi lägger in textrutor i dialoger, men det tar vi när vi kommer dit. ClassWizard hade från början inte all denna automatiska koppling via de olika verktygen. I stället gjorde man det mesta via ClassWiz­ards egen dialogruta. Denna finns fortfarande kvar, och ibland är den praktisk att ha till hands. I nästa avsnitt ska vi titta på ClassWizards egen dialog, vilken innehåller fem fli­kar för olika inställningar och tjänster.

 

7. ClassWizard.

[View - ClassWizard...] Ctrl + W

 

Vi har nu använt ClassWizard till diverse olika saker. Låt oss ta en titt på dess huvuddialogruta. Man kan öppna den via menykommandot [View - ClassWiz­ard...] eller snabbtangent Ctrl + W. Dess dialog har fem flikar. Den första fliken behandlar meddelande­hantering:

 

 

Här finns de listrutor vi redan sett i källkodsredigeraren, en för objekt och en för med­del­an­de. Man kan byta klass i listrutan ‘Class name’. Det finns en listruta för klas­sens metoder, och man kan ‘hoppa’ till en funktion genom att dubbelklicka på dess namn, eller markera den och klicka på ‘Edit Code’. En markerad metod kan även tas bort med hjälp av ‘Delete Function’. Man ombeds då att själv ta bort själva funktionskoden, endast Afx-hanteringen tas bort av ClassWizard.

 

Knappen ‘Add Function’ lägger till en funktion, och ‘Add Class’ lägger till en klass. Det är lönt att prova ‘Help’-knappen i denna dialogruta, den ger en kort beskrivning av vad man kan göra med de kontroller som finns under respektive flik. Behöver man mer djupgående förklaring måste man dock leta upp motsvarande hjälpavsnitt på annat sätt.

 

ClassWizard kan här hjälpa oss att associera kontroller med variabler i våra klasser, i syfte att kunna sköta den uppdatering som behövs. Vi kommer att skapa kontroller i en dialogruta, och be ClassWizard skapa en variabel som associeras med kontrollen. Den kod som behövs för att vår variabel ska åter-spegla en kontrolls status eller innehåll lägger ClassWizard in åt oss, och vi behöver inte tänka mer på det.

 

Man kan arbeta från den redigerare som vi använder till att redigera dialogrutan med, och det förfarandet beskrivs i kapitlet 'Dialogruta'. Det går även att skapa variablerna i ClassWizards huvuddialog, andra fliken:

 

 

Object Linking and Embedding (OLE) behandlas i två flikar, ‘OLE Automa-tion’ och ‘OLE Events’. Vi tar inte upp OLE i den här nybörjarkursen, så de flikarna hoppar vi över.

 

Den sista fliken visar information om de olika klasserna:

 

 

Här kan man se vilka filer klassen finns deklarerad i, vilken klass vi ärvt från, och eventuell resurs, om klassen tillhör en sådan (till exempel dialogrutor och menyer).

 

Sammanfattning: ClassWizard hjälper oss att skapa mycket av den kod som är viktig i Windowsprogram. Många av de övriga verktyg vi använder, redigerare och programguide, använder ClassWizards tjänster för vår räk-ning. ClassWizard har en egen dialog med fem flikar. Under dessa flikar kan man nå samma tjänster. Oavsett arbetsmetod är det samma verktyg som utför arbetet åt oss, nämligen ClassWizard.

 

8. AFX meddelandehantering.

 

Vi har redan lagt in ett menyval kopplat till en funktion i MainFrame. Denna funktion visar en MessageBox() som hälsar oss med texten ‘Hello Windows!’. Kom­mer du ihåg hur vi gjorde detta i kapitel 1, när vi skrev hela Windowspro­grammet själva? Först skrev vi in ett 'case' i 'callback'-funktionen, eller ‘fönster­proceduren’ som vi kallade den:

 

...

case WM_COMMAND:

switch(wParam)

{

case ID_MENY_MENYVAL:

OnMenyMenyval();

break;

...

 

Huvudswitchen känner igen meddelandet WM_COMMAND och därunder finns en switch som avgör vilket kommando som angivits. I Den underliggande switchens aktuella 'case' anropas en funktion, en så kallad kommandohanterare. Den funktionen skrev vi själva:

 

void OnMenyMenyval()

{

MessageBox(NULL, "Hello Windows!", "Hello Message", MB_APPLMODAL);

}

 

I ett MFC-projekt sker detta på ett litet annorlunda sätt, men resultatet måste bli detsamma: När ett kommando angivits ska en funktion, en kommandohanterare, anropas. Klassen CCmdTarget har hand om själva meddelandemottagandet. Vi har fått detta automatiskt av AppWizard. Efter att vi låtit ClassWizard lägga in en hantering av menykommandot 'Hello' i 'File'-menyn ser det ut så här i filen mainframe.cpp, om vi nu lade kommandohanteringen i MainFrame:

 

...

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)

//{{AFX_MSG_MAP(CMainFrame)

ON_COMMAND(ID_FILE_HELLO, OnFileHello)

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

...

 

Vi ska strax titta litet mer på vad detta betyder, men först ska vi konstatera att i samma fil finner vi även den funktion som ska anropas:

 

void CMainFrame::OnFileHello()

{

// TODO: Add your command handler code here

}

Och det var i den vi lade till vårt anrop till MessageBox. Ta nu fram dina utskrifter som du gjorde i övning 2.6.1 och sök upp dessa två kodstycken.

 

Om vi nu tittar på det första avsnittet ovan, ser vi genast en ovanlighet: Koden i fråga står inte i en metod. Ska detta tolkas som att den är global i klassen? Nej, inte nödvändigtvis. Första och sista raden i avsnittet är nämligen två makro:

 

...

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)

...

END_MESSAGE_MAP()

...

 

Dessa har lagts in av AppWizard. De innehåller den kod som behövs för att im­plementera själva fönsterproceduren. Observera att varje klass som ärver från CCmdTarget har en egen fönsterprocedur, vilken implementeras med dessa mak­ro. Det första makrot tar två argument, och det är därigenom som de olika fönsterprocedurerna får olika utseende trots att koden finns färdigskriven. Vi ska inte fördjupa oss i hur de fungerar, eftersom detta är en nybörjarkurs.

 

I stället tittar vi på två kommentarer:

 

//{{AFX_MSG_MAP(CMainFrame)

...

//}}AFX_MSG_MAP

 

ClassWizard söker i källkoden efter kommentarer som börjar med //{{ respek­tive //}}. Dessa inleder respektive avslutar avsnitt i källkoden där ClassWizard redigerar in kod som vi beställt genom dess tjänster. I dessa kommentarer står ett nyckelord som meddelar ClassWizard vilken typ av kod som ska stå här, i detta fall kod för meddelandehantering, det vill säga kod som kopplar ett kommando till en kommandohanterare. I vårt fall har vi redan beställt en hantering, och raden som står däremellan är följande:

 

ON_COMMAND(ID_FILE_HELLO, OnFileHello)

 

ON_COMMAND är även det ett makro, vilket tar kommandots identifiering och funktionens namn för att skapa ett 'case' i den switch som väljer kommando enligt informationen i wParam.

 

Dessa makro skapar någonting som antagligen liknar det vi skrev själva i första kapitlet. Vi behöver inte veta mer för att kunna använda det, utan behöver bara följa 'receptet'. Det är viktigt att inte ändra kod som ClassWizard lägger in, eller att ändra de kommentarer som AppWizard lagt till. Man måste veta vad man gör innan man manipulerar det som står här, och i regel klarar verktygen allt år oss.

 

Nu ska vi se hur man kan lägga in meddelanden i andra objekt. Hur valde vi vilket objekt funktionen skulle hamna i? Jo, vi öppnade filen MainFrm.cpp (t.ex. genom att dubbelklicka på någon medlemsfunktion tillhörande CMain­Frame i Class­View), och valde objektet ‘ID_FILE_HELLO’ och meddelandet ‘COMMAND’ i de två listrutor som finns i redigeringsfönstrets verktygsfält. (Se avsnitt 6, ‘koppla menykommando’.)

 

Detta verktygsfält kommer automatiskt fram om man redigerar en fil innehål­lan­de fullständig deklaration av de medlemsfunktioner som ingår i en klass som finns med i ClassView. En sådan fil kallas ‘implementeringsfil’. Verktygsfältet innehål­ler även en knapp märkt ‘.h’, vilken öppnar motsvarande klass headerfil, den fil som innehåller klasshuvudet eller klassbeskrivningen. (Har du tänkt på att man kan ha flera filer öppna i redigeringsfönstret samtidigt, och växla mel­lan dem med hjälp av <Ctrl + Tab>? Redigeraren har således MDI, Multiple Document Interface.)

 

Vi ska experimentera litet med detta i nedanstående två övningsuppgifter, men först ett par ord om MessageBox(). Funktionen MessageBox() är långt ifrån ny. Den har använts i äldre versioner av Windows, och är fullt tillräcklig när man skriver programmen helt själv. Om man använder AppWizard och MFC funger­ar den dock inte i alla klasser. Man har skapat en ny funktion som är 'AFX-an­pas­sad', och som fungerar i alla tillämpliga klasser i MFC. För att inte förlora kompatibilitet med äldre projekt finns den gamla versionen MessageBox() kvar, och för att vi ska kunna ange att vi vill använda den nya måste den ha ett annat namn. Man har valt att kalla den AfxMessageBox(). Slå upp den i hjälpen innan du går vidare. Den skiljer sig litet i argumentlistan.

 

Och nu två övningsuppgifter som går ut på att lägga till kommandohanterare i dokumentklassen respektive vyklassen:

 

Övningsuppgift 2.8.1:

·      Lägg på motsvarande sätt till ett menyval ‘Dokument’ direkt under menyvalet ‘Hello’. ID ska bli ‘ID_FILE_DOKUMENT’.

·      Koppla menyvalets kommando till en funktion i klassen CHelloDoc på samma sätt som vi gjorde med ID_FILE_HELLO.

·      Lägg in en AfxMessageBox() i den nya funktionen. Texten ska vara ‘Detta är dokumentet!’. (Vi ska strax diskutera varför du ska lägga till bokstäverna 'Afx' i början av 'MessageBox'.)

·      När du kompilerar kommer först resurserna att kompileras om. Lägg sedan speciellt märke till vilka filer som kompileras. Det är bara de filer som ändrats som kompileras.

·      Öppna de filer som kompilerades om och försök hitta de ändringar som Class­Wizard lagt in. Ändringarna kan finnas i såväl headerfil som implementerings­fil.

·      Om du inte hittar alla ändringar, skriv ut filerna på papper. Du får en chans till i nästa uppgift, och då kan du jämföra med dessa utskrifter.

 

Övningsuppgift 2.8.2:

·      Lägg på motsvarande sätt till ett menyval som aktiverar en funktion i ‘Vyn’ (CHelloView). Funktionen ska skriva ut meddelandet ‘Detta är vyn!’.

·      Observera kompileringen igen, leta upp ändringarna och använd eventuella utskrifter från föregående exempel.

·      Jämför med diskussionerna i detta avsnitt.

 

? AfxMessageBox().

 

9. Variablernas plats.

 

Vi har redan sett att ett objekt bör innehålla de variabler som beskriver objektet, de utgör objektets ‘attribut’. När vi nu har ett program som består av ett antal objekt, applikation, programfönster, dokument och vy, så måste vi ta ställning till vilka variabler som hör till vilka objekt. Vi måste tänka i termer som ‘vilket obj­ekt hjälper denna variabel att beskriva?’, ‘vad behöver detta objekt för attri­but?’ och så vidare.

 

När vi senare lägger till nya objekt, till exempel en dialogruta, kan man kanske tänka att avgörandet är lätt, alla nya variabler hör naturligtvis till det nya obj­ektet. Vi kommer dock att se att det inte är helt självklart att det är så i ett kom­mande kapi­tel.

 

Vi börjar från början. Klassen CHelloApp innehåller från början allt som behövs för att initiera och köra programmet. Det är vanligt att man inte lägger några nya variabler alls här, även om det kan finnas situationer då det kan vara påkallat. Den klassen behöver vi egentligen bara för att kunna göra egna initieringar, vilket vi inte tar upp här.

 

CMainFrame är klassen för vårt programfönster. Här sker meddelandehantering etcetera. Om man inte använder arbetsytan (engelska client area) till något speciellt, utan bara som ett utrymme där dokumentet visas, så behöver man inte heller här lägga till några variabler.

 

Förutom About-dialogen är det nu bara två klasser kvar, dokumentet och vyn. Som hörs på namnet är dokumentet avsett att innehålla vårt dokument. Det vi bearbetar i programmet lägger vi in här. Skriver vi en ordbehandlare bör dok­umentet användas för att administrera vår text. Det finns som sagt speciella funktioner i klassen CDocument (vilken vår dokumentklass CHelloDoc ärver av) för dokumenthantering, till exempel spara på disk och hämta från disk.

 

Titta i avsnittet CDocument Class Members, och skaffa dig en allmän uppfatt­ning om hur informationen är indelad, och vilka medlemmar som finns.

 

Till sist har vi vyn, CHelloView, vilken vi låtit ärva av CView (förvalt). Denna klass hanterar det grafiska gränssnittet, det vill säga det vi ser på skärmen. Ofta inne­håller vyn en delmängd av dokumentet, till exempel en post i en lista, där hela listan finns i dokumentklassen, och den post som syns på skärmen finns i vyn. Vanligt är dock även att man använder variabler direkt från dokumentet.

 

I vyn finns funktioner för visning på skärmen. De finns listade i hjälpen under ‘CView Class Members’, och de är många. Slå upp dem i hjälpen för en allmän orientering.

 

I CDocument och CView finns funktioner för korsreferens mellan de olika klass­er­na (egentligen mellan de objekt som skapas med de ärvande klasserna som mall). De fungerar så att de ger adressen till det andra objektet. Behöver man nå någon publik medlem i dokumentet från vyn, så behöver man adressen till vyn. Den kan man få med hjälp av funktionen GetDocument(). Slå upp den i hjälpen.

 

Vi ska nu testa att använda GetDocument(). Enligt hjälpen returnerar den en pekare av typen CDocument*. Det betyder att vi kan nå en publik medlem i dokumentet med hjälp av den indirekta operatorn, '->'. Om vi till exempel har en variabel m_iVar i dokumen­tet kan vi i en funktion i vyn deklarera en pekare avsedd att peka mot CHelloDoc, och sedan använda den för att komma åt variabeln:

 

// Detta sker i en funktion i CHelloView:

CHelloDoc *pDoc = GetDocument();

pDoc->m_iVar = 1; // Tilldela dokumentets variabel värdet 1.

 

Detta kräver naturligtvis att CHelloDoc är känt när CView kompileras. De kompileras inte samtidigt, utan en i taget. Därför finns kompileringsdirektivet #include "HelloDoc.h" med i HelloView.cpp, tillsammans med #include "HelloView.h" och #include "Hello.h".

 

För att testa variabeln lägger vi upp en ny meny som har menyval för att sätta vissa fasta värden, samt ett för att rapportera värdet via en AfxMessageBox(). Ob­servera att AfxMessageBox() inte understödjer intelligent stränghantering, så vi gör i stället en switch som använder värdet i variabeln för att skriva ut en av flera med­delanderutor med olika fasta texter som talar om vilket värde variabeln har, t.ex:

 

AfxMessageBox("m_iVar innehåller värdet 1.");

 

Övningsuppgift 2.9.1:

·      Skapa en publik variabel av typen int i CHelloDoc. Den ska heta m_iVar. Initiera den till noll i dokumentets konstruktor.

·      Skapa en ny meny som heter ‘Variabel’.

·      Lägg in menyvalen ‘Ett’, ‘Två’, ‘Tre’ och ‘Visa Aktuellt Värde’.

·      Koppla de fyra menykommandona till funktioner i CHelloView.

·      I alla fyra funktionerna skapas en lokalt deklarerad pekare pDoc som tilldelas adressen till dokumentet.

·      Skriv i de tre första funktionerna in kod som ger variabeln m_iVar i CHelloDoc värdet 1, 2 respektive 3.

·      Skriv i den sista en switch som testar på variabeln m_iVar i CHelloDoc och i tre olika fall, 1, 2 respektive 3, skriver ut meddelanderutor med texten ‘m_iVar innehåller värdet 1.’, ‘m_iVar innehåller värdet 2.’ Respektive ‘m_iVar innehåller värdet 3.’.

·      Om man inte har valt något värde innan man väljer menykommandot ‘Visa Aktuellt Värde’ ska meddelandet ‘V.G.  välj ett värde först!’ visas. Detta kan ske i samma switch där vi har ett ‘case 0:’ eftersom vi i första punkten initierade m_iVar till 0.

·      Så här ska det se ut om man valt först ‘Två’ och sedan ‘Visa Aktuellt Värde’:

 

 

Observera att du inte behöver deklarera variabeln på vanligt sätt. Prova gärna att klicka med höger mustangent på namnet ‘CHelloDoc’ i ClassView, och välj ‘Add Variable…’ i menyn som dyker upp:

 

 

På motsvarande sätt kan man lägga till medlemsmetoder.