2. Ny resurs.
3. CDialog.
5. DoModal().
Man kan inte använda Windowsprogram särskilt länge innan man stöter på en dialogruta. En dialogruta är ett fönster med kontroller, vanligen avsedda att göra inställningar av olika slag. De flesta program använder dialogrutor, och nästa alla program har en 'About...' eller 'Om...' där version och copyright anges. I den dialogen kan man i regel inte göra några inställningar. Den brukar bara ha en OK-knapp. Även Windows självt har många dialogrutor. Om man till exem-pel använder 'Kör' till att starta ett program dyker genast en dialogruta upp där man kan ange programnamnet.
Det finns oräkneliga dialogrutor med olika utseende, men de har alla en sak gemensamt: de har kontroller. Det kan vara tryckknappar, textrutor, kryssrutor, listrutor och så vidare. Själva dialogrutan är som sagt ett fönster, så ser man tekniskt på den så ska den baseras på en klass som ärver av CWnd, vilket den också indirekt gör, via klassen CDialog.
Klassen CDialog gör bland annat vissa standardinställningar, som till exempel att fönstret ska ha en tunn ram etcetera. Viktigare är dock att den hanterar kontrollerna. Varje kontroll är ett litet fönster i sig. En tryckknapp till exempel, baseras på klassen CButton, vilken ärver av CWnd, och därmed av CCmdTarget. Den har således en egen fönsterprocedur, och det är upp till dialogrutan att reagera på att knappen 'säger' att användaren har klickat på den. Knappen kan i sin kommandohanterare för WM_LBUTTONDOWN skicka ett meddelande till förälderfönstret, det vill säga själva dialogrutan, varvid det i sin tur måste innehålla kod som reagerar på att man tryckt på knappen. Förälderfönstret är av en klass som ärvt från klassen CDialog, vilken har kod som tar hand om meddelandet. Därefter lägger vi själva till det som till sist ska 'hända'.
Definition: En dialogruta är ett fönster vilket ärver av klassen CWnd via klassen CDialog. I dialogrutan finns kontroller vilka ärver av klassen CWnd. Det finns olika klasser för olika kontroller, till exempel en tryckknapp är ett objekt av typen CButton., en textruta är av typen CEdit och så vidare. En dialogruta består således av ett antal fönster som samarbetar. |
Dialogrutans utseende beskrivs av ett så kallat 'template'. Detta är en resurs, och finns i en fil med samma namn som projektet, och med ett filnamnstillägg .rc, där 'rc' står för resource. Tittar vi i exemplet från föregående kapitel så finns det faktiskt en dialogruta där, nämligen 'About...'. Projektet heter 'Hello', så filen heter alltså Hello.rc. I den finns kod skrivet i ett speciellt språk för resurser. Dialogen beskrivs så här:
IDD_ABOUTBOX
DIALOG DISCARDABLE 0, 0, 217, 55
STYLE
DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION
"About Hello"
FONT 8,
"MS Sans Serif"
BEGIN
ICON IDR_MAINFRAME,IDC_STATIC,11,17,20,20
LTEXT "Hello Version 1.0",IDC_STATIC,
40,10,119,8,SS_NOPREFIX
LTEXT "Copyright © 1997",IDC_STATIC,40,25,119,8
DEFPUSHBUTTON "OK",IDOK,178,7,32,14,WS_GROUP
END
Om du vill titta i filen måste du göra en speciell inställning i filvalsdialogrutan innan du öppnar filen. Längst ner finns en kombinationsruta som heter 'Open As:'. Den är inställd på 'Auto'. Det innebär att filtypen bestämmer hur filen öppnas, och vi öppnar bara ett resursträd. Vill vi se ovanstående kod måste vi ändra till 'Text'.
I filen beskrivs även övriga resurser. Språket är inte C++. Det är ett specialspråk för resurser, och en speciell kompilator översätter den när vi väljer 'Build', den så kallade resurskompilatorn, rc.exe. När den startar står det 'Compiling resources...' i listfönstret.
Allt detta kan vara bra att känna till, speciellt om man vill finjustera kontrollerna i en dialogruta. Som synes ovan står alla koordinater angivna för varje kontroll. Vi behöver dock inte veta så mycket, eftersom detta sköts av Developer Studios dialogredigerare och ClassWizard, så vi ska i stället koncentrera oss på hur vi använder dessa verktyg till att åstadkomma en dialogruta. Du får sedan gärna öppna resursfilen i textläge för att se vad de inställningar du gjort ser ut i det speciella resursspråket. Innan du gör det bör du stänga resursredigeraren, eftersom det inte går att redigera samma fil med två redigerare samtidigt.
[Insert - Resource...] Ctrl + R, Ctrl + 1
Man kan skapa en ny resurs, i detta fall en dialogruta, med hjälp av ett menykommando, snabbtangent eller verktygsknapp enligt ovan. Om man klickar på verktygsknappen för ny dialog, eller trycker på snabbtangenten Ctrl+1 skapas en ny dialog direkt, men om man väljer menykommando eller snabbtangenten Ctrl+R öppnas vissa valmöjligheter via dialogrutan ‘Insert Resource’:
Som synes finns ett antal standarddialoger att välja bland, samt möjlighet att importera och sedan anpassa en dialogruta. Dialogen 'Insert resource' är till för alla de resurstyper som resursredigeraren kan hantera, inte bara dialogrutor.
Om vi dubbelklickar på ‘Dialog’, eller om vi från början använde verktygsknappen eller Ctrl+1, startas dialogredigeraren:
Liksom vi fann färdiga menyval i menyn vi redigerade i andra kapitlet, finner vi här två färdiga knappar: OK och Cancel. Samma sak gäller för dem som för menyvalen: de är redan kopplade till vissa funktioner, och vi tar bara bort dem om vi är säkra på att vi inte kommer att använda dem. Annars blir vi tvungna att 'koppla rätt' när vi senare lägger till dem igen. Vi ska behålla dem i vårt exempel.
Här finns också ett verktygsfält med diverse kontroller. En gul informationsruta visas om man håller muspekaren över en verktygsknapp, samtidigt som en något fylligare information syns i statusraden. Vi ska just nu intressera oss för två sorters kontroller: 'Edit Box' och 'Button'.
Man kan lägga till en kontroll på flera olika sätt. Man kan kopiera en befintlig kontroll och göra lämpliga ändringar i kopian, man kan trycka in en knapp och sedan 'dra ut' kontrollens form i dialogrutan och därigenom ge den vissa dimensioner. Man kan även trycka ned en verktygsknapp och dra den till den position där man vill ha den, så skapas kontrollen med fördefinierade dimensioner. Vill man snabbt skapa många kontroller av samma typ kan man hålla ned 'Ctrl'-tangenten när man klickar på verktygsknappen. Denna stannar sedan i nedtryckt läge tills man klickar på den knapp som har en muspekare. Innan man trycker på sistnämnda kan man alltså lägga till flera kontroller av den valda typen.
I nederkanten har vi fått ett annat verktygsfält som hör till dialogredigeraren:
Längst till vänster finns en testknapp som gör att man kan testa hur dialogrutan kommer att se ut när man använder den. Man lämnar sedan testläget med hjälp av Esc. Testlägen kan även aktiveras och avaktiveras med snabbtangenten Ctrl+t. Längst till höger finns en knapp som visar/gömmer linjalerna, och bredvid den en knapp som aktiverar/stänger av rutnätet. Alla knappar däremellan är till för att justera markerade kontroller på olika sätt. Vi har dessutom fått en extra meny 'Layout', som bland annat har motsvarande funktioner.
Man kan markera kontroller genom att klicka på dem, eller genom att dra ut en ram runt dem. Vill man markera flera kontroller kan man låta ramen innefatta de kontroller man vill ha markerade, eller klicka på kontrollerna en i taget med Ctrl-tangenten nedtryckt.
Dubbelkickar man på en kontroll, eller trycker på 'Enter' när den är markerad, öppnas egenskapsrutan för kontrollen.
Så här ser det ut om man öppnar egenskaperna för 'OK'-knappen:
Här finns en knapp i vänstra övre hörnet, med en anslagstavlenål. Om man trycker in den kommer egenskapsrutan att vara öppen hela tiden. När man sedan klickar på olika objekt byter egenskapsrutan utseende och innehåll allteftersom den visar de olika objektens egenskaper. När man lämnar egenskapsrutan med ‘Enter’, eller klickar utanför rutan, uppdateras kontrollen enligt de ändringar man gjort.
Knappen med frågetecknet leder till ett hjälpavsnitt som i korthet beskriver de egenskaper man kan ställa in. Egenskapsrutan har tre flikar för en tryckknapp, men för vårt exempel behöver vi bara använda den första.
Observera att knappen har ett ID angivet, nämligen IDOK. Det är via detta ID som programmet kan veta vilken knapp användaren klickat på. Detta ID används på samma sätt som de ID vi använde för menykommandon.
Övningsuppgift
3.2.1:
· Skapa ett nytt projekt typ 'Single document'. Kalla projektet Dialog.
· Ta bort 'Edit'-menyn och döp om 'Help'-menyn till 'Hjälp'.
· Redigera 'File'-menyn så att den heter 'Arkiv' och innehåller menyvalen 'Visa Min Dialog', 'Visa Texten' samt 'Avsluta'. Den sista gör du genom att döpa om 'Exit'.
· Skapa en dialogruta och redigera den enligt följande:
·
Skapa en tryckknapp med texten ‘Skriv’ och id = IDC_SKRIV.
·
Skapa en tryckknapp med texten ‘Sudda’ och id = IDC_SUDDA.
·
Lägg till en textruta (Edit Box) med id = IDC_TEXT.
·
Anpassa storleken på kontrollerna och dialogrutan så att alla kanter
passar snyggt och prydligt.
· Du kan lämna projektet öppet, vi ska fortsätta i nästa avsnitt.
· Så här kan det se ut:
Just nu kanske det passar att dialogrutan heter 'Dialog', men jag kan knappast tänka mig någon större användning av just det namnet. Om man dubbelklickar på dialogrutan, men utanför kontrollerna, så visar egenskapsrutan själva dialogrutans egenskaper. Är egenskapsrutan fastnålad räcker det med att klicka på dialogrutan någonstans där det inte finns någon kontroll, så växlar egenskapsrutan till att visa dialogrutans egenskaper. Själva dialogen måste ju också ha egenskaper. Här kan man bland annat ange rubrik i titelraden, typsnitt för kontrollerna och så vidare.
Dialogrutan måste representeras av ett objekt som innehåller de funktioner som den behöver. Objektet skapas efter en mall, en klass, som vanligt. Vi skapar klassen genom att ärva från MFC-klassen CDialog. Man kan när som helst lägga till en klass, och vill vi lägga till en till vår dialogruta är det speciellt fördelaktigt att starta ClassWizard när dialogredigeraren är öppen för redigering, för då kommer allt att vara förberett för det vi vill göra:
Vi behöver skapa en ny klass, så det är bara att klicka på ‘OK’:
I dialogrutan Create New Class som då dyker upp (se ovan) kan man ange det klassnamn man vill att dialogrutan ska ha. Vi kan till exempel kalla den CMinDialog. ClassWizard genererar då ett klassarv enligt följande:
class
CMinDialog : public CDialog
Base class är redan förvalt till CDialog. Ändra inte det, för då får du inte rätt förälderklass. Filnamnen för klasshuvudet och implementeringsfilen är föreslagna till samma namn som klassen, förutom det inlidande C:et. I detta fall skulle de komma att heta MinDialog.h respektive MinDialog.cpp. Man har möjlighet att ändra det om filnamnen inte faller en i smaken.
Observera dialogrutans id. Det är ifyllt med det vi angivit, eller rättare sagt inte angivit i vårt fall. När vi skapade dialogrutan fick den id = IDD_DIALOG1. Vi kunde ha ändrat det tidigare, men här får vi inte ange annat än vad dialogrutan verkligen heter, så nu ändrar vi det inte. När vi i framtiden gör dialoger som har ett verkligt syfte ger man den ett id som har ett värde som associeras till vad dialogrutan är till för, till exempel IDD_EGENSKAPER, IDD_PALETT, IDD_FONT och så vidare.
Sist har vi en grupp för Component Gallery. Där är förbockat att spara vår nya resurs, vilket kan vara vettigt om man skapar resurser som är bra att ha. Denna dialog är inte mycket att spara, så jag har klickat bort den bocken. Component Gallery innehåller de standardresurser vi kunde välja på i dialogrutan 'Insert Resource'. Vi kan alltså spara våra resurser för framtida bruk och därmed utöka de möjligheter vi har nästa gång vi skapar en resurs. Slå upp Component Gallery i hjälpen och läs mer om detta verktyg.
Övningsuppgift 3.3.1:
· Om du stängde dialogredigeraren och/eller projektet i förra övningsuppgiften är det bara att öppna projektet Dialog och öppna dialogrutan för redigering igen.
· Skapa en klass till dialogrutan.
· Kalla klassen CMinDialog.
· Läs informationen i ClassWizards Class Info-flik.
· Stäng ClassWizard.
· Kontrollera i FileView att de nya filerna har skapats.
· Kontrollera att klassen har lagts till i ClassView.
· Skriv ut de två filerna på papper så att du senare kan se vilka tillägg ClassWizard gör.
· Du kan lämna projektet öppet, vi ska strax fortsätta med det.
I klassen CMinDialog finns enligt ClassView två funktioner: konstruktorn samt DoDataExchange():
Den senare har samma typ av makro som vi känner igen från de andra klasserna. Varje objekt har sin egen fönsterprocedur registrerad, och detta gäller även vår nya dialogruta:
void CMinDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CMinDialog)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
Vid
kommentaren...
// NOTE: the
ClassWizard will add DDX and DDV calls here
...lägger ClassWizard automatiskt in de kopplingar som behövs mellan våra variabler och dialogrutans kontroller, vilka som sagt egentligen är 'child windows' till dialogrutan, och har egna fönsterprocedurer.
ClassWizard lägger in speciella anrop här vilka kopplar kontrollerna till variabler, så kallade DDE-anrop, Dynamic Data Exchange. Dessa administreras av AFX och aktiveras av funktionen DoDataExchange(). Olika typer av kontroller har olika typer av variabler. En del kontroller kan ha flera variabler, en del inga alls, medan åter andra har en. Det är här som de variabler vi kan se och manipulera i ClassWizards andra flik kommer in i bilden. Vi ska dock här använda en genväg för att deklarera variablerna.
I resursredigeraren dubbelklickar man på en kontroll samtidigt som man håller Ctrl-tangenten nedtryckt. Då öppnar ClassWizard en speciell dialogruta för tillägg av medlemsvariabel för kontrollen i fråga. Så här kan det se ut när man vill lägga till en variabel som är avsedd att innehålla den text som ska synas i vår textruta:
Variabelns namn föreslås börja på 'm_', vilket är det vi vant oss vid när det gäller medlemsvariabler. Man kan skapa en variabel av två olika kategorier, 'Value' eller 'Control'. 'Value' ger oss det vi nu behöver, en variabel att innehålla texten. 'Control' använder man endast om det inte räcker med en standardhantering av kontrollen, utan man vill han tillgång till kontrollens objekt direkt. Observera att det finns en funktion i CDialog som ger oss adressen till valfri kontroll, så att man kan nå den via pekare, utan att skapa ett objekt.
Variabeln kan vara av olika typ. Tidigare versioner av Visual C++ understödde endast text i en textruta, men nu har vi tillgång till omvandling mellan olika datatyper. Text hanteras i en klass som heter CString, och det är den datatypen vi ska välja.
Övningsuppgift 3.3.2:
· Om du stängde dialogredigeraren och/eller projektet i förra övningsuppgiften är det bara att öppna projektet Dialog och öppna dialogrutan för redigering igen.
· Koppla en variabel m_Text till textkontrollen. Den ska vara av typen CString.
· Stäng dialogredigeraren.
· Öppna dialogrutans implementeringsfil och titta i metoden DoDataExchange(). Identifiera AFX-kopplingen, jämför med utskriften ovan.
· Titta i construktorn. Där finns den rad som initierar m_Text. Ändra så att m_Text initieras till "Starttext.".
· I redigeringsfönstrets verktygsrad kan man välja olika objekt-id i en så kallad 'drop-down-list'. Klicka på pilen och kontrollera de olika id som finns tillgängliga, känns de igen? Välj det id som motsvarar textrutan, det vill säga IDC_TEXT, och välj den andra 'drop-down'-listen. Som synes finns det ett flertal meddelanden kopplade till en textkontroll.
· Jämför med meddelanden för en av knapparna och observera att det finns ett antal olika meddelanden knutna till en textruta, medan en knapp har bara två.
· Koppla funktioner till knapparna IDC_SKRIV och IDC_SUDDA, meddelande BN_CLICKED. Dessa kommer att heta OnSkriv() respektive OnSudda().
· Öppna dialogrutans headerfil och leta upp den rad där m_Text deklarerats. Du ska inte ändra något här, men jämför med utskriften du gjorde innan. ClassWizard har på egen hand deklarerat variabeln åt dig. Observera att den är av typen CString, en klass vi ska diskutera litet i nästa avsnitt.
· Du kan lämna projektet öppet om du vill, vi ska arbeta vidare i nästa avsnitt.
När du kopplade hanteringsfunktioner till knapparna skapades nedanstående funktioner:
void CMinDialog::OnSkriv()
{
// TODO: Add your control notification handler code here
}
void CMinDialog::OnSudda()
{
// TODO: Add your control notification handler code here
}
I nästa avsnitt ska vi skriva in kod i dessa två hanterare. Meningen är att ett klick på en knapp ska skriva en text i textrutan, respektive sudda ut texten. Först måste vi dock titta litet närmre på hur man får AFX att uppdatera det som står i textrutan gentemot innehållet i variablerna. Detta sker med UpdateData(), vilken gäller för alla kontroller till vilka vi kopplat variabler, och den funktionen kan uppdatera åt bägge hållen, det vill säga från skärm till variabel och från variabel till skärm.
? Slå upp klassen
CString i hjälpen och läs om de olika metoder den har. Läs inte allt, utan
välj ut något intressant som till exempel olika operatorer och tillgång till
objektets textbuffer. Meningen är att du ska lära dig förstå och använda det
som du läser i hjälpen, inte att du ska bli expert på klassen CString.
En kontroll innehåller ofta någon form av variabel som beskriver kontrollens tillstånd. Vi ska titta på hur man arbetar med en textruta, en 'edit'-kontroll, vilken som sagt baseras på klassen CEdit. När man skriver något i en textruta hamnar det man skriver i en variabel avsedd för text. Det finns nu ingen sådan i C++, vilket vi ju redan vet, men det går att skapa en sådan med hjälp av klassmekaniken i språket. Vi behöver nu inte göra detta eftersom Microsoft redan skapat en åt oss, nämligen klassen CString. Klassen CEdit innehåller en variabel av typen CString.
Nu är det bara det att vi normalt inte arbetar direkt med klassen CEdit, och definitivt inte i detta exempel. Vi arbetar med klassen CMinDialog, vilken ärver av CDialog, och det är den som sköter om dialogrutans kontroller. Vi vill emellertid ha en CString i vår klass CMinDialog, och att den ska innehålla samma text som kontrollens variabel.
Detta löses med hjälp av en metod vi ärvt från CWnd, nämligen UpdateData(). Denna funktion tar ett argument av datatypen BOOL. Detta argument kan bara ha ett av två värden, nämligen TRUE eller FALSE. Anger man argumentet TRUE avser man att det som syns på skärmen är korrekt, och programsatsen UpdateData(TRUE); kopierar alla kontrollers variabler till dialogklassens motsvarigheter, förutsatt att vi angivit för Class Wizard som 'bundna' till kontrollerna. Om vi till exempel har en textkontroll och en textruta och vill uppdatera dessa kan det se ut ungefär så här:
I vårt fall blir det precis tvärt om. Vi vill att en knapptryckning ska ändra texten i textrutan. Vi måste i så fall ha en hanterare för knapptryckningen, och i den påverkar vi innehållet i m_Text. Därefter anropar vi UpdateData med argumentet FALSE, så kommer texten att synas i textrutan.
Definition: Funktionen UpdateData() kopierar data mellan kontroller och till dem bundna variabler i dialogruteklassen. Funktionen tar ett argument av typen BOOL. Är argumentet TRUE kopieras kontrollens innehåll till dialogruteklassens variabel. Är argumentet FALSE kopieras dialogruteklassens variabel till kontrollen. |
Alltså: anger man argumentet TRUE hämtas data från dialogrutan och sparas i medlemsvariabeln. Argumentet FALSE har motsatt effekt, det som finns i variabeln syns på skärmen. Detta kan vi testa i våra nyskapade funktioner.
Övningsuppgift 3.4.1:
· Om du stängde projektet i förra övningsuppgiften är det bara att öppna projektet Dialog och filen MinDialog.cpp igen.
· Lägg in följande två programsatser i funktionen OnSkriv():
· En sats som tilldelar m_Text texten ”Nu tryckte du på skrivknappen!”.
· En sats som anropar UpdateData() med argumentet FALSE.
· Lägg in följande två programsatser i funktionen OnSudda():
· En sats som tilldelar m_Text texten ””.
· En sats som anropar UpdateData() med argumentet FALSE.
· Du kan lämna projektet öppet. Vi ska fortsätta i nästa avsnitt.
Nu är det bara ett litet krux kvar, vi kan inte få fram dialogrutan. Det måste ju till för att vi ska kunna se vad allt det vi gjort ser ut. Vi ska titta på hur man kan få fram dialogrutan i nästa avsnitt.
Vi kan fortfarande inte testa dialogrutan. Vi har nämligen inte lagt till någon möjlighet att öppna den i vårt program. Det finns dock en metod i CDialog även för detta, och den heter DoModal(). Men innan vi kan anropa den måste vi göra ett objekt av vår nya klass CMinDialog.
Slå gärna upp klassen CDialog i hjälpen, och läs om dess medlemmar. Läs om hur man anropar DoModal().
Man skapar objektet där man har mest nytta av det. Om dialogrutan innehåller t.ex. inställningar som behövs i dokumentet är det bäst att deklarera objektet globalt i dokumentklassen. I vårt fall lägger vi det till att börja med lokalt i en funktion i vyn. För att kunna använda klassen CMinDialog i vyn måste vi lägga till kompileringsdirektivet #include "MinDialog.h" i början av filen DialogView.h.
Övningsuppgift 3.5.1:
· Om du stängde projektet i förra övningsuppgiften är det bara att öppna projektet Dialog och filen DialogView.cpp igen.
· Koppla menyvalet 'Visa Min Dialog' till en funktion i vyn. (Detta menyval skapade vi i övningsuppgift 3.2.1.)
· Deklarera ett lokalt objekt 'dia' av typen CMinDialog i funktionen.
· Anropa DoModal() för objektet 'dia' med hjälp av elementoperatorn.
· Skriv in #include-direktivet enligt ovan.
· Kompilera, länka och testa. Så här ska det bli:
Prova gärna knapparna. När du trycker på 'Skriv' ska det bli så här:
Trycker man sedan på 'Sudda' försvinner texten igen:
Och var hamnar då texten? Jovisst ja, i m_Text, vilken i sin tur finns i objektet dia. Objektet dia är deklarerat i metoden OnArkivVisamindialog(), som ju finns i filen DialogView.cpp. När den funktionen tar slut försvinner objektet dia och därmed även texten m_Text. Om vi stänger dialogrutan och sedan öppnar igen 'minns' den alltså inte vilken text den innehöll senast:
Det gör ingen skillnad om vi stängde med 'Cancel' eller 'OK'. Vill man att dialogrutan ska 'komma ihåg' sitt innehåll mellan anropen måste man alltså deklarera objektet någon annanstans. Det gör vi i nästa avsnitt.
Om vi testar att stänga dialogrutan medan texten "Nu tryckte du på skrivknappen!" syns i textrutan, och sedan öppna den igen, ser vi att texten återställt till "Starttext". Oavsett om vi avslutar med 'OK' eller 'Cancel' öppnas alltid dialogrutan i ett initialtillstånd varje gång. Anledningen till detta är att objektet ligger lokalt i en funktion. Vill vi att den ska 'komma i håg' sitt tidigare tillstånd, det vill säga variablerna ska behålla sina värden, behöver vi bara se till att objektet existerar så länge vi behöver det.
Därför testar vi att deklarera det globalt i stället. Flytta satsen CMinDialog dia; till DialogView.h och lägg den där under attributes (public;). Om detta ska bli godkänt måste #include Mindialog.h göras på rätt ställe. Vi lägger den i början av DialogView.h. Man skulle kunna tycka att det räcker med att man ser till att den står före #include DialogView.h i DialogView.cpp, men direktivet #include DialogView.h finns med i flera andra implementeringsfiler, så då skulle CMinDialog vara okänd i de filsammanhangen, och vi skulle få ett felmeddelande som säger att kompilatorn inte känner igen datatypen: 'Error: missing dekl. Specifiers'. Tänk på att varje fil som är med i projektet kompileras för sig, och till exempel CDialogDoc.cpp innehåller också ett direktiv #include DialogView.h och i filen Dialogview.h står vår deklaraion av objektet dia, vilket baseras på klassen CMinDialog.
Övningsuppgift 3.6.1:
· Flytta objektdeklarationen till vyns klassdeklaration enligt ovan.
· Flytta #include-direktivet enligt ovan.
· Kompilera, länka och testa så att dialogrutan kan komma ihåg sitt tidigare tillstånd mellan anropen.
· Observera att det inte gör någon skillnad om vi avslutar med 'OK' eller 'Cancel'.
· Du behöver inte stänga projektet...
Varför är det ingen skillnad på knapparna 'OK' och 'Cancel'? Vi har här en dialogruta som ändrar sin medlemsvariabel direkt när man trycker på knappen, så det är 'för sent' att ångra någonting när vi stänger dialogrutan. Däremot kan ju DoModal() returnera ett värde, och då kan man skilja på 'OK' och 'Cancel', för att eventuellt ändra en medlemsvariabel i vyn i stället. Om DoModal() returnerar IDOK kan vi lagra värdet i vyns variabel, annars låta bli. Eftersom texten finns i variabeln m_Text i objektet dia når vi den genom att skriva dia.m_Text. Den variabel vi deklarerar i vyn kan även den heta m_Text.
Innan vi öppnar dialogrutan kan det vara lämpligt att initiera dialogrutans m_Text med vyns m_Text. Nu förlorar vi den text som dialogrutans m_Text initierats med i dialogrutans constructor, den som lyder "Starttext". Initierar därför vyns variabel m_Text i vyns constructor i stället, så har man ett nytt initieringsställe.
Övningsuppgift 3.6.2:
· Om du stängde projektet i förra övningsuppgiften är det bara att öppna projektet Dialog och filen DialogView.cpp igen.
· Deklarera en variabel m_Text i vyn.
· Initiera variabeln m_Text i vyn i vyns constructor med texten "Starttext från vyn".
· Tilldela m_Text i objektet dia värdet i m_Text i vyn före anropet till DoModal().
· Testa returvärdet från DoModal(). Om det är lika med IDOK så ska du tilldela m_Text i vyn värdet i m_Text i objektet dia.
· Kompilera, länka och testa. Första gången ska textkontrollen innehålla texten "Starttext från vyn". Om man stänger dialogrutan med Cancel ska eventuella ändringar inte synas nästa gång dialogrutan öppnas, och stänger man med OK ska eventuella ändringar finnas kvar nästa gång den öppnas.
Nu har du bara påverkat texten genom att trycka på knapparna 'Skriv' respektive 'Sudda'. Om man skriver text i textrutan borde väl även den finnas kvar när man stänger med 'OK'-knappen, och sedan öppnar dialogrutan igen. Det gör den faktiskt också. Prova själv.
Men om man läser i hjälpen om hur det här datautbytet sker så står det att man måste anropa UpdateData() med argumentet TRUE för att medlemsvariabeln ska få samma värde som kontrollen visar. Faktum är att ingen uppdatering sker. Läser man i stället om metoden OnOK() i klassen CDialog, finner man att den uppdaterar medlemsvariablerna. Vi har alltså ändå fått en uppdatering när vi stänger dialogrutan med knappen 'OK'.
Skulle vi ha en dialogruta där vi är beroende av att medlemsvariablerna uppdateras direkt måste man lägga till hanterare som reagerar när man förändrar kontrollens innehåll, och i den hanteraren anropa UpdateData() med argumentet TRUE. Vi kommer att behöva detta i nästa kapitel där vi ska ha en lista med namn och telefonnummer. När man ändrar i namnfältet ska ett namn i listan uppdateras, och därför måste vi 'hämta ner' rätt data från kontrollen innan vi kopierar namnet till listan. Samma sak gäller naturligtvis telefonnumret.
Det sista vi ska göra är att kontrollera att vi verkligen har rätt text i vyn. Därför kopplar vi en hanterare till menyvalet 'Visa Texten', i vilken vi visar värdet i m_Text med hjälp av en AfxMessageBox(). Med detta demonstreras att vi har en koppling mellan två olika klasser i vårt projekt, nämligen CDialogView och CMinDialog.
Övningsuppgift 3.6.3:
· Koppla en hanterare till menyvalet 'Visa Texten' i vyn.
· I denna hanterare ska innehållet i m_Text redovisas med hjälp av en AfxMessageBox.
· Om man till exempel matar in texten "Detta har jag skrivit i min dialogruta!" i dialogrutans textfält och sedan stängt dialogrutan med knappen 'OK' så ska man kunna läsa texten med hjälp av menyvalet 'Visa Texten':
Sammanfattning: I vilka klasser finns nu vår text? Till att börja med finns den i kontrollen, vilken baseras på klassen CEdit. Vad medlemmen heter där vet vi inte, och det vill vi heller inte veta. Vi hämtar ju värdet med funktionsanropet UpdateData(TRUE). Efter det anropet har vi texten i vår variabel m_Text i klassen CMinDialog. Vi har även en variabel m_Text i vyn, och i den funktion där dialogrutan aktiveras kopierar vi texten mellan m_Text i CMinDialog och m_Text i CDialogView. |