18
Tags:
c++
windows
win32
api
gui
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.
- #define BUTTON_WIDTH 75
- #define BUTTON_HEIGHT 25
- #define IDC_BTN_YES 1
- #define IDC_BTN_NO 2
- #define IDC_BTN_MAYBE 3
-
- LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
- {
- // opret tre window handles og en pointer til en streng
- static HWND hBtnYes;
- static HWND hBtnNo;
- static HWND hBtnMaybe;
-
- static LPSTR szMessage;
-
- switch (message)
- {
- case WM_CREATE:
- hBtnYes = CreateWindow("button", "Ja",
- WS_CHILD | WS_VISIBLE,
- 10, 10,
- BUTTON_WIDTH, BUTTON_HEIGHT,
- window,
- (HMENU)IDC_BTN_YES,
- 0, 0);
-
- hBtnNo = CreateWindow("button", "Nej",
- WS_CHILD | WS_VISIBLE,
- 10, BUTTON_HEIGHT + 10,
- BUTTON_WIDTH, BUTTON_HEIGHT,
- window,
- (HMENU)IDC_BTN_NO,
- 0, 0);
-
- hBtnMaybe = CreateWindow("button", "Måske",
- WS_CHILD | WS_VISIBLE,
- 10, 2 * BUTTON_HEIGHT + 10,
- BUTTON_WIDTH, BUTTON_HEIGHT,
- window,
- (HMENU)IDC_BTN_MAYBE,
- 0, 0);
- return 0;
-
- case WM_COMMAND:
- switch (LOWORD(wParam))
- {
- case IDC_BTN_YES:
- szMessage = "Du trykkede Ja";
- break;
-
- case IDC_BTN_NO:
- szMessage = "Du trykkede Nej";
- break;
-
- case IDC_BTN_MAYBE:
- szMessage = "Du trykkede Måske";
- break;
- }
-
- MessageBox(window, szMessage, "Knapper", MB_ICONINFORMATION);
- return 0;
-
- case WM_DESTROY:
- PostQuitMessage(0);
- return 0;
-
- default:
- return DefWindowProc(window, message, wParam, lParam);
- }
- }
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).
- if (HIWORD(wParam) == BN_CLICKED)
- {
- switch (LOWORD(wParam))
- {
- case IDC_BTN_YES:
- szMessage = "Du trykkede Ja";
- break;
-
- case IDC_BTN_NO:
- szMessage = "Du trykkede Nej";
- break;
-
- case IDC_BTN_MAYBE:
- szMessage = "Du trykkede Måske";
- break;
- }
- }
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.
- #include <windows.h>
-
- #define IDC_TEXTBOX 1
- #define IDC_BTN_TITLE 2
- #define IDC_BTN_DELETE 3
-
- LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
- {
- // opret window handles samt et TCHAR-array til at opbevare den indtastede tekst
- static HWND hTextBox;
- static HWND hBtnTitle;
- static HWND hBtnDelete;
-
- static TCHAR szText[MAX_PATH];
-
- switch (message)
- {
- case WM_CREATE:
- hTextBox = CreateWindowEx(WS_EX_CLIENTEDGE,
- "edit", "",
- WS_CHILD | WS_VISIBLE,
- 10, 10,
- 200, 22,
- window,
- (HMENU)IDC_TEXTBOX, 0, 0);
-
- hBtnTitle = CreateWindow("button", "Skift titel",
- WS_CHILD | WS_VISIBLE,
- 10, 50,
- 75, 25,
- window,
- (HMENU)IDC_BTN_TITLE, 0, 0);
-
- hBtnDelete = CreateWindow("button", "Ryd tekst",
- WS_CHILD | WS_VISIBLE,
- 100, 50,
- 75, 25,
- window,
- (HMENU)IDC_BTN_DELETE, 0, 0);
- return 0;
-
- case WM_COMMAND:
- switch (LOWORD(wParam))
- {
- case IDC_BTN_TITLE:
- // hent teksten i hTextBox og placér den i szText
- // udskift derefter titlen i window med szText
- GetWindowText(hTextBox, szText, MAX_PATH);
- SetWindowText(window, szText);
- break;
-
- case IDC_BTN_DELETE:
- // set teksten i hTextBox til en tom streng
- SetWindowText(hTextBox, "");
- break;
- }
- return 0;
-
- case WM_DESTROY:
- PostQuitMessage(0);
- return 0;
-
- default:
- return DefWindowProc(window, message, wParam, lParam);
- }
- }
ResultatJeg 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å.
- #define QUESTION1 "Har du kørekort?"
- #define QUESTION2 "Vil du afbryde, forsøge igen eller ignorere?"
-
- LPCSTR ButtonToString(int idChoice)
- {
- switch (idChoice)
- {
- case IDYES:
- return "Ja";
- case IDNO:
- return "Nej";
- case IDABORT:
- return "Afbryd";
- case IDRETRY:
- return "Forsøg igen";
- case IDIGNORE:
- return "Ignorer";
- default:
- return "Ukendt knap";
- }
- }
-
- LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
- {
- static int idChoice1;
- static int idChoice2;
-
- static TCHAR szHistory[256];
-
- switch (message)
- {
- case WM_CREATE:
- // stil et ja/nej-spørgsmål
- idChoice1 = MessageBox(window, QUESTION1, "Spørgsmål 1", MB_ICONQUESTION | MB_YESNO);
-
- // vis nogle komplicerede knapper
- idChoice2 = MessageBox(window, QUESTION2, "Spørgsmål 2", MB_ICONEXCLAMATION | MB_ABORTRETRYIGNORE);
-
- // vis brugerens valg
- wsprintf(szHistory, "Spørgsmål: %s\nSvar: %s\n\nSpørgsmål: %s\nSvar: %s",
- QUESTION1, ButtonToString(idChoice1),
- QUESTION2, ButtonToString(idChoice2));
- MessageBox(window, szHistory, "Dine valg", MB_ICONINFORMATION | MB_OK);
- return 0;
-
- case WM_DESTROY:
- PostQuitMessage(0);
- return 0;
-
- default:
- return DefWindowProc(window, message, wParam, lParam);
- }
- }
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.
- #define IDM_EXIT 1
- #define IDM_HELP 2
-
- LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
- {
- // HMENU bruges for popups såvel som selve menubjælken
- static HMENU hMenu;
- static HMENU hFile;
- static HMENU hHelp;
-
- switch (message)
- {
- case WM_CREATE:
- hMenu = CreateMenu();
- hFile = CreateMenu();
- hHelp = CreateMenu();
-
- // tilføj (append) 'Afslut' til hFile og 'Om...' til hHelp
- AppendMenu(hFile, MF_STRING, IDM_EXIT, "Afslut");
- AppendMenu(hHelp, MF_STRING, IDM_HELP, "Om...");
-
- // tilføj hFile og hHelp til hMenu
- AppendMenu(hMenu, MF_POPUP, (UINT)hFile, "&Filer");
- AppendMenu(hMenu, MF_POPUP, (UINT)hHelp, "&Hjælp");
-
- SetMenu(window, hMenu);
- return 0;
-
- case WM_COMMAND:
- switch (LOWORD(wParam))
- {
- case IDM_EXIT:
- DestroyWindow(window); // send WM_DESTROY til window
- break;
- case IDM_HELP:
- MessageBox(window, "Et tag i hånden er bedre end en fugl", "Om", MB_ICONINFORMATION | MB_OK);
- break;
- }
- return 0;
-
- case WM_DESTROY:
- PostQuitMessage(0);
- return 0;
-
- default:
- return DefWindowProc(window, message, wParam, lParam);
- }
- }
ResultatHer 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.
- #define IDC_BUTTON 1
-
- LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
- {
- static HWND hTextBox;
- static HWND hButton;
- static HWND hLabel;
-
- static HFONT hTimes; // handle til fonten Times New Roman
- static TCHAR szText[MAX_PATH]; // array til at opbevare teksten
-
- switch (message)
- {
- case WM_CREATE:
- hTextBox = CreateWindowEx(WS_EX_CLIENTEDGE,
- "edit", "",
- WS_CHILD | WS_VISIBLE,
- 10, 10,
- 100, 22,
- window, 0, 0, 0);
-
- hButton = CreateWindow("button", "Vis i label",
- WS_CHILD | WS_VISIBLE,
- 120, 10,
- 75, 25,
- window,
- (HMENU)IDC_BUTTON, 0, 0);
-
- hLabel = CreateWindow("static", "",
- WS_CHILD | WS_VISIBLE,
- 10, 50,
- 100, 22,
- window, 0, 0, 0);
-
- // opret fonten
- hTimes = CreateFont(20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Times New Roman");
-
- // send en WM_SETFONT med Times New Roman-fonten til alle tre kontroller
- SendMessage(hTextBox, WM_SETFONT, (WPARAM)hTimes, TRUE);
- SendMessage(hButton, WM_SETFONT, (WPARAM)hTimes, TRUE);
- SendMessage(hLabel, WM_SETFONT, (WPARAM)hTimes, TRUE);
- return 0;
-
- case WM_COMMAND:
- if (wParam == IDC_BUTTON)
- {
- GetWindowText(hTextBox, szText, MAX_PATH);
- SetWindowText(hLabel, szText);
- }
- return 0;
-
- case WM_DESTROY:
- PostQuitMessage(0);
- return 0;
-
- default:
- return DefWindowProc(window, message, wParam, lParam);
- }
- }
ResultatOpgaver[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.
- #include <windows.h>
- #include <fstream>
- #include <string>
-
- using namespace std;
-
- #define IDM_FILE_OPEN 101
- #define IDM_FILE_SAVE 102
-
- // handles til programmets menu
- HMENU hMenu;
- HMENU hFile;
-
- // handle til tekstboksen og fonten Courier New
- HWND hTextBox;
- HFONT hFont;
-
- // struktur for åben- og gem som-dialogen
- OPENFILENAME filedlg;
-
- // TCHAR-arrays, der indeholder sti og filnavn på den åbnede eller gemte fil
- TCHAR szPath[MAX_PATH];
- TCHAR szFilename[MAX_PATH];
-
- VOID SaveTextToFile()
- {
- static ofstream stream;
- static int nTextLength;
-
- nTextLength = GetWindowTextLength(hTextBox);
- TCHAR* szText = new TCHAR[nTextLength+1];
-
- GetWindowText(hTextBox, szText, nTextLength);
- stream.open(szPath);
- stream.write(szText, nTextLength);
- stream.close();
- }
-
- VOID LoadTextFromFile()
- {
- static ifstream stream;
- static string str;
- static TCHAR szLine[128];
-
- str = "";
- stream.open(szPath);
-
- while (!stream.eof())
- {
- stream.read(szLine, 128);
- str += szLine;
- str += "\r\n";
- }
-
- stream.close();
- SetWindowText(hTextBox, str.c_str());
- }
-
- LRESULT CALLBACK WndProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
- {
- switch (message)
- {
- case WM_CREATE:
- // opret menuen og sæt den på vinduet
- hMenu = CreateMenu();
- hFile = CreateMenu();
-
- AppendMenu(hFile, MF_STRING, IDM_FILE_OPEN, "Åben");
- AppendMenu(hFile, MF_STRING, IDM_FILE_SAVE, "Gem");
- AppendMenu(hMenu, MF_POPUP, (UINT)hFile, "&Filer");
- SetMenu(window, hMenu);
-
- // opret tekstboksen og giv den fonten Courier New
- hTextBox = CreateWindowEx(WS_EX_CLIENTEDGE,
- "edit", "",
- WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | ES_MULTILINE,
- 0, 0, 0, 0,
- window, 0, 0, 0);
-
- hFont = CreateFont(16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Courier New");
- SendMessage(hTextBox, WM_SETFONT, (WPARAM)hFont, TRUE);
-
- // udfyld OPENFILENAME-strukturen, så den er klar til brug når brugeren vælger Åben eller Gem
- memset(&filedlg, 0, sizeof(OPENFILENAME));
- filedlg.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
- filedlg.hwndOwner = window;
- filedlg.lpstrDefExt = "txt";
- filedlg.lpstrFile = szPath;
- filedlg.lpstrFileTitle = szFilename;
- filedlg.lpstrFilter = "TXT-filer\0*.txt\0HTML-dokumenter\0*.htm;*.html\0Alle filer\0*.*\0";
- filedlg.lStructSize = sizeof(OPENFILENAME);
- filedlg.nMaxFile = MAX_PATH;
- filedlg.nMaxFileTitle = MAX_PATH;
- return 0;
-
- case WM_SIZE:
- // skift teksboksens størrelse, så den fylder hele vinduets klientområde
- MoveWindow(hTextBox,
- 0, 0,
- LOWORD(lParam), HIWORD(lParam), TRUE);
- return 0;
-
- case WM_COMMAND:
- switch (LOWORD(wParam))
- {
- case IDM_FILE_OPEN:
- filedlg.lpstrTitle = "Åben fil";
- if (GetOpenFileName(&filedlg))
- LoadTextFromFile();
- break;
-
- case IDM_FILE_SAVE:
- filedlg.lpstrTitle = "Gem fil";
- if (GetSaveFileName(&filedlg))
- SaveTextToFile();
- break;
- }
- return 0;
-
- case WM_SETFOCUS:
- SetFocus(hTextBox);
- return 0;
-
- case WM_DESTROY:
- PostQuitMessage(0);
- return 0;
-
- default:
- return DefWindowProc(window, message, wParam, lParam);
- }
- }
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
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)
I må meget gerne rate og kommentere artiklen
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
Endelig en kommentar! Mange tak, Kevin
Du skal være
logget ind for at skrive en kommentar.