MFC 03 Drukowanie



Rozdział 3 Drukowanie


Programy wygenerowane przez kreatora App Wizard potrafią automatycznie drukować. Oczywiście, każdy program, który potrafi wyświetlać informacje na ekranie, potrafi także wydrukować te informacje na drukarce. Jednakże czasami otrzymane rezultaty nie są takie, jakich można by oczekiwać. Różnice w skalowaniu, dzielenie wydruku na strony oraz drukowanie dodatkowych informacji (jak na przykład czas wydruku lub nazwa drukowanego pliku), wszystkie te czynniki dodatkowo utrudniają drukowanie. Podgląd wydruku jest kolejnym miejscem, gdzie bardzo często dokonywane są modyfikacje, jed­nakże i tym razem MFC ukrywa szczegóły zastosowanych rozwiązań. W tym rozdziale dowiesz się, jak można dodać do programu możliwość edycji danych podczas wyświetla­nia podglądu wydruku.

Jeśli przeczytałeś ostatni rozdział, to nie będziesz zaskoczony tym, że jestem wielkim fanem serialu „Star Trek". Sądząc z wypowiedzi niektórych osób w telewizji, nie zaliczam się jednak do największych fanów tego serialu. Mój samochód nie przypomina statku kosmicznego i nie ubieram się w uniformy Floty Gwiezdnej. Z drugiej strony, posiadam kopie „Star Trek Technical Manuals" oraz publikacje autorstwa Leonarda Nimoya. Miałem nawet zaszczyt porozmawiać z Genem Roddenberym zaraz po emisji jednego z odcinków serialu. Tak więc może i nie jestem fanatykiem, ale na pewno godnym szacunku fanem.

Zawsze starałem się zainteresować dzieci serialem „Star Trek". Jestem przekonany, że można znaleźć znacznie gorszych bohaterów do Kirka i jego towarzyszy. Sądzę, że bo­haterowie niektórych nowych filmów także mogą zasługiwać na odrobinę szacunku. Ale i tak moje dzieci z założenia nie interesują się rzeczami, które lubię, dlatego też rzadko kiedy mam okazję oglądać „Star Trek" w towarzystwie.

Kiedyś, w rocznicę odkrycia Ameryki, postanowiliśmy pojechać na krótką wycieczkę -coś w stylu mini wakacji na koniec lata. Po spędzeniu całego dnia w muzeum Sea World wpadło nam do głowy przejechać na chwilę granicę meksykańską- tylko i wy­łącznie po to, żebyśmy mogli powiedzieć, że byliśmy w Meksyku. Powiedziałem mojemu najmłodszemu synowi, że jest to dla niego okazja, aby z dumnie podniesioną głową wkroczyć na obszary, w których nikt jeszcze nie przebywał (a przynajmniej, w których ja jeszcze nie przebywałem). Oczywiście, usłyszawszy tekst zaczerpnięty ze „Star Treka", syn przewrócił jedynie oczami i zrobił stosownie znudzoną minę.

Cóż, potwierdziło to jedynie fakt, iż jestem całkowicie niepodobny do wielkiego kapi­tana Kirka. Gdy dojechaliśmy do granicy uświadomiliśmy sobie, że nie mamy żadnego dokumentu identyfikacyjnego dla naszego siedmioletniego syna, Patryka. Pomyśl tylko, czy wozisz przy sobie dokumenty siedmioletniego dziecka? Jeśli wycieczka byłaby za­planowana, to niewątpliwie nie zapomnielibyśmy wziąć odpowiednich dokumentów. Szkoda tylko, że nie pomyśleliśmy o tym przed dojechaniem do granicy meksykańskiej. Bardzo poważnie zastanawialiśmy, się, czy mimo wszystko nie przejechać granicy, ale gdybyśmy to zrobili, Patryk mógłby nie wrócić do kraju!

Po rozmowie z amerykańskim Patrolem Granicznym (mili goście) zdecydowaliśmy się jednak zaryzykować i przekroczyć granicę. Muszę przyznać, że czułem dziwny dresz­czyk emocji, przekraczając granicę ze świadomością, że podejmujemy takie ryzyko. Oczywiście, wiedzieliśmy, że w każdej chwili możemy dowieść obywatelstwa Patryka, niemniej jednak narażaliśmy się na konieczność pozostania w Meksyku przez kilka dni lub nawet tygodni, gdyby coś poszło nie tak.

Wszystko oczywiście skończyło się dobrze -jak w filmie „Star Trek Następne Pokolenia", gdzie wszystkie problemy same się rozwiązują wraz ze zbliżaniem się do końca filmu. Patryk dostał na macado (targu) blaszany bębenek, a kiedy wracaliśmy, na granicy zapytano nas, czy jesteśmy obywatelami amerykańskimi, odpowiedzieliśmy, że jesteśmy i mogliśmy jechać dalej - nawet nie musiałem pokazywać swojego prawa jazdy. Żadnych problemów, żadnych silnych wrażeń.

Sądzę, że czasami programowanie jest bardzo podobne do opisanych powyżej zdarzeń. Jest naturalne, że jesteśmy pełni obaw i ostrożnie wkraczamy na nowe, nieznane terytoria (jak na przykład nowy system operacyjny, nowy język czy technologia). Odkrywanie czegoś nowego również przysparza miłego dreszczyku emocji.

Gdy rozmawiam z programistami używającymi MFC, odkrywam, że wielu z nich obawia się drukowania. Oczywiście każdy może coś wydrukować. Ale jak wydrukować popraw­nie wyskalowany, wielostronicowy dokument? Co z nagłówkami, stopkami, margine­sami albo numerami stron?

Na dobry początek pokażę prosty program do gry w kółko i krzyżyk. Sam program jest całkowicie nieinteresujący (patrz rysunek 3.1.), będzie jednak potrafił poprawnie dru­kować. W dalszej części rozdziału pokażę Ci, w jaki sposób można przejąć kontrolę nad sposobem sporządzania podglądu wydruku i dostosować go do swoich potrzeb. Zanim jednak zaczniemy, to porozmawiamy trochę o narzędziach wspomagających drukowanie, dostępnych w MFC.

Drukowanie w MFC - wielkie kłamstwo?

MFC twierdzi, że możesz rysować i drukować za pomocą tego samego kodu. Jednak czy rozwiązanie takie jest w rzeczywistości praktyczne? Czasami tak. W większości wypadków będziesz jednak musiał stworzyć odrębny kod służący tylko do drukowania. Jedyną pocieszającą wiadomością jest to, że kod, który będziesz musiał napisać, jest znacznie prostszy od tego, który wykorzystywany jest przez sam system Windows.


Kiedy użytkownik wybiera opcję drukowania z menu Plik, w aktywnym widoku wywoływana jest metoda OnPreparePrinting. Metoda ta posiada jeden argument -wskaźnik do struktury CPrintlnfo (patrz Tabela 3.1.). Określenie wartości składowych tej struktury pozwoli Ci na dokładne opisanie sposobu drukowania (zwanego także zada­niem drukowania). Jeśli już na tym etapie znasz ilość stron, które będziesz chciał wydruko­wać, to możesz je podać. Kiedy przekażesz strukturę w wywołaniu metody DoPrepare-Printing, to ustawione w niej dane zostaną odzwierciedlone w standardowym dialogu służącym do wybierania opcji drukowania. Standardowy kod generowany przez kreato­ra App Wizard zakłada, że nie wiesz niczego na temat zadania drukowania i w związku z tym przekazuje strukturę CPrintlnfo bez wprowadzenia jakichkolwiek modyfikacji.

Aby uruchomić zadanie drukowania, MFC wywołuje metodę OnBeginPrinting. Metoda ta wymaga podania dwóch argumentów: wskaźnika na obiekt klasy CDC oraz wskaźnika na strukturę CPrintlnfo. Obiektu CDC możesz użyć do określenia cech drukarki (wystar­czy w tym celu wywołać metodę GetDeviceCaps). Jest to Twoja ostatnia szansa określenia ilości drukowanych stron; możesz to zrobić za pomocą metody CPrintlnfo::SetMaxPage. Jeśli ilość stron wydruku zależna jest od cech charakterystycznych drukarki, to właśnie teraz powinieneś ją określić. Dzięki temu MFC będzie mogło wywołać metody służące do drukowania tylko raz dla każdej strony wydruku. Jeśli jednak wciąż nie jesteś w sta­nie określić ilości stron, które chcesz wydrukować, to będziesz musiał ręcznie określać wartość składowej m_bContinuePrinting struktury CPrintlnfo. Modyfikacji tej bę­dziesz musiał dokonywać w metodzie OnPrepareDC (jak się już wkrótce dowiesz, metoda ta wywoływana jest podczas drukowania).

Metoda OnBeginPrinting najlepiej nadaje się do przydzielenia zasobów, które będą Ci potrzebne podczas wykonywania zadania drukowania. Dla przykładu, specyficzne czcionki używane podczas drukowania przygotowywane są zazwyczaj właśnie w tej metodzie.


Tabela 3.1. Struktura CPrintlnfo.



Składowa

Typ

m_bDocObj ec t

m_dwFlags

m_nOffsetPage

m_pPD

m_bDirect

m_bPreview

m_bContinuePrinting zmienna

m_nCurPage zmienna
m_nNumPreviewPages zmienna

m_lpUserData m_rectDraw

m_strPageDesc

SetMinPage SetMaxPage GetMinPage GetMaxPage GetOffsetPage

GetFromPage GetToPage

Opis

zmienna Określa, czy drukowany jest dokument klasy CDocument.

zmienna Określa operacje wykonywane przez obiekt DocObject.

zmienna Określa przesunięcie pierwszej strony konkretnego obiektu DocObject w połączonym zadaniu drukowania.

zmienna Wskaźnik na okno dialogowe Drukuj skojarzone z konkretnym zadaniem drukowania.

zmienna Określa ,czy dokument ma zostać wydrukowany bez wyświetlania okna dialogowego Drukuj.

zmienna Określa, czy program działa aktualnie w trybie podglądu wydruku.

Określ, czy aktualna strona powinna zostać wydrukowana (patrz tekst).

Numer aktualnej strony.

Ilość stron wyświetlanych w podglądzie wydruku (l lub 2).

zmienna Wskaźnik do struktury danych dostarczanej przez użytkownika.

zmienna Prostokąt określający wielkość obszaru strony

dostępnego do drukowania (składowa ta może być używana dopiero po rozpoczęciu drukowania).

zmienna Zawiera łańcuch znaków określający postać nume­racji stron.

funkcja Określa numer pierwszej strony wydruku,

funkcja Określa numer ostatniej strony wydruku,

funkcja Zwraca numer pierwszej strony wydruku,

funkcja Zwraca numer ostatniej strony wydruku.

funkcja Zwraca ilość stron poprzedzających pierwszą stron? aktualnie drukowanego obiektu DocObject, wchodzącego w skład złożonego zadania drukowania.

funkcja Zwraca numer pierwszej drukowanej strony, funkcja Zwraca numer ostatniej drukowanej strony.



Podczas drukowania MFC wywołuje następujące trzy metody:

  1. OnPrepareDC;

2. OnPrint;

3. OnDraw.



Metody te wywoływane są dla każdej drukowanej strony. Zauważ, że metody OnPrepare-DC oraz OnDraw wywoływane są także podczas rysowania na ekranie. Jeśli będziesz chciał określić, czy metoda została wywołana jako część procesu drukowania, to będziesz mógł to zrobić za pomocą metody CDC::IsPrinting.

Jest kilka powodów, dla których mógłbyś chcieć przesłonić standardową definicję metody OnPrepareDC. Po pierwsze możesz w ten sposób zmodyfikować kontekst urządzenia. Możesz to zrobić na przykład po to, aby zmienić początek układu współrzędnych widoku, dzięki czemu będziesz w stanie wydrukować odpowiednią stronę. Możesz zmienić tak­że tryb mapowania w kontekście urządzenia, dzięki czemu wydruk zostanie przeskalo-wany inaczej niż zawartość ekranu.

Możesz także podejmować decyzje, czy chcesz kontynuować drukowanie, czy też je prze­rwać. Jest to szczególnie przydatne w wypadkach, gdy nie jesteś w stanie dokładnie określić ilości stron, które chcesz wydrukować. Aby móc postąpić w ten sposób, będziesz musiał upewnić się, że argument plnfo ma wartość różną od NULL. Pamiętaj, że metoda OnPre­pareDC wywoływana jest zarówno w przypadku drukowania, jak i rysowania na ekranie. Jeśli argument plnfo nie ma wartości NULL, to będziesz mógł określić, czy chcesz dalej drukować, czy nie (badając wartość składowej m_nCurPage). Jeśli będziesz chciał konty­nuować drukowanie, to po wywołaniu metody OnPrepareDC klasy bazowej będziesz mu­siał przypisać składowej m_bContinuePrinting wartość TRUE. Metoda OnPrepareDC klasy bazowej zawsze przypisuje tej składowej wartość FALSE, a więc pamiętaj, aby od­powiednio ją zmodyfikować. Pamiętaj także, iż zawsze będziesz musiał wywołać tę metodę klasy bazowej, gdyż wiele rodzajów widoków używa jej do specjalnych celów.


Drukowanie

Proste programy zazwyczaj nie przejmują się takimi szczegółami. Dzieje się tak dlatego, iż metoda OnDraw (używana do drukowania widoku i wyświetlania go na ekranie), potrafi automatycznie zadbać o wszystkie sprawy związane z drukowaniem. Jednakże musisz poznać wszystkie tajniki procesu drukowania, aby móc go zmodyfikować - odpowiednio przeskalować widok, dodać nagłówki i stopki wydruku lub w jakikol­wiek inny sposób zmodyfikować proces drukowania.


Jeśli chcesz, aby kod metody OnDraw obsługiwał zarówno wyświetlanie danych na ekranie, jak i ich drukowanie, to nie będziesz musiał przesłaniać metody OnPrint. Do­myślna implementacja tej metody wywołuje metodę OnDraw za Ciebie. Jednakże jeśli będziesz chciał dodać specjalny kod, który modyfikuje postać wydruku i powoduje, że różni się ona od wyglądu informacji wyświetlanych na ekranie, to będziesz mógł dokonać odpowiednich modyfikacji właśnie w tej metodzie. Dla przykładu załóżmy, że tworzysz program wyświetlający na ekranie schemat sieci, jednakże podczas drukowania zamiast schematu tworzona jest tabela zawierająca zestawienie połączeń pomiędzy poszczegól­nymi węzłami sieci. Schemat sieci będzie w takim przypadku tworzony w metodzie OnDraw, tabela z zestawieniem połączeń - w metodzie OnPrint. Innym zastosowa­niem metody OnPrint jest wykorzystanie jej do wydrukowania elementów, które mają się pojawić tylko i wyłącznie na wydruku. Dla przykładu bardzo częstym rozwiązaniem jest tworzenie w tej metodzie nagłówków i stopek wydruku. Po ich stworzeniu wystar­czy zmienić początek układu współrzędnych widoku, aby zapobiec przesłonięciu na­główka przez kod używany przy wyświetlaniu informacji na ekranie. Możesz także zmody­fikować wielkość obszaru przycinania, aby zapobiec przesłonięciu stopki wydruku. Kiedy skończysz wprowadzać modyfikacje, będziesz mógł wywołać metodę OnDraw, aby wydrukowała ona resztę zawartości strony.

Oczywiście istnieje także alternatywne rozwiązanie, które polega na umieszczeniu kodu odpowiadającego za tworzenie nagłówków i stopek w kodzie metody OnDraw. Kod ten mógłby być wykonywany tylko wtedy, gdy metoda IsPrinting zwróci wartość TRUE. Wybór jednego z tych dwóch rozwiązań jest jedynie kwestią osobistych preferencji.

Metoda OnDraw odpowiada za wyświetlanie informacji na ekranie. Jeśli w odpowiedni sposób obsłużysz kontekst urządzenia w wywołaniach wcześniejszych metod, to w metodzie OnDraw nigdy nie będziesz musiał się interesować tym, czy prezentowane przez nią informacje kierowane są na ekran, czy na drukarkę. Zawsze jednak możesz skorzystać z metody IsPrinting, aby rozróżnić obie te sytuacje.

MFC cyklicznie wywołuje przedstawione powyżej trzy metody dla każdej strony wy­druku. Ilość stron wydruku określana jest na podstawie informacji zapisanych w struk­turze CPrintlnfo (lub też na podstawie Twoich operacji na składowej m_bContinuePrinting). Przed zakończeniem drukowania wywoływana jest metoda OnEndPrinting. W metodzie tej będziesz mógł zwolnić wszystkie zasoby przydzielone na potrzeby drukowania (w metodzie OnBeginPrinting).

Tak wygląda zarys procesu drukowania stosowanego przez MFC. Cały proces przed­stawiony został w Tabeli 3.2. Najciekawsze jest jednak to, że MFC używa dokładnie tego samego procesu do tworzenia podglądu wydruku. Jeśli drukowanie będzie działało poprawnie, to automatycznie uzyskasz poprawnie działający podgląd wydruku.

Tabela 3.2. Proces drukowania.

MFC wywołuje Powód Przesłoń gdy

CView::OnFilePrint wybór opcji z menu wszystko chcesz zrobić samemu

CView::OnPreparePrinting rozpoczęcie procesu drukowania chcesz wpisać odpowiednie informacje w okno dialogowe Drukuj

CView::DoPreparePrinting wyświetlenie okna dialogowego Drukuj chcesz wyświetlić własne okno dialogowe Drukuj

CView::OnBeginPrinting przydzielenie zasobów chcesz jeden raz przydzielić zasoby GDI potrzebne do sporządzenia wydruku

CView::OnPrepareDC określenie kontekstu urządzenia chcesz przydzielić zasoby lub zmodyfikować kontekst urządzenia

CView::OnPrint wykonanie drukowania chcesz wydrukować widok różniący się od widokuwyświetlanego na ekranie lub jeśli chcesz wydrukować dodatkowe informacje

CView::OnDraw odświeżenie ekranu chcesz coś wydrukować (prawie zawsze)

Cview::OnEndPrinting zakończenie drukowania musisz zwolnić przydzielone wcześniej zasoby GDI

Dylemat

W idealnej sytuacji powinieneś być w stanie użyć do drukowania dokładnie tego samego kodu, którego używasz do wyświetlania informacji na ekranie. W praktyce jednak ist­nieje kilka czynników, które poważnie utrudniają takie postępowanie. Najważniejszym z tych powodów jest skalowanie. Rozdzielczość ekranu oraz rozdzielczość drukarki są zazwyczaj zupełnie inne. Na ekranie linia o długości 100 pikseli będzie całkiem długa. Jednakże ta sama linia będzie bardzo krótka na drukarce o rozdzielczości 600 DPI (punktów na cal). Coś, co na ekranie ma rozsądne rozmiary, po wydrukowaniu będzie znacznie mniejsze, no chyba, że podejmiesz odpowiednie środki zaradcze.

Istnieją dwa sposoby rozwiązania tego problemu. Jednym z nich jest zastosowanie odpo­wiedniego trybu mapowania. Aby zastosować to rozwiązanie, wywołaj metodę CDC::SetMapMode, która pozwala Ci na wybranie trybu mapowania wykorzystującego jednostki logiczne. Dla przykładu, jeśli wybierzesz tryb MM_LOMETRIC, to podstawową jednostką będzie 0,1 mm (patrz Tabela 3.3.). Oczywiście wielkość ekranu nie będzie w ta­kim trybie bardzo precyzyjna, gdyż sterownik nie zna jego dokładnej rozdzielczości. Większość sterowników celowo zwiększa wymiary po to, aby małe elementy (na przykład, wielkości 10 pikseli) były łatwiej zauważalne. Jednakże drukarki dysponują precyzyjnie określoną ilością punków na cal, w związku z czym wymiary będą przeliczane bardzo dokładnie.

Tabela 3.3. Tryby mapowania.


Tryb

MM_TEXT

MM_HIENGLISH

MM_LOENGLISH

MM_HIMETRIC

MM_LOMETRIC

MM_TWIPS

MM_ISOTROPIC

MM_ANISOTROPIC

Opis

l jednostka logiczna == l piksel.

l jednostka logiczna == 0,001 cala.

l jednostka logiczna == 0,01 cala.

l jednostka logiczna == 0,01 milimetra.

l jednostka logiczna == 0,1 milimetra.

l jednostka logiczna == 1/1440 cala (lub 1/20 punktu drukarskiego).

Używa funkcji SetWindowExt oraz SetViewportExt do wyskalowania osi X i Y, zachowując przy tym stosunek wielkości krawędzi okna; okrąg w jednostkach logicznych będzie wyświetlony jako okrąg na ekranie.

Używa funkcji SetWindowExt oraz SetViewportExt do nadania podanych wielkości osiom X i Y; okrąg w jednostkach logicznych zostanie wyświetlony na ekranie jako elipsa, chyba że ręcznie przeliczysz współczynnik skalowania, tak aby zachować proporcje okręgu.



Stosowanie trybów mapowania

Wszystkie tryby mapowania, za wyjątkiem MM_TEXT, znacznie ułatwiają drukowanie, dlatego też zawsze warto ich używać. Należy jednak pamiętać, iż we wszystkich tych trybach (prócz MM_TEXT) oś Y jest odwrócona. Innymi słowy, należy używać liczb ujemnych jako współrzędnych tej osi (chyba że samemu przesunąłeś początek układu współrzędnych).

Oczywiście zdarzają się i takie wypadki, gdy zastosowanie pikseli jest wygodniejsze od użycia jednostek logicznych. Dobrym tego przykła­dem jest przedstawiony w tym rozdziale program do gry w kółko i krzy­żyk. Nie jest łatwo myśleć o planszy w milimetrach lub calach dlatego, że wymiary planszy ulegają zmianie wraz ze zmianami wielkości okna.

Domyślnym trybem mapowania jest MM_TEXT, w którym jednej jednostce logicznej odpowiada jeden piksel. W tym trybie kierunkiem poziomym jest oś X, a jednostki rosną od strony prawej do lewej. Współrzędne osi pionowej (Y) rosną od góry ku dołowi. Inne tryby mapowania (takie jak MM_LOENGLISH) odwracają kierunek osi Y. W ten sposób oś Y rozpoczyna się u góry kontekstu urządzenia, a współrzędne rosną od dołu ku gó­rze. Oznacza to, że wszystkie wartości, które będziesz stosował, będą wartościami ujemnymi! Oś X pozostaje w tym trybie niezmieniona. Te domyślne ustawienia możesz zmodyfikować za pomocą metody CDC::SetViewportOrg.

Choć może użycie tych samych układów współrzędnych dla wyświetlania danych na ekranie i drukowania ich może się wydawać bardzo atrakcyjnym rozwiązaniem, to jest jednak wiele przypadków, w których przysparza ono tyle samo kłopotów ile ich rozwiązuje, Czasami nie chcesz wyświetlać danych na ekranie, licząc wszystko w centymetrach i calach. Współrzędne dotyczące manipulacji myszką są zawsze podawane w pikselach. Może się także zdarzyć, że będziesz chciał, aby prezentowane dane zajmowały zawsze cały ekran oraz stronę wydruku, niezależnie od ich wielkości. W takich przypadkach logicz­ne tryby mapowania nie są zbytnio przydatne.

Jeśli mimo wszystko będziesz chciał pozostać przy stosowaniu pikseli, to najprawdopo­dobniej przyda Ci się wyskalowanie kontekstu drukarki w taki sposób, aby jednostki na ekranie i drukarce były podobne. Do wykonania tego zadania najlepiej nadają się meto­dy OnPrepareDC oraz OnPrint. Kod odpowiedzialny za skalowanie możesz także umieścić w metodzie OnDraw. W takim wypadku będziesz musiał sprawdzać, czy trwa proces drukowania (za pomocą metody IsPrinting), a jeśli tak, wykonać odpowiednie przeskalowanie. Wszystko zależy od tego, jak wiele metoda OnDraw ma wiedzieć o tym, czy drukuje, czy wyświetla dane na ekranie.

Pełny przykład drukowania

Rozpatrzmy przykład programu przedstawionego na Listingu 3.1. Jest to program do gry w kółko i krzyżyk pozwalający na wydrukowanie planszy z rozgrywką. Jedynymi ciekawszymi elementami jakie zastosowałem w tym programie są: winietka wyświetlana podczas uruchamiania programu oraz umieszczony na pasku stanu komponent przed­stawiający aktualny czas (umieszczony za pomocą Galerii Komponentów wywoływanej po wybraniu opcji Insert^Componeni).

Listing 3.1. Drukowanie widoku.

// mfctttView.cpp


ttinclude "mfctttDoc.h" ttinclude "mfctttview.h"

ttifdef _DEBUG

tdefine new DEBUG_NKW

ttundef THIS_FILE

static char THIS_FILE[] =

ttendif


/ / /1111111111111 /1111111111 /1111 II CMfctttYiew

IMPLEMENT_DYNCREATE(CMfctttYiew, CYiew)

BEGIN_MESSAGE_MAP(CMfctttYiew, CYiew)

//{{AFX_MSG_MAP(CMfCtttYiew)

ON_WM_LBUTTONDOWN()

ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)

ON_UPDATE_COMMAND_UI (ID_EDIT_UNDO, OnUpdateEdi tUndo)

//}}AFX_MSG_MAP

// Standard printing commands

ON_COMMAND(ID_FILE_PRINT, CYiew::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_DIRECT, CYiew::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_PREVIEW, CYiew::OnFilePrintPreview) END_MESSAGE_MAP ()

11111111111111111111111111111111

II CMfctttYiew construction/destruction

CMfctttYiew::CMfctttYiew()

// TODO: add construction code here

CMfctttView: :-CMfctttView()

{

}

BOOL CMfctttView: :PreCreateWindow(CREATESTRUCT& es) {

return CView: :PreCreateWindow(cs) ;

/ / 1 1 1 1 1 1 1 1 1 1 / 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 II CMfctttYiew drawing

// Odstęp od krawędzi pola planszy ttdefine OFFSET 15

void CMfctttYiew: :OnDraw(CDC* pDC) {

CMfctttDoc* pDoc = GetDocument ( ) ASSERT_VALID(pDoc) ; // Stwórz panele

// Pióro linii planszy

CPen p(PS_SOLID, 10, RGB (O, O, 0) ) ; // X pióro

CPen xp(PS_SOLID,5,RGB(OxFF,O,0)); //O pióro

CPen op(PS_SOLID, 5,RGB(0, O, OxFF) ) ,-

CPen *old;

// rysuj plansze

CRect r;

GetClientRect(&r);

old=pDC->SelectObject(&p);

// Poniżej podałem przykładowy kod testujący // umożliwiający sprawdzenie czcionek //CFont f,*oldf;

//f.CreateFont(-30,O,O,O,O,O,O,O,O,O,O,O,O,"Arial" ); //oldf=pDC->SelectObject(&f); //pDC->TextOut(0,0,"Hello"); //pDC->SelectObject(oldf);

// Rysuj siatkę

pDC->MoveTo(r.right/3,0);

pDC->LineTo(r.right/3,r.bottom);

pDC->MoveTo(2*r.right/3,0);

pDC->LineTo(2*r.right/3,r.bottom);

pDC->MoveTo(0,r.bottom/3);

pDC->LineTo(r.right,r.bottom/3);

pDC->MoveTo(O,2*r.bottom/3);

pDC->LineTo(r.right,2*r.bottom/3); // Rysuj klocki

for (int x=0;x<3;x++)

for (int y=0;y<3;y++)

CPoint pt(x,y);

CPoint ptl(x+l,y+l);

GridToMouse(pt);

GridToMouse(ptl);

switch (pDoc->GetBoardState(x,y))

case X:

pDC->SelectObject(&xp);

pDC->MoveTo(pt.x+OFFSET,pt.y+OFFSET);

pDC->LineTo(ptl.x-OFFSET,ptl.y-OFFSET);

pDC->MoveTo(ptl.x-OFFSET,pt.y+OFFSET);

pDC->LineTo(pt.x+OFFSET,ptl.y-OFFSET);

break; case O:

pDC->SelectObject(&op);

pDC->SelectStockObject(HOLLOW_BRUSH) ;

pDC->Ellipse(pt.x+OFFSET,pt.y+OFFSET, ptl.x-OFFSET,ptl.y-OFFSET);

break;

pDC->SelectObject(old); }

// CMfctttView printing

BOOL CMfctttYiew::OnPreparePrinting(CPrintlnfo* plnfo) {

p!nfo->SetMaxPage(2);

return DoPreparePrinting(plnfo);

// Ta wersja metody OnPrint pozwala Ci na użycie trybu MM_TEXT w Twoim // widoku, dzięki czemu wydruk może być poprawnie wyskalowany void CMfctttView::OnPrint(CDC* pDC, CPrintlnfo* plnfo) {

CDC *dc=GetDC();

CString s;

int x=dc->GetDeviceCaps(LOGPIXELSX) ,-

int y=dc->GetDeviceCaps(LOGPIXELSY);

int xl=pDC----GetDeviceCaps(LOGPIXELSX) ;

int yl=pDC->GetDeviceCaps(LOGPIXELSY);

// nagłówek wyświetlaj jedynie podczas drukowania

s.Format("Tic Tac Toe gamę: %s",

GetDocument()->GetTitle() ) ; pDC->TextOut(O,O,s); pDC->MoveTo(0,75) ;

pDC->LineTo(pinfo->m_rectDraw.Width(),75); if (pinfo->m_nCurPage==2) {

CMfctttDoc *doc=GetDocument(); s.Format("Games I Won=%d, Games I Lost=%d" " Draw Games=%d",doc->wins,doc->loss, doc->draw);

pDC->TextOut (O, 100, s) ,-return; }

// Zmień tryb mapowania tak, aby piksele były poprawnie wyskalowane pDC->SetMapMode(MM_ISOTROPIC) ; pDC->SetWindowExt(x,y); pDC->SetViewportExt(xl,yl); // Górny margines wielkości 100 pikseli pDC->SetViewportOrg(0,100); CView::OnPrint(pDC, plnfo);

void CMfctttView: :OnBeginPrinting (CDC* /*pDC*/,

CPrintlnfo* /*plnfo*/) {

// TODO: add extra initialization before printing

void CMfctttView: :OnEndPrinting(CDC* /*pDC*/,

CPrintlnfo* /*plnfo*/) {

// TODO: add cleanup after printing

111 /1! 1111111111111111111111111 / II CMfctttView diagnostics

łtifdef _DEBUG

void CMfctttView::AssertValid() const

{

CYiew::AssertValid();

void CMfctttView::Dump(CDumpContext& dc) const {

CView::Dump(dc); } CMfctttDoc* CMfctttView::GetDocumentO

ASSERT(m_pDocument->IsKindOf( RUNTIME_CLASS(CMfctttDoc)));

return (CMfctttDoc*)m_pDocuinent; } #endi£ //_DEBUG

1111111111111111 /111111111111111 II CMfctttView message handlers

// Skonwertuj współrzędne myszy na współrzędne siatki

void CMfctttYiew::MouseToGrid(CPoint &pt>

{

CRect r;

GetClientRect(&r);

pt.x/=r.right/3;

pt.y/=r.bottom/3;

// Skonwertuj współrzędne siatki na współrzędne myszy

void CMfctttView: :GridToMouse (CPoint &pt)

{

CRect r;

GetClientRect (&r) ;

pt .x*=r . right/3, •

pt.y*=r .bottom/3;

void CMfctttView: :OnLButtonDown(UINT nFlags, CPoint point) {

CMfctttDoc *doc=GetDocument ( ) ;

// Skonwertuj do pozycji siatki MouseToGrid (point) ;

// Jeśli pole planszy nie jest puste, to zasygnalizuj i pomiń ruch

if (doc->GetBoardState (point .x, point .y) !=EMPTY) {

MessageBeep(MB_ICONEXCLAMATION) ; return; }

// Pusty - umieść w polu X lub O, w zależności od numeru ruchu // Uwaga: ten kod został umieszczony w celach testowych, // komputer zawsze wyświetla O, dlatego ten ruch zawsze musi // wyświetlać X

doc->SetBoardState (point .x, point .y,

(doc->turn&l)?0:X) ; doc->turn++;

doc->UpdateAHViews(NULL) ; doc->Play(); // Wykonaj ruch

void CMfctttYiew: :OnEditUndo O {

CMfctttDoc *doc=GetDocument ( ) ;

doc->undo (TRUE) ;

void CMfctttYiew: :OnUpdateEditUndo (CCmdUI* pCmdUI) {

CMfctttDoc *doc=GetDocument () ;

pCmdUI->Enable(doc->undo( FALSE) ) ;

Najbardziej interesującą częścią programu jest kod odpowiedzialny za drukowanie. W kodzie przedstawionym na Listingu 3.1. możesz pominąć metodę OnPrint lub wewnątrz niej odwołać się bezpośrednio do tej samej metody klasy bazowej. Jeśli tak zrobisz, to przekonasz się, że pomimo wszystko zarówno drukowanie, jak i podgląd wydruku działają poprawnie. Jednakże wydrukowana plansza będzie bardzo mała w porównaniu z planszą widoczną na ekranie.

Jest kilka możliwości rozwiązania tego problemu. Po pierwsze mógłbyś rysować planszę w trybie mapowania innym niż MM_TEXT. Spowoduje to jednak powstanie nowego problemu związanego z tym, iż plansza powinna zmieniać swoje wymiary wraz ze zmianami wielkości okna programu. Znacznie łatwiej jest modyfikować wielkość plan­szy w trybie mapowania MM_TEXT. Najprostszym rozwiązaniem jest przeskalowanie kontekstu drukarki w taki sposób, aby linie zajmujące cały ekran przebiegały także przez całą stronę wydruku.

Poniżej przedstawiona została uproszczona wersja kodu realizującego takie przeskalowanie w metodzie OnPrint (wersję pełną znajdziesz na Listingu 3.1.):

void CMfctttYiew::OnPrint(CDC* pDC, CPrintlnfo* plnfo)

CDC *dc=GetDC();

int x=dc->GetDeviceCaps(LOGPIXELSX); int y=dc->GetDeviceCaps(LOGPIXELSY); int xl=pDC->GetDeviceCaps(LOGPIXELSX); int yl=pDC->GetDeviceCaps(LOGPIXELSY); // modyfikacja trybu mapowania pDC->SetMapMode(MM_ISOTROPIC); pDC->SetWindowExt(x,y); pDC->SetViewportExt(xl,yl); CView::OnPrint(pDC,plnfo);

W pierwszej linii metody pobierany jest wskaźnik do kontekstu urządzenia (DC) skoja­rzonego z widokiem. Kontekst urządzenia, przekazywany jako argument wywołania metody (pDC), odnosi się do drukarki. Kolejne cztery linie kodu określają ilości pikseli przypadające na cal w każdym z kontekstów (zarówno w pionie, jak i w poziomie). Kolejnym krokiem jest przejście do trybu mapowania MM_ISOTROPIC i ustawienie rozmiarów tak, aby cal na ekranie odpowiadał mniej więcej calowi na wydruku. Przy okazji powinieneś zapamiętać, że po przejściu do trybu mapowania MM_ISOTROPIC powinieneś w pierwszej kolejności określić wymiary okna, a dopiero potem wymiary widoku. Po wykonaniu powyższego kodu narysowanie linii o długości x jednostek w kontekście drukarki spowoduje wydrukowanie linii o długości xl jednostek fizycznych.

Stosując ten lub inny, podobny sposób postępowania, musisz wiedzieć o jeszcze jednej istotnej rzeczy. Jeśli zmienisz używany tryb mapowania w opisany powyżej sposób, to nie będziesz mógł korzystać z domyślnych czcionek. Kod odpowiedzialny za rysowanie będzie musiał stworzyć czcionki po zmianie trybu mapowania. W przeciwnym wypadku wyniki drukowania nie będą zbyt atrakcyjne.

Kod przedstawiony na Listingu 3.1., oprócz samej planszy, drukuje także nagłówek (w postaci tekstu i poziomej linii - patrz rysunek 3.2.). W przykładzie użyta została meto­da SetYiewportOrg, dzięki czemu metoda OnDraw rozpocznie rysowanie 100 jednostek poniżej początku strony, robiąc w ten sposób odpowiednio dużo miejsca dla nagłówka.

W MFC algorytm używany do drukowania wykorzystywany jest także przy tworzeniu podglądu wydruku, dzięki czemu ten sam kod obsługuje obie te czynności. Przy takim rozwiązaniu jedynym problemem pozostaje podział wydruku na strony. Jeśli wieś/, jaka będzie maksymalna ilość stron już podczas wywoływania metody OnPreparePrinting, to będziesz ją mógł od razu podać. W przeciwnym razie powinieneś ją określić podczas wykonywania metody OnBeginPrinting. Możesz także dokonywać zmian w podziale dokumentu na strony podczas jego drukowania - sprawdzając numer drukowanej strony przechowywany w strukturze CPrintlnfo. Jeśli będziesz chciał wydrukować stronę, to nadaj wartość TRUE składowej m_bContinuePrinting struktury CPrintlnfo. Przykłado­wy program na drugiej stronie wydruku wyświetla statystyki gry. Zauważ, że zastosowana została tu pierwsza omówiona metoda, a cały kod odpowiedzialny za wydrukowanie statystyki umieszczony jest w metodzie OnPrint.

Dostosowywanie podglądu wydruku do własnych potrzeb

Pamiętam, że gdy byłem dzieckiem fascynowało mnie struganie. Podejrzewam, że dzieci w dzisiejszych czasach już nie strugają (oczywiście nie oznacza to wcale, że nie mają noży, o czym łatwo można się przekonać oglądając informacje w telewizji). Nic zrozum mnie źle - nigdy nie byłem dobry w struganiu, chociaż zawsze chciałem być.

Niezależnie od tego, co zaczynałem strugać, to i tak w końcu wychodził z lego mały totem. W ten sposób udało mi się zrobić wiele całkiem fajnych totemów. Te same problemy miałem na zajęciach praktycznych w szkole średniej. Pewnie bym je oblał, gdyby nie

część poświęcona elektryczności. Trochę gorzej szło mi z materiałami plastycznymi. Zaczynałem robić krzyż, ale w efekcie i tak wychodziło z tego coś przypominającego cyfrę „7". Całe szczęście, że instruktor nie pytał, co robimy, aż do momentu, gdy przychodziło się do niego z ukończonym dziełem.

Doświadczenia te nauczyły mnie dwóch rzeczy:

• Powinienem trzymać się jak najdalej od noży i innych narzędzi;

• Zazwyczaj łatwiej jest zmniejszać rzeczy, niż je powiększać.

To ostatnie stwierdzenie można także z powodzeniem zastosować do podglądu wydru­ku. W stosunkowo łatwy sposób można bowiem ograniczyć możliwości podglądu wy­druku i zmusić go, żeby robił tylko to, co chcemy. Znacznie trudniej jest dodać do niego nowe możliwości. W dalszej części rozdziału pokażę, jak można zrobić obie te rzeczy.

W celu przedstawienia możliwości dostosowywania podglądu wydruku do własnych potrzeb napisałem bardzo prosty program umożliwiający łączenie punktów na ekranie (patrz rysunek 3.3.). Gdy klikniesz lewym przyciskiem myszy, to program narysuje linię prostą łączącą ostatnio narysowany punkt z miejscem, w którym aktualnie umieszczony jest wskaźnik myszy. Gdy klikniesz prawym przyciskiem myszy, to program nie rysuje nowej linii, a jedynie zaznacza na ekranie nowy punkt, od którego zostanie rozpoczęta następna linia. Jak widać, program nie jest zbytnio skomplikowany, a momo to doskonale będzie się nadawał na przykład prezentujący drukowanie.



Gdy kiedyś będziesz miał okazję pisania programu takiego jak len, to pierwszą rzeczą, na którą zwrócisz uwagę, będzie to, że opcja podglądu wydruku dostarczana przez MFC wykonuje za Ciebie „kawał porządnej roboty". Dla przykładu, MFC automatycznie obsłu­guje wyświetlanie dokumentów zawierających więcej stron, jak również pozwala na wyświetlanie dwóch stron jednocześnie.

Dostosowywanie podglądu wydruku

Dostosowanie podglądu wydruku do własnych potrzeb jest dość prostym zadaniem. Wszystko, co będziesz musiał zrobić, ogranicza się bowiem do usunięcia przycisków, których nie chcesz używać. Oczywiście, kod obsługujący odpowiednie polecenia cały czas jest gdzieś tam wewnątrz MFC. Ale co Ci to przeszkadza? Jeśli użytkownik nie może uruchomić kodu, to tak, jak gdyby w ogóle tego kodu nie było.

Cała sztuczka polega na przejęciu kontroli nad metodą OnFilePrintPreview. Standar­dowo, kreator App Wizard dodaje makro definiujące procedurę obsługi tego polecenia do mapy obsługi komunikatów widoku. Makro to jest jednak umieszczane poza ko­mentarzami oznaczającymi komunikaty obsługiwane przez kreatora Class Wizard, a do obsługi polecenia używana jest metoda klasy bazowej. Dlatego też nie jesteś w stanie zobaczyć ani kodu obsługi polecenie wyświetlania podgląd wydruku, ani odpowiednie­go elementu mapy obsługi komunikatów.

Naszym pierwszym krokiem będzie przeniesienie makra O1SL.COMMAND, zawierają­cego metodę OnFilePrintPreview, do obszaru makr zarządzanych przez kreatora Class Wizard. Kolejny krok to usunięcie operatora zakresu (często ma on postać: CView::) umieszczonego przed nazwą metody OnFilePrintPreview, dodanie deklaracji tej metody do pliku nagłówkowego i zdefiniowanie jej w pliku CPP. Po zakończeniu tych czynności mapa komunikatów Twojego widoku powinna przypominać tę przedstawioną poniżej.

BEGIN_MESSAGE_MAP(CConndotView, CView)

//{{AFX_MSG_MAP(CConndotYew)

ON_WM_LBUTTONDOWN()

ON_WM_RBUTTONDOWN ( )

ON_COMMAND(ID_FILE_PRINT_PREVIEW, OnFilePrintPreview)

//}}AFX_MSG_MAP

//Standard printing comments

ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint) END_MESSAGE_MAP()

Oryginalna wersja kodu metody OnFiIePrintPreview ma następującą postać (możesz ją odnaleźć w pliku VIEWPREV.CPP, w kodzie źródłowym MFC):

void CView::OnFilePrintPreview()

{

// In derived classes, implement special window handling here // Be surę to Unhook Frame Window close if hooked.

// must not create this on the frame. Must outlive this function CPrintPreviewState* pState = new CPrintPreviewState;

// DoPrintPreview's return value does not necessarily indicate that

// Print preview succeeded or failed, but rather what actions

// arę necessary at this point. If DoPrintPreview returns TRUE,

// it means that OnEndPrintPreview will be (or has already been)

// called and the pState structure will be/has been deleted.

// If DoPrintPreview returns FALSE, it means that

// OnEndPrintPreview WILL NOT be called and that cleanup,

// including deleting pState must be done here.

if (!DoPrintPreview(AFX_IDD_PREVIEW_TOOLBAR, this, RUNTIME_CLASS(CPreviewView), pState)) {

// In derived classes, reverse special window handling // here for Preview failure case

TRACEO("Error: DoPrintPreview failed.\n"); AfxMessageBox(AFX_IDP_COMMAND_FAILURE); delete pState; // preview failed to initialize, // delete State nów


Jest oczywiste, że wszystkie najważniejsze czynności muszą być wykonywane w metodzie DoPrintPreview. Jeśli z metody OnFilePrintPreview usuniesz niepotrzebne komenta­rze i makra używane przy testowaniu, to okaże się, że cała metoda ma niewiele więcej niż 4 linie kodu. Kluczem do dostosowania podglądu wydruku do swoich potrzeb jest metoda DoPrintPreview.

Przyjrzymy się teraz czterem argumentom wywołania tej metody. Pierwszym z nich jest identyfikator zasobu zawierającego pasek narzędzi podglądy wydruku. Zmiana tego za­sobu spowoduje automatyczną zmianę paska narzędzi. Proste, prawda? W zasadzie mógłbyś stworzyć zupełnie nowy pasek narzędzi, jednakże znacznie łatwiej jest posłu­żyć się oryginalnym paskiem, który przechowywany jest w pliku AFXPR1NT.RC (plik ten z niewiadomych powodów umieszczony został w kartotece MFCMNCLUDE}. Po skopiowaniu oryginalnego paska narzędzi i umieszczeniu w Twoim pliku zasobów, wy­starczy go odpowiednio zmodyfikować, dostosowując jego postać do własnych potrzeb. Nie zapomnij także zmienić identyfikatora zasobu i użyć tego nowego identyfikatora w wywołaniu metody DoPrintPreview.


Przykład własnego podglądu wydruku


Na Listingu 3.2. przedstawiony został kod modyfikujący postać podglądu wydruku (wy­gląd zmodyfikowanego podglądu wydruku możesz zobaczyć na Rysunku 3.3.). Jedy­nym interesującym aspektem tworzenia własnej metody OnFi!ePrintPreview jest wyko­rzystanie w tej metodzie odwołania do obiektu CPreviewView. Obiekt ten jest używany w MFC do reprezentowania okna podglądu wydruku. Jedyny plik nagłówkowy zawie­rający deklarację tej klasy nosi nazwę AFXPRIV.H. Jeśli chcesz, aby Twój kod został po­prawnie skompilowany, to będziesz musiał dołączyć do niego ten plik.



Używanie pliku nagłówkowego AFXPRIV.H

W zasadzie wszystkie deklaracje umieszczone w pliku AFXPRIV.H mogą się zmieniać w kolejnych wersjach MFC. Jednakże firma Microsoft okazała się na tyle łaskawa, aby nie zmieniać tych deklaracji, które mogą spowodować błędy w kodzie wielu programistów. De facto, wiele elementów umieszczonych początkowo w pliku nagłówkowym AFXPRIV.H (jak na przykład niektóre makra służące do konwersji danych) zostały ostatnio oficjalnie udokumentowane i przeniesione do innych plików nagłówkowych, a to wszystko z tego powodu, że były one tak często wykorzystywane.

Jeśli chcesz napisać profesjonalnie wyglądający program, to będziesz musiał czasami podjąć pewne ryzyko i wykorzystać te części MFC, które nie są oficjalnie udokumentowane. Może się zdarzyć, że po po­jawieniu się nowszej wersji MFC będziesz musiał dokonać w swoim kodzie jakichś modyfikacji, jednakże taka już jest cena „życia na krawędzi".

Poniżej przedstawiłem oficjalny komentarz firmy Microsoft zaczerpnięty z pliku nagłówkowego AFXPRIV.H.

„Ten plik nagłówkowy zawiera przydatne klasy, które są udokumentowa­ne jedynie w Notatkach Technicznych MFC. Klasy te mogą się zmieniać w kolejnych wersjach MFC, dlatego w przypadku wykorzystania tego pliku będziesz musiał być przygotowany na konieczność modyfikacji swojego kodu. W przyszłości częściej używane fragmenty tego pliku nagłówkowego mogą zostać oficjalnie udokumentowane i przeniesione w inne miejsca."

Metoda OnDraw przedstawiona na Listingu 3.2. wykonuje specjalne czynności pod­czas drukowania. W przypadku wykrycia, że program jest w trakcie drukowania (za pomocą metody CDC::IsPrinting), metoda ta zmienia tryb mapowania, dzięki czemu widok wydrukowany będzie wiernym odzwierciedleniem widoku ekranu. Aby móc to zrobić, kod tej metody zmienia tryb mapowania kontekstu drukarki (na MM_ISOTROPIC), pobiera kontekst aktualnego widoku i na podstawie jego wielkości określa parametry kontekstu drukarki.

Listing 3.2. Własny pasek narzędzi okna Podglądu Wydruku.

/ l conndotview.cpp : implementation of the CConndotView class

#include "stdafx.h"

#include "conndot.h"

tfinclude "CustomPreview.h" // dołącz okno podglądu wydruku

ttinclude "conndotDoc .h" tinclude "conndotView.h"

ttifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] = _ FILE _ ;

ttendif

// CConndo tVi ew

IMPLEMENT_DYNCREATE(CConndotView, CView)

BEGIN_MESSAGE_MAP(CConndotView, CView) //{{AFX_MSG_MAP(CConndotView) ON_WM_LBUTTONDOWN() ON_WM_RBUTTONDOWN()

ON_COMMAND(ID_FILE_PRINT_PREVIEW, OnFilePrintPreview)

//}}AFX_MSG_MAP

// Standard printing commands

ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)

ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)

END_MESSAGE_MAP()

// CConndotView construction/destruction

CConndotView: :CConndotView( ) {

// TODO: add construction code here

}

CConndotView: : ~CConndotView( )

{ }

BOOL CConndotYiew: :PreCreateWindow(CREATESTRUCT& es) {

// TODO: Modify the Window class or styles here by modifying

// the CREATESTRUCT es


}

return CView: : PreCreateWindow(cs) ;


/ / CConndotView drawing

void CConndotView: :OnDraw(CDC* pDC) {

CConndotDoc* pDoc = GetDocument ( ) ;

ASSERT_VALID(pDoc) ;

CPoint pt;

if (pDC->IsPrinting( ) )

{

CDC *vdc=GetDC() ; int n;

n=vdc->GetDeviceCaps (LOGPIXELSX) ; pDC->SetMapMode(MM_ISOTROPIC) ; pDC->SetWindowExt (n, n) ,-n=pDC->GetDeviceCaps (LOGPIXELSX) ; pDC->SetViewportExt (n, n) ; }

pDC->MoveTo(0, 0) ;

for (int i=0; i<pDoc->points -GetSize ( ) ; i++ {

pt=pDoc->points [ i] ;

if (pt.x<0)

{

pt .x=-pt .x; pt.y=-pt.y; pDC->MoveTo (pt) ;


else

pDC->LineTo (pt) ;

// CConndotYiew printing

BOOL CConndotYiew::OnPreparePrinting(CPrintlnfo* plnfo)

// default preparation

return DoPreparePrinting(plnfo);

void CConndotView: :OnBeginPrinting (CDC* /*pDC*/, CPrintlnfo*

/*plnfo*/)

{

// TODO: add extra initialization before printing

void CConndotView: :OnEndPrinting (CDC* /*pDC*/, CPrintlnfo*

/*plnfo*/)

{

// TODO: add cleanup after printing

// CConndotView diagnostics

łtifdef _DEBUG

void CConndotView::AssertValid() const

{

CView::AssertValid();

void CConndotYiew::Dump(CDumpContext& dc) const CYi ew: : Dump (dc ) ;

CConndotDoc* CConndotView::GetDocument() // non-debug version is

inline

{

ASSERT (m_pDocument->IsKindOf (RUNTIME_CLASS (CConndotDoc) ) ) ,-

return (CConndotDoc*)m_pDocument; } łtendif //_DEBUG

// CConndotView message handlers

void CConndotView::OnLButtonDown(UINT nFlags, CPoint point) {

CConndotDoc *doc=GetDocument();

// Rysuj

doc->points.Add(point);

doc->UpdateAllViews(NULL);

CView::OnLButtonDown(nFlags, point); }

void CConndotView::OnRButtonDown(UINT nFlags, CPoint point)

{

// przesuń tylko aktualny punkt

CPoint mover=point;

mover.x--mover.x;

mover.y=-mover.y;

GetDocument()->points.Add(mover); CView::OnRButtonDown(nFlags, point);

void CConndotView::OnFilePrintPreview()

CPrintPreviewState* pState = new CPrintPreviewState;

if (!DoPrintPreview(IDD_PREVIEW_TOOLBAR, this,

RUNTIME_CLASS(CCustomPreview),pState))

// W klasie potomnej odtwórz tutaj specjalną obsługę okna // gdyż w przeciwnym razie okno podglądu wydruku nie będzie // działało prawidłowo

TRACEO("Error: DoPrintPreview failed.\n");
Af xMessageBox (AFX_IDP_COMMAND_FAILURE) ;
delete pState; // okno podglądu nie zostało
// zainicjalizowane usuń State

Bardziej zaawansowane dostosowywanie podglądu wydruku

Jeśli się dokładniej przyjrzysz pozostałym trzem argumentom wywołania metody DoPrint-Preview, to najprawdopodobniej samemu zorientujesz się, do czego one służą. Drugim argumentem wywołania metody DoPrintPreview jest wskaźnik na widok, który ma zostać wydrukowany (zazwyczaj będzie to this). Trzecim argumentem jest klasa czasu wykona­nia (otrzymana za pomocą makra RUNTIME_CLASS) okna podglądu wydruku. Za­zwyczaj jest to klasa CPrintYiew, jednakże nic nie stoi na przeszkodzie, aby użyć ja­kiejś innej klasy. Na przykład, można by stworzyć klasę potomną klasy CPrevie-wYiew, dodać do niej nowe możliwości, a następnie podać jej nazwę jako trzeci argu­ment wywołania metody DoPrintPreview.

Ale co mógłbyś robić we własnej klasie widoku podglądu wydruku? Cokolwiek byś so­bie wymyślił. Ot chociażby - napisać wielkimi literami „PODGLĄD WYDRUKU" (oczywiście oprócz pozostałej zawartości podglądu). Jednakże rewelacyjną modyfikacją byłoby uaktywnienie okna podglądu wydruku w taki sposób, aby użytkownik mógł modyfikować dane podczas oglądania podglądu (takie rozwiązanie mogłoby troszkę przypominać postać ukladu strony dostępny w wielu popularnych edytorach).

Podczas tworzenia własnej klasy potomnej klasy CPrintYiew możesz odkryć, że po­trzebne Ci będą pewne dodatkowe elementy. Po pierwsze, bez większych trudności bę­dziesz mógł dodawać nowe przyciski do paska narzędzi swojego widoku. Cała modyfikacja będzie się sprowadzała do zmienienia szablonu paska narzędzi przechowywanego w pliku zasobów (tak jak w poprzednim przykładzie). Możesz także stworzyć swoją klasę jako klasę potomną klasy CPrintPreviewState i dodać do niej dowolne dane, jakie będą Ci potrzebne. Jedyną rzeczą, o jakiej nie możesz zapomnieć, to umieszczenie nazwy Two­jej nowej klasy w wywołaniu operatora new w metodzie OnFHePrintPreview.

Wyprowadzanie klasy

Niestety, podczas tworzenia nowej klasy kreator Class Wizard nie umożliwia wybrania klasy CPreviewView jako klasy bazowej. Klasa CPreviewView jest klasą potomną klasy CScroIlYiew; nie polecałbym Ci jednak wyprowadzania swojej nowej klasy z klasy CPreviewView ze względu na to, iż obsługuje ona wszystkie czynności charakterystyczne dla przewijalnych widoków (widoków wyprowadzonych z klasy CScrollYiew). Naj­lepszym wyjściem z sytuacji jest wyprowadzanie klasy widoku z klasy CYiew. Po stworzeniu klasy będziesz musiał zamienić wszystkie wystąpienia łańcucha znaków „CYiew" na „CPreviewView" (zarówno w pliku nagłówkowym - .H, jak i pliku źró­dłowym - .CPP). Upewnij się, że wszystkie wystąpienia łańcucha znaków „CYiew" zostały poprawnie zamienione. Będziesz także musiał dołączyć do pliku źródłowego plik nagłówkowy AFXPRIV.H, aby uzyskać definicję klasy CPreviewView.

Kolejnym etapem będzie zmodyfikowanie istniejącego widoku. Czynności, jakie będziesz musiał w tym celu wykonać, są bardzo podobne do czynności, które wykonywałeś podczas modyfikowania paska narzędzi w oknie podglądu wydruku. Jedyna różnica polega na tym, iż zamiast klasy CPreviewView, w wywołaniu metody DoPrintPreview użyjesz swojej, stworzonej przed chwilą klasy; oprócz tego, zamiast pliku AFXPR1V.H, dołączysz do widoku plik nagłówkowy Twojej klasy.

Jeśli będziesz chciał, to możesz przesłonić metodę OnDraw, aby samemu narysować coś na stronie (lub w obszarze poza stroną). Problem jednak polega na tym, że nie wiadomo gdzie rysować. Podobny problem pojawi się, kiedy będziesz chciał obsługiwać kliknięcia myszką- współrzędne kliknięcia będziesz musiał przeliczać na współrzędne, które mają jakikolwiek sens dla Twojego programu.

Wewnętrzne tajniki sporządzania podglądu wydruku

Kod odpowiadający za sporządzanie podglądu wydruku umieszczony jest w trzech pli­kach źródłowych MFC. Kod obsługujący okno podglądu wydruku umieszczony jest w pliku V1EWPREV.CPP. Specjalny kontekst urządzenia odpowiadający za poprawne drukowanie znajduje się w pliku DCPREY.CPP. I wreszcie wszystkie odpowiednie de­finicje umieszczone zostały w pliku AFXPRIV.H. Przestudiowanie tych trzech plików w znacznej mierze może wyjaśnić Ci szczegóły sposobu sporządzania podglądu wydruku.

Podstawowym źródłem informacji dla podglądu widoku jest składowa m_pPage!nfo. Jest to tablica struktur typu PAGE_INFO (patrz Tabela 3.4.). W tablicy tej umieszczane są informacje określające każdą stronę sporządzanego wydruku. Jeśli cały wydruk skła­da się tylko z jednej strony, to oznacza to, że w tablicy tej umieszczony będzie tylko je­den element (zostanie on umieszczony w komórce tablicy o indeksie 0). Jeśli na wydruk składają się dwie strony, to odpowiednie informacje zostaną umieszczone w komórkach i indeksach O i 1.

W składowej rectScreen przechowywane są współrzędne obszaru (prostokątnego), jaki aktualna strona zajmuje na ekranie. Pozostałe trzy składowe tej struktury są obiektami klasy CSize. Jednakże MFC nie używa ich do określania wielkości. Zamiast tego są one używane do zapamiętania ułamka'(cx/cy); będziesz musiał o tym pamiętać, analizując oryginalny kod MFC.

Tabela 3.4. Postać struktury PAQE_INFO.


Składowa

Definicja.


rectScreen

sizeUnscaled

sizeScaleRatio

sizeZoomOutRatio

Współrzędne tej strony na ekranie.

Prostokąt określający niewyskalowany obszar ekranu.

Współczynnik (cx/cy) pomiędzy jednostkami drukarki a jednostkami ekranu.

Współczynnik skalowania używany podczas zmniejszania powiększonego podglądu wydruku.



Poprzez manipulowanie dostępnymi współczynnikami skalowania będziesz mógł prze­kształcać punkty na drukarce na punkty na ekranie (i na odwrót). Praktyczny sposób przekształcania będziesz mógł zobaczyć w przykładowym programie przedstawionym w dalszej części rozdziału. Niestety, nie udało mi się znaleźć łatwego sposobu prze­kształcania punków na ekranie na punkty, którymi możesz się posługiwać w programie. Na szczęście, jeśli raz napiszesz odpowiedni kod, to już nigdy więcej nie będziesz mu­siał tego robić powtórnie. Oczywiście, zamiast pisać własny kod, równie dobrze możesz skopiować kod przedstawiony w przykładowym programie.

Klasa CPreviewView dysponuje wieloma innymi składowymi, jednakże nie są one dla nas interesujące. Numer aktualnie drukowanej strony możesz określić za pomocą skła­dowej m_nCurrentPage. Kontekst urządzenia podglądu wydruku jest przechowywany w składowej m_pPreviewDC.

Tworzenie podglądu wydruku umożliwiającego edycję

Na Rysunku 3.4. przedstawiona została kolejna wersja programu umożliwiającego ry­sowanie i łączenie punktów. Ta wersja programu dysponuje własnym obiektem klasy CPreviewView. Okno podglądu wydruku wygląda niemalże identycznie z oknem do­stępnym w poprzedniej wersji programu; jedyną różnicą jest to, że na pasku narzędzi umieszczony został dodatkowy przycisk o nazwie Edit. Kliknięcie na tym przycisku powoduje zmianę kształtu kursora (będzie on przypominał strzałkę) i umożliwienie ry­sowania bezpośrednio w oknie podglądu wydruku. Powtórne kliknięcie przycisku (które­go nazwa została zmieniona na Zoom) spowoduje przejście do normalnego trybu pod­glądu wydruku i zmianę kształtu kursora.

W jaki sposób można to wszystko zrobić? Otóż klasa podglądu wydruku stworzona na potrzeby tego przykładu zawiera składową typu Boolean (editmode) używaną do prze­chowywania informacji o trybie, w jakim aktualnie znajduje się okno podglądu wydru­ku (dostępne są dwa tryby: tryb edycji oraz tryb powiększania). Oprócz tego w mapie komunikatów dostępnych w tej klasie umieszczone zostały makra określające procedury obsługi kliknięć prawym i lewym przyciskiem myszy, procedurę obsługi komunikatu WM_SETCURSOR oraz procedurę obsługi przycisku ID_EDITBUTTON (używanego do przełączania trybu pracy okna podglądu wydruku).

Kiedy użytkownik naciska przycisk zmiany trybu pracy, wartość zmiennej editmode jest zamieniana na przeciwną. Dodatkowo zdefiniowana została procedura obstugi UPDATE_COMMAND_UI odpowiadająca za modyfikowanie nazwy przycisku (więcej informacji na temat procedur używanych do aktualizacji opcji menu i innych elementów interfejsu użytkownika). Gdy program otrzymuje komunikat WM_SETCURSOR, to sprawdza czy podgląd wydruku znajduje się w trybie edycji (czy składowa editmode posia­da wartość TRUE) oraz czy wskaźnik myszy znajduje się w obszarze roboczym okna podglądu wydruku. Jeśli oba te warunki zostaną spełnione, to wskaźnikowi myszy nadawany jest kształt strzałki. W przeciwnym razie, wywoływane są odpowiednie metody klasy bazowej, dzięki czemu wykonane zostaną standardowe czynności.

Listing 3.3. Edycja danych w oknie podglądu wydruku.______

// CustomPreview.cpp : implementation file


_FILE_

łtinclude "stdafx.h" ttinclude "conndot.h" ttinclude "CustomPreview.h"

#ifdef _DEBUG

#define new DEBUG_NEW

#undef THIS_FILE

static char THIS_FILE[] =

#endif


// CCustomPreview

IMPLEMENT_DYNCREATE(CCustomPreview, CPreviewView)

CCustomPreview::CCustomPreview() {

editmode=FALSE; }

CCustomPreview::~CCustomPreview()

BEGIN_MESSAGE_MAP(CCustomPreview, CPreviewView)

//{{AFX_MSG_MAP(CCustomPreview)

ON_WM_RBUTTONDOWN()

ON_WM_LBUTTONDOWN ( )

ON_COMMAND(ID_EDITMODE, OnEditMode)

ON_UPDATE_COMMAND_UI(ID_EDITMODE, OnUpdateEditMode)

ON_WM_SETCURSOR()

//} }AFX_MSG_MAP END_MESSAGE_MAP()

// CCustomPreview drawing

// CCustomPreview diagnostics

łłifdef _DEBUG

void CCustomPreview::AssertValid() const

{

CYiew::AssertValid();

void CCustomPreview::Dump(CDumpContext& dc) const

CView::Dump(dc); #endif //_DEBUG

11111111 /11111111111111111111 /11111111111111111111111 /1111 /11111111 II CCustomPreview message handlers

void CCustomPreview::OnRButtonDown(UINT nFlags, CPoint point) if (editmode)

ConvertPoint(point);

// przesuń tylko aktualny punkt

CPoint mover=point;

mover.x=-mover.x;

mover.y=-mover.y;

GetDocument()->points.Add(mover);


else

CPreviewView::OnRButtonDown(nFlags, point);


void CCustomPreview::OnLBUttonDown(UINT nFlags, CPoint point)


i f (editmode)


CConndotDoc *doc=GetDocument ( ) ; ConvertPoint (point) ; doc->points .Add (point) ; doc->UpdateAHViews (NULL) ;


else

CPreviewView: .-OnLButtonDown (nFlags, point)


CConndotDoc *CCustomPreview: :GetDocument ( ) {

return (CConndotDoc * ) CPreviewView: :GetDocument ( ) ,-

void CCustomPreview: : ConvertPoint (CPoint & point)

{

// zakładamy, że widoczna jest tylko l strona,

// jeśli to nie jest dobre założenie, to będziesz musiał sprawdzić

// numer wyświetlanej strony - możesz to zrobić, badając wartość

// każdej składowej rectScreen elementu tablicy m_pPage!nfo.

// Nie zapomnij, że podgląd wydruku używa sizeScaleRatio jako ułamka, // nie są stosowane czynniki skalujące x, y. CPoint ViewportOrg; if (nunZoomState != ZOOM_OUT)

ViewportOrg = -GetDeviceScrollPosition ( ) ; else

ViewportOrg=GetDC ( ) ->GetViewportOrg ( ) ; m_pPreviewDC->SetScaleRatio (m_pPage!nf o [

m_nCurrentPage-l ] . sizeScaleRatio . ex,

m_pPage!nfo [m_nCurrentPage-l] . sizeScaleRatio . cy) ,-

// określ wielkość marginesu CSize PrintOffset; m_pPreviewDC->Escape(GETPRINTINGOFFSET, O, NULL,

(LPVOID)&PrintOffset) ;

m_pPreviewDC->PrinterDPtoScreenDP( (LPPOINT) &PrintOf f set ) ; PrintOffset += (CSize) m_pPage!nf o [

m_nCurrentPage-l] . rectScreen. TopLef t () ; PrintOffset += CSized, 1);

PrintOffset += (CSize) ViewportOrg; // na potrzeby przewijania point- =PrintOf f set ;

// dostosuj punkt do pozycji strony

point. x = MulDiv(point .x, m_pPage!nfo[

m_nCurrentPage-l] . sizeScaleRatio . cy, m_pPage!nf o [m_nCurrentPage-l ] . sizeScaleRatio. ex) ; point. y = MulDiv(point .y, m„pPageInf o [

m_nCurrentPage-l] . sizeScaleRatio . cy,

m_pPage!nfo [m_nCurrentPage-l ] . sizeScaleRatio. ex) ;

void CCustomPreview: : OnEdi tMode ( ) {

ed t tmode- i editmode ;

void CCustomPreview::OnUpdateEditMode(CCmdUI* pCmdUI) {

pCmdUI->SetText(editmode?"Zoom":"Edit"};

pCmdUI->Enable();

BOOL CCustomPreview: :OnSetCursor (CWnd* pWnd, UINT nHitTest, UINT

message) {

if (editmode && nHitTest==HTCLIENT) {

: : SetCursor (Af xGetApp ( ) ->LoadStandardCursor ( IDC_ARROW) ) ; return TRUE;


else

return CPreviewView: :OnSetCursor (pWnd, nHitTest,

message) ;


Najważniejsza część pracy wykonywana jest w procedurach obsługi kliknięć prawym i lewym przyciskiem myszy. Kod umieszczony w tych procedurach musi w pierwszej kolejności sprawdzić wartość składowej editmode. Jeśli wartość tej składowej wynosi FALSE, to program nie wykonuje żadnych dodatkowych czynności i wywołuje metodę klasy bazowej umożliwiającą standardową obsługę obydwu komunikatów. Jeśli jednak wartość tej składowej wynosi TRUE, to program pobiera współrzędne położenia wskaźnika myszy w momencie kliknięcia i przekształca je na współrzędne stosowane w programie. Następnie współrzędne te zapisywane są w dokumencie.

Aby maksymalnie uprościć program, konwersja punktów realizowana jest w specjalnej metodzie o nazwie ConvertPoint. Funkcja ta wykonuje za Ciebie całą „brudną robotę", o czym łatwo możesz się przekonać, przeglądając jej kod. Wbrew pozorom to nie prze­liczenie współrzędnych za pomocą funkcji MulDiv oraz odpowiednich współczynników skalujących stwarza najwięcej problemów. Prawdziwych kłopotów przysparza określe­nie wielkości marginesów strony wydruku oraz przeliczenie tych marginesów na pik-sele. Co więcej, musisz także wziąć po uwagę możliwość przewinięcia okna. Na szczę­ście będziesz mógł skopiować tę metodę bezpośrednio ż przykładu do swojego kodu.

W przykładowym programie wykorzystana została jeszcze jedna chytra sztuczka, dotyczy ona pobierania wskaźnika do obiektu dokumentu. W Rozdziale 1. miałeś okazję zoba­czyć, że kreator App Wizard przesłania metodę GetDocument obiektu widoku w taki sposób, iż zwraca ona wskaźnik na dokument typu wykorzystywanego w Twoim programie, a nie wskaźnik na ogólny typ dokumentu - CDocument. Jednakże gdy tworzysz nową klasę widoku za pomocą kreatora Class Wizard, to nie ma on żadnego pojęcia o tym, z jakim dokumentem nowy widok będzie współpracował. Dlatego też podczas tworzenia nowego widoku za pomocą tego kreatora metoda GetDocument nie jest przesłaniana. Jeśli wywołasz tę metodę w nowej klasie widoku, to zostanie zwrócony wskaźnik do obiektu CDocument. W takim wypadku, aby móc skorzystać z metod charakterystycznych dla Twojego dokumentu, będziesz musiał odpowiednio rzutować otrzymany wskaźnik. Oczywiście możesz samemu przesłonić standardową definicję metody GetDocument, takie rozwiązanie zostało zastosowane w przedstawionym przykładzie.

Podsumowanie

Jeśli jesteś podobny do wielu innych programistów, to będziesz odkładał implementowanie drukowania aż do ostatnich etapów powstawania programu. Na szczęście MFC zachęca Cię i umożliwia takie postępowanie, gdyż drukowanie i rysowanie na ekranie są w MFC bardzo ściśle ze sobą związane. Czasami bez obaw możesz powierzyć drukowanie MFC, a samemu zająć się rozwiązywaniem bardziej złożonych problemów.

Jednakże jeśli wymagania stawiane sposobowi drukowanie lub działania podglądu wy­druku w Twoim programie są bardziej wymagające, to będziesz musiał dokładnie zro­zumieć, jak MFC realizuje drukowanie i tworzy podgląd wydruku. Dzięki informacjom przedstawionym w tym rozdziale będziesz w stanie zmodyfikować sposób drukowania tak, aby dokładnie odpowiadał Twoim wymaganiom.

Praktyczny przewodnik Drukowanie


Zarządzanie oknem dialogowym Drukuj

Skalowanie wydruku

Drukowanie innych elementów

Drukowanie nagłówków i stopek

Modyfikowanie paska narzędzi okna podglądu wydruku

Modyfikowanie działania podglądu wydruku

W przypadku wielu aplikacji jedyną rzeczą, jaką będziesz musiał zrobić, aby opcje druko­wania zaczęły działać poprawnie, będzie użycie innego trybu mapowania niż MM_TEXT. Kreator App Wizard dołącza do tworzonych aplikacji prostą obsługę drukowania i wybór jakiegoś „normalnego" trybu mapowania powinien spowodować, że wydruki sporządzane przez program będą automatycznie i poprawnie skalowane.

Zarządzanie oknem dialogowym Drukuj

Pierwszą metodą wywoływaną po rozpoczęciu procesu drukowania jest metoda On-PreparePrinting zdefiniowana w klasie widoku. Jej działanie ogranicza się do wywołania kolejnej metody - DoPreparePrinting. Do metody OnPreparePrinting przekazywany jest obiekt klasy CPrintlnfo, dzięki któremu będziesz w stanie przejąć kontrolę nad in­formacjami wyświetlanymi w oknie dialogowym Drukuj (czyli, jednocześnie, także nad samym procesem drukowania).

Dwiema najbardziej interesującymi składowymi dostępnymi w klasie CPrintlnfo są: m_pPD (w której przechowywany jest wskaźnik do okna dialogowego CprintDialog wyświetlanego przez MFC) oraz SetMaxPages, która to metoda pozwala Ci na określenie
ilości stron wydruku. . •

Jeśli w ogóle nie będziesz chciał wyświetlać okna dialogowego Drukuj, to przed wy­wołaniem metody DoPreparePrinting przypisz składowej m_bDirect (klasy CPrintlnfo)

wartość TRUE. W takim wypadku wykonane zostanie zadanie drukowania, podczas którego, bez żadnych interwencji ze strony użytkownika, wydrukowane zostaną wszystkie strony. Podczas drukowania wykorzystana zostanie domyślna drukarka.

Skalowanie wydruku

Jeśli w programie wykorzystujesz tryb mapowania MM_TEXT lub któryś inny jednorodny (isotropic) tryb, to sporządzony wydruk będzie się zazwyczaj różnił od wyglądu ekranu. Po zastanowieniu przyznasz, że nie jest to pozbawione sensu. Rozdzielczość monitora wynosi bowiem około 72 punktów na cal. Dlatego też, jeśli narysujesz linię składającą się z 75 punktów, to będzie ona miała długość około jednego cala (około - gdyż roz­dzielczości ekranów nie są dokładne). Jednakże na drukarce o rozdzielczości 300 punktów na cal (dpi), ta sama linia będzie miała długość dokładnie 1/4 cala; na drukarce o rozdzielczości 600 dpi - ta sama linia będzie już miała tylko 1/8 cala długości.

Istnieje kilka sposobów przezwyciężenia tego problemu. Najprostszym z nich jest użycie trybu mapowania niezależnego od wykorzystywanego sprzętu. Zamiast tego można użyć takich trybów mapowania jak: MM_LOENGLISH lub MM_HIMETRIC (patrz Ta­bela 3.3).

Chociaż nie zawsze jest to możliwe do wykonania, to jednak czasami będziesz musia) przeskalować wydruk, używając przy tym jednego z bardziej popularnych trybów mapowania, Pomysł tego rozwiązania polega na tym, żeby używając trybu MM_ISOTROPIC tak ustawić parametry kontekstu drukarki, aby narysowanie na drukarce linii prostej składającej się z 75 punków spowodowało wydrukowanie linii o długości nieco powyżej jednego ca­la.

Sposób przeskalowania kontekstu drukarki, tak aby był on zgodny z parametrami ekranu, przedstawiony został na Listingu 3.1. (metoda OnPrint). Oczywiście, może się zdarzyć, że będziesz chciał postąpić w nieco inny sposób. Na przykład mógłbyś chcieć, aby element na wydruku był dokładnie dwa razy większy od tego samego elementu na ekranie. Mo i żesz to osiągnąć bez jakichkolwiek większych problemów poprzez odpowiedni współczynników skalowania.

Drukowanie innych elementów

Generalnie rzecz biorąc, MFC zakłada, że będziesz chciał drukować te same elementy, które są widoczne na ekranie. Zazwyczaj jest to całkiem słuszne założenie; mogą się jednak zdarzyć takie przypadki, kiedy na wydruku będziesz chciał umieścić zupełnie co innego. Załóżmy, że częścią Twojego widoku jest formularz służący do wprowadzania danych. Formularz ten prezentuje pojedynczy rekord z bazy danych. Podczas drukowa­nia danych takiej aplikacji nie będziesz chciał wydrukować tylko jednego - aktualnego rekordu; zapewne będziesz chciał sporządzić tabelaryczny wydruk wszystkich rekordów zapisanych w bazie.

Taka modyfikacja sposobu drukowania jest wyjątkowo prosta. Wystarczy przesłonić standardową definicję metody OnPrint. Domyślna definicja tej metody powoduje wy­wołanie metody OnDraw, jednakże możesz utworzyć swoją własną wersję tej metody, która umożliwi Ci wydrukowanie dowolnych informacji pod dowolną postacią. Rozwiąza­nie to jcsi szczególnie istotne w przypadku tworzenia programów, w których widok jest klasą potomną klasy CFormYiew. Wynika to z faktu, iż widoki takie nie mają możliwości wydrukowania swojej zawartości.

Drukowanie nagłówków i stopek

Kolejnym powodem, dla którego mógłbyś chcieć przesłonić standardową definicję metody OnPrint, jest konieczność lub chęć umieszczenia na wydruku dodatkowych elementów. Doskonałym przykładem takiego wykorzystania metody OnPrint jest drukowanie na­główków i stopek, które nie są widoczne na ekranie.

Przykład takiego zastosowania metody OnPrint możesz znaleźć na Listingu 3.1. Bardzo istotną rzeczą, o której nie można zapomnieć podczas drukowania nagłówków i stopek, jest konieczność określenia odpowiedniej wielkości regionu przycinania (w celu ochronienia stopki) oraz współrzędnych początku układu współrzędnych (w celu ochronienia nagłów­ka). Jeśli uważasz, że powyższe rozwiązanie wymaga zbyt wiele pracy, to zawsze będziesz mógł umieścić kod tworzący nagłówki i stopki wydruku wewnątrz metody OnDraw.

Modyfikowanie paska narzędzi okna podglądu wydruku

Jeśli będziesz chciał zmodyfikować postać standardowego paska narzędzi wyświetlanego w oknie podglądu wydruku, to będziesz musiał przesłonić standardową definicję meto­dy OnFilePrintPreview. Poniżej przedstawione zostały czynności, który będziesz mu­siał wykonać:

1. Umieścić makro ON_COMMAND zawierające metodę OnFilePrintPreview

wewnątrz części mapy komunikatów zarządzanej przez kreatora Class Wizard.

2. Z wywołania metody OnFilePrintPreyiew usunąć modyfikator zawierający nazwę klasy bazowej (CView).

3. Dodać własną definicję metody OnFilePrintPreview (do pliku nagłówkowego - H i źródłowego - CPP).

4. Wewnątrz metody OnFilePrintPreview stworzyć na stercie (za pomocą operatora New) nowy obiekt klasy CPrintPreviewState.

5. Wywołać metodę DoPrintPreyiew, przekazując jako argumenty jej wywołania następujące dane: identyfikator zasobu określającego postać paska narzędzi, wskaźnik RUNTIME_CLASS(CPrintView) oraz obiekt, który stworzyłeś w poprzednim kroku.

6. Jeśli metoda DoPrintPreview zwróci wartość FALSE, to powinieneś wyświetlić komunikat o błędzie i usunąć obiekt stworzony w kroku 4.

7. Upewnić się, że do pliku źródłowego Twojego nowego widoku dołączyłeś plik nagłówkowy AFXPRIV.H.

8. Stworzyć zasób określający postać paska narzędzi i nadać mu ten sam identy­fikator, którego użyłeś w kroku 5. (określanie postaci paska narzędzi możesz rozpocząć od skopiowania oryginalnego paska, który możesz znaleźć w pliku AFXPRINT.RC znajdującym się w kartotece MFCMNCLUDE).

Gdy zakończysz pracę, to Twoja mapa komunikatów powinna mieć następującą postać:

BEGIN_MESSAGE_MAP (CConndotYiew, CView)

// { {AFX_MSG_MAP (CConndotView)

ON_WM_LBUTTONDOWN ( )

ON_WM_RBUTTONDOWN ( )

ON_COMMAND ( ID_FILE_PRINT_PREVIEW, OnFilePrintPreview)

//}}AFX_MSG_MAP

//Standard printing corranents

ON_COMMAND(ID„FILE_PRINT, CView: : OnFilePrint )

ON_COMMAND(ID„FILE_PRINT_DIRECT, CView: : OnFilePrint ) END_MESSAGE_MAP ( )

Poniżej przedstawiona została postać Twojej wersji metody OnFilePrintPreview:

void CMyYiew: : OnFilePrintPreview ( ) {

CPrintPreviewState* pState = new CPrintPreviewState; if ( !DoPrintPreview(AFX_IDD_PREVIEW_TOOLBAR, this,

RUNTIME_CLASS ( CPreviewView) , pState) ) {

// domyślny komunikat o błędzie Af xMessageBox (AFX_IDP_COMMAND_FAILURE) ; delete pState;

Kompletny przykład prezentujący opisaną tutaj metodę modyfikowania paska narzędzi okna podglądu wydruku możesz znaleźć na Listingu 3.2.

Modyfikowanie działania podglądu wydruku

Opisane powyżej czynności służące do zmodyfikowania postaci paska narzędzi okna podglądu wydruku, mogą zostać użyte także do całkowitego zmienienia wyglądu oraz sposobu działania okna podglądu. Jednakże w takim przypadku będziesz musiał utworzyć swoją własną klasę, wyprowadzoną z klasy CPrevłewView i użyć jej nazwy jako trzeciego argumentu wywołania metody DoPrintPreview. Przykład zastosowania tej metody przedstawiony został na Listingu 3.3.



Wyszukiwarka

Podobne podstrony:
03 38 zmiana wzorów druków informacji dotyczących bezzbi (1)
03 37 wzory druków informacji o bezzbiornikowym magazyn (1)
2004 03 Wykonanie płytek drukowanych w warunkach domowych, część 1
2003 03 Wykonywanie płytek drukowanych w warunkach domowych
03 Sejsmika04 plytkieid 4624 ppt
03 Odświeżanie pamięci DRAMid 4244 ppt
podrecznik 2 18 03 05
od Elwiry, prawo gospodarcze 03
Probl inter i kard 06'03
TT Sem III 14 03
03 skąd Państwo ma pieniądze podatki zus nfzid 4477 ppt
03 PODSTAWY GENETYKI
Wyklad 2 TM 07 03 09
03 RYTMY BIOLOGICZNE CZŁOWIEKAid 4197 ppt

więcej podobnych podstron