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