Windowsprogrammering for absolutte nybegyndere

Tags:    c++ windows win32 api gui
<< < 123 > >>
Skrevet af Bruger #8985 @ 13.09.2011


Brugerkontroller


Brugen af kontroller er en af de mest udbredte måder for et program at kommunikere med brugeren på. Med brugerkontroller mener jeg elementer som knapper, tekstbokse, rullegardiner et cetera. Det er vigtigt at notere sig, at brugerkontroller også er vinduer, og at de ligesom egentlige vinduer oprettes med funktionen CreateWindow. Følgende kodeeksempel demonstrerer brugen af knapper. Programmet vil skabe tre knapper, 'Ja,' 'Nej' og 'Måske,' og når en af knapperne klikkes på, vil en MessageBox identificere den valgte knap.

Fold kodeboks ind/udC++ kode 

Du vil nu se følgende, når du klikker på 'Måske':



Opgaver

[1] Inkludér i beskeden, hvor mange gange den enkelte knap er blevet trykket på.

Det giver sig selv, at vi er i stand til at identificere hver af de tre knapper ved hjælp af de tilsvarende tre konstanter, der er defineret i starten af programmet. Hvad der er en del mindre logisk, er måden hvorved konstanterne er tilknyttet knapperne. Dette sker i den tredjesidste parameter af CreateWindow, som faktisk er af typen HMENU - typen, man normalt bruger i forbindelse med menuer, hvilket vi kommer til senere. Brugerkontroller, det vil sige knapper, tekstbokse og deslige, kan ikke have menuer, men vi kan stadig bruge HMENU-parametren til at identificere en kontrol med ved at tildele den en unik værdi. Vi er nødt til at typecaste den unikke værdi til typen HMENU, og det er det, der godt kan snyde en.

Næste del af programmet foregår i WM_COMMAND. Denne begivenhed opstår under mange forskellige omstændigheder, men den involverer typisk en kontrol eller en menu, der på en eller anden måde bliver arbejdet med. I fald det er en kontrol, eksempelvis en knap, der er i spil, vil LOWORD(wParam) returnere kontrollens 'identifier,' altså den unikke værdi, kontrollen er tilknyttet, og HIWORD(wParam) vil afsløre handlingen udført at brugeren eller programmet. Et eksempel er med knapperne. I teorien kunne vi godt fremprovokere dialogboksen med beskeden 'Du trykkede Måske' uden rent faktisk at trykke på Måske-knappen. Dette kan vi gardere os mod ved at tjekke HIWORD(wParam).

Fold kodeboks ind/udC++ kode 

Selvom der er visse regler for, hvad der gemmes i wParam og hvad der gemmes i lParam, kan man aldrig være helt sikker, og det kan være svært at huske hvordan de opfører sig under de forskellige beskeder. Igen er MSDN uundværlig, så væn dig til at bruge den.

Lad os kigge på nogle flere kontroller. En af de mest populære og dynamiske kontroller er utvivlsomt tekstboksen. Kombineret med andre kontroller præsenterer den et værdifuldt redskab, hvilket jeg vil demonstrere om et øjeblik. Det næste program vil indeholde en tekstboks og to knapper. Trykker man på den første knap, 'Skift titel,' vil hovedvinduets titel skifte til hvad end brugeren indtastede i tekstboksen. Trykker man på knap nummer to, 'Ryd tekst,' vil teksten i tekstboksen blive slettet.

Fold kodeboks ind/udC++ kode 

Resultat



Jeg har gjort to ting anderledes denne gang. Den første er, at jeg selvfølgelig ikke har skrevet 'button' i det første kald til CreateWindow, men derimod 'edit', hvilket er Windows-API'et's navn for en tekstboks. Den anden nye ting er, at jeg oprettede tekstboksen med funktionen CreateWindowEx fremfor den sædvanlige CreateWindow. Den eneste forskel på disse to, er at -Ex-versionen tillader dig at specificere udvidede ('extended', heraf 'ex'-suffixet) 'styles'. WS_EX_CLIENTEDGE er en konstant, der danner en dybdeskabende 3D-ramme rundt om en hvilken som helst kontrol, men den er som regel brugt på tekstbokse.

MessageBox


Jeg ved godt, vi allerede har kigget på MessageBox-funktionen, men hidtil har vi kun brugt den som budbringer. Med andre ord har vi brugt den til at informere brugeren af vores program om dit og dat, men det er kun halvdelen af idéen bag denne yderst nyttige funktion. En dialog går begge veje, og eftersom MessageBox er en dialogboks, kan vi også bruge den til at fortælle programmet noget. Vi kan naturligvis ikke bede den sige hvad som helst, men vi kan få den til at fortælle programmet, hvilken knap vi trykkede på.

En lidt begrænsende detalje ved MessageBox, er at du er tvunget til at vælge mellem et sæt forudbestemte knapper. Vælger du ikke at specificere, hvilke knapper der skal stilles til rådighed, indeholder dialogboksen blot en 'OK'-knap, nøjagtig som det var tilfældet i nogle af de tidligere kodeeksempler. Det gør dog imidlertid ikke det store, eftersom man jo 'bare' kan designe sin egen dialogboks hvis nøden skulle opstå, og i de tilfælde hvor MessageBox er nyttig, vil man typisk ikke behøve andre knapper end dem, der er stillet til rådighed af API'et.

Nøjagtig det samme gælder ikonet, der vises til venstre for knapperne. Du har muligvis allerede lagt mærke til konstanter som MB_ICONINFORMATION i nogle af kodeeksemplerne. Der findes en række konstanter tilknyttet de forskellige ikoner og knapper. Du vælger ikon og knap ved at kombinere en konstant repræsenterende et ikon med en konstant repræsenterende en knap. Et eksempel er MB_ICONQUESTION | MB_YESNOCANCEL. Denne kombination er typisk anvendt i skriveprogrammer, når man forsøger at lukke programmet uden at have gemt ændringerne foretaget i en fil. Jeg vil ikke lave en liste over alle de forskellige konstanter her, eftersom du skal øve dig i at slå ting op på MSDN. I stedet vil jeg vise, hvordan vi kan vælge knapper og ikon i MessageBox, og hvordan vi kan se, hvilken knap, brugeren trykkede på.

Fold kodeboks ind/udC++ kode 

Efter at have svaret på to spørgsmål, vil du nu se noget lignende dette:



Selvom spørgsmålene bliver stillet i WM_CREATE, er det faktisk den hjemmestrikkede funktion, ButtonToString, der er den mest afgørende del af ovenstående program. Det er nemlig den, der oversætter en værdi, der reflekterer en knap i MessageBox, til en streng.

Menuer


Menuer er en anden herlig ting, Windows stiller til rådighed. De er måske ikke så pæne, hvorfor mange programmer har hjemmelavede menuer, men de er ekstremt nyttige og enkle at lave.
Menuer kan skabes på to måder. De kan defineres i en såkaldt resursefil, hvor de, sammen med blandt andet dialogbokse, ikoner, markører og så videre, bliver indlæst af Windows før programmet eksekveres, eller de kan oprettes mens programmet kører, det vil sige 'during runtime'. Vi vælger metode nummer to, da den ikke introducerer nye, store koncepter, der efter min mening bør placeres i en artikel for sig.

Følgende program minder lidt om det forrige. Vi opretter en menu med to popups: 'Filer' og 'Hjælp'. Under 'Filer' har vi 'Afslut' og under 'Hjælp' har vi 'Om...', hvor den sidste kalder en MessageBox, der fortæller nogle visdomsord. Forresten, 'Afslut' afslutter programmet.

Fold kodeboks ind/udC++ kode 

Resultat



Her ser du menubjælken, som altid placerer sig selv øverst i et vindue. Menuen er den eneste kontrol, jeg kan komme i tanker om, som ikke er en del af klientområdet. Dette betyder to ting: at du ikke kan lægge noget 'over' menuen; knapper og lignende vil altid ligge nedenunder menuen hvis de placeres negativt på y-aksen. Det andet er, at hvis du giver dit vindue højden 300px, så vil vinduet ende med at blive 300px plus menuens højde.

Som du kan se, kalder vi 'hele' tre funktioner for at oprette vores menu: CreateMenu, AppendMenu og SetMenu. Den første er nødvendig, fordi Windows skal foretage nogle interne forberedelser angående din menu. AppendMenu gør hele benarbejdet med at tilføje menupunkter til popups og derefter tilføje disse popups til menubjælken. Til sidst kommer SetMenu og fastgør menuen til vinduet. Som du kan se på billedet ovenover, er der en streg under forbogstaverne på de to popups. Det skyldes, at jeg skrev '&Filer' fremfor blot 'Filer'. Denne streg viser, hvilket bogstav du skal holde nede sammen med Alt-knappen for at åbne den pågældende popup uden brug af musen. Vi kunne også have skrevet 'Hj&ælp'. I så fald ville tastekombinationen Alt+Æ udfolde popup'en 'Hjælp'.

Som du nok efterhånden er ved at indse, går Windows-API'et, ligesom andre API'er, for det meste bare ud på at udfylde strukturer og bruge disse strukturer i funktionskald. Det nederdrægtige ved Windows-API'et i starten er bare det, at det ser så forskelligt ud fra konsolprogrammerne, men så snart du har vænnet dig til måden hvorpå tingene gøres, er det meget let at anvende.

Label


Udover knappen og tekstboksen er en label, som i Windows-API'et kaldes for en 'static', en meget anvendt kontrol. Det er utvivltsom den simpleste af alle kontroller, da den ikke gør andet end at vise det indhold, man tildeler den. Dette er ofte en besked af en art. Følgende program skaber en tekstboks, en knap og en label. Når man trykker på knappen, bliver teksten i tekstboksen vist i labelen. Men det er ikke alt. Inden vi foretager os noget, vil vi gerne ændre fonten på alle tre kontroller, så de får stilfulde Times New Roman.

Fold kodeboks ind/udC++ kode 

Resultat



Opgaver

[1] Vis alle beskeder indtastet på linjer unden hinanden i labelen, fremfor kun at vise den seneste.
[2] Giv en fejlmeddelelse, hvis brugeren ikke indtaster noget.
[3] Udfør en handling, hvis brugeren indtastet noget specifikt. Du kan for eksempel lukke vinduet, hvis brugeren indtastet 'luk'.

Vi har kun brug for at identificere knappen denne gang, og derfor får kun den en unik værdi i dens HMENU.

Lad os prøve rent faktisk at lave noget brugbart. En lille tekstbehandler, hvor man kan åbne en fil, ændre dens indhold og gemme filen under et nyt navn. Med andre ord et Notepad-agtigt program. Programmet skal have en menu, der stiller de forskellige funktioner til rådighed, og en tekstboks. Denne tekstboks skal naturligvis ikke længere begrænses til en linje.

Fold kodeboks ind/udC++ kode 

Og her ses vores lille editor:



Opgaver

[1] Lav et menupunkt, der fremkalder en MessageBox, der fortæller diverse informationer om teksten, for eksempel antal linjer, antal tegn, hvor kareten - den blinkende streg, der viser hvor det næste bogstav vil indfinde sig - står i teksten, og så videre.
[2] Lav en ny popup i menuen døbt 'Rediger,' der stiller funktioner som klip, kopier, indsæt og marker alt til rådighed.
[3] Få programmet til at spørge om brugeren ønsker at gemme dokumentet, såfremt det er blevet redigeret, når man forsøger at lukke vinduet.

Vi bruger her to separate funktioner, SaveTextToFile og LoadTextFromFile, til henholdsvis at gemme til en fil og læse fra en fil. Du har måske lagt mærke til, at vi bruger samme struktur, OPENFILENAME, i kald til både GetOpenFileName og GetSaveFileName. Det skyldes, at åben fil-dialogen og gem fil-dialogen praktisk talt er identiske. Jeg kan kun komme i tanker om to forskelle: når man vælger en fil i gem fil-dialogen, spørger den om du vil overskrive filen, såfremt du har specificeret OFN_OVERWRITEPROMPT i flags-medlemmet, som vi jo har. Dette spørger en åben fil-dialog naturligvis ikke om. Den anden forskel er titlen, som vi ændrer til enten 'Åben fil' eller 'Gem fil' umiddelbart inden vi viser dialogen, afhængig af hvilket menupunkt i 'Filer' brugeren valgte.

Når brugeren åbner fildialogen, kan han vælge at isolere tekstdokumenter eller HTML-dokumenter, eller han kan vælge at se alle filer i mappen. Denne funktion dannes ved at specificere de tilbudte filendelser i OPENFILENAMEs lpstrFilter-medlem. Den indeholder et sæt strenge, hvor hvert sæt i sig selv består af to strenge: en tekst, der fortæller brugeren, hvilke filer der skal isoleres, eksempelvis 'TXT-filer,' og en streng, der indeholder den tilsvarende filendelse, eksempelvis '*.txt'. Asterisken, '*', foran filendelsen betyder, at dialogboksen ikke skal tage højde for filnavnet, men kun fokusere på endelsen. De to strenge i et sæt adskilles med '\0', og alle sæt adskilles ligeledes med '\0'. Filteret ender også med '\0'.

Vi bruger tre nye konstanter under oprettelsen af tekstboksen: WM_VSCROLL skaber det vertikale rullegardin, WM_HSCROLL det horisontale rullegardin, og ES_MULTILINE, som gør det muligt at redigere mere end én linje i tekstboksen. Når vinduet ændrer størrelse under WM_SIZE, følger tekstboksen med og fylder hele vinduet. WM_SIZE bliver sendt kort efter WM_CREATE, og derfor fylder tekstboksen hele vinduet fra starten af.

Det var alt for denne gang. Du skulle nu meget gerne være i stand til at oprette dine egner vinduer med flotte og nyttige brugergrænseflader. Der er naturligvis mange flere kontroller end dem vi har gennemgået her, men dem kan du læse alt om på MSDN. Hvis artiklen bliver populær nok, skriver jeg utvivlsomt en mere, der tager fat på nogle andre aspekter i Windows-API'et. Det næste du bør gøre, er at læse min anden artikel - 'Grafik med GDI' - som også ligger her på Udvikleren under C++. Den forklarer, hvordan du kan bruge grafik i dine applikationer og dermed peppe dem endnu mere op.

God fornøjelse med din videre brug af Windows-API'et :)



<< < 123 > >>

Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.

Del også gerne artiklen med dine Facebook venner:  

Kommentarer (3)

User
Bruger #8985 @ 14.09.11 15:09
I må meget gerne rate og kommentere artiklen :)
User
Bruger #15301 @ 02.10.11 11:00
En meget behagelig tilgang til GUI i c/c++.. Det var godt, at der var lidt flere eksempler end bare lige at vise et vindue, da der også var hvordan man kunne få noget tekst input, samt hvordan man kunne bruge dette test input i sit program :)

User
Bruger #8985 @ 04.10.11 23:06
Endelig en kommentar! Mange tak, Kevin :)
Du skal være logget ind for at skrive en kommentar.
t