plik


Rozdział 12 Dokumenty, widoki, ramki oraz ich stosowanie Tworzenie aplikacji jednodokumentowych Rozbudowa aplikacji przy użyciu architektury MFC dokument /widok_______________________________ Istota klas dokumentu, widoku, ramki i szablonu dokumentu Umieszczanie dodatkowych elementów standardowego menu Miliardy dolarów wydane podczas projektowania samochodów, pozwoliły ujednolicić współpracę człowieka z maszyną. Dzięki temu, przesiadając się z jednego samochodu do drugiego, jesteśmy w stanie w ciągu kilku minut opanować jego obsługę. Umiejętność prowadzenia można z łatwością przenieść na inny samochód, oferujący wirtualnie takie same elementy sterowania, różniące się głównie pod względem estetycznym. Popularność aplikacji Windows opiera się na jednolitym interfejsie użytkownika. Typowa taka aplikacja wyposażona jest w pasek tytułowy u góry okna, umieszczone pod nim rozwijane menu oraz jeden lub więcej pasków narzędziowych, których ikony stanowią skróty do możliwych do wykonania operacji. Poniżej pasków narzędziowych znajduje się główny obszar wyświetlania, a całe okno zamyka od dołu pasek stanu. Przeglądając wiele spośród aplikacji zauważymy podobieństwa pozycji menu oraz przycisków kontrolnych opatrzonych wizerunkami, które ujednolicają ich znaczenie. Dzięki temu w łatwy sposób przenieść się można z edytora tekstu do programu e-mail i w ciągu kilku minut zredagować wiadomość. Dla użytkowników Visual C++ tworzenie aplikacji Windows, z ich elementami gra- ficznymi i użytkowymi jest zajęciem w ogóle niewymagającym wysiłku. Biblioteka MFC wspiera ich w ogromnym stopniu podczas tworzenia szkieletu aplikacji, dzięki kreatorowi AppWizard, automatycznie umieszczającemu paski menu, narzędziowy oraz stanu, a także inne komponenty i pozwalającemu na dostosowywanie ich do własnych oczekiwań. Klasy utworzone przez AppWizard współpracują ze sobą, tworząc zorganizowaną strukturę, zwaną potocznie architekturą dokument/widok. 274 Poznaj Visual C++ 6 Prawdziwe znaczenie terminu „dokument" Pierwszą aplikacją korzystającą z techniki dokument/widok był MS Word, pozwalający tworzyć dokumenty tekstowo-graficzne. Słowo dokument jest jednak znacznie szerszym pojęciem, niż zwykło się sądzić. Klasa Document może zawierać dane każdego typu, czyniąc architekturę dokument/widok dostępną dla przeróżnych aplikacji. Podstawowym założeniem takiej architektury jest oddzielenie danych wymaganych dla działania aplikacji od danych przeznaczonych dla użytkownika. Osiąga się to poprzez przechowywanie danych aplikacji w jednej klasie (klasa dokumentu), podczas gdy informacje dla użytkownika prezentowane są poprzez inną (klasę widoku), stąd nazwa dokument/widok. Rozwiązania proponowane przez MFC dostarczają jednak o wiele więcej ponad tę podstawową organizację aplikacji. MFC zawiera także wiele rozwiązań dotyczących funkcjonalności interfejsu użytkownika, a także metod ładowania i przechowywania danych w plikach. Istnieją dwie kategorie aplikacji typu dokument/widok, SDI (Single Document Interface — aplikacja je,dnodokumentowa oraz MDI (Multiple Document Interface — aplikacja wielo- dokumentowa). Niniejszy rozdział koncentruje się na aplikacjach typu SDI. PATRZ TAKŻE • Więcej szczegółów dotyczących aplikacji MDI znajdziesz w rozdziale 21. Tworzenie aplikacji SDI Zadanie utworzenia aplikacji Windows wraz ze wszystkimi elementami jej interfejsu, takimi jak paski narzędzi, stanu, mogłoby być zniechęcające, gdyby nie kreator AppWi-zard, wykonujący całą pracę za programistę. W rzeczywistości AppWizard daje nam znacznie więcej, niż tylko podstawowe elementy aplikacji i włącza do niej kilka opcjonalnych dodatków. Na przykład połączenie aplikacji z bazą danych jest takim właśnie dodatkiem; innym może być włączenie podstawowych funkcji do obsługi poczty elektronicznej. W tym punkcie utworzymy aplikację typu SDI, która posłuży nam do prześledzenia wygenerowanych klas oraz metod działania architektury dokument/widok. Utworzymy zatem aplikację, nadając jej nazwę SDICoin. Tworzenie aplikacji SDI przy użyciu AppWizard 1. Wybierz pozycję New z menu File. Otwórz kartę Projects okna dialogowego New, a następnie wybierz MFC AppWizard (exe). 2. Wpisz nazwę projektu (SDICoin w tym przykładzie) i kuknij OK. Dokumenty, widoki, ramki oraz ich stosowanie 275 3. W oknie kroku l (MFC AppWizard Step l) kliknij przełącznik Single Document i upewnij się, czy zaznaczona została opcja Document/View Architecture Support? (patrz rysunek 12.1). Kliknij Next. MFC AppWizaid. Stępi What type of applicafion would you like to create? ^ ISingtedocumetili ^ ^ititiple documents (~ fiialog based P Document/Yiew architecture support? Whatjanguage wouldyou likeyoui resources in? English [Uniled Slates] [APPWZENLLDLL^ Finish Cancel Rysunek 12.2. Krok 4 kreatora AppWizard New Pioiect Infolinalion AppWizard will create a new ®Rdeton project with the following specifications: Application type of SDICcnn: Single Document InterfaceApptication targeting: Win32 CIasses to be cieated: Application: CSDICoinApp in SDlCoin.h and SDICoincpp Frame; CMairiFrame in MainFrm.h and MainFrm.cpp Document CSDICoinDoc in SDICoinDoe.h and SDICoinDoc.cpp View: CSDICoirf/iew in SDICoinYiew.h and SDICohView.cpp Features: + initiat tootbaf in rnain fiame + ReBaf ?^le menus and toolbafs + Initiaf status bar in rnain frame + Pnnting and Print Prevtew suppoft in view + 3D Conhols + Uses shated DLL implemenlation (MFC4ŁDLL) + ActiveX Conirob support enabled ł Localizable teKt in: English [United Stałeś] Project Directory: C:'>Ptogran> Files\Miciosoft Visual StudB',MyPiojec[s\SDICoin Cancel Rysunek 12.3. Okno dialogowe The New Project Infonnation Aktualnie tworzoną aplikację będziemy rozbudowywać, tak by w efekcie końcowym wyświetlała stos monet. Opcje menu będą umożliwiały dodawanie i zabieranie monet ze stosu. Dane, które będą obejmowały jedynie aktualną liczbę monet, przechowywane będą w klasie dokumentu, a klasa widoku będzie miała do nich dostęp w celu wyświetlenia stosu. Dokumenty, widoki, ramki oraz ich stosowanie 277 Jakkolwiek jest to przykład bardzo prosty, powinien rzucić nieco światła na pewne fundamentalne założenia architektury dokument/widok, takie jak hermetyzacja danych. Dzięki hermetyzacji dane przechowywane są tylko w klasie dokumentu, natomiast funkcje dostępu pozwalają klasie widoku na dostęp do nich w celu przekazania użytkownikowi informacji w wybrany sposób. Przykładowo, zamiast wyświetlania obrazu stosu monet, klasa widoku może przekazać informacje o nim w postaci aktualnej sumy monet, bez potrzeby wprowadzania zmian w klasie dokumentu. Jednakże przed dokonaniem rozbudowy aplikacji, konieczne są pewne wyjaśnienia dotyczące klas wygenerowanych przez AppWizard. Dokumenty zawierają dane aplikacji Klasa dokumentu jest wyprowadzana z CDocument, a jej zmienne składowe przechowują dane aplikacji. Funkcje składowe wyprowadzonej z CDocument klasy operują na tych danych. Na przykład, funkcja Serialize () służy do ładowania i zapisywania danych na dysku. Aplikacja jednodokumentowa posiada jedną klasę dokumentu wyprowadzoną z klasy MFC CDocument oraz drugą, wyprowadzoną z jednej z klas widoku MFC. Te klasy widoku znajdują zastosowanie w różnych typach aplikacji opartych na architekturze dokument/widok. Tabela 12.1 wymienia dostępne klasy. Aplikacja SDICoin korzysta z klasy domyślnej cview. Tabela 12. l Bazowe klasy widoku dostępne dla AppWizard Nazwa klasy Opis cview Klasa domyślna, implementująca wszelkie najważniejsze funkcje związane z widokiem, włączając w to interakcje z klasą dokumentu CSrollview Wyprowadzona z CView i rozszerzona o możliwość przewijania, gdy logiczny obszar wyświetlania jest większy od fizycznie dostępnego CListView Używa listy jako widoku, umożliwiając aplikacjom wyświetlanie ikon lub tekstu w kolumnach CTreeView Używa listy drzewiastej jako widoku, umożliwiając aplikacjom wyświetlanie informacji hierarchicznych CEditView Używa wieloliniowego pola edycji jako klasy widoku, rozszerzonego o możliwości przewijania i szukania CRichEditView Używa pola tekstu formatowanego jako widoku, umożliwiając wszechstronniejszą edycję tekstu, niż oferuje to CEditView CFormView Używa szablonu okna dialogowego jako widoku, umożliwiając aplikacji przybranie formy bazy danych 278 Poznaj Visual C++ 6 CRecordView CHtmlView Wyprowadzona z CFormView, rozszerzona o możliwość połączenia widoku z określonym rekordem bazy danych. Klasa ta dostępna jest jedynie wtedy, gdy zostało ustalone wsparcie obsługi baz danych Używa Intemet Explorera jako widoku, pozwalając na tworzenie aplikacji przeglądarek WWW PATRZ TAKŻE Informacji o klasie CListYiew szukaj w rozdziale 19. Informacji o klasie CTreeYiew szukaj w rozdziale 19. Informacji o klasie CRichEdi t szukaj w rozdziale 19. Informacji o klasie CHtmlView szukaj w rozdziale 19. Informacji o klasie CSrollYiew szukaj w rozdziale 18. Informacji o klasach CFormView i CRecordView szukaj w rozdziale 24. Informacji o obsłudze plików w architekturze dokument/widok szukaj w rozdziale 23. Informacji o ustalaniu wsparcia baz danych szukaj w rozdziale 24. Informacji na temat automatyzacji OLE szukaj w rozdziale 25. Klasy aplikacji SDI Struktura klas jednodokumentowej aplikacji różni się od istniejącej w aplikacjach budowanych na bazie okien dialogowych. Przeglądając zawartość panelu ClassView, zobaczymy utworzone przez AppWizard w projekcie SDICoin cztery klasy stanowiące strukturę aplikacji. Możemy to zobaczyć na rysunku 12.4. COocTemplateN CWhApp H L;CD«wmen(;,,|J |" "^liSlli^sCWnifI.^' ^"'ag, CSDICoinApp J CSDICoinDoc J CSDICoinView J CMainFrame Rysunek 12.4. Hierarchia klas w aplikacji SDI Dokumenty, widoki, ramki oraz ich stosowanie 279 Wyprowadzona z cwinApp klasa cSDlCoin spełnia kilka funkcji i różni się w implementacji od klasy obiektu w projekcie zbudowanym na bazie okna dialogowego. Zarządza inicjalizacją aplikacji i odpowiedzialna jest za tworzenie powiązań pomiędzy klasami dokumentu, widoku i ramki. Odbiera również komunikaty od systemu Windows, kierując je następnie do odpowiedniego okna docelowego. Jak napisano wcześniej, w aplikacji jednodokumentowej utworzony zostanie tylko jeden egzemplarz klasy csoiCoinDoc, wyprowadzonej z CDocument. Klasa csoiCoinDoc pełni rolę kontenera zawierającego dane aplikacji. Dane te mogą występować w każdej formie. W programie SDICoin będą miały formę po prostu pewnej liczby monet. Zwykle jednak dane te bywają o wiele bardziej złożone. W programie do edycji tekstu, danymi będą zarówno treść tekstu, jak i informacje o jego formatowaniu. Program typu CAD potrzebował będzie z kolei danych o współrzędnych i innych informacji dotyczących tworzonego rysunku. Sposób implementacji mechanizmu przechowywania danych zależy od programisty. Idealnie, gdy zostanie to rozwiązane poprzez obiektowe podejście do sprawy, przez co dane mogą być przechowywane w sposób całkiem niezależny od interfejsu użytkownika. Stosowanie się do takiej metodologu nadaje kodowi zwartość i elastyczność. W przykładzie SDICoin wykorzystywany jest tylko jeden widok, lecz być może będziemy chcieli korzystać z kilku widoków, operujących na tych samych danych dokumentu. Widoki mogą występować w różnych typach; jeśli jednak tak będzie, musimy zachować pewną ostrożność w stosunku do mechanizmu stosowanego przez klasę dokumentu, dostarczającego danych do każdego widoku. Przodkiem jest CObject Klasa CObject jest klasą bazową dla wszystkich klas MFC, poza kilkoma wyjątkami. Wszystkie wyprowadzone z niej klasy dziedziczą zdolność do sprawdzania typu klasy obiektu podczas pracy programu, udzielania informacji diagnostycznych oraz wsparcia serializacji. Klasa C3DlCoinView odpowiedzialna jest za formowanie wizualizacji danych dokumentu w celu przedstawienia ich użytkownikowi oraz umożliwienia mu interakcji z owymi danymi. Każdy widok może być połączony z jednym tylko dokumentem, natomiast każdy dokument może zostać połączony z wieloma obiektami widoku. Klasa widoku uzyskuje dostęp do danych, których wymaga, za pośrednictwem funkcji składowych dostarczanych przez dokument. Modyfikowanie stylu ramki okna Poprzez zmianę treści funkcji preCreateWindowO można za pomocą znaczników stylu modyfikować ramkę okna głównego przed jego wyświetleniem. Funkcji tej przekazuje się odwołanie do struktury CREATESTRUCT zawierającej atrybut style, który można modyfikować zanim zostanie przekazany klasie bazowej funkcji. 280 Poznaj Visual C++ 6 Rolą klasy CMainFrame jest dostarczenie aplikacji okna roboczego. Jest to klasa wy- prowadzona z CFrameWnd, dostarczającej prostego okna. Domyślne znaczniki stylu okna obejmują WS_OVERLAPPEDWINDOW, który umieszcza w oknie głównym pasek tytułowy zawierający przyciski minimalizowania, maksymalizowania oraz zamykania okna, menu systemowe i umożliwia zmianę położenia i wymiarów okna. Znaczniki stylu obejmują także znacznik FWS_ADDTOTITLE, który powoduje automatyczne wyświetlanie w pasku tytułowym okna tytułu dokumentu po jego załadowaniu. Klasa CMainFrame obsługuje również procesy kreacji, inicjalizacji i usuwania pasków narzędziowych i stanu. PATRZ TAKŻE • Aby poznać inne różnice pomiędzy klasami SDI a MDI, zajrzyj do rozdziału 21. Elementy wizualne aplikacji SDI Akceptując domyślne ustawienia zaproponowane przez AppWizard w 4 kroku, utwo- rzyliśmy aplikację posiadającą pasek tytułowy z przyciskami minimalizacji, maksymalizacji i zamykania, rozwijanym menu zawierającym polecenia wydruku i podglądu wydruku, paskiem narzędziowym oraz paskiem stanu. Niektóre z tych elementów są jednak opcjonalne. Więcej opcji możemy wybierać, klikając przycisk Advanced w oknie dialogowym wyświetlanym w czwartym kroku kreatora AppWizard. Okno Advanced pokazane jest na rysunku 12.5. Wykazane tu opcje pozwalają na zmianę stylu okna głównego. Advanced Optioni Docurnent Template Strings Window Styles F" U sesplit aindow -Mainftamestyles • • •••••—•-••••.•---.••- . l F ll-lick frame P |Syrfern mew F M.inimizebon r" Mirumized F Manimize bon r" Maumized Close Rysunek 12.5. Okno dialogowe Advanced Options dostępne w 4 kroku kreatora AppWizard Dokumenty, widoki, ramki oraz ich stosowanie 281 Komunikaty ramki okna Niektóre komunikaty wysyłane są tylko do okien posiadających ramki. Obejmują one komunikaty dotyczące przemieszczania, zmiany wymiarów, minimalizacji oraz maksymalizacji okna aplikacji. Obszar roboczy okna głównego nie implementuje widoku. Widok posiada własne okno, bez ramki i menu, które jest oknem potomnym okna głównego. Okno widoku nakładane jest na obszar roboczy okna głównego (rysunek 12.6). W ten sposób widok zostaje zaimplementowany, co powoduje, iż ten sam model stosowany może być w aplikacjach zarówno jedno-, jak i wielodokumentowych, z różnicą tkwiącą w klasach okna głównego i potomnego. Rysunek 12.6. Graficzne elementy aplikacji SDI O CMainFrame @ CToolbar © CSDICoinYiew O CStatusBar © Okno widoku 282_____________________________________Poznaj Visual C++ 6 Poniżej widzimy objaśnienia dotyczące poszczególnych elementów okna: " CMainFrame klasa wyprowadzona z CFrameWnd; tworzy okno główne aplikacji • CToolBar implementuje pasek narzędziowy dokowany w oknie głównym • CSDlCoinView implementuje widok, jako okno potomne okna głównego • CStatusBar implementuje pasek stanu dołączony do okna głównego • View window nakłada się na obszar roboczy okna głównego. Można się zastanawiać, skąd właściwie biorą się takie elementy wizualne jak menu, czy paski narzędziowe. Są one zasobami dodawanymi do projektu przez AppWizard podczas jego tworzenia. Gdy otworzymy panel ResourceView, zobaczymy cztery rodzaje zasobów (tablica akceleratorów, ikona, menu i pasek narzędziowy) opatrzone tym samym identyfikatorem, czyli IDR_MAINFRAME. Każdy z tych zasobów możemy wywołać do edycji, klikając dwukrotnie jego pozycję. Identycznym identyfikatorem oznaczona jest również specjalna tablica. Zawiera ona łańcuch znakowy w specyficznym formacie. Łań- cuch jest podzielony na siedem oddzielnych łańcuchów ograniczonych znakiem \n (przejście do nowej linii). Każdy z łańcuchów opisuje odrębny element dokumentu. Na przykład, pierwszy z nich jest tytułem okna aplikacji. Istota szablonów dokumentów SDI Rozdział ten w dalszym ciągu skoncentrowany będzie na indywidualnych klasach aplikacji dokument/widok oraz elementach wizualnych. Wszystkie te elementy zebrane są i zarządzane przez klasę szablonu dokumentu. Gdy spojrzymy na rysunek 12.4 ukazujący hierarchię klas aplikacji SDI, zobaczymy znajdującą się tam klasę CSingleDocTemplate, która jest klasą szablonu dokumentu, używaną w aplikacjach jednodokumentowych. Utworzenie egzemplarza klasy CSingleDocTemplate i jego wykorzystanie następuje wewnątrz funkcji CSDlCoinApp: :lnitlnstance (listing 12.1). Funkcję tę wywołuje osnowa MFC do inicjalizacji aplikacji w chwili jej startu. Listing 12.1. LST13_4.CPP- funkcja Initinstance 1 BOOL CSDlCoinApp::Initinstance () O 2 { 3 // ** Uwaga: pewne linie usunięto dla zwięzłości 4 5 // .Rejestracja szablonu dokumentu. 6 // Szablony tę służą połączeniu dokumentów, okien i widoków. 7 8 CSingleDocTemplate* pDocTemplate; 9 pDocTemplate = new CSingleDocTemplate( 10 IDR_MAINFRAME, @ 11 RUNTIME CLASS(CSDICoinDoc), Dokumenty, widoki, ramki oraz ich stosowanie 283 12 RUNTIME_CLASS(CMainFrame), // Główne okno SDI 13 RUNTIME_CLASS(CSDICoinYiew)) ; 14 AddDocTemplate(pDocTemplate) ; 15 16 // Parse command linę for standard snęli commands, DDE, file open 17 CCommandLineInfo cmdinfo; 18 ParseCommandLine(cmdinfo); ® 19 20 // Wysłanie komend, określonych w linii poleceń 21 if (!ProcessShellCommand(cmdinfo)) O 22 return FALSE; 23 24 // Tylko jedno okno zostaje zalnicjalizowane i następuje jego otwarcie 25 m_pMainWnd->ShowWindow(SW_SHOW) ; 26 m_pMainWnd->UpdateWindow(); 27 28 return TRUE; 29 t 21 if (!ProcessShellCommand(cmdinfo)) 22 return FALSE; 23 24 // Tylko jedno okno zostaje zainicjalizowane i następuje jego otwarcie 25 m_pMainWnd->ShowWindow(SW_SHOW); ® 26 m_pMainWnd->UpdateWindow(); © 27 28 return TRUE; 29 ) O Wywołanie przez MFC w momencie startu programu. @ Inicjalizacja szablonu dokumentu z identyfikatorem zasobów, klasami dokumentu, okna i widoku. © Analiza składni argumentów linii poleceń. O Przetworzenie argumentów linii poleceń i utworzenie nowego dokumentu lub otwarcie pliku. © Wyświetlenie głównego okna aplikacji. 284_____________________________________Poznaj Visual C++ 6 Jak widzimy, w linii 9 utworzony został obiekt CSingleDocTemplate, któremu przekazano cztery parametry. Pierwszym jest identyfikator zasobów IDR_MAINFRAME. W przykładowym programie SDICoin jest to identyfikator wspólny dla zasobów czterech typów: ikony aplikacji, menu, paska narzędziowego oraz tablicy akceleratorów. Następne trzy parametry są wskaźnikami klas wykonawczych dla odpowiednio: dokumentu, okna głównego oraz widoku. Wskaźniki te generowane są przez makro RUNTIME_CLASS. Jest to możliwe za sprawą AppWizard, który włączył do projektu makra DECLARE_DYNCREATE i IMPLEMENT_DYNCREATE, pozwalające na dynamiczne tworzenie klas. Obiekty dokumentu, widoku i okna nie są jeszcze utworzone na tym etapie. Powyższy kod inicjalizuje obiekt CSingleDocTemplate, zawierający wszelkie informacje niezbędne w chwili ładowania zasobów, alokacji dokumentów, widoków i okien. Klasa szablonu dokumentu nazywana jest ciass factory, co można przetłumaczyć jako fabryka klas. Fabryka klas Klasa szablonu dokumentu jest przykładem „fabryki klas". Jest to klasa definiująca proces tworzenia innych klas. Zna ona zatem zasady tworzenia klas charakterystycznych dla danej aplikacji. Obiekt szablonu dokumentu przetrzymywany jest w klasie aplikacji. Wywołana w linii 14 funkcja AddDocTemplate rejestruje nowo tworzony obiekt szablonu dokumentu w klasie cwinApp. Klasa cwinApp przechowuje obiekt CSingleDocTemplate do czasu jego destrukcji, która następuje dopiero podczas zamykania aplikacji. Wykonując operację destrukcji, klasa cwinApp uwalnia pamięć przydzieloną dokumentowi, dzięki czemu nie musimy zawracać sobie tym głowy. PATRZ TAKŻE • Więcej informacji o klasie wykonawczej znajdziesz w rozdziale 23. Stosowanie funkcji osnowy w architekturze dokument/widok Być może zagadnieniem najtrudniejszym do zrozumienia, a co za tym idzie, do stosowania w praktyce, dotyczącym architektury dokument/widok jest przebieg sekwencji tworzenia obiektów oraz funkcji wirtualnych. Trudność polega nie na złożoności, ale na tym, iż większość procesów implementacyjnych przebiega bez naszego udziału. Jest to jednak rozwiązanie korzystne, gdyż zwalnia nas z wykonania dużej części pracy. Aby w pełni zrozumieć istotę architektury dokument/widok, musimy poznać te zakulisowe procesy. Dokumenty, widoki, ramki oraz ich stosowanie 285 Analizowanie argumentów linii poleceń Najprostszym sposobem dostarczania dodatkowych argumentów linii poleceń jest wyprowadzenie klasy z CommandLineinfo i zmiana treści funkcji ParseParam. Podczas tworzenia i inicjalizacji obiektu CSingleDocTemplate w liniach 8-14 listingu 12.1 powiązane z nim są parametry linii poleceń i wtedy okno aplikacji zostaje wyświetlone. Klasa ccommandLineinfo (linia 17) jest pomocna przy obsłudze parametrów przekazywanych aplikacji z linii poleceń. Funkcja CWinApp: : ParseCommandLine (linia 18) pobiera odwołanie do tej klasy jako parametr. Wywołanie funkcji ParseParam następuje dla wszystkich argumentów linii poleceń, zapisywanych w zmiennych obiektu ccomman-dLineinfo. Tabela 12.2 wymienia domyślne opcje linii poleceń oraz ich znaczenia. Tabela 12.2. Argumenty linii poleceń Argument Działanie NONE Tworzy nowy dokument f i l e Otwiera wskazany plik /p f ile Drukuje wskazany plik na drukarce domyślnej /p t f ile Drukuje wskazany plik na wskazanej drukarce printer driver port /dde Rozpoczyna sesję DDE /automation Uruchamia aplikację jako serwer OLE /embedding Uruchamia aplikację w celu edycji obiektu OLE osadzonego w innej ____ aplikacji_________ _________________________ Z powyższej tabeli możemy wyczytać, że jeżeli z linii poleceń nie zostaną przekazane żadne argumenty, podczas startu aplikacji tworzony jest nowy dokument. Jest to oczywiście działanie domyślne. Wraz z dokumentem, utworzone zostaje okno główne aplikacji oraz widok. Ma to miejsce w funkcji CWinApp: :ProcessShellCommand, widocznej w linii 21. Listing 12.2 służy zapoznaniu się z sekwencją tworzenia kolejnych elementów aplikacji. Musimy jednak pamiętać, iż jest listing uproszczony, podany jedynie w celu zobrazowania owej sekwencji. 286_____________________________________Poznaj Visual C++ 6 Listing 12.2. LST13_3.CPP — sekwencja tworzenia dokumentu, widoku oraz okna głównego 1 BOOL CWinApp::ProcessShellCommand( O CCommandLineInfo& rCmdInfo) 2 ( 3 if(rCmdInfo.NewFile) 4 ( 5 OnFileNewO; 6 } 7 if(rCmdInfo.OpenFile) 8 ( 9 OpenDocumentFile(rCmdInfo.strFileName); 10 } 11 12 ) 13 void CWinApp::OnFileNewO 14 { 15 m pDocManager->OnFileNew() ; 16 } 17 void CDocManager::OnFileNew() 18 ( 19 pTemplate->OpenDocumentFile(NULL); @ 20 } 21 CSingleDocTemplate::OpenDocumentFile (LPCTSTR IpszPathName) 22 { 23 if(m_pOnlyDoc != NULL) 24 pDocument->SaveModified(); ® 25 else 26 ( 27 pDocument = CreateNewDocument() ; 28 pFrame = CreateNewFrame(pDocument); O 29 ) 30 31 pDocument->OnNewDocument(); © 32 InitialUpdateFrame(pFrame, pDocument); © 33 O Wywołanie to następuje z funkcji initinstance @ pTemplatejest wskaźnikiem do zarejestrowanego obiektu CSingleDocTemplate Dokumenty, widoki, ramki oraz ich stosowanie 287 © Jeśli dokument istnieje, zostaje zainicjalizowany ponownie O Obiekty dokumentu i okna konstruowane są w szablonie, za pomocą klasy wykonawczej. CreateNewFrame konstruuje również widok. © Inicjalizacja dokumentu przez funkcję wirtualną © Inicjalizacja okna głównego poprzez wywołanie LoadFrame. Aktywowanie widoku i wysłanie komunikatu WM_INITIALUPDATE, w celu wywołania cview: :0nlni-tialUpdate. Funkcja cwinApp zapisana w linii l powoduje serię wywołań funkcji osnowy aplikacji, zależnie od wyników analizy argumentów linii poleceń. Jeśli nie zostały tam określone żadne argumenty, wywołana zostanie funkcja CWinApp: :OnFileNew (linia 13). Ta sama funkcja wywołana będzie również wtedy, gdy użytkownik wybierze pozycję New z menu File, które jest automatycznie umieszczane w projekcie przez AppWizard. Listing 12.2 obrazuje logikę kreacji nowego dokumentu podczas startu aplikacji, lecz różnica pomiędzy tą sekwencją, a procedurą otwierania pliku nie jest duża. Leży ona na samym początku sekwencji, gdzie CWinApp: :processShellCommand odnajduje wskazany plik lub wzywa użytkownika do wskazania go poprzez standardowe okno dialogowe. Po zlokalizowaniu pliku, jego nazwa oraz ścieżka dojścia przekazane zostają tej samej funkcji CSingleDocTemplate: :OpenDocumentFile (linia 21) zamiast argumentu NULL, który powoduje utworzenie nowego dokumentu. CWinApp: :OnFileNew bezzwłocznie przekazuje kontrolę funkcji CDocManager: :OnFileNew (linia 15), która pobiera wskaźnik do utworzonego wcześniej obiektu csin-gleDocTemplate. Używając wskaźnika szablonu, wywołuje OpenDocumentFile z parametrem NULL. W tym miejscu rozpoczyna się właściwa praca. CSingleDocTemplate:: OpenDocumentFile Obiekt dokumentu w aplikacji SDI tworzony jest tylko raz (w pierwszym wywołaniu OpenFileDocument). Kolejne wywołania funkcji w odpowiedzi na wybór opcji New lub Open z menu File powodują reinicjalizację dokumentu, przez co może on być użyty ponownie. Reinicjalizacja następuje w funkcji SaveModified. Funkcja ta sprawdza fakt wprowadzenia zmian w dokumencie. Klasa CDocument, będąca klasą bazową wszystkich klas dokumentu, posiada zmienną składową typu BOOL, której wartość świadczy o obecności zmian w dokumencie. Zawartość tej zmiennej ustalana jest przez funkcję SetModifiedFIag. Jeśli w dokumencie nastąpiły zmiany, użytkownik zostaje wezwany do zapisania dokumentu przed kontynuacją pracy. Okno komunikatu umożliwia użytkownikowi kontynuację z zapisaniem zmian, bez zapisywania lub cofnięcie funkcji OpenDocumentFile. Zapisanie dokumentu odbywa się poprzez wywołanie OnSaveDocument, a dalej następuje jego reinicjalizacja poprzez kolejną funkcję DeleteContents. Obie wymienione funkcje opisane są dalej, w tym rozdziale. 288_________________________________ Poznaj Visual C++ 6 Dokumenty OLE Jeśli AppWizard tworzy aplikację serwera lub kontenera OLE, jej klasa dokumentu wyprowadzona zostanie z jednej spośród podklas CDocument: COleDocument, co-leLinkingDoc lub C01eServerDoc. Klasy te umożliwiają traktowanie dokumentów jako obiekty OLE. Jeśli zachodzi potrzeba utworzenia nowego obiektu dokumentu, wywołana zostaje funkcja szablonu dokumentu CreateNewDocument (linia 27). Używa ona informacji klasy wykonawczej do skonstruowania obiektu dokumentu, specyficznego dla danej aplikacji. Funkcja ta dodaje również nowy dokument do listy dokumentów bieżących. Wraz z nowym dokumentem, utworzony zostaje również widok oraz okno. Wykonuje to kolejna funkcja, CreateNewFrame. Funkcja CreateNewFrame (linia 28) korzysta z informacji klasy wykonawczej szablonu do skonstruowania obiektów okna i widoku. Po utworzeniu okna, następuje wywołanie funkcji CFrameWnd: :LoadFrame, której przekazuje się identyfikator zasobów, określony w konstruktorze szablonu. Przypomnijmy sobie, iż identyfikatorem tym jest IDR_MAIN-FRAME. 'LoadFrame ładuje każdy z zasobów (menu, pasek narzędziowy, ikona, tablice akceleratorów i łańcuchów znakowych) i dołącza je do okna. Jeśli załadunek któregokolwiek z zasobów zakończy się niepowodzeniem, funkcja LoadFrame zakończy działanie, wysyłając komunikat: Warning: CDocTemplate couldn't create a f ramę (Ostrzeżenie: CDoc-Template nie mógł utworzyć okna). Kiedy wszystkie obiekty są już skonstruowane, następuje inicjalizacja dokumentu poprzez funkcję OnNewDocument (linia 31), opisaną nieco dalej. Na końcu sekwencji wywoływana jest funkcja InitialUpdateFrame (linia 32). Uaktywnia ona nowo utworzony widok, wysyłając następnie komunikat WM_INITIAL-UPDATE do okien potomnych okna głównego, po którym wywołana zostaje funkcja wirtualna CView: :OnlnitialUpdate. Normalną praktyką jest rozszerzenie treści tej funkcji w celu specyficznej inicjalizacji widoku. Konieczne zwykle jest również upewnienie się, iż wywołano również właściwą klasę bazową. CDocument:: OnNewDocument Funkcja ta wywoływana jest podczas tworzenia obiektu dokumentu. Ma to miejsce podczas startu aplikacji lub po wydaniu przez użytkownika polecenia New z menu File. Dalej następuje wywołanie funkcji DeleteContents w celu uporządkowania zawartości dokumentu, ustalenie stanu modyfikacji na FALSE (niezmodyfikowany). Funkcja ta opróżnia również łańcuch, w którym zapisana była nazwa pliku zawierającego dokument. CDocument: :OnOpenDocument Funkcję tę wywołuje się podczas otwierania istniejącego pliku. Może się to odbyć podczas startu aplikacji, jeśli plik został umieszczony w wierszu poleceń, jako jego argu- Dokumenty, widoki, ramki oraz ich stosowanie 289 ment lub w odpowiedzi na polecenie Open z menu File, wydane przez użytkownika. Wywołana zostaje następnie funkcja DeleteContents. Implementacja domyślna otwiera plik, a następnie wywołuje funkcję Serialize dla załadowania danych dokumentu. Po załadowaniu danych, plik zostaje zamknięty, a stan modyfikacji otrzymuje wartość FALSE (niezmodyfikowany). CDocument: :OnSaveDocument Funkcja wywoływana jest podczas zapisywania pliku. Dzieje się to po wydaniu przez użytkownika polecenia Save lub Save As z menu File. Wywołuje się ją również, gdy użytkownik zapytany w momencie zamykania zmodyfikowanego dokumentu wyda decyzję o zapisaniu pliku. Funkcja ta wykorzystuje jeden parametr, będący stałym łańcuchem znakowym, zawierającym nazwę pliku. Implementacja domyślna otwiera plik, a następnie wywołuje funkcję Serialize w celu zapisania danych dokumentu. Gdy dane zostaną już zapisane, plik ulega zamknięciu, a stan modyfikacji przybiera wartość FALSE. CDocument: :DeIeteContents Funkcja ta wywoływana jest przez funkcje OnNewDocument, OnOpenDocument oraz OnCloseDocument. Jest odpowiedzialna za usunięcie zawartości bieżącego dokumentu. Implementacja domyślna nie czyni niczego, gdyż dane dokumentu są specyficzne dla danej aplikacji. Musimy rozszerzyć treść tej funkcji, aby powodowała zwolnienie zawartości zmiennych dokumentu i ich reinicjalizację. CDocument:: OnCloseDocument Funkcja ta wywoływana jest w momencie zamykania dokumentu. W aplikacjach SDI ma to miejsce, gdy użytkownik nakazuje otwarcie innego lub utworzenie nowego dokumentu, jak również w chwili zamykania aplikacji. Funkcja OnCloseDocument zamyka najpierw wszystkie widoki należące do dokumentu, a następnie wywołuje DeleteContents przed destrukcją obiektu dokumentu. PATRZ TAKŻE • Więcej informacji na temat automatyzacji OLE znajdziesz w rozdziale 25. • Informacji o obsłudze plików i serializacji szukaj w rozdziale 23. Używanie dokumentów i widoków Implementacja architektury dokument/widok dostarcza podstawowych sposobów rozwijania aplikacji. Oddzielenie kodu obsługującego dane od kodu obsługującego interfejs użytkownika daje w efekcie aplikację modułową, która jest łatwiejsza do rozbudowy. Przykładowo, zmiana sposobu prezentacji istniejących danych na nowy jest kwestią zbu- 290 Poznaj Visual C++ 6 dowania nowej klasy widoku, używającej interfejsu istniejącego dokumentu dla uzyskania dostępu do żądanych informacji. Zaprojektowanie interakcji pomiędzy parą klas, dokumentu i widoku, jest najważniejszym zadaniem. Zwykle, największą trudnością, nawet dla doświadczonych programistów, jest ustalenie, które informacje powinny znaleźć się w klasie dokumentu, a które w klasie widoku. Nie istnieją tutaj twarde reguły i wszystko zależy w największym stopniu od specyfiki aplikacji. Optymalnie sam widok nie powinien przechowywać danych, tylko pobierać je w razie potrzeby z dokumentu. Tym sposobem zmiany w danych zachodzą tylko w jednym miejscu, skąd wprowadzane są do wszystkich widoków dokumentu. Jednakże takie rozwiązanie nie zawsze jest wygodne lub wystarczające. Weźmy jako przykład edytor tekstu, w którym często kopia wszystkich lub części danych dokumentu przechowywana jest w klasie widoku, gdyż w przeciwnym wypadku, widok musiałby mieć stały dostęp do klasy dokumentu (nawet za każdym naciśnięciem klawisza), co spowalniałoby pracę aplikacji. Istnieje wiele sytuacji, w których wskazane jest umieszczanie pewnych porcji danych w widoku w celu przyspieszenia działania. Nie stanowi to problemu - mamy pełną swobodę w tworzeniu własnego kodu w tym zakresie. Jednak niepotrzebne duplikowanie danych skomplikuje nam pracę, powinniśmy zatem takich sytuacji unikać. Inicjalizacja danych dokumentu Klasa CDocument jest klasą abstrakcyjną, musimy zatem wyprowadzić z niej własną klasę. Składowe klasy dokumentu służą do przechowywania danych aplikacji. W każdej, nawet najprostszej aplikacji, dokumenty wymagają licznych zmiennych składowych, często w formie tablic lub list łączonych. Klasy kolekcji MFC Biblioteka MFC dostarcza kilku klas kolekcji, m.in. CArray i CObList, do projektowania złożonych struktur. Pamiętajmy, iż w aplikacji SDI, obiekt dokumentu tworzony jest tylko raz i używany dalej dla kolejnych dokumentów. Z tego też powodu konstruktor dokumentu nie zawsze jest właściwym miejscem dla inicjalizacji zmiennych składowych, ponieważ byłyby one wywołane tylko raz, a nie dla każdego nowego dokumentu. W podrozdziale „Stosowanie funkcji osnowy w architekturze dokument/widok" widzieliśmy, że wirtualna funkcja dokumentu DeleteContents wywoływana jest automatycznie z funkcji OnNewDocument, OnOpenDocument oraz OnCloseDocument. Są to sytuacje, w których zawartość dokumentu ulega zmianie. Funkcja DeleteContents odpowiedzialna jest za usunięcie zawartości dokumentu i może z powodzeniem inicjalizo-wać zmienne dokumentu, po wykonaniu podstawowego zadania. Dokumenty, widoki, ramki oraz ich stosowanie 291 Dodawanie do dokumentu zmiennych składowych Klasa dokumentu jest klasą jak każda inna w projekcie, możemy więc dodawać do niej zmienne stosując menu skrótów panelu ClassView wybierając z niego pozycję Add Member Variable. Przy dodawaniu wielu zmiennych może się okazać, iż wygodniejszym sposobem jest bezpośrednia edycja kodu. Możemy wejść w ten tryb klikając dwukrotnie pozycję klasy dokumentu w panelu ClassView. Ochrona zmiennych dokumentu Klasa dokumentu powinna zawierać zmienne chronione, do których dostęp uzyskuje się poprzez publiczne funkcje składowe. Aby zapewnić integralność danych, zmienne dokumentu powinny być zmiennymi chronionymi (protected). Zmienna chroniona może być zmieniona jedynie przez funkcje własnej klasy lub klasy z niej wyprowadzonej. Zakazanie wszystkim innym klasom dokonywania zmian w danych dokumentu oznacza, iż istnieje tylko jeden punkt, z którego zmienne mogą być zmienione; stąd wszystkie widoki powiązane z dokumentem są zawiadamiane o zmianie danych. Aby umożliwić widokom wgląd w chronione dane dokumentu, klasa dokumentu musi dostarczyć funkcji dostępowych, zwracających odwołania do zmiennych składowych. Teraz, gdy poznaliśmy już architekturę dokument/widok, zajmiemy się rozbudową programu SDICoin. W obecnym punkcie zajmiemy się klasą dokumentu, CSDICoinDoc. Klasa dokumentu wymaga liczby monet, jaką można zapisać w pojedynczej zmiennej składowej. Chociaż do klasy widoku należy jedynie narysowanie stosu monet, klasa ta wymaga dostępu do tej liczby. Najprostszą metodą zapewnienia jej tego, będzie określenie publicznej zmiennej składowej dokumentu przechowującej tę liczbę, umożliwiającej klasie widoku bezpośredni dostęp do tej liczby. Pewną niedogodnością takiego rozwiązania jest możliwość dokonania zmiany zawartości zmiennej przez klasę widoku, nawet przez przypadek. Preferowaną metodą jest posiadanie chronionej zmiennej składowej przechowującej liczbę monet dla klasy dokumentu oraz dostarczenie klasie dokumentu funkcji dostępowej, zapewniającej klasie widoku dostęp do danych. Aby dodać niezbędny ku temu kod, należy wykonać poniższą procedurę. Implementacja przechowywania danych dokumentu oraz metod dostępu 1. W panelu ClassView wybierz klasę CSDICoinDoc. Następnie z menu skrótów wybierz Add Member Variable. Otwarte zostanie okno dialogowe Add Member Variable. 2. W polu Variable Type wprowadź typ int. W polu Variable Name wpisz nazwę zmiennej: m_nCoins, a także zaznacz przełącznik alternatywny Protected Access. Kliknij OK. 292_____________________________________Poznaj Visual C++ 6 3. Mając cały czas podświetloną pozycję CSDICoinDoc, otwórz menu skrótów, a z niego wybierz tym razem Add Member Function. 4. Wprowadź int jako Function Type. Wpisz GetCoinCount w polu Function Decla- ration i wybierz przełącznik Public Access. 5. Wprowadź następujący kod do treści funkcji GetCoinCount: return m_nCoins ;. 6. Naciśnij Ctri+W lub wybierz CIassWizard z menu View, aby uruchomić kreator. 7. Wybierz kartę Message Maps. 8. Z listy Ciass Name wybierz CSDICoinDoc. 9. Z listy Object IDs wybierz taką samą pozycję. 10. Z listy Messages wybierz DeleteContents, a następnie kliknij przycisk Add Function. 11. Kliknij OK, by zamknąć okno CIassWizard. 12. Wprowadź do kodu funkcji DeleteContents po komentarzu TODO następujący wpis: m_nCoins = l ; csDlCoin posiada w tej chwili chronioną zmienną składową m_nCoins, inicjalizowa-ną w funkcji DeleteContents. Dodaliśmy również dostępową funkcję składową GetCoinCount, która będzie służyła do pobierania wartości zmiennej m_nCoins. Dostęp widoku do danych dokumentu Aby widok miał możliwość pobierania danych z dokumentu, musi mieć dostęp do jego obiektu. Osnowa MFC dostarcza takiego rozwiązania, automatycznie dodając funkcję GetDocument do klasy widoku aplikacji. Funkcja ta zwraca wskaźnik do dokumentu, z którym widok jest związany poprzez szablon dokumentu. W rzeczywistości dostarczane są dwie funkcje GetDocument. W bieżącym przykładzie zobaczymy, że klasa CSDICO-inView posiada implementacje GetDocument w plikach SDlCoinView.cpp oraz SDlCo-inView. h. Obie funkcje wykonują to samo zadanie. Jedyną różnicą pomiędzy nimi jest to, iż pierwsza użyta jest w trybie projektu Debug, druga natomiast w trybie Release. Powodem takiego rozwiązania jest fakt, że funkcje iniine są skuteczniejsze i zastępują wielokrotne wywoływanie podczas pracy programu. SDlCoinView.cpp łifdef _DEBUG CSDICoinDoc* CSDICoinView::GetDocument() ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CSDICoinDoc))) ; return (CSDICoinDoc*)m_pDocument ; łendif // _DEBUG Ol/H-^Ulll V ICYT.ll #ifdef _DEBUG // wersja debug w pliku SDICoinView.cpp iniine CDSICoinDoc* CSDICoinView::GetDocument() ( return (CSDICoinDoc*)m_pDocument; } #endif Klasa CSDICoinView odpowiada za wyświetlenie stosu monet. Kod rysujący imitację monet jest bardzo prosty i znajduje się wewnątrz funkcji CSDICoinView:: OnDraw. Dzięki kreatorowi AppWizard mamy już w programie szkielet tej funkcji. Osnowa MFC wywołuje tę funkcję zawsze, gdy widok musi zostać wysłany na ekran - na przykład, gdy wymiary okna głównego uległy zmianie. Funkcja ta wykorzystywana jest również podczas drukowania i podglądu wydruku. Zależnie od tego, gdzie widok ma zostać wyrysowany, funkcji OnDraw przekazywane są różne konteksty urządzeń. Teraz zajmiemy się edycją funkcji OnDraw, korzystając z listingu 12.3. Listing 12.3. LST13_1.CPP - dostęp do danych dokumentu w celu wyświetlenia rysunku 1 void CSDICoinView::OnDraw(CDC* pDC) 2 { 3 // ** Pobranie wskaźnika do dokumentu 4 CSDICoinDoc* pDoc = GetDocument(); O 5 ASSERT_VALID(pDoc) ; 6 7 // TODO: add draw code for native data here 8 // ** Zapisanie bieżącego pędzla 9 CBrush* pOldBrush = pDC->GetCurrentBrush(); @ 10 11 // ** Utworzenie pełnego, żółtego pędzla 12 CBrush br; 13 br.CreateSolidBrush(RGB(255,255,0) ) ; @ 14 15 // Wybór żółtego pędzla w kontekście urządzenia 16 pDC->SelectObject(&br); O 17 18 // Pobranie liczby monet z dokumentu 19 // -i rysowanie dwóch elips dla każdej z monet 20 for(int nCount = 0; © nCount < pDoc->GetCoinCount(); nCount++) 21 { 22 int y = 200 - 10 * nCount; 23 pDC->Ellipse(40, y, 100, y-30); ® 24 pDC->Ellipse(40, y-10, 100, y-35); 25 } 26 27 II** Przywróć pędzel bieżący 28 pDC->SelectObject(p01dBrush); O 29 } O Wywołanie funkcji GetDocument widoku w celu pobrania wskaźnika do klasy dokumentu aplikacji. © Pobranie wskaźnika do pędzla bieżącego, ustalonego w kontekście urządzenia p DC. © Utworzenie pędzla malującego pełnym, żółtym kolorem. O Wybór nowego pędzla w kontekście urządzenia. © Pętla wykonywana do momentu narysowania właściwej liczby monet. Ich liczba pobrana została poprzez funkcję dostępu dokumentu, GetCoinCount (). © Rysowanie dwóch elips w ustalonych odstępach dla każdej monety. O Przywrócenie oryginalnego pędzla kontekstu urządzenia. Pierwsze dwie linie (4 i 5) zawarte w funkcji są automatycznie tworzone przez AppWizard. Funkcja GetDocument zwraca wskaźnik do obiektu dokumentu. Wskaźnik ten zapisany jest w pDoc. Zostaje on użyty w pętli for (w linii 20) do pobrania bieżącej liczby monet poprzez wywołanie metody GetCoinCount. W linii 9 następuje przechowanie w pOldBrush wskaźnika do bieżącego pędzla w kontekście urządzenia. Pędzel ten przywracany jest na końcu funkcji, w linii 28. Linie 12, 13 oraz 16 tworzą żółty pędzel w lokalnej zmiennej br i ustawiają go w kontekście urządzenia, w gotowości do rysowania monet. Każda z monet składa się z dwóch elips, umieszczonych jedna nad drugą w celu utworzenia efektu trójwymiarowości. Do tego stosowana jest funkcja CDC :: Ellipse, co możemy zobaczyć w liniach 23 i 24. PATRZ TAKŻE • Informacji na temat kontekstów urządzenia szukaj w rozdziale 15. * Więcej informacji o rozwiązaniach graficznych znajdziesz w rozdziale 16. Używanie standardowych zasobów szablonu Jak mówiliśmy wcześniej, w projekcie umieszczane są automatycznie pewne zasoby, wspierające aplikację SDI od strony wizualnej. Są to: menu, pasek narzędziowy, ikona, tablica akceleratorów oraz łańcuch znakowy z nazwą dokumentu. Wszystkie te standardowe zasoby są w pełni funkcjonalne. Przykładowo, wybór pozycji New z menu File spowoduje wywołanie funkcji CWinApp: : OnFileNew, co pociąga Dokumenty, widoki, ramki oraz ich stosowanie 295 za sobą utworzenie nowego dokumentu. Podobne efekty daje użycie ikony New z paska narzędziowego. Nie oznacza to jednak, iż nie możemy zmienić domyślnego sposobu działania tych opcji. Możemy zmienić treść funkcji zarządzających i powodować wykonywanie dowolnych operacji. Wolno nam również usunąć menu lub pasek narzędziowy, jeśli nie są przydatne w naszej aplikacji. Jednak stosowniejszym sposobem postępowania jest pozostawienie tych elementów w ustalonym porządku i dodanie do nich własnych, specyficznych dla danej aplikacji. Jest to bardzo proste zadanie, gdyż każdy zasób może być edytowany przy użyciu edytora zasobów i łączony za pomocą CIassWizard z funkcjami przetwarzającymi polecenia. W przykładzie SDICoin musimy umieścić dwie nowe pozycje menu: jedna będzie dodawać monetę do stosu, a druga zabierać ją stamtąd. Po wybraniu jednej z nich, wywołana zostanie funkcja w klasie dokumentu, która powiększy lub pomniejszy liczbę monet na stosie, a następnie spowoduje odświeżenie widoku. W celu umieszczenia dodatkowych pozycji menu wykonamy poniższy algorytm. Dodawanie akceleratorów dla opcji menu Podczas edycji właściwości opcji menu można określić skrót klawiaturowy, dopisując go na końcu treści nagłówka. Przykładowo, nagłówek &Toolbar\tCtrl+T oznacza, że kombinacja klawiszy CtrI+T będzie wywoływała widok paska narzędziowego. Można dodać również wpis ID_VIEW_TOOLBAR w tablicy akceleratorów, definiując taką samą kombinację klawiszy. Dodawane nowych opcji menu 1. Rozwiń katalog Menu w panelu ResourceView i kliknij dwukrotnie IDR_MAINFRAME. Zasób menu wyświetlony zostanie w edytorze zasobów. 2. Kliknij menu Edit edytowanego zasobu. Ukaże się menu rozwijane. 3. Kliknij dwukrotnie pustą pozycję u dołu listy opcji. Wyświetlone zostanie okno dialogowe The Menu Item Properties, jak na rysunku 12.7. 4. Wprowadź identyfikator w polu ID. W obecnym przykładzie niech to będzie ID_EDIT_ ADD_COIN. 5. Wpisz w polu Caption nazwę nowej pozycji menu: Add a Coin (Dodaj monetę). 6. W polu Prompt wpisz treść informacyjną dla użytkownika: Increase the number of coins (Zwiększ liczbę monet). 7. Kliknij dwukrotnie kolejną pustą pozycję menu. 8. Wpisz identyfikator: ID_EDIT_REMOVE_COIN. 296 Poznaj Visual C++ 6 '^i Fte ^ift V»w intwt gicwct fiutó Jod ^a c? a9 x. sss i; a- • s ^'"do^ y^p ^lai-i ! ,'t ;,1 • . " 33 ^%' .qi»ja"FH<- N^ -d: 'n; - •ii^... ....^':. ••—• -^l*"--- J: ^•\';|iS'S1^ :~—„-—,—„—;„;^..„...„„—••-- ^J.5i • •.::^•;;.•. BtaSDICoinnmiuc..- '':• * ^JAcMtealCT l »j.L3 Diaku i mOkm i la -a MOTU | S 11 SUllM* ; a; C) Toobaf : ; B-C.jVetiiwt Undo Ctrk2 C^ Ctrtó Sopy Ctfl.C AchlaCoR MemoyflaL on' -» t Geiie^ | Efiend&jSl^EŁ | 11.): jlLi^[iirALi^l:fJ H ^•'J i;^iW JAddaCw ,.r;E.e[>.yala • rpgMip r l[3aclr^ Sieak-. JN^ne~^:„. i^.i^w^^ Ofi*^:: • r ^e '^ll^lll: l I^R^tfcI^Incieate iht omniw olcans W l 11 11111 11"1111 1'111" 11" ' '11 "iiiiii "llll'lf """" "iili iiiiiiiiii 1""1111 iiiii ,|[ ®Ma»M^«httfc»^A:B«rt(i.teteKiiA*Łjllat: il.yiSBSM^iSffii^^-i^sPs^a^i-^ii^asi Rysunek 12.7. Okno właściwości pozycji menu 9. Wpisz w polu Caption treść nagłówka nowej pozycji: Remove a Coin (Usuń monetę). 10. W polu Prompt wpisz: Decrease the number of coins (Zmniejsz liczbę monet). 11. Wywołaj kreator CIassWizard, naciskając Ctri+W lub wybierając stosowną pozycję menu View. 12. Wyświetl kartę Message Maps. 13. Z listy Ciass Name wybierz CSDICoinDoc. 14. Z listy Object IDs wybierz pozycję ID_EDIT_ADD_COIN. 15. Z listy Messages wybierz COMMAND, a następnie kliknij przycisk Add Function. W oknie dialogowym Add Member Function kliknij OK. 16. Wybierz pozycję ID_EDIT_REMOVE_COIN z listy Object IDs. 17. Z listy Messages wybierz COMMAND, a następnie kliknij przycisk Add Function. W oknie dialogowym Add Member Function kliknij OK. 18. Kliknij przycisk Edit Code. Odświeżanie widoku Po wprowadzeniu każdej modyfikacji danych dokumentu, prawie zawsze zachodzi konieczność odzwierciedlenia tych zmian w widoku. Przeprowadza się to poprzez wywołanie funkcji, które powodują przerysowanie widoku. W poprzednim punkcie dodaliśmy dwie nowe pozycje menu i utworzyliśmy dla nich w klasie dokumentu funkcje obsługi poleceń. Teraz musimy jeszcze przeprowadzić edycję dwóch funkcji, według listingu 12.4. Dokumenty, widoki, ramki oraz ich stosowanie 297 Listing 12.4. LST13_2.CPP — odświeżanie widoku po zmianach w dokumencie 1 void CSDICoinDoc::OnEditAddCoin()0 2 { 3 // TODO: Tutaj umieść kod funkcji obsługi polecenia 4 5 // ** Inkrementacja liczby monet 6 m nCoins++; @ 7 8 // ** Odświeżenie widoku stosu 9 UpdateAllYiews(NULL); © 10 } 11 12 void CSDICoinDoc::OnEditRemoveCoin() 13 { 14 // TODO: Tutaj umieść kod funkcji obsługi polecenia 15 16 // ** Dekrementacja liczby monet 17 if(m_nCoins > 0) 18 m nCoins—; 19 20 // ** Odświeżenie widoku stosu 21 UpdateAllViews(NULL); 22 } O Funkcja obsługi polecenia wywołana zostaje po wybraniu z menu Edit pozycji Add a Coin. © Inkrementacja wartości zmiennej składowej dokumentu, przechowującej liczbę monet. © Wywołanie funkcji OnUpdate () powodującej odświeżenie wszystkich widoków danego dokumentu. Jak widzimy, funkcje powodują zwiększenie lub zmniejszenie wartości zmiennej m_nCoins, należącej do dokumentu. Następnie wywołana zostaje funkcja UpdateAllViews z parametrem NULL. Parametr ten nakazuje odświeżenie wszystkich widoków związanych z dokumentem. Każdy z widoków jest odświeżany po wywołaniu funkcji OnUpdate. Funkcja ta unieważnia obszar roboczy okna widoku i powoduje wywołanie funkcji OnDraw. Skompilujemy i uruchomimy teraz nasz program. Spróbujemy użyć poleceń Add a Coin oraz Remove a Coin z menu Edit. Ekran programu powinien wyglądać jak na rysunku 12.8. 298 Poznaj Visual C++ 6 ^^^^^^^^^^^Hrr Untilleil - SUlLom ; miStt fte El yiew Het. : D .:'•- Re(nove a Coio ? ' lir Rysunek 12.8. Gotowa aplikacja SDICoin

Wyszukiwarka

Podobne podstrony:
3 19 Dokumenty, widoki i SDI (2)
Dlaczego zwierzęta 13 Rozdział 12 – Dokumentacja
W6 1 12 DokumentacjaMedyczna
Wykład 12 XML NOWOCZESNY STANDARD ZAPISU I WYMIANY DOKUMENTU
Angielski nauka słówek Nowy Dokument tekstowy (12)
MJ 12 SOM1 1 Tajny dokument o ufo Operacja Majestic
Nowy Dokument tekstowy (12)
Nowy Dokument tekstowy (12)
248 12
Biuletyn 01 12 2014

więcej podobnych podstron