4. Kryssrutor

 

 

·     Kryssrutan

 

·     Variablerna.

 

·     Kryssets varaktighet.

 

·     Använda krysset.

 

·     Aktiv/passiv kontroll.

 

·     Synlig/osynlig kontroll.

 

·     Initierad kryssruta.

 

·     Initiera dialog.

 

·     Initiera synlighet.

 

 

 

 

 

 

 

 

 


Kryssrutan                

 

Det finns många aspekter kring användningen av kontroller. Vissa aspekter gäller alla typer av kontroller, vissa gäller en del medan vissa bara gäller för en viss kontroll. Vi kommer i detta och kommande avsnitt behandla en del av dessa i samband med att vi testar några olika kontroller. Redan i förra kapitlet såg vi en del av aspekterna:

 

·     Egenskaperna, vi ändrade egenskaper efter behov.

·     Variablerna, textrutan har en variabel för automatisk AFX-hantering.

·     Varaktighet, det var inte meningsfull i förra kapitlet att skilja på ‘OK’ och ‘Cancel’.

 

Detta och mer därtill kommer vi som sagt att utveckla efterhand som vi tittar på diverse kontroller. Vi börjar i detta kapitel med kontrollen för kryssruta. Under Windows-95 har man visserligen bytt ut krysset mot en bock, men vi behåller den gamla beteckningen i stället för att börja tala om ‘förbockningsrutor’.

 

Vi skapar ett nytt projekt, Kryssa, enligt samma modell som tidigare, med ett menyval för att testa dialogrutan:

 

 

Dialogrutan ska innehålla en textruta, en kryssruta, samt fyra knappar utöver de två som fanns med från början. Glöm inte att skapa en klass CKryssDialog till dialogrutan. Så här ska den se ut:

 

        

 

Lämpliga id för kontrollerna är IDC_ + namnet på kontrollen, till exempel IDC_KRYSS, IDC_TEXT och IDC_VISA.

 

Skapa en ‘control handler’ till menyvalet ‘Testa’, det vill säga en funktion kopplad till ID_FILE_TESTA - COMMAND, vilken skapar ett objekt av dialogrutans klass (CKryssDialog) och sedan anropar objektets DoModal. I detta exemplet lägger vi den i CKryssaView.

 

Observera att dialogrutan har 3D-kontroller. Detta väljer man i AppWizard.

 

Tryck på hjälpknappen i egenskaprutan för var och en av de olika typer av kontroller som finns i dialogrutan (medan du redigerar dialogrutan) och läs om de enklare egenskaper som finns. Hoppa över de speciella som handlar om till exempel OLE etc.

 

Vi har ännu inte någon kod knuten till dialogrutans kontroller, det ska vi påbörja i nästa avsnitt.

 

Passa även på att redigera den dialogruta som du fick automatiskt när du skapade projektet, IDD_ABOUTBOX. Lägg till ditt namn i copyrightsatsen:

 

 

 

 

 

         

 

 

 

 

? Hjälpknappen i egenskapsrutan för en kontroll under redigering.


Variablerna.

 

Vi behöver en variabel till textrutan, precis som förut. Vi behöver också en till kryssrutan. Den är av typen BOOL, som finns definierad någonstans i MFC. Lägg till dessa två. Kalla textvariabeln ‘m_Text’, och kryssvariabeln m_KryssStatus. Du kommer väl ihåg att man kan dubbelklicka på en kontroll under redigering, samtidigt som man håller ‘Ctrl’-tangenten nedtryckt, så får man en genväg till att lägga till variabler till kontrollen:

 

 

 

 

 

 

 

 

 

 

         

 


Annars kan man ju använda ClassWizard, både med och utan att ha dialogredig­er­ar­en aktiv:

 

 

 

 

 

 

 

 

Observera att man kan lägga till och ta bort variabler under denna flik. Det finns även möjlighet att till exempel maximera längden på en text som tillhör en textruta. Klas­sen CString innehåller längdkontroll. Om man har fler än en variabel knuten till en kontroll ser man lika många rader med kontrollens id i denna ruta. Vi ska sen­are i detta avsnitt testa att använda två variabler till kryssrutan.

 

 

 

 

 

? Tryck på Help i ClassWizards dialogruta när fliken ‘Member Variables’ är vald och läs om de olika optionerna.


Kryssets varaktighet.

 

Det går bra att kompilera, länka och testa programmet nu. Observera att alla kanppar utom ‘OK’ är utan funktion. Det går dock att kryssa i kryssrutan. Dock är det så att vi alltid finner kryssrutan utan markering när vi öppnar dialogen, även om vi tidigare kryssat för kryssrutan, och avslutat dialogrutan med ‘OK’.

 

Det är dock lätt att få behålla dialogrutans variabler, och en av dem återspeglar ju kryssrutans status, m_KryssStatus. Observera att UpdateData() anropas med ar­gumentet FALSE när dialogrutan visas, och med agumentet TRUE när man klick­ar på ‘OK’. Detta betyder dock inte att vi har en genväg till att kunna ångra in­stäl­lningarna i en dialogruta i alla lägen. Vi kommer att visa text i textrutan, och som vi såg i föregående kapitel så måste vi själva anropa UpdateData() med ar­gumentet FALSE, och då uppdateras m_KryssStatus också. Alla variabler påverkas näm­ligen av UpdateData().

 

Som vi såg i föregående kapitel är det bara att deklarera dialogruteobjektet på ett ställe där det existerar även efter det att vi stängt dialogrutan. I detta fall lägger vi objektet i CKryssaView, och kallar det m_KryssDialog.

 

Kommer du ihåg att du kan använda ClassWizard till att leta upp dina funktioner? Dub­belklicka på funktionsnamnet i ClassView, eller, om du har ClassWizards hu­v­ud­dialogruta öppen med första fliken ‘Message Maps’ aktiv, så kan du mar­kera funk­tionen och klicka på ‘Edit Code’.

 

Du ska alltså skriva in satsen:

 

CKryssDialog m_KryssDialog;

 

i filen MainFrm.h under ‘public’ efter kommentaren ‘Attributes’. Glöm inte att flytta ‘#include ”KryssDialog.h”’ till samma fil.

 

Nu kan du kompilera, länka och testa. Klickar du på ‘OK’ behåller kryssrutan sin status, men om du avslutar dialogrutan med hjälp av avstängningsknappen i övre högra hörnet, så behålls inte den nya statusen (UpdateData() anropas inte).


Använda krysset.

 

De flesta dialogrutor är till för att göra inställningar som används på andra ställen i programmet. Då finns två strategier för att lagra variablerna.

 

1.   Man kan lagra variablerna i dialogrutan och sedan via dialogrutans objekt eller pekare mot detta läsa variablerna, direkt eller via accessfunktioner.  Denna metod möjliggör även att man skriver variablerna, varigenom man kan göra inställningar i förväg, vilka kan variera efter programmets behov. (Andra förinställningar kan naturligtvis göras i dialogrutan, när den skapas eller när den initieras, men det är inte det samma som att programmet kan ändra dem.)

2.   Man kan lagra variablerna i något av programmets objekt, och kopiera dem dit när inställningarna är gjorda. I så fall behöver inte dialogrutans objekt deklareras så att det existerar hela program­körningen. Samma metod som i punkt 1 kan användas av program­met för att förinställa/hämta värden i dialogrutans objekt, men man kan också låta dialogrutans objekt läsa och skriva programmets variabler.

 

Nu ska vi dock hålla det enkelt. Vi använder kryssets status till att avgöra vad det ska stå för text i textrutan. Detta måste alltså ske när vi klickar på kryss­rutan, och därför behöver vi en ‘control notification handler’ till denna. Detta kan du redan så vi hoppar direkt till vad vi ska skriva i funktionen. Texten i textrutan ska lagras i dess variabel, och vi skapade ju en som heter ‘m_Text’. Om krysset är förkrys­sat ska det stå ‘Nu är det ett kryss i rytan!’ i m_Text, annars ska det stå ‘Nu är det inget kryss i rutan!’.

 

Hur vet vi då om det är ett kryss i rutan? Jo, i variabeln m_KryssStatus kommer det att stå, under förutsättning att vi anropat UpdateData() med argumentet TRUE efter det att vi klickade i kryssrutan, och innan vi tittar i variabeln.

 

UpdateData() fungerar alltså på så sätt att argumentet FALSE skickar information från våra variabler till respektive kontroll, och TRUE gör tvärt om, uppdaterar våra variabler i enighet med vad som syns på skärmen. Observera att om vi anropar UpdateData() med argumentet FALSE kommer ingenting att ske förrän Windows har tid, vi kan alltså inte göra flera konsekutiva (på varandra följande) ändringar i en funktion avseende samma kontroll. Vi ser i så fall endast den sista, den som gällde när funktionen kommer till sitt slut. Det vanligaste är alltså att UpdateData(TRUE) står först och UpdateData(FALSE) sist i en funktion.

 

I vårt fall skriver vi alltså UpdateData(TRUE) i början av funktionen, för att vi ska kunna använda m_KryssStatus, och UpdateData(FALSE) i slutet av funktion­en, för att den nya texten ska synas.

 

Och var ska denna funktion stå då? Vi arbetar med variabler i CKryssDialog, och kontroller i IDD_KRYSSDIALOG, så vi bör väl lägga in koden i KryssDia­log.cpp. Sedan kan vi kompilera, länka och testa. Så här ska det se ut när vi har kryssat i rutan:

 

 

 

 

 

 

Och så här när vi tagit bort krysset igen:

 

 

 

                                  

 

 

 

Funktionen UpdateData() ingår egentligen i konceptet DDX, Dialog Data Ex­change. Den meddelandehantering som krävs för detta lägger ClassWizard in åt oss, men när vi lägger till egna objekt, som t.ex. en CRecordset för databas­han­tering, kan det hända att vi blir tvungna att själva lägga till dessa. Vi kommer till det när vi går igenom självstudien ‘Student Registration’.


Aktiv/passiv kontroll.

 

Kontroller kan göras aktiva eller passiva. Det är därför vi har de två knapparna ‘Aktiv’ och ‘Passiv’ i vår dialogruta. Observera att alla kontroller är fristående fönster, var och en med sin egen fönsterprocedur. Dessa skapas automatiskt av resurseditorn, och vi vet bara av dialogrutan som ett enda objekt. Dock vidare­befodras meddelanden från kontrollerna till dialogrutan.

 

Kontrollerna innehåller naturligtvis då även sina egna metoder och variabler. Man kan använda dessa under förutsättning att de är publika. I hjälpen står alla dessa beskrivna, och vi kan börja med att slå upp CButton, klassen för alla knappar, även kryssrutan. (Är du osäker på vad klassen för en kontroll kan heta, så kan ‘hierarchy chart’ ge vägledning.)

 

I hjälpen kan vi se att CButton ärver av CWnd, och det är ju naturligt om vi ska få en fönsterprocedur, och CWnd har många funktioner, bland annat en som aktiverar/passiverar ett fönster, EnableWindow(), vilken tar argumentet TRUE eller FALSE. Nu gäller det bara att hitta kontrollen i minnet. Det är som sagt ett eget objekt, men det enda objekt vi har tillgång till är dialog­rutans objekt. I CDialog finner vi dock en metod som heter GetDlgItem, vilken returnerar adressen till angiven kontroll. Vi kan kombinera ihop dessa och skriva:

 

GetDlgItem(IDC_KRYSS)->EnableWindow(TRUE);

 

Detta i en funktion kopplat till knappen ‘Aktiv’. Knappen ‘Passiv’ innehåller naturligtvis samma sak, men EnableWindow() ska då ha argumentet FALSE. Så här kan det se ut när vi klickat på ‘Passiv’:

 

 

 

 

 

 

 

 

? CDialog::GetDlgItem, CWnd::EnableWindow, Hierarchy Chart.


Synlig/osynlig kontroll.

 

Det som diskuterats i föregående gäller alla kontroller. Dessutom kan alla kon­troller göras osynliga. De kan då inte användas. Man kan på så sätt anpassa en dialogruta efter behov medan programmet körs (at run-time). Precis samma för­farande som användes när vi gjorde kryssrutan passiv, kan användas för att göm­ma den, eller visa den, det är bara en annan metod i CWnd, nämligen ShowWin­dow(). Tyvärr tar inte ShowWindow() TRUE och FALSE, eftersom det finns så många olika visibilitetstillstånd för ett fönster (se kapitel 10 i första häftet, där finns en hänvisning till hjälpen). Vi måste använda WM_SHOW och WM_HIDE i stället.

 

Nu skulle du kunna klara detta på egen hand, så nu blandar vi in en nyhet i leken. Vi skapar en extra variabel till kryssrutan, kallad m_KryssKontroll. Denna gång ändrar vi valet i kombinationsrutan ‘Category’ till ‘Control’. Som variabeltyp har vi inte längre möjlighet att välja ‘BOOL’, utan endast CButton. Så bra, det är det vi vill ha. Genom att skapa en kontrollvariabel, vilken fungerar som objekt till kon­trollen, så har vi direkt tillgång till kontrollens metoder via objektet. Vi skapar variabeln:

 

         

 

Nu har vi tillgång till ShowWindow() i objektet m_KryssKontroll, så i stället för att skriva t.ex.:

 

GetDlgItem(IDC_KRYSS)->ShowWindow(SW_HIDE);

 

…i OnGom() skriver vi:

 

m_KryssKontroll.ShowWindow(SW_HIDE);

Samma sak gör vi med OnVisa(), fast med argumentet SW_SHOW. Så här ser det ut när vi gömt kryssrutan:

 

 

 

 

 

 

 

Observera att man kan ha en kontroll som är både passiv och gömd. När man visar den igen är den fortfarande passiv. Man kan även påverka den till att bli passiv eller aktiv trots att den inte är synlig.

 


Initierad kryssruta.

 

När vi skapar dialogrutans objekt blir kryssrutan tom, så även texten. Måste det vara så? Nej, tittar vi bland de meddelanden som en dialogruta får, finner vi ett meddelande WM_INITDIALOG. (Redigera Kryss­Dialog.cpp så finns alla med­delanden i kombinationsrutan till höger i redigerarens verktygsfält om CKryss­Dia­log är vald i den vänstra kombina­tionsrutan.) Detta verkar ju vara det vi be­höver. WM_INITDIALOG är inte skrivet fetstil, så när vi väljer detta meddelan­de blir vi tillfrågade huruvida vi vill lägga till en funktion för att hantera det:

 

 

Accepterar vi får vi följande funktion:

 

BOOL CKryssDialog::OnInitDialog()

{

    CDialog::OnInitDialog();

        

    // TODO: Add extra initialization here

        

    return TRUE;  // return TRUE unless you set the

                  // focus to a control

                  // EXCEPTION: OCX Property Pages should

                  // return FALSE

}

 

Observera att den vanliga initieringen anropas först, vår extra initiering kommer därefter. Vi initierar krysstatus och text direkt under kommentaren:

 

    // TODO: Add extra initialization here

    m_KryssStatus = TRUE;

    m_Text = ”Jag har redan kryssat i rutan!”;

    UpdateData(FALSE);

 

 


Det fungerar alltså, men har vi tänkt rätt? Varje gång vi öppnar dialogrutan blir kryssrutan förbockad och texten ifylld. Men det vi ställde in senast har gått förlor­at. Antagligen, men inte säkert, är det inte så vi vill ha det. Därför tar vi nu bort funktionen. Öppna ClassWizard, klicka på första fliken (Message Maps), marke­ra funktionen och klicka på ‘Delete Function’:

 

 

 

 

 

 

 

 

Vad menas egentligen? Själva meddelandehanteringen, vilken vi tittade på redan i första kapitlet, det vill säga afx-hanteringen tas bort, men inte funktionen vi lade till. Det skulle inte vara lämpligt om ClassWizard tog bort en funktion som vi kanske lagt in mycken ny kod i, så den får vi ta bort själva. Klicka på ‘Ja’ och ta sedan bort funktionskoden manuellt. Glöm inte att stänga ClassWizard med hjälp av ‘OK’-knap­pen, annars tar inte ändringarna effekt.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

? CDialog::OnInitDialog().


Initiera dialog.

 

Vi vill alltså initiera kryssrutan en och endast en gång. Då faller det sig naturligt att lägga initieringen i dialogrutans konstruktorfunktion. När vi öppnar CKryss­Dialog.cpp finner vi att konstruktorn redan innehåller initieringskoden, det är ba­ra att ändra den:

 

Det som ClassWizard skrev:

 

CKryssDialog::CKryssDialog(CWnd* pParent /*=NULL*/)

    : CDialog(CKryssDialog::IDD, pParent)

{

    //{{AFX_DATA_INIT(CKryssDialog)

    m_KryssStatus = FALSE;

    m_Text = _T("");

    //}}AFX_DATA_INIT

}

 

Det vi ändrar det till:

 

CKryssDialog::CKryssDialog(CWnd* pParent /*=NULL*/)

    : CDialog(CKryssDialog::IDD, pParent)

{

    //{{AFX_DATA_INIT(CKryssDialog)

    m_KryssStatus = TRUE; // Ändring!

    m_Text = _T("Jag har redan kryssat i rutan!");

    //}}AFX_DATA_INIT

}

 

Nu fungerar det så att initieringen bara gäller första gången vi visar dialogrutan.

 

Man har i version 4.0 lagt till ett makro ‘_T’ som typomvandlar texten enligt oli­ka kodtabeller. Det är lätt att hitta i hjälpen. Markera ‘_T’ och tryck  ‘F1’, så kan du läsa mer om detta.


Initiera synlighet.

 

När man stänger dialogen och sedan öppnar den är kryssrutans status aktiv och synlig. Detta initieras när man visar den. Vi kan påverka detta i egenskaperna för denna och andra kontroller. Öppna dialogrutan för redigering och ta fram egen­ska­perna för kryssrutan. Under fliken ‘General’ finner vi kryssrutor för bl.a. Visible och Disabled, de två egenskaper vi påverkar med våra tryckknappar:

 

 

Tag bort bocken framför Visible, kompilera, länka och testa. När dialogrutan öppnas första gången ser den ut så här:

 

 

Övningsuppgift:

·      while(!m_Utantill)

·      {

·              TryAgain(&OvanstEx);

·      }

 

Övningsuppgift:

·      Gör om exemplet med en skillnad:

·      Skapa inte knapparna ‘Aktiv’, ‘Passiv’, ‘Visa’ och ‘Göm’.

·      Skapa i stället två kryssrutor extra: ‘Aktiv’ och ‘Visa’.

·      Dessa två ryssrutor ska fylla samma funktion som de fyra knapparna gjorde förut.

 

? Slå upp egenskaperna för en kryssruta (?-knappen i egenskapsrutan) och läs om alla egenskaper utom de tre sista i tredje fliken.