OSA III 249 LUKU 12 12 LUKU Single document interface sovelluksen luominen MFC:n dokumentti-näkymä rakennetta hyödyntävän sovelluksen kehittäminen Dokumentti-, näkymä-, kehys- ja dokumenttimalliluokat Lisäalkioiden lisääminen vakiovalikkoon
250 Mitä dokumentti oikein tarkoittaa? Ensimmäinen dokumentti/näkymä -arkkitehtuuria käyttävä sovellus oli MS Word -tekstinkäsittelyohjelma. Dokumentti-termin (tai Asiakirjan) ei tarvitse rajoittua tähän perinteiseen määrittelyyn. Document-luokassa voi olla mitä tahansa tietoa, jolloin dokumentti/näkymä -arkkitehtuuria voidaan soveltaa mitä erilaisimpiin sovelluksiin. Sovelluksen osat Auton tuotekehitykseen uhratut miljardit ovat saaneet aikaan autoihin varsin vakiintuneen käyttäjäliittymän. Tästä syystä voit siirtyä autosta toiseen ja olet tien päällä minuuteissa. Kun ajamisen perustaidot on kerran hankittu, niiden käyttö uudessa, melkein samanlaisin, korkeintaan ulkonäöltään poikkeavin, hallintalaittein varustetussa autossa. Windows-sovellusten suosio perustuu myös paljon yhtenäisen ulkoasun konseptiin. Tyypillisessä Windows-sovelluksessa on ylinnä otsikkopalkki, jonka alla ovat valikkorivi ja mahdolliset työkalurivit toimien pikavalintoina valikon komentoihin. Työkalurivien alla on ohjelman varsinainen näyttöalue ja aivan alimmaisena on vielä tilarivi. Vaikka sovellukset kuinka poikkeaisivat toisistaan, on niissä yleensä tietyt samat valikon komennot ja työkalurivin painikkeet. Tämä on mahdollistanut siirtymisen tekstinkäsittelyohjelmasta sähköpostin käyttöön muutaman minuutin opiskelulla. Visual C++ -ohjelmoijalla näiden Windows-standardien noudattaminen käy varsin vaivattomasti. MFC-kirjasto tarjoaa valtavan määrän apua jo sovelluksen luomisessa: AppWizard osaa lisätä sovellukseen automaattisesti valikon, työkalurivit, tilarivin ja muita komponentteja. Luomisen jälkeen näitä sovelluksen osia voidaan sitten vielä muokata lisää. AppWizardin luomat luokat toimivat yhdessä muodostaen MFC:lle ominaisen dokumentti/näkymä arkkitehtuurin. Dokumentti/näkymä arkkitehtuurin perusajatus on erottaa sovelluksen data sen esittämisestä käyttäjälle. Tämä saavutetaan eristämällä sovelluksen data omaan luokkaansa (dokumenttiin) ja esittämällä sen perusteella tietoja käyttäjälle toisen luokan kautta (näkymä, view). MFC-kirjaston tarjoama kokonaisuus on paljon muutakin kuin tämä sovellusrunko. Siihen kuuluu useita käyttäjäliittymäominaisuuksia sekä keinot tallettaa ja lukea tietoja tiedostoista. Dokumentti/näkymä arkkitehtuuri jakaantuu kahteen erilaiseen toteutukseen SDI, yksi dokumenttirajapinta ja MDI, monidokumenttirajapinta. Tässä luvussa keskitymme SDI:hin. KATSO MYÖS Lisätietoja MDI-sovelluksista luvussa 21. SDI-sovelluksen luominen Tyypillisen Windows-sovelluksen luominen tuttuine näkyvine ominaisuuksineen, kuten työkalurivit ja tilarivi, saattaisi olla varsin
haastavaa, ellei käytössä olisi AppWizardin kaltaista työkalua. AppWizard menee itse asiassa vielä perusasioita pidemmälle saat siihen valinnaisesti lisäominaisuuksia. Eräs näistä on mahdollisuus tietokantaliittymän tekemiseen, toinen sähköpostitoimintojen liittäminen sovellukseen. Tässä luvussa teet SDI-sovelluksen, jonka avulla tutkimme luotuja luokkia sekä dokumentti/näkymä arkkitehtuurin toimintaa. Luo siis aluksi projekti nimeltä SDICoin. OSA III LUKU 12 251 SDI-sovelluksen luominen AppWizardilla 1. Valitse File-valikosta New. Valitse sitten New project valintaikkunassa Projects-välilehdeltä MFC AppWizard (exe). 2. Anna projektille nimi (tässä SDICoin) ja napauta OK. 3. Valitse MFC AppWizard Step1 ikkunassa Single Document valintapainike ja varmista, että Document/View Architecture Support? on valittuna (kuva 12.1). Napauta Next-painiketta. KUVA 12.1 AppWizard, vaihe 1. 4. Valitse Step 2 ikkunassa None-valintapainike. Tällä sivulla voisit halutessasi liittää sovellukseen tietokantatukea, mutta juuri nyt emme sitä tarvitse. Napauta siis Next-painiketta. 5. Valitse Step 3 ikkunassa None-valintapainike ja rastita ActiveX Controls valintaruutu. Ikkunan muut valinnat liittyvät tässä esimerkissä tarpeettomiin OLE-automaatio-ominaisuuksiin. Napauta Next. 6. Rastita Step 4 ikkunassa seuraavat valinnat: Docking Toolbar, Initial Status Bar, Printing and Print Preview ja 3D
252 Sovelluksen osat Controls. Valitse työkalurivin tyyli Normal-valintapainikkeella. Tarkista asetukset vielä kuvasta 12.2. Napauta Next. KUVA 12.2 AppWizard, vaihe 4. 7. Valitse Step 5 ikkunassa projektin tyyliksi MFC Standard, hyväksy englanninkieliset kommentit lähdekoodiin Yes-valinnalla ja valitse MFC-kirjasto käytettäväksi As a Shared DLL tyyliin. Napauta sitten Next. 8. Napauta Step 6 ikkunassa Finish. Saat esiin kuvan 12.3 mukaisen New Project Information -ikkunan. Tässä ikkunassa on tietoja luoduista luokista ja lähdekooditiedostojen nimistä. Voit myös tarkistaa, mitkä ominaisuudet sovellukseen otettiin. Napauta OK. Nyt uuden projektin pitäisi olla valmis ja avattuna Developer Studiossa. KUVA 12.3 New Project Information valintaikkuna.
OSA III LUKU 12 253 Tulet esittämään valmiissa sovelluksessa kolikkopinon. Valikosta voit valita kolikon lisättäväksi tai poistettavaksi pinosta. Sovelluksen tiedot, tässä siis vain kolikoiden lukumäärä, pidetään dokumenttiluokassa ja niitä käsitellään näkymäluokan kautta tarkoituksena on näyttää kolikkopino. Vaikka tämä on varsin yksinkertainen esimerkki, osannet sen jälkeen antaa arvoa dokumentti/näkymä arkkitehtuurin perustavoitteelle, joka on tiedon paketointi (encapsulation). Paketoituna sovelluksen tiedot talletetaan ainoastaan dokumenttiluokkaan ja luokka tarjoaa tiedon käsittelyyn omat saantifunktionsa, jotta näkymäluokka (view) voi esittää tiedot käyttäjälle sopivassa muodossa. Esimerkiksi kolikkopinokuvan esittämisen sijaan näkymäluokka voi esittää tiedon kolikoiden määrästä pelkkänä numerona, eikä dokumenttiluokkaan tarvitse tehdä tätä varten muutoksia. Ennen kuin käymme projektin paranteluun käsiksi, lienee tarpeen tutustua AppWizardin tekemiin luokkiin hieman tarkemmin. SDI-sovelluksessa on yksi dokumenttiluokka, joka on johdettu MFC:n CDocument-kantaluokasta sekä yksi näkymäluokka, joka voidaan johtaa jostain MFC:n näkymäluokasta. Eri näkymäluokat heijastelevat erityyppisten dokumentti/näkymä-arkkitehtuurin ympärille perustuvien sovellustyyppien eroa. AppWizardin vaiheessa 6 on valittu perusnäkymäluokka. Taulukossa 12.1 on esitetty valittavissa olevat luokat; SDICoin käyttää näistä oletusluokkaa CView. TAULUKKO 12.1 AppWizardista käytettävissä olevat näkymäkantaluokat Luokan nimi CView CScrollView CListView CTreeView CEditView Kuvaus Oletusluokka, joka toteuttaa kaikki näkymään tarvitta vat perusfunktiot, myös toiminnan dokumenttiluokan kanssa. CView-luokasta johdettu luokka, johon on lisätty vieritysmahdollisuus, kun näkymän looginen alue on käytettävissä olevaa fyysistä näyttöalaa suurempi. Käyttää näkymänä luettelokontrollia, jossa sovellus voi esittää kuvakkeita tai tekstisarakkeita. Käyttää näkymänä puukontrollia, jossa sovellus voi esittää tietoa hierarkkisessa muodossa. Käyttää näkymänä monirivistä tekstiruutua, jossa ovat mukana vierityspalkit sekä hakujen käyttömahdollisuus. Dokumentti paketoi sovelluksen tiedot Dokumenttiluokka on johdettu CDocument-kantaluokasta ja sen jäsenmuuttujissa pidetään sovelluksen tietoja. Johdetun CDocument-luokkasi jäsenfunktioilla päästää käsittelemään noita tietoja. Esimerkiksi Serialize()-funktiota käytetään tietojen lataamiseen levyltä ja tallentamiseen sinne.
254 Sovelluksen osat Luokan nimi Kuvaus CRichEditView Käyttää näkymänä rich edit -kontrollia, jossa tekstin muokkausmahdollisuudet ovat tekstiruutuakin paremmat. CFormView Käyttää näkymänä valintaikkunapohjaa, jolloin sovellus voi esittää tietonsa tietokantalomakkeena. CRecordView CFormView-luokasta johdettu näkymä, jota on parannettu mahdollisuudella kytkeytyä joukkoon tietokannan tietueita. Tämä luokka on käytettävissä vain, jos sovellusta luodessa on valittu käytettäväksi tietokantatukea. CHtmlView Auttaa luomaan webbiselainsovelluksia Internet Explorer näkymän avulla. KATSO MYÖS Lisätietoja CListView, CTreeView, CRichEditView ja CHtmlView-luokista luvusta 19. Tietoja CScrollView-luokasta luvusta 18. Tietoja CFormView- ja CRecordView-luokista luvusta 24. Lisätietoja tiedostokäsittelystä dokumentti/näkymä-arkkitehtuurissa luvusta 23. Lisätietoja tietokantatuen käyttämisestä luvusta 24. Tietoja OLE automaatiosta luvusta 25. SDI-sovelluksen luokat SDI-sovelluksen luokkarakenne poikkeaa valintaikkunasovelluksesta selvästi. Jos tutkit SDICoin-projektin ClassView-ikkunaa, huomaat AppWizardin luoneen neljä luokkaa käsittelemään sovelluksen rakennetta. Näiden luokkien sukulaisuussuhteet selviävät kuvasta 12.4.
OSA III LUKU 12 255 KUVA 12.4 SDI-sovelluksen luokkahierarkia. CWinApp-kantaluokasta johdetulla CSDICoinApp-luokalla on useita rooleja ja sen toteutus poikkeaa valintaikkunaprojektin sovellusoliosta. Luokka hallitsee sovelluksen alustamista. Se vastaa liitynnästä dokumentti-, näkymä- ja kehysluokkien välillä. Se vastaanottaa myös Windowsin sanomat ja ohjaa ne edelleen oikeaan kohdeikkunaan. Kuten aiemmin todettiin, SDI-sovelluksessa luodaan ainoastaan yksi CDocument-luokasta johdetun CSDICoinDoc-luokan olio. Tämä luokka tulee toimimaan sovelluksen tietosäilönä. Sovelluksen tieto voi toki olla missä tahansa muodossa: kolikkoesimerkissämme ei tarvita muuta kuin kolikoiden lukumäärä, mutta usein sovelluksen tarvitsemat tiedot ovat huomattavasti mutkikkaampia. Tekstinkäsittelyohjelmassa sovelluksen tiedot koostuvat asiakirjan tekstisisällöstä sekä siihen kohdistetusta muotoilusta ja CAD-sovelluksessa käsiteltävän kuvan osien koordinaateista. CObject on hyvä kantaisä CObject on melkein kaikkien MFC-kirjaston luokkien kantaluokka. Kaikki CObjectluokasta johdetut luokat osaavat tarkistaa olion luokan ohjelman ajon aikana, tulostaa diagnostiikkatietoa ja tukea serialisointia.
256 Sovelluksen osat Tiedontallennustapa dokumenttiin on täysin sovelluksen ohjelmoijan käsissä. Ihanteellisin tapa olisi käyttää oliolähestymistapaa, jolloin itse tiedot ovat täysin erillään käyttöliittymästä. Tällä tavoin koodista saadaan mahdollisimman siirrettävä ja joustava. Kolikkoesimerkissä näkymiä on vain yksi, mutta joskus saatat tarvita niitä useita ja vieläpä saman dokumentin tietojen käsittelyyn. Elleivät näkymät ole samantyyppisiä, tulee dokumenttiluokkaan suunnitella tarkkaan, millaisena tieto näkymälle lähetetään. Kehysikkunan tyylien muokkaminen Voit muuttaa pääkehysikkunan tyylejä ennen sen piirtämistä korvaamalla PreCreateWindow()- funktion. Tälle funktiolle välitetään viittaus CREATESTRUCT-tietorakenteeseen, jossa styleattribuutti sijaitsee. Voit muuttaa sitä ennen tietorakenteen välittämistä edelleen kantaluokan versiolle funktiosta. CSDICoinView-luokka vastaa dokumentin tietojen näyttämisestä käyttäjälle sekä käyttäjän toimien välittämisestä dokumentin tietoihin. Kukin näkymä voi käsitellä vain yhtä dokumenttia, mutta yhdellä dokumentilla voi olla useita näkymiä. Näkymäluokka saa tietonsa dokumentista dokumenttiluokan jäsenfunktioilla. CMainFrame-luokka antaa sovelluksen käyttöön ikkunan. Luokka periytyy CFrameWnd-kantaluokasta, joka on yksinkertaisen ikkunan toiminnot paketoiva luokka. Oletuksena ikkunan tyylimäärittelyissä on mukana WS_OVERLAPPEDWINDOW, jolloin kehysikkuna saa otsikkopalkin painikkeineen, järjestelmävalikon ja mahdollisuuden tulla siirrettäväksi tai suurennettavaksi/pienennettäväksi käyttäjän toimesta. Kehysikkunan tyyleihin kuuluu myös FWS_ADDTOTITLE, jolloin ikkunaan ladatun dokumentin nimi tulee ikkunan otsikkopalkkiin. CMainFrameluokka osaa myös käsitellä työkalu- ja tilarivien luomisen, alustamisen ja tuhoamisen. KATSO MYÖS Lisätietoja SDI- ja MDI-luokkien eroista luvussa 21. SDI-sovelluksen näkyvät osat Kun AppWizardin ikkunassa Step 4 hyväksytään SDI-sovellusta luotaessa oletusasetukset, saadaan sovellus, jossa ikkunalla on otsikkopalkki painikkeineen, valikko tulostus- ja esikatselumahdollisuuksineen, työkalurivi sekä tilarivi. Kaikki näistä eivät suinkaan ole pakollisia; valinnat näkyvät tarkemmin kuvassa 12.2. Lisää valintoja voidaan tehdä vaiheen 4 valintaikkunan Advancedpainiketta napauttamalla (kuva 12.5). Näillä voidaan räätälöidä vielä kehysikkunan tyyliä.
OSA III LUKU 12 257 KUVA 12.5 AppWizardin vaiheen 4 lisäasetukset (Advanced Options). Kehysikkunan työalue ei tarkkaan ottaen toteuta sovelluksen näkymää. Näkymällä on aivan oma, reunaton ja valikoton kehysikkunan lapsiikkuna. Nämä näkymäikkuna korvaa kehysikkunan koko työalueen (kuva 12.6). Näkymä on toteutettu näin, jotta sama malli olisi hyödynnettävissä sekä SDI- että MDI-sovelluksissa, tosin MDI-sovelluksella on eri luokat kehys- ja lapsi-ikkunoille. Juuri kehysikkuna saa kehysikkuna- ja valikkosanomat. 5 1 2 Kehysikkunan sanomat Tiettyjä sanomia lähetetään ainoastaan kehystetyille ikkunoille. Näitä ovat koon muuttaminen, siirtäminen, ikkunan pienentäminen tai suurentaminen ja sovellusikkunan työalueen ulkopuolelle kohdistuvat sanomat. KUVA 12.6 SDI-sovelluksen näkyvät osat. 1 CMainFrame 2 CToolbar 3 CSDICoinView 4 CStatusBar 5 Näkymäikkuna 4 3 Ikkunan osat ovat siis seuraavat: CMainFrame. Periytyy CFrameWnd-luokasta ja tarjoaa sovellukselle kehysikkunan. CToolbar. Toteuttaa kehysikkunaan telakoidut työkalurivit. CSDICoinView. Toteuttaa näkymän kehysikkunan lapsiikkunana.
258 Sovelluksen osat CStatusBar. Tekee kehysikkunaan liitetyn tilarivin. Näkymäikkuna. Korvaa kehysikkunan työalueen. Mistä ihmeestä valikko ja työkalurivi sitten ilmestyivät? Nämä ovat AppWizardin sovellukseen tuomia resursseja. Jos tutkit projektisi ResourceView-sivua, löydät siitä neljä IDR_MAINFRAME-nimistä resurssia (pikavalintataulukko accelerator, kuvake icon, valikko menu ja työkalurivi toolbar). Pääset muokkaamaan näitä resursseja kaksoisnapauttamalla resurssin tunnusta. Mukana on myös erikoinen IDR_MAINFRAME-niminen merkkijonotaulukko (string table). Tämän taulukon merkkijono jakautuu seitsemään rivivaihdon (\n) erottamaan osaan. Kukin osista vastaa sovelluksen dokumentin tiettyä attribuuttia, ensimmäinen esimerkiksi sovelluksen ikkunan otsikkoa. SDI-dokumenttimallit Toistaiseksi olemme keskittyneet dokumentti/näkymä-sovelluksen yksittäisiin luokkiin ja näkyviin ominaisuuksiin. Kaikki nämä kokoaa yhteen ja niitä hallitsee dokumenttimalliluokka (document template). Jo kuvasta 12.4 löydät CSingleDocTemplate:n SDI-sovelluksen luokkahierarkiasta. Kyseessä on juuri SDI-sovelluksen käyttämä dokumenttimalliluokka. CSingleDocTemplate-luokan ilmentymä luodaan ja sitä käytetään CSDICoin::InitInstance-funktiossa (listaus 12.1). MFC-sovellusrunko kutsuu InitInstance-funktiota juuri ohjelman suorituksen alussa sovelluksen alustamiseksi. 1 MFC kutsuu tätä ohjelman suorituksen alussa. 2 Alustaa dokumenttimallin resurssitunnuksella sekä dukumentti-, kehys- ja näkymäluokalla. LISTAUS 12.1 LST12_1.CPP InitInstance-funktio. 1 BOOL CSDICoinApp::InitInstance() 1 2 { 3 // Tästä on poistettu rivejä selkeyden parantamiseksi 4 // Register the application's document templates. // Document templates 5 // serve as the connection between documents, // frame windows and views. 6 CSingleDocTemplate* pdoctemplate; 7 pdoctemplate = new CSingleDocTemplate( 8 IDR_MAINFRAME, 2 9 RUNTIME_CLASS(CSDICoinDoc), 10 RUNTIME_CLASS(CMainFrame), // main SDI frame window
OSA III LUKU 12 259 11 RUNTIME_CLASS(CSDICoinView)); 12 AddDocTemplate(pDocTemplate); 13 // Parse command line for standard shell commands, DDE, file open 14 CCommandLineInfo cmdinfo; 15 ParseCommandLine(cmdInfo); 3 16 // Dispatch commands specified on the command line 17 if (!ProcessShellCommand(cmdInfo)) 4 18 return FALSE; 19 // The one and only window has been initialized, so show and update it. 20 m_pmainwnd->showwindow(sw_show); 5 21 m_pmainwnd->updatewindow(); 3 Tulkitsee komentorivin argumentit. 4 Käsittelee komentorivin argumentit ja luo uuden dokumentin tai avaa olemassaolevan tiedoston. 5 Näyttää ja päivittää sovelluksen pääikkunan. 22 return TRUE; 23 } Rivillä 7 luodaan CSingleDocTemplate-olio neljän syötetyn parametrin perusteella. Ensimmäinen näistä on resurssitunnus IDR_MAINFRAME. SDICoin-sovelluksessa IDR_MAINFRAME-tunnuksella viitataan neljään eri resurssiin: sovelluksen kuvakkeeseen, valikkoon, työkaluriviin sekä näppäimistöpikavalintojen taulukkoon. Seuraavat parametrit ovat osoittimia dokumentti-, näkymä- ja kehysluokan ajonaikaisten luokkien tietoihin. Nämä osoittimet tehdään RUNTIME_CLASS-makrolla. Tämä kaikki on mahdollista, koska AppWizard tukee näiden luokkien dynaamista luomista käyttämällä DECLARE_DYNCREATE- ja IMPLEMENT_DYNCREATE makroja. Tässä vaiheessa ei vielä luoda itse dokumentti-, näkymä- tai kehysolioita. Tämä koodi alustaa CSingleDocTemplate-olion tarvittavilla tiedoilla resurssien lataamista varten sekä antaa mahdollisuuden varata tarvittaessa muistia dokumenteille, näkymille sekä kehyksille. Dokumenttimalliluokkaa (document template) kutsutaankin luokkatehtaaksi (class factory). Itse dokumenttimalliluokan oliota säilytetään sovellusluokassa. Rivillä 12 AddDocTemplate-funktiolla rekisteröidään vasta luotu dokumenttimalliolio CWinApp-luokkaan. Luokkatehdasmetodi Dokumenttimalliluokka on esimerkki luokkatehtaasta eli luokasta, joka määrittää, kuinka luodaan ilmentymiä toisista luokista. Näin ollen luokkatehdas osaa tuottaa sovelluskohtaisia luokkien ilmentymiä, olioita. CWinApp-luokka säilyttää CSingleDocTemplate-oliota kunnes se tuhoaa itsensä, eli sovelluksen päättyessä. Tuhoamisen aikana CWinAppvapauttaa dokumenttimallioliolle varatun muistin, joten sinun ei tarvitse huolehtia siitä. KATSO MYÖS Lisätietoja ajonaikaisista luokkatiedoista luvusta 23.
260 Omien komentoriviargumenttien tulkitseminen Helpoin tapa käsitellä omat komentoriviargumentit on johtaa luokka CCommandLineInfosta ja korvata ParseParam-funktio. Sovelluksen osat Dokumentti/näkymä-sovellusrungon funktioiden käyttö Dokumentti/näkymä-arkkitehtuurin vaikeimmin ymmärrettävä ja käytettävä ominaisuus on ehkä mukana olevien olioiden luontijärjestyksen ja tämän aikana kutsuttavien virtuaalifunktioiden seuraaminen. Syy ei ole monimutkaisuudessa, vaan näkyvän koodin takana piilevässä toiminnallisuudessa. Hyvää tässä on, että sovellusrunko tekee paljon asioita puolestasi. Kuitenkin toiminnan ymmärtämiselle on eduksi, jos opit tuntemaan piilossa oleviakin toimintoja. Listauksessa 12.1 olevan CSingleDocTemplate-olion luomisen ja alustamisen jälkeen riveillä 6-12 käsitellään komentorivin parametrit ja sovellusikkuna näytetään. CCommandLineInfo-luokka (rivi 14) huolehtii sovellukselle komentorivillä syötetyistä argumenteista. Funktio CWinApp:: ParseCommandLine (rivillä 15) käyttää parametrinä viittausta CCommandLineInfo-olioon. Kullekin argumentille kutsutaan ParseParam-funktiota ja CCommandLineInfo-olio täytetään näin argumenttien arvoilla. Taulukossa 12.2 esitetään komentorivin oletusparametrit ja niiden merkitys. TAULUKKO 12.2 Komentorivin argumentit Argumentti Tarkoitus Ei argumentteja Luo uusi dokumentti/asiakirja. Tiedosto Avaa annettu tiedosto. /p tiedosto Tulosta annettu tiedosto. /pt tiedosto Tulosta annettu tiedosto määrätyllä tulostimella. tulostinportti /dde Käynnistä DDE-istunto. /automation Käynnistä sovellus OLE-palvelimena. /embedding Käynnistä sovellus toisessa sovelluksessa olevan OLE-objektin muokkaukseen. Kuten taulukosta 12.2 huomaat, ilman argumentteja luodaan sovelluksen käynnistyessä uusi dokumentti. Tämä on luonnollisesti oletustoiminto. Dokumentin ohella luodaan myös kehysikkuna sekä näkymä. Tämä kaikki tapahtuu CWinApp::ProcessShellCommands-
funktiossa rivillä 21. Listauksessa 12.2 on esitelty mukana olevat tapahtumat. Huomaa, että listauksen varsinainen koodiosa on ohitettu kolmella pisteellä ja siinä on keskitytty kuvaamaan ainoastaan luomistyötä. LISTAUS 12.2 LST12_2.CPP Dokumentin, näkymän ja kehyksen luomiskertomus OSA III LUKU 12 261 1 BOOL CWinApp::ProcessShellCommands(CCommandLineInfo& rcmdinfo) 1 2 { 3 if(fcmdinfo.newfile) 4 { 5 OnFileNew(); 6 } 7 if(fcmdinfo.openfile) 8 { 9 OpenDocumentFile(rCmdInfo.strFileName); 10 } 11 12 } 13 void CWinApp::OnFileNew() 14 { 15 m_pdocmanager->onfilenew(); 16 } 17 void CDocManager::OnFileNew() 18 { 19 ptemplate->opendocumentfile(null); 2 20 } 21 CSingleDocTemplate::OpenDocumentFile(LPCTSTR lpszpathname) 22 { 23 if(m_ponlydoc!= NULL) 24 pdocument->savemodified(); 3 25 else 26 { 27 pdocument = CreateNewDocument(); 28 pframe = CreateNewFrame(pDocument): 4 29 } 30 31 pdocument->onnewdocument(); 5 32 InitialUpdateFrame(pFrame, pdocument); 6 1 Tätä kutsuu InitInstancefunktio. 2 ptemplate on osoitin jo rekisteröityyn CSingleDocTemplate-olioon. 3 Jos dokumentti on olemassa se alustetaan uudelleen. 4 Dokumentti ja kehys muodostetaan ajonaikaisten luokkatietojen perusteella dokumenttimallissa. 5 Tämä virtuaalifunktio korvataan omalla dokumentin alustamista varten. 6 Alustaa kehysikkunan kutsumalla LoadFrame-funktiota. Aktivoi näkymän ja lähettää WM_INITIALUPDATE-sanoman, joka aiheuttaa CView::OnInitialUpdatekäsittelijän kutsumisen. Riveillä 1-12 esitetty CWinApp::ProcessShellCommand-funktio käynnistää komentoriviparametrien perusteella tarvittavia funktioita. Rivin 13 funktiota CWinApp::OnFileNew kutsutaan, ellei komentorivillä ole annettu argumentteja. Tätä kutsutaan myös käyttäjän valitessa Tiedosto-valikosta Uusi (File/New), minkä AppWizard kytkeekin automaattisesti valikkoon.
262 Sovelluksen osat Listauksessa 12.2 esitetään uuden dokumentin luominen sovelluksen käynnistyessä, eikä ero tämän ja tiedoston avaamisen välillä ole kovin suuri. Ero tehdään alussa, jolloin CWinApp::ProcessShellCommandfunktio joko löytää annetun tiedoston tai kysyy sitä käyttäjältä valintaikkunassa. Tiedoston paikantamisen jälkeen sen hakemistopolku ja nimi syötetään rivin 21 samalla CSingleDocTemplate:: OpenDocumentFile-funktiolle. Ilman tiedostoa funktio saisi NULLarvon eli loisi uuden dokumentin. CWinApp::OnFileNew antaa välittömästi suoritusvastuun (rivillä 15) CDocManager::OnFileNew-funktiolle, joka hakee käyttöönsä osoittimen juuri luotuun CSingleDocTemplate-olioon. Osoittimen avulla kutsutaan OpenDocumentFile-funktiota NULL-parametrillä. Tämä funktio tekeekin sitten varsinaisen työn. OLE-dokumentit Jos määrität AppWizardissa sovelluksesi OLE-säilöksi tai OLE-palvelimeksi, dokumenttiluokkasi johdetaan jostain CDocumentin kolmesta aliluokasta: COleDocument, COleLinkingDoc tai COleServerDoc. Kukin näistä pohjaa toimintansa edellisen toiminnoille. Perusero tavallisiin dokumentteihin on kyky käsitellä dokumenttia OLEobjektiluettelona. CSingleDocTemplate::OpenDocumentFile SDI-sovelluksen dokumenttiolio luodaan vain kerran (kun OpenDocumentFile-funktiota kutsutaan ensimmäisen kerran). Uudet tämän funktion kutsut Tiedosto-valikon Uusi tai Avaa-komentojen (File/New, File/Open) seurauksena alustavat avoimen dokumentin uudelleenkäyttöä varten. Uudelleenalustaminen tehdään SaveModified-funktiossa, joka aluksi tarkistaa dokumentin muutetun tilan. Kaikkien dokumenttien kantaluokalla CDocument on BOOLtyylinen jäsenmuuttuja, jossa on tieto siitä, onko dokumenttia muutettu. Tämä jäsenmuuttuja voidaan asettaa SetModifiedFlag-funktiolla. Jos käsiteltävää dokumenttia muutetaan, käyttäjää pyydetään tallettamaan dokumentti ennen jatkamista. Sanomaruudussa käyttäjä voi jatkaa toimintaa tallettamalla dokumentin, tallettamatta dokumenttia tai perumalla koko OpenDocumentFile-funktion käytön. Dokumentti talletetaan niin haluttaessa OnSaveDocument-funktiolla ja se alustetaan DeleteContents-funktiolla; näitä molempia käsitellään hieman myöhemmin. Jos dokumentti joudutaan luomaan, se tehdään funktiolla CreateNewDocument (rivillä 27). Tämä luo sovelluskohtaisen dokumenttiolion dokumenttimallin ajonaikaisen luokan tietojen perusteella. Kyseinen funktio myös lisää uuden dokumentin nykyisten dokumenttien luetteloon. Kun dokumentti on luotu, luodaan myös kehys ja näkymä toisella dokumenttimallin funktiolla, CreateNewFrame. CreateNewFrame-funktio (rivillä 28) tekee kehysikkuna- ja näkymäoliot
myös dokumenttimallin ajonaikaisen luokan tietojen perusteella. Kehyksen luomisen jälkeen kutsutaan CFrameWnd::LoadFrame-funktiota, jolle välitetään parametreinä dokumenttimallin muodostimessa määritetty resurssitunnus. Kuten muistanet, tämä oli IDR_MAINFRAME. LoadFrame-funktio lataa eri resurssit (valikon, työkalurivin, kuvakkeen, pikavalintataulukon ja merkkijonot) ja liittää ne kehysikkunaan. Huomaa myös, että resurssien lataamisen epäonnistuminen aiheuttaa koko LoadFrame-funktion epäonnistumisen, jolloin annetaan debuggaussanoma Warning: CDocTemplate couldn t create a frame. Nyt kun kaikki oliot on muodostettu, dokumentti alustetaan OnNewDocument-funktiossa rivillä 31 (funktio selitetään tarkemmin myöhemmin). Luomistoiminnon lopuksi kutsutaan dokumenttimallin funktiota InitialUpdateFrame (rivi 32). Tämä asettaa vasta luodun näkymän aktiiviseksi ja lähettää sitten WM_INITIALUPDATE-sanoman kehysikkunan lapsille, myös näkymäikkunalle. Tämän sanoman saamisesta seuraa CView::OnInitialUpdate-virtuaalifunktion kutsuminen. On hyvin tavallista, että tämä funktio korvataan omalla näkymän alustuksella. Muista myös, että yleensä on tarpeen kutsua lisäksi kantaluokan versiota kyseisestä funktiosta. OSA III LUKU 12 263 CDocument::OnNewDocument Tätä funktiota kutsutaan uutta dokumenttioliota luotaessa. Tämä voi tapahtua jo sovelluksen käynnistymisvaiheessa tai käyttäjän valitessa Tiedosto-valikosta Uusi-komennon (File/New). Funktio kutsuu DeleteContents-funktiota dokumentin sisällön siistimiseksi ja tilatiedon asettamiseksi FALSE-arvoon (eli dokumenttia ei ole muutettu). Se myös tyhjentää merkkijonon, jossa dokumentin tiedostonimi oli. CDocument::OnOpenDocument Tätä funktiota kutsutaan avattaessa dokumenttia tiedostosta. Tämä voi tapahtua jo sovelluksen käynnistymisvaiheessa komontorivin parametrien perusteella tai käyttäjän valitessa Tiedosto-valikosta Avaa-komennon (File/Open). Funktio kutsuu DeleteContentsfunktiota dokumentin sisällön tyhjentämiseksi. Oletustoteutuksessa annettu tiedosto avataan ja Serialize-funktiota kutsutaan dokumentin tietojen lukemiseksi sovellukseen. Tietojen lataamisen jälkeen tiedosto suljetaan ja muuttamistieto nollatan FALSE-arvoon ( ei muutettu ).
264 Sovelluksen osat CDocument::OnSaveDocument Tätä funktiota kutsutaan tiedostoa talletettaessa. Tämä tapahtuu käyttäjän valitessa Tiedosto-valikosta Tallenna tai Tallenna nimellä -komennon (File/Save, File/Save As). Tätä kutsutaan myös käyttäjän sulkiessa dokumentin ja valitessa muuttuneen dokumentin tallennuksen siinä vaiheessa. Funktio saa yhden parametrin, osoittimen tiedostonimeen. Oletustoteutuksessa tiedosto avataan ja dokumentin tiedot talletetaan Serialize-funktiolla. Tietojen tallentamisen jälkeen tiedosto suljetaan ja dokumentin muuttamistilaksi asetetaan FALSE ( ei muutettu ). CDocument::DeleteContents Tätä funktiota kutsuvat OnNewDocument, OnOpenDocument ja OnCloseDocument. Funktio huolehtii nykyisen dokumentin sisällön tuhoamisesta. Oletustoteutusta ei ole, koska dokumentin tiedot ovat aina sovelluskohtaisia. Joudut siis korvaamaan tämän funktion omallasi, jonka pitäisi vapauttaa dokumenttisi varaamat jäsenmuuttujat sekä alustaa muuttujat uudelleen. CDocument::OnCloseDocument Tätä funktiota kutsutaan dokumenttia suljettaessa. SDI-sovelluksessa tämä tapahtuu käyttäjän avatessa uuden tiedoston tai luodessa uuden dokumentin sekä sovelluksen sulkeutuessa. Aluksi funktio sulkee kaikki dokumenttiin liitetyt näkymät ja kutsuu sitten DeleteContents-funktiota ennen dokumenttiolion tuhoamista. KATSO MYÖS Lisätietoja OLE-automaatiosta luvusta 25. Tietoja tiedostonkäsittelystä ja serialisoinnista luvun 23 alusta. Dokumenttien ja näkymien yhteiskäyttö Dokumentti/näkymä-arkkitehtuuri on hyvä pohja toiminnallisesti eri kokoisille sovelluksille. Tiedonkäsittelykoodin erottaminen käyttöliittymästä luo helpommin hallittavan ja ylläpidettävän, modulaarisen sovelluksen. Esimerkiksi dokumentin tietojen esittäminen jollain uudella tavalla onnistuu yksinkertaisesti luomalla uusi näkymäluokka, joka
hakee tarvitsemansa tiedot nykyisen dokumentin rajapinnan kautta. Yhteistoiminnan suunnittelu dokumentti- ja näkymäluokkien välille on erityisen tärkeä toimi. Yleensä kokeneidenkin ohjelmoijien on vaikea päättää, mitkä tiedot kuuluvat dokumenttiin, mitkä näkymään. Tähän ei ole olemassa selkeitä sääntöjä ja päätös riippuu myös sovelluksesta. Ihanteellisessa tapauksessa näkymässä ei tarvittaisi olenkaan omia tietoja, vaan se saisi kaikki tarvitsemansa dokumentista. Näin muutokset tehtäisiin vain yhteen paikkaan, josta dokumenttiin liitetyille näkymille tiedotettaisiin tiedon muuttumisesta. Tämä lähtökohta ei kuitenkaan ole aina selkein ja tehokkain. Esimerkiksi tekstinkäsittelysovellukset pitävät usein kopiota osasta tiedoista itsellään, jotta dokumenttiin ei tarvitsisi koskea aivan jokaisen näppäimenpainalluksen jälkeen (vaikuttaa sovelluksen nopeuteen). On useita tilanteita, jolloin dokumentin tietojen välivarastointi on suotavaa. Tämän tekemiseen ei ole mitään estettä, voithan koodata sovelluksesi niin kuin haluat. Tarpeeton tiedon kaksinkertainen tallettaminen kuitenkin hankaloittaa ohjelmointityötä, joten sitä kannattaa välttää. OSA III LUKU 12 265 Dokumentin tietojen alustaminen CDocument on abstrakti luokka, joten joudut periyttämän siitä oman dokumenttiluokkasi. Sovelluksesi tiedot pidetään tuon dokumenttiluokan jäsenmuuttujissa. Aivan yksinkertaisimpia sovelluksia lukuun ottamatta dokumenteissa tarvitaan useita jäsenmuuttujia, usein taulukoina tai linkitettyinä listoina. Muista, että SDI-sovelluksissa dokumenttiolio muodostetaan vain kerran ja sitä käytetään uudelleen seuraavien dokumenttien sitä vaatiessa. Tästä syystä dokumentin tietojen alustamista ei kannata sijoittaa dokumenttiolion muodostimeen, eihän sitä enää kutsuta seuraavien dokumenttien kohdalla. Aiemmin dokumentti/näkymä-arkkitehtuurin funktioista puhuttaessa huomasit, että dokumentin virtuaalifunktiota DeleteContentskutsutaan automaattisesti funktioista OnNewDocument, OnOpenDocument ja OnCloseDocument. Näissä tilanteissa dokumentin sisältö vaihtuu. DeleteContents vastaa dokumentin tyhjentämisestä, joten sen jälkeen on hyvin sopiva hetki alustaa dokumentin jäsenmuuttujat uudelleen. MFC-kokoelmaluokat MFC-kirjastossa on useita apuluokkia mutkikkaiden tietorakenteiden suunnitteluun. Näitä kokoelmia (collection) ovat muun muassa CArray ja CObList. Käytä näitä valmiita toimintoja aina, kun se on mahdollista.
266 Dokumentin muuttujien suojaaminen Dokumenttiluokassasi tulee olla protected-tyyppisiä jäsenmuuttujia, joita käsitellään julkisten public-tyyppisten saantifunktioiden kautta. Sovelluksen osat Jäsenmuuttujien lisääminen dokumenttiin Dokumenttiluokka on samanlainen kuin projektin muutkin luokat, joten myös siihen voidaan lisätä jäsenmuuttujia ClassView-sivun Add Member Variable pikavalikosta. Jos lisäät useita muuttujia, saattaa olla helpompaa lisätä ne suoraan ohjelmakoodiin. Saat avattua dokumenttiluokan otsikkotiedoston (.h) kaksoisnapauttamalla dokumenttiluokan nimeä ClassView-sivulla. Jotta tieto pysyisi ehyenä, tulee dokumentin jäsenmuuttujat määritellä suojatuiksi (protected). Suojattua muuttujaa voi muuttaa ainoastaan oman luokan tai luokasta perityn luokan jäsenfunktio. Kun muut luokat eivät pääse muuttamaan muuttujia, käyttöön jää vain yksi paikka, josta muutoksia voidaan tehdä ja näkymän liittäminen dokumenttiin tulee helpommaksi. Jotta näkymät voisivat käsitellä dokumentin tietoja, tulee dokumenttiluokassa olla tietojen käsittelyfunktiot, jotka palauttavat viittaukset dokumentin jäsenmuuttujiin. Kun nyt tunnet dokumentti/näkymä-arkkitehtuuria, on aika kehittää aloittaa SDICoin-esimerkin työstäminen. Tässä kappaleessa käsittelet dokumenttiluokkaa CSDICoinDoc. Dokumenttiluokan tarvitsema data koostuu pelkästään kolikoiden nykyisestä lukumäärästä, mikä mahtuu yhteen jäsenmuuttujaan. Kun näkymä joutuu piirtämään kolikkokasan, sen tulee tietää tämä kolikoiden määrä. Yksinkertaisin tapa saada tämä tieto käyttöön olisi määritellä dokumenttiluokkaan globaali muuttuja, jota näkymäluokka voisi suoraan käsitellä. Haittana olisi kuitenkin näkymäluokan koodin saama mahdollisuus muuttaa kolikoiden määrästä kertovaa muuttujaa, esimerkiksi vahingossa. Parempi tapa toimia onkin asettaa lukumääräjäsenmuuttuja suojatuksi ja varustaa dokumenttiluokka muuttujan käsittelyfunktioilla. Lisää siis tähän tarvittava koodi seuraavasti. Dokumentin tiedon tallennuksen sekä saantimetodien toteutus 1. Valitse ClassView-sivulla CSDICoinDoc-luokka ja se pikavalikosta Add Member Variable. Saat esiin vastaavan ikkunan. 2. Syötä muuttujan tyypiksi Variable Type ruutuun int. Anna muuttujan nimeksi Variable Name ruutuun m_ncoins ja valitse Protected Access valintapainike. Napauta OK.
3. Valitse CSDICoinDoc-luokan pikavalikosta Add Member Function. 4. Syötä funktion tyypiksi int ja Function Declaration ruutuun GetCoinCount. Valitse myös Public Access valintapainike. 5. Syötä GetCoinCount-funktion sisällöksi seuraava koodirivi: return m_ncoins; 6. Käynnistä ClassWizard näppäilemällä Ctrl+W tai valitsemalla View-valikosta ClassWizard. 7. Valitse Message Maps-välilehti. 8. Valitse Class Name yhdistelmäruudusta CSDICoinDoc. 9. Valitse Object IDs luetteloruudusta CSDICoinDoc. 10. Valitse Messages-luetteloruudusta DeleteContents ja napauta Add Function painiketta. 11. Sulje ClassWizard napauttamalla OK. 12. Syötä seuraava koodirivi DeleteContents-funktioon TODOkommenttien jälkeen: m_ncoins = 1; CSDICoinDoc-luokassa on nyt suojattu jäsenmuuttuja m_ncoins, joka alustetaan korvaamassasi DeleteContents-funktiossa. Olet myös lisännyt saantifunktion GetCoinCount, jolla näkymä saa m_ncoinsmuuttujan arvon. OSA III LUKU 12 267 Dokumentin tietojen hakeminen näkymästä Jotta näkymä saisi käsittelyyn dokumentin tietoja, sen tulee ensin päästä käsiksi itse dokumenttiolioon. Tämä käy automaattisesti MFCsovellusrungon avulla lisäämällä sovelluskohtaiseen näkymäluokkaan GetDocument-funktiokutsun. Tämä funktio palauttaa osoittimen dokumenttimallissa näkymään liitettyyn dokumenttiin. Oikeastaan sovellusrunko tarjoaa kaksikin GetDocument-funktiota. Tässä esimerkissä huomaat, että CSDICoinView-luokalla on GetDocument-toteutus SDICoinView.cpp-tiedostossa sekä SDICoinView.h-tiedostossa, kuten seuraavassa esitetään. Nämä kaksi funktiota toimivat täsmälleen samoin, ainoa ero on, että ensimmäistä käytetään projektin kehitysaikaisessa (debug)-versiossa ja toista varsinaisessa julkaisuversiossa (release). Syy tähän on, että koodin sekaan kirjoitetut inline-funktiot toimivat tehokkaammin, ja tätä funktiota tullaan todennäköisesti kutsumaan useasti suorituksen aikana. Inline-funktioiden käyttö saattaa nopeuttaa ohjelmaa inline-avainsanalla kääntäjä korvaa funktiokutsun funktion koodilla. Ohjelma nopeutuu, koska funktion kutsumista ei tarvita. Huomaa, että inlinefunktioita ei voi debuggerissa käydä läpi rivi riviltä.
268 Sovelluksen osat SDICoinView.cpp #ifdef _DEBUG CSDICoinDoc* CSDICoinView::GetDocument() { ASSERT(m_pDocument->IsKindOf( RUNTIME_CLASS(CSDICoinDoc))); return (CSDICoinDoc*) m_pdocument; } #endif // _DEBUG SDICoinView.h #ifndef _DEBUG // _DEBUG-versio oli SDICoinView.cpp:ssä inline CSDICoinDoc* CSDICoinView::GetDocument() { return (CSDICoinDoc*) m_pdocument; } #endif CSDICoinView-luokka vastaa kolikkopinon näyttämisestä. Itse kolikon kuvan piirtävä koodi on varsin yksinkertainen ja se sijaitsee CSDICoinView::OnDraw-funktiossa. AppWizard on jo luonut tuon funktion rungon valmiiksi. MFC-sovellusrunko kutsuu OnDrawfunktiota aina, kun näkymä pitää piirtää näytölle esimerkiksi kehysikkunan kokoa muutettaessa. OnDraw-funktiota kutsutaan myös tulostettaessa sekä tulostuksen esikatselutoiminnassa. Erona näytölle ja tulostimelle piirtämisessä on piirtofunktiolle välitettävä eri piirtopinta (device context). Muokkaa nyt CSDICoinView::OnDraw-funktio listauksen 12.3 mukaiseksi. LISTAUS 12.3 LST12_3.CPP Dokumentin datan lukeminen näkymän piirtämiseksi 1 Hae osoitin dokumenttiluokkaan kutsumalla näkymäluokan GetDocument()-funktiota. 2 Hae osoitin pdcpiirtopinnalla valittuna olevaan siveltimeen. 3 Luo keltainen sivellin. 4 Valitse uusi sivellin piirtopinnalle. 1 void CSDICoinView::OnDraw(CDC* pdc) 2 { 3 // ** Retrieve a pointer to the document 4 CSDICoinDoc* pdoc = GetDocument(); 1 5 ASSERT_VALID(pDoc); 6 7 // TODO: add draw code for native data here 8 // ** Save the current brush 9 CBrush* poldbrush = pdc->getcurrentbrush(); 2 10 11 // ** Create a solid yellow brush 12 CBrush br; 13 br.createsolidbrush(rgb(255,255,0)); 3 14 15 // ** Select the yellow brush in to the device context 16 pdc->selectobject(&br); 4 17
OSA III LUKU 12 269 18 // ** Retrieve the number of coins from the document 19 // ** and draw two ellipses to represent each coin 20 for(int ncount = 0; ncount < pdoc->getcoincount(); ncount++) 5 21 { 22 int y = 200-10 * ncount; 23 pdc->ellipse(40, y, 100, y-30); 6 24 pdc->ellipse(40, y-10, 100, y-35); 25 } 26 27 // ** Restore the current brush 28 pdc->selectobject(poldbrush); 7 29 } 5 Käy läpi kaikki piirrettävät kolikot. Kolikoiden lukumäärä saadaan saantifunktiolla GetCoinCount(). 6 Piirrä kaksi ellipsiä. Ellipsien y-suuntainen paikka määräytyy kolikon numeron mukaan. 7 Valitse alkuperäinen piirtopinnan sivellin takaisin käyttöön. AppWizard tekee automaattisesti funktion kaksi ensimmäistä riviä (4 ja 5). GetDocument palauttaa osoittimen dokumenttiolioon ja tämä talletetaan muuttujaan pdoc. Osoitinta käytetään rivin 20 forsilmukassa hakemaan kolikoiden sen hetkinen lukumäärä saantifunktion GetCoinCount avulla. Rivillä 9 talletetaan osoitin piirtopinnalla valittuna olevaan siveltimeen (brush) muuttujaan poldbrush, joka palautetaan ennalleen funktion lopussa rivillä 28. Riveillä 12, 13 ja 16 luodaan keltainen sivellin paikalliseen muuttujaan br ja se valitaan piirtopinnan siveltimeksi, valmiiksi piirtämään kolikoita. Kukin kolikko piirretään kahdella ellipsillä, toinen toisensa päälle, jolloin saadaan kolmiulotteinen vaikutelma kolikosta. Tämä tehdään käytännössä CDC::Ellipse-funktiota käyttäen, riveillä 23 ja 24. KATSO MYÖS Lisätietoja piirtopinnasta luvusta 15. Lisätietoja grafiikan piirtämisestä luvusta 16. Vakiomallin resurssien käyttö Kuten jo aiemmin mainittiin, projektiin on lisätty automaattisesti useita resursseja SDI-sovelluksen näkyviä osia varten. Näitä ovat valikko, työkalurivi, kuvake, näppäimistöpikavalinnat sekä merkkijonotaulukko. Kaikki nämä vakioresurssit ovat täysin toimivia. Esimerkiksi Filevalikon New-komento kutsuu CWinApp::OnFileNew-käsittelijää, joka luo uuden dokumentin, kuten tekee myös työkalurivin New-kuvake. Tämä ei tarkoitta, ettetkö voisi muuttaa näiden valintojen oletus-
270 Sovelluksen osat käyttäytymistä. Voit toki korvata haluamasi funktiot omillasi ja tehdä funktiossa mitä haluat. Saat jopa poistaa koko valikon ja työkalurivin, elleivät ne sovi sovellukseesi. On kuitenkin tavallisempaa jättää vakiotoiminnot ennalleen ja ainoastaan lisätä omat sovelluskohtaiset valinnat. Tämä käy helposti, koska kaikkia resursseja voidaan muokata resurssieditorissa ja ne voidaan liittää komentoja käsitteleviin funktioihin ClassWizardilla. Pikavalintojen lisääminen valikon komentoihin Kun muokkaat valikkoa resurssieditorissa, voit määrittää valikon komennolle näppäinyhdistelmän valikkotekstin perään. Jos esimerkiksi valikon komennon teksti määritellään &Toolbar\tCtrl+T, saadaan työkalurivi (toolbar) näkyviin Ctrl+T-näppäilyllä. joudut myös lisäämään pikavalintataulukkoon (accelerator table) ID_VIEW_TOOLBAR-tunnuksen ja määrittämään sille näppäinyhdistelmän Ctrl ja T. Joudut lisäämään kaksi valikon komentoa SDICoin-esimerkkiin, toisen kolikon lisäämistä, toisen poistamista varten. Kun valitset toisen komennoista, se kutsuu dokumenttiluokan funktiota, joka joko kasvattaa tai vähentää kolikoiden lukumäärää ja varmistaa, että näkymä päivitetään. Lisää komennot valikkoon seuraavasti. Valikon komentojen lisääminen 1. Avaa ResourceView-sivulla Menu-kansio ja kaksoisnapauta IDR_MAINFRAME-alkiota. Saat esiin valikkoresurssin resurssieditoriin. 2. Napauta Edit-alkiota resurssieditorissa ja saat esiin alasvetovalikon. 3. Kaksoisnapauta alasvetovalikon alinta tyhjää alkiota. Saat tällöin esiin Menu Item Properties valintaikkunan (kuva 12.7). 4. Syötä valikon komennon ID-tunnukseksi tässä esimerkissä ID_EDIT_ADD_COIN. 5. Anna valikkoteksti Caption-ruutuun, esimerkiksi Add a Coin. 6. Syötä komennon ohjetekstiksi Prompt-ruutuun Increase the number of coins. 7. Kaksoisnapauta uudestaan alasvetovalikon alinta tyhjää alkiota. 8. Syötä komennon ID-tunnukseksi ID_EDIT_REMOVE_COIN.
OSA III LUKU 12 271 KUVA 12.7 Menu Item Properties valintaikkuna. 9. Anna valikkoteksti Caption-ruutuun, esimerkiksi Remove a Coin. 10. Syötä komennon ohjetekstiksi Prompt-ruutuun Decrease the number of coins. 11. Käynnistä ClassWizard näppäilemällä Ctrl+W tai valitsemalla View-valikosta ClassWizard. 12. Valitse Message Maps välilehti. 13. Valitse luokan nimeksi Class Name yhdistelmäruutuun CSDICoinDoc. 14. Valitse Object IDs luetteloruudussa ID_EDIT_ADD_COIN. 15. Valitse Messages-luetteloruudussa COMMAND ja napauta Add Function painiketta. Napauta OK Add Member Function valintaikkunassa. 16. Valitse Object IDs luetteloruudussa ID_REMOVE_ADD_COIN. 17. Valitse Messages-luetteloruudussa COMMAND ja napauta Add Function painiketta. Napauta OK Add Member Function valintaikkunassa. 18. Napauta Edit Code painiketta.
272 Sovelluksen osat Näkymän päivittäminen Miltei aina, kun dokumentin tietoja on muutettu, on tarpeen esittää tämä myös näkymässä. Toiminto saadaan aikaan kutsumalla funktiota, joka saa aikaan näkymän piirtämisen uudelleen. Edellisessä kappaleessa lisäsit kaksi komentoa valikkoon sekä käsittelijäfunktiot niille dokumenttiluokkaan. Muokkaa nyt käsittelijäfunktiot listauksen 12.4 mukaisiksi. LISTAUS 12.4 LST12_4.CPP - Näkymän päivittäminen dokumentista 1 Tätä valikon komennon käsittelijää kutsutaan valittaessa Add a Coin -komento. 2 Kasvattaa dokumentin jäsenmuuttujaa, jossa pidetään yllä kolikoiden lukumäärää. 3 Saa aikaan dokumenttiin liitettyjen näkymien OnUpdate()- funktion kutsumisen, jonka seurauksena näkymät piirretään uudelleen. 1 void CSDICoinDoc::OnEditAddCoin() 1 2 { 3 // TODO: Add your command handler code here 4 // ** Increment the number of coins 5 m_ncoins++; 2 6 // ** Update view to redraw coin stack 7 UpdateAllViews(NULL); 3 8 } 9 void CSDICoinDoc::OnEditRemoveCoin() 10 { 11 // TODO: Add your command handler code here 12 // ** Decrement the number of coins 13 if(m_ncoins > 0) 14 m_ncoins--; 15 // ** Update view to redraw coin stack 16 UpdateAllViews(NULL); 17 } Kuten huomaat, lisäämäsi käsittelijäfunktio joko kasvattaa tai vähentää m_ncoins-muuttujan arvoa, joka onkin dokumentin koko data. Tämän jälkeen kutsutaan UpdateAllViews-funktiota parametrillä NULL. NULL parametrin arvona tarkoittaa, että kaikki dokumenttiin liitetyt näkymät päivitetään, kutsumalla kunkin näkymän omaa OnUpdatefunktiota. Tämä asettaa näkymäikkunan työalueen (client area) sisällön vanhentuneeksi ja saa aikaan OnDraw-funktion kutsumisen. Käännä ja aja nyt SDICoin-projektisi. Kokeile valikon Add a Coin ja Remove a Coin komentojen käyttöä. Näkymän tulisi olla kuvan 12.8 näköinen.
OSA III LUKU 12 273 KUVA 12.8 SDICoin-sovellus.
274 Sovelluksen osat