Rozdział 13.
Zagadnienia nieco bardziej skomplikowane
W rozdziale tym omówione zostaną zagadnienia, które pozwolą jeszcze bardziej udoskonalić tworzone aplikacje - między innymi:
Dekoracje okna: paski narzędzi i paski stanu
Udostępnianie poleceń
Drukowanie w aplikacjach Delphi
Użytkowanie kursorów
Rozważania te są kontynuowane w następnym rozdziale, w którym omawiane jest wykorzystanie zaawansowanych cech programowania dla Windows w aplikacjach Delphi.
Tworzenie dekoracji okna
Nie chodzi tu bynajmniej o ozdobne światełka wokół frontowego okna domu, lecz o takie elementy jak paski narzędzi i paski stanu, nazywane często dekoracjami okna. Sekcja ta dotyczy właśnie takich dekoracji, a także sposobów implementowania ich w aplikacjach.
Paski narzędzi
Obecnie paski narzędzi (zwane również paskami sterującymi lub paskami przyśpieszającymi(ang. speedbars) ) są niemal standardowym wyposażeniem programów przeznaczonych dla Windows. Użytkownicy oczekują określonych ułatwień i pasek narzędzi jest właśnie jednym z nich. Wysokiej jakości pasek narzędzi powinien posiadać następujące cechy i możliwości:
Przyciski reprezentujące określone zadania, które można również wywołać z menu aplikacji.
Możliwość uaktywniania i dezaktywowania przycisków w miarę potrzeb.
Podpowiedzi opisujące funkcje przycisków.
Dodatkowe teksty pomocy wyświetlane na pasku stanu aplikacji.
Możliwość dokowania.
Inne kontrolki, takie jak listy rozwijalne lub przyciski rozwijalnych menu.
Niektóre z tych cech są opcjonalne i nie muszą występować na każdym pasku. W przypadku Delphi zaimplementowanie powyższych elementów jest operacją łatwą do przeprowadzenia. Nieco później w tym rozdziale omówione zostaną zasady udostępniania poleceń (sekcja „Udostępnianie poleceń”); dyskusję na temat udostępniania przycisków paska narzędzi również odłożę do tego momentu.
|
Za dobrą praktykę uważa się umieszczanie na pasku narzędzi tylko takich przycisków, które posiadają odpowiadające im opcje w menu. Pasek narzędzi pomyślany jest jako alternatywa dla opcji menu, nie powinien więc zawierać elementów w tym menu nieobecnych. |
W rozdziale 8. „Tworzenie aplikacji w Delphi” padło stwierdzenie, iż najprostszym sposobem skonstruowania paska narzędzi jest użycie Kreatora Aplikacji. Jego użycie jest możliwe nawet wtedy, gdy aplikacja zostanie już częściowo napisana. Wystarczy wygenerować nową aplikację, skopiować panel z paskiem narzędzi do Schowka, otworzyć aplikację nad którą pracujemy (nie ma potrzeby zapisywania stworzonej przed chwilą aplikacji) i wkleić do niej pasek przechowywany w Schowku. Zręcznie i prosto.
Niemniej jednak Kreator Aplikacji nie zapewnia wszystkich elementów, których obecności należałoby się spodziewać na pasku. Korzysta on ze starej metody tworzenia pasków narzędzi - zawierających przyciski przełączające i przyśpieszające. Bardziej polecaną metodą tworzenia pasków narzędzi jest użycie komponentów ToolBar i CoolBar (rezydujących na stronie Win32 Palety Komponentów). Komponentom tym przyjrzymy się już za chwilę.
|
Komponenty CoolBar i ToolBar wymagają pliku biblioteki COMCTL32.DLL w wersji 4.72.2106.4 lub nowszej, która to biblioteka powinna zostać dołączona w ramach instalacji Delphi. Jeżeli jednak nie dysponujesz najnowszą wersją tej biblioteki, możesz znaleźć ją na stronie WWW firmy Microsoft (http://www.microsoft.com). W przypadku rozpowszechniania aplikacji korzystających z tych komponentów należy instalować bibliotekę COMCTL32.DLL w wersji 4.70 lub nowszej. Do instalacji najlepiej użyć dobrego programu instalacyjnego, aby podczas instalacji swojej aplikacji nie dopuścić do zastąpienia nowej wersji biblioteki wersją starszą. |
Komponent CoolBar
Komponent CoolBar jest odpowiednikiem kontrolki Win32 o nazwie cool bar. Jest to specjalna kontrolka-pojemnik. W większości przypadków CoolBar służy jako pojemnik dla pasków narzędzi, ale jego użycie nie jest ściśle ograniczone do tego typu obiektów.
Wstęgi komponentu CoolBar
CoolBar składa się z tzw. wstęg (ang. bands), które mogą być przesuwane i skalowane w trakcie pracy aplikacji. Po lewej stronie każdej wstęgi znajduje się uchwyt dający użytkownikowi wizualną wskazówkę co do możliwości jej przesuwania i skalowania. Wstęgi reprezentowane są przez klasę TCoolBand. Wstęga może zawierać w sobie tylko jeden komponent, którym zazwyczaj jest pasek narzędzi, ale może to być również lista rozwijalna lub dowolny inny komponent. Żeby lepiej zrozumieć zasady działania komponentu CoolBar wykonaj proste ćwiczenie:
Rozpocznij nową aplikację, w jej formularzu umieść komponent CoolBar.
W komponencie CoolBar umieść komponent ComboBox. W wyniku tej operacji Delphi stworzy nową wstęgę i w niej umieści obiekt listy; zauważ, że lista zajmuje całą szerokość wstęgi.
Umieść kolejny komponent ComboBox w obszarze komponentu CoolBar. Utworzona zostanie kolejna wstęga, w której wnętrzu znajdzie się drugi obiekt listy.
Umieść kursor między uchwytem skalowania, a obiektem listy w drugiej wstędze. Kursor zmieni swój kształt informując w ten sposób o możliwości przemieszczenia panelu. (Do przesunięcia panelu można również użyć uchwytu skalowania.) Przeciągnij wstęgę w kierunku tej znajdującej się bezpośrednio nad nią. W trakcie przesuwania pierwsza wstęga zmniejszy się, aby zrobić miejsce dla tej aktualnie przemieszczanej. Puść ją mniej więcej w środku komponentu CoolBar. Teraz można zmienić rozmiar dowolnej ze wstęg, chwytając jej uchwyt skalowania.
Umieść w komponencie CoolBar komponent Panel. Ponownie utworzona zostanie nowa wstęga, w której znajdzie się nowo dodany panel.
Wybierz komponent CoolBar i zmień właściwość AutoSize na wartość True.
Umieść w formularzu (poniżej komponentu CoolBar) komponent Memo, a następnie ustaw jego właściwość Align na wartość alClient.
W tej chwili formularz powinien wyglądać tak, jak przedstawia to rysunek 13.1.
Uruchom teraz swoją aplikację. Poeksperymentuj ze wstęgami przesuwając je w górę i w dół lub zmieniając ich rozmiar. Zauważ że w miarę poruszania wstęg CoolBar zmienia swój rozmiar dopasowując się w miarę potrzeb, a obiekt Memo zawsze wypełnia pozostałą przestrzeń obszaru użytkownika.
Rysunek 13.1. Formularz zawierający komponent CoolBar i trzy wstęgi |
|
Dostęp do wstęg umożliwia właściwość Bands. Jest to obiekt klasy TCoolBands będący listą komponentów TCoolBand. Poniższa instrukcja ukrywa drugą wstęgę:
CoolBar.Bands[1].Visible:=False;
Istnieją dwa sposoby dodawania wstęg. Pierwszy, przedstawiony przed chwilą, polega na umieszczeniu dowolnego komponentu we wnętrzu komponentu CoolBar. Drugi sposób opiera się o Edytor Wstęg. Wywołanie tego edytora wymaga dwukrotnego kliknięcia na komponencie CoolBar lub kliknięcia na przycisku z wielokropkiem, umieszczonym obok właściwości Bands w oknie Inspektora Obiektów. Przycisk Add służy do dodawania nowych wstęg, a przycisk Delete - do ich usuwania. Przyciski Move Up i Move Down umożliwiają zmianę kolejności wstęg.
|
Kiedy właściwość AutoSize jest ustawiona na wartość True, trzeba ją tymczasowo wyłączyć, jeżeli chcemy dodawać nowe wstęgi metodą upuszczania obiektów nad komponentem CoolBar. Ustaw właściwość AutoSize na False, powiększ komponent CoolBar, umieść w nim wybrany komponent, a następnie ponownie nadaj wartość True właściwości AutoSize. |
Po wybraniu wstęgi w oknie Edytora Wstęg jej właściwości wyświetlane zostają w oknie Inspektora Obiektów (rysunek 13.2).
Właściwość Bitmap określa tło wstęgi. Do ustawienia rysunku, który pojawi się po lewej stronie wstęgi, służy właściwość ImageIndex. ImageIndex wymaga, aby właściwość ImageList komponentu CoolBar była ustawiona na poprawny obiekt klasy TImageList. Do ustawienia minimalnej wysokości i szerokości służą pola - odpowiednio - MinHeight i MinWidth. Po ustawieniu właściwości FixedSize na wartość True wstęga staje się nieruchoma.
Rysunek 13.2. Edytor Wstęg |
|
Inne właściwości komponentu CoolBar
CoolBar może przyjmować położenie pionowe albo poziome. Decyduje o tym właściwość Align - jej domyślną wartością jest alTop, co odpowiada ułożeniu poziomemu; żeby uczynić CoolBar paskiem pionowym, trzeba zmienić tę wartość na alRight lub alLeft. Niektóre komponenty po umieszczeniu w obszarze CoolBar, automatycznie dopasowują się do jego aktualnej orientacji.
Innym sposobem zmiany orientacji komponentu Coolbar jest nadanie właściwości Vertical wartości True.
Do ustawienia tła komponentu CoolBar służy właściwość Bitmap. Wybrana bitmapa zostanie powielona tak, aby wypełnić cały obszar tła komponentu. Zauważ, że chodzi tu tło obiektu CoolBar, a nie jakiejkolwiek indywidualnej wstęgi w nim umieszczonej (jak miało to miejsce w poprzedniej sekcji). Właściwość ImageList służy do ustawienia listy rysunków, z której to listy korzystają wstęgi przy wyświetlaniu rysunków (poczynając od lewej strony) - o ile dana wstęga ma ustawioną właściwość ImageIndex.
Właściwość AutoSize określa, czy komponent CoolBar będzie miał prawo rozszerzać się w trakcie przemieszczania wstęg. Efekt działania właściwości AutoSize był widoczny w poprzednim ćwiczeniu.
|
Wypróbuj komponent TControlBar umieszczony na stronie Additional Palety Komponentów. TControlBar jest rodzimym komponentem biblioteki VCL i przypomina swym który działaniem komponent TcoolBar, w odróżnieniu od niego nie jest jednak zależny od biblioteki COMCTL32. DLL, co czyni go mniej podatnym na kaprysy firmy Microsoft. |
Komponent ToolBar
Komponent ToolBar odpowiada kontrolce Win32 o tej samej nazwie. Komponent ToolBar automatycznie dopasowuje rozmiar umieszczanych w nim kontrolek, dzięki czemu wszystkie one mają jednakową wysokość. ToolBar może być stosowany razem z komponentem CoolBar lub bez niego. Jeżeli dysponujesz tylko jednym paskiem narzędzi, stosuj wyłącznie komponent ToolBar. Jeżeli posiadasz kilka pasków narzędzi i chcesz dać użytkownikowi możliwość ich przemieszczania, użyj komponentu CoolBar umieszczając te paski w jego wnętrzu.
Utworzenie paska narzędzi i dodanie do niego przycisków jest operacją prostą. Jeżeli na przyciskach budowanego paska będą umieszczane ikony (a na większości z nich są one umieszczane), trzeba będzie skorzystać z komponentu ImageList. Aby zilustrować sposób budowy paska narzędzi przy użyciu komponentu ToolBar, wróćmy ponownie do programu ScratchPad. Rozbierzemy go na części i złożymy z powrotem.
Usunięcie istniejącego paska
Jeżeli pamiętasz, oryginalny pasek narzędzi stworzony dla programu ScratchPad nie pełnił żadnej funkcji poza zajmowaniem miejsca. Pierwszą rzeczą, jaką musisz wykonać, jest pozbycie się dotychczasowego paska narzędzi. Zrób to w następujący sposób:
Kliknij na komponencie Memo, a następnie zmień jego właściwość Align na wartość alNone. Przesuń górną część tego komponentu, aby zrobić miejsce dla nowego paska narzędzi.
Kliknij na komponencie paska narzędzi i usuń go.
Dodawanie nowego paska narzędzi
Teraz możesz przystąpić do dodawania komponentów. Po pierwsze trzeba dodać dwa komponenty CoolBar i ToolBar. W tej chwili komponent CoolBar nie jest tak naprawdę potrzebny, ponieważ zajmujesz się tylko jednym paskiem narzędzi, ale być może kiedyś będziesz chciał dodać kolejne, dlatego lepiej jest planować na przyszłość. Wykonaj poniższe kroki:
Dodaj do formularza komponent CoolBar; zostanie on automatycznie ulokowany wzdłuż górnej krawędzi formularza. Zmień jego właściwość Name na CoolBar.
Umieść w komponencie CoolBar komponent ToolBar. Zmień jego właściwość Name na MainToolBar.
W oknie Inspektora Obiektów kliknij podwójnie na właściwości EdgeBorders aby wyświetlić wszystkie elementy obramowania. Zmień styl ebTop na wartość False (wszystkie style właściwości EdgeBorders powinny teraz posiadać wartość False).
Zmień właściwość Flat na wartość True, dzięki czemu przyciski paska narzędzi będą posiadały płaski wygląd do momentu, kiedy znajdzie się nad nimi kursor.
Kliknij na komponencie CoolBar i ustaw jego właściwość AutoSize na wartość True. CoolBar dopasuje swój rozmiar do rozmiaru paska narzędzi.
Wybierz komponent Memo i zmień jego właściwość Align na wartość alClient.
Dodawanie przycisków do paska narzędzi
Teraz można rozpocząć dodawanie przycisków; na początku przyciski będą bez ikon, ale tym zajmiemy się później. Na razie wykonaj następujące kroki:
Kliknij prawym przyciskiem na pasku narzędzi i wybierz polecenie New Button. Na pasku umieszczony zostanie nowy przycisk - zmień jego właściwość Name na FileNewBtn. Właściwości Hint przypisz tekst Nowy | Utwórz nowy plik, a właściwości ShowHint nadaj wartość True. (W rozdziale 8. napisałeś kod obsługujący mechanizm pomocy kontekstowej. Kod ten dalej znajduje się w programie, dlatego nowe elementy pomocy kontekstowej będą działać niezwłocznie.)
Ponownie kliknij prawym przyciskiem na pasku narzędzi i wybierz polecenie New Button. Za pierwszym przyciskiem paska narzędzi pojawi się kolejny. Zmieć jego właściwość Name na FileOpenBtn. Właściwości Hint przypisz tekst Otwórz | Otwórz istniejący plik i ustaw ShowHint na True.
Dodaj kolejny przycisk. Zmieć właściwość Name na FileSaveBtn. Ustaw właściwość Hint na Zapisz | Zapisz plik, a ShowHint na True.
|
Przyciski i separatory dodawane do paska narzędzi pojawiają się zawsze na prawo od ostatnio dodanego elementu. Nie ma możliwości umieszczenia przycisku w dowolnym punkcie paska narzędzi, ale po jego dodaniu można przeciągnąć go w inne miejsce. Istniejące przyciski zrobią miejsce dla nowego. |
W tym miejscu kończy się dodawanie pierwszego zestawu przycisków (z wyjątkiem ikon). Za chwilę przejdziesz do utworzenia drugiego zestawu, ale wcześniej trzeba wstawić separator, który oddzieli pierwszy zestaw przycisków od drugiego:
Ponownie kliknij prawym przyciskiem na pasku narzędzi, ale tym razem wybierz polecenie New Separator. Do paska zostanie dodany separator.
Dodaj kolejny przycisk. Zmień jego właściwość Name na EditCutBtn, właściwość Hint na Wytnij | Wytnij do Schowka.
Dodaj przyciski dla poleceń Kopiuj i Wklej. Zmień odpowiednio ich właściwości Name i Hint.
Dodaj kolejny separator.
Dodaj przycisk o nazwie HelpAboutBtn. Zmień jego właściwość Hint na O programie | O programie ScratchPad.
Wybierz przyciski Wytnij, Kopiuj, Wklej i Pomoc (użyj kombinacji Shift+Click). Zmień wartość właściwości ShowHint na True - właściwość ta zostanie ustawiona dla wszystkich wybranych przycisków.
Twój formularz powinien wyglądać tak jak na rysunku 13.3.
Rysunek 13.3. Główny formularz programu ScratchPad po dodaniu paska narzędzi |
|
Przypisywanie funkcji przyciskom paska narzędzi
Masz przed sobą dobrze wyglądający pasek narzędzi, który nie pełni jednak żadnej konkretnej roli. Dzieje się tak, ponieważ do zdarzeń OnClick przycisków nie zostały przypisane procedury obsługujące to zdarzenie. Zajmiemy się tym w następnej kolejności:
Kliknij na pierwszym przycisku paska (FileNewBtn) i wybierz zakładkę Events okna Inspektora Obiektów. Kliknij na przycisku listy rozwijalnej obok zdarzenia OnClick i wybierz FileNewClick. Od tego momentu przycisk jest związany z procedurą o tej nazwie.
Powtórz pierwszy krok dla każdego z pozostałych przycisków, zwracając uwagę na wybór prawidłowej procedury zdarzeniowej (FileOpenClick, FileSaveClick, EditCutClick itd.)
Jeżeli nie stworzyłeś jeszcze okna informacji o programie (About Box) dla programu ScratchPad, zrób to teraz. Kiedy skończysz, stwórz nową procedurę zdarzeniową dla elementu menu HelpAbout. Połącz zdarzenie OnClick przycisku HelpAboutBtn z procedurą obsługującą zdarzenie polecenia menu HelpAbout.
Dodawanie bitmap do przycisków
Wyraźnie widać, że pasek narzędzi jest niekompletny - przyciski trzeba zaopatrzyć w reprezentujące je ikony. W tym celu do formularza trzeba dodać komponent ImageList:
Umieść w formularzu komponent ImageList (w Palecie Komponentów znajduje się on na stronie Win32). Zmień jego właściwość Name na ImageList.
Otwórz menu kontekstowe komponentu ImageList umieszczonego w formularzu (klikając prawym przyciskiem na jego ikonie) i wybierz polecenie ImageList Editor. (Ten sam efekt można uzyskać klikając dwukrotnie na ikonie komponentu.)
Kliknij na przycisku Add. Przejdź do katalogu Common Files\Borland Shared\ Images\Buttons. Wybierz plik FILENEW.BMP i kliknij na przycisku Open.
Na ekranie pojawi się zapytanie, czy chcesz podzielić bitmapę na dwa oddzielne rysunki. Chodzi tutaj o to, że właściwości Width i Height listy rysunków są ustawione na wartość 16 pikseli. Wybrana bitmapa jest szersza od 16 pikseli, dlatego musi być podzielona na dwie części lub pomniejszona, aby zmieścić się w określonych granicach. Jak pamiętasz, bitmapy przycisków dostarczone razem z Delphi są w rzeczywistości pojedynczymi bitmapami zawierającymi dwa rysunki. Pierwszy rysunek dotyczy normalnego stanu przycisku, natomiast drugi - stanu dezaktywacji. Musisz nakazać edytorowi podzielnie bitmapy na dwie części, a następnie usunąć drugą z nich.
Podziel bitmapę na dwie części wybierając przycisk Yes. W oknie edytora komponentu ImageList wyświetlane są teraz dwie bitmapy. Tobie potrzebna jest tylko pierwsza z nich - wybierz więc drugą (przedstawiającą ikonę przycisku nieaktywnego) i kliknij na przycisku Delete.
Ponownie wybierz przycisk Add. Tym razem otwórz plik o nazwie FILEOPEN.BMP. Podziel bitmapę na dwie części (przycisk Yes) - wybierz ikonę przycisku nieaktywnego i usuń ją. Okno edytora, tuż przed usunięciem drugiej ikony, jest widoczne na rysunku 13.4.
Rysunek 13.4. Edytor komponentu ImageList po dodaniu trzech rysunków |
|
Powtórz krok 5 dla pozostałych przycisków (Zapisz, Wytnij, Kopiuj, Wklej i O programie). Możesz użyć dowolnych bitmap, ale po każdym dodaniu nowej ikony usuwaj drugą z nich. Zwróć również uwagę na to, aby rysunki dodawane do komponentu ImageList były uporządkowane według kolejności występowania przycisków w pasku narzędzi. W efekcie na liście powinno znaleźć się siedem bitmap ponumerowanych od 0 do 6.
Zamknij okno edytora przyciskiem OK.
|
Okno Add Image (wywoływane przyciskiem Add) umożliwia jednoczesny wybór wielu plików i dodanie zawartych w nich bitmap do listy rysunków. |
Teraz listę rysunków można dodać do paska narzędzi. Kliknij na pasku narzędzi. Zlokalizuj właściwość Images w oknie Inspektora Obiektów, z listy rozwijalnej wybierz komponent ImageList. Jeżeli wykonałeś wszystko poprawnie, na przyciskach powinny pojawić się ikony. Prawdopodobnie nie zauważyłeś tego, ale podczas dodawania przycisku do paska narzędzi Delphi automatycznie zwiększa wartość jego właściwości ImageIndex. Ponieważ zarówno przyciski, jak i rysunki tworzone były w tym samym porządku, ikony przycisków powinny być ułożone poprawnie. Jeżeli wygląd przycisku jest nieprawidłowy, można zmienić wartość jego właściwości ImageIndex lub wrócić do edytora komponentu ImageList i zmienić uporządkowanie rysunków na liście.
|
Zmiany uporządkowania rysunków na liście edytora komponentu ImageList dokonuje się metodą „przeciągnij i upuść” (drag-and-drop). |
Ikony przycisków nieaktywnych
W obecnej chwili dysponujesz jedynie ikonami przycisków aktywnych. Potrzebne są również ikony, które będą wyświetlane na przyciskach w czasie ich nieaktywności. Nie zajmowaliśmy się do tej pory dezaktywacją przycisków, ale przyjdzie na to jeszcze pora przed końcem tego rozdziału. Istnieją dwa sposoby zaimplementowania ikon przycisków nieaktywnych:
Zezwolenie paskowi narzędzi na automatyczne tworzenie ikon nieaktywnego stanu przycisków.
Stworzenie drugiej listy rysunków, która zawierać będzie ikony stanu nieaktywnego dla wszystkich przycisków.
Oczywiście prostszą z tych dwóch metod jest pozwolenie paskowi na automatyczne tworzenie ikon stanu nieaktywnego przycisków. W większości przypadków metoda ta jest wystarczająca. Czasami jednak algorytm tworzący ikony stanu nieaktywnego działa niezadowalająco. (W końcowej postaci ikona zatraca swoją pierwotną postać i wygląda źle.) Dzieje się tak, gdy przyciski zawierają za mało kontrastujących kolorów. Rozwiązaniem jest stworzenie drugiej listy rysunków reprezentujących ikony przycisków nieaktywnych. Listę tą wystarczy przypisać właściwości paska narzędzi o nazwie DisabledImages, reszta dzieje się automatycznie. W przypadku programu ScratchPad zastosujesz automatyczne tworzenie ikon stanu nieaktywnego, stąd żadne dodatkowe operacje nie są wymagane.
Program ScratchPad został ponownie złożony w całość. Jest to dobra okazja, aby zachować projekt. Kiedy to zrobisz, uruchom program i sprawdź jak działa. Kliknij na poszczególnych przyciskach, sprawdź czy wykonują one zadania, do jakich są przypisane. Jeżeli wszystko poszło dobrze, ponownie masz przed sobą działający program. Jeżeli program nie będzie chciał się skompilować, przejdź ponownie przez wszystkie kroki i spróbuj usunąć przyczynę błędu. Kiedy wszelkie próby zawiodą, możesz odwołać się do projektu ScratchPad przechowywanego razem z całym kodem źródłowym przeznaczonym dla tej książki.
Podpowiedzi i pomoc kontekstowa dla paska narzędzi
Temat podpowiedzi i pomocy kontekstowej został niemal dogłębnie wyczerpany podczas rozważań na temat komponentów w rozdziale siódmym „Komponenty VCL”, następnie w rozdziale ósmym „Tworzenie aplikacji w Delphi”, podczas dodawania pomocy kontekstowej do programu ScratchPad i jeszcze raz w tym rozdziale podczas przebudowy paska narzędzi.
Pominięte zostało jedno zagadnienie dotyczące zmian właściwości podpowiedzi. Klasa TApplication posiada cztery właściwości, które wpływają na sposób zachowania się podpowiedzi. Ich opis znajduje się w tabeli 13.1.
Tabela 13.1. Właściwości klasy TApplication związane z podpowiedziami
Właściwość |
Opis |
HintColor |
Określa kolor tła okna podpowiedzi. Wartość domyślna: clInfoBk |
HintHidePause |
Definiuje okres czasu (w milisekundach) po jakim podpowiedź zostanie ukryta, jeżeli kursor pozostanie nieruchomy nad komponentem. Wartość domyślna: 2500 milisekund. |
HintPause |
Definiuje okres czasu (w milisekundach) jaki mija od momentu zatrzymania kursora nad komponentem do chwili wyświetlenia podpowiedzi. Wartość domyślna: 500 milisekund. |
HintShortPause |
Definiuje czas oczekiwania przed pokazaniem podpowiedzi, jeżeli były one już wyświetlone - np., kiedy użytkownik wędruje po grupie przycisków paska narzędzi. Wartość domyślna: 50 milisekund. |
Większości aplikacji w pełni odpowiadają ustawienia domyślne, ale zawsze istnieje możliwość ich zmiany.
|
Podstawowe zasady: Paski narzędzi i pomoc kontekstowa
|
Dodawanie innych komponentów do paska narzędzi
Ze względu na niezwykłą wszechstronność komponentu ToolBar nie trzeba wykonywać żadnych szczególnych operacji, aby móc dodawać inne typy komponentów do paska narzędzi. Komponentem najpowszechniej stosowanym w paskach narzędzi jest lista rozwijalna (obiekt combo). Może ona służyć do wyboru czcionki, opcji konfiguracyjnej, ustawienia powiększenia itp. - możliwości są nieograniczone.
Aby dodać komponent do paska narzędzi, wystarczy wybrać go z Palety Komponentów i umieścić w obszarze paska. Pasek narzędzi automatycznie rozmieści nowo dodany komponent. W miarę potrzeby można użyć separatorów, aby wizualnie oddzielić komponenty. Kiedy komponent znajdzie się już na pasku, można traktować go tak, jakby był umieszczony w formularzu. Stwierdzenie to można byłoby przedstawić w sposób bardziej skomplikowany, ale nie ma w tym żadnego celu, ponieważ proces ten jest naprawdę prosty. Jeżeli do tej pory nie próbowałeś zaimplementować listy rozwijalnej na pasku narzędzi za pomocą biblioteki Windows API, nie jesteś w stanie ocenić, jak wiele pracy oszczędza Ci Delphi. Uwierz na słowo, że jest to spora oszczędność.
Paski narzędzi przyjmują wiele kształtów i rozmiarów, ale ich implementacja jest znacznie ułatwiona dzięki Delphi. Korzystając z Delphi nie będziesz mógł sobie pozwolić na wymówkę, że coś jest za trudne. W rzeczywistości tworzenie pasków narzędzi przy użyciu Delphi może być nawet niezwykle przyjemne.
Dokowalne paski narzędzi
Dokowalne paski narzędzi są powszechnie stosowane w wielu aplikacjach Windows. Są one również pewnego rodzaju paradoksem. Z jednej strony paski takie są doskonałą cechą, której obecności w aplikacjach oczekuje większość użytkowników. Z drugiej strony wątpliwe jest, aby ktokolwiek rzeczywiście wykorzystywał możliwości dokowania większości pasków narzędzi (posiadających tę cechę). Ponieważ jednak dokowalne paski narzędzi są łatwe do zaimplementowani, Ty również możesz udostępnić je w swoich aplikacjach.
|
Zagadnienie dokowania omawiane w tej sekcji odnosi się do wszelkich obiektów kontrolnych typu okienkowego, nie tylko do pasków narzędzi. |
Tworzenie dokowalnego paska narzędzi
Uczynienie paska dokowalnym wymaga dwóch kroków:
Ustawienia właściwości DragKind na wartość dkDock.
Ustawienia właściwości DragMode na wartość dmAutomatic.
Ustawienie tych dwóch właściwości umożliwia przeciąganie paska po ekranie. Sama ta możliwość nie daje jednak wiele; aby dokowalne paski narzędzi miały sens, trzeba ustawić cel ich upuszczenia.
Miejsca dokowania
Dokowalny pasek narzędzi musi posiadać miejsce do którego może zostać zadokowany. Jak powiedział Roger Waters „Każdy głupiec wie, że pies potrzebuje schronienia”; schronieniem dla dokowalnego paska narzędzi jest miejsce dokowania (ang. dock site). Miejscem dokowania jest każdy komponent typu okienkowego, którego właściwość DockSite jest ustawiona na wartość True. Komponentami, które zazwyczaj służą jako miejsca dokowania, są TCoolBar, TControlBar, TPageScroller, TPanel i TPageControl. Istnieją również inne komponenty posiadające właściwość DockSite, ale są one raczej rzadko stosowane jako miejsca dokowania.
Poniższy przykład pomoże lepiej zilustrować zastosowanie miejsc dokowania. Postępuj według poniższych kroków:
Do pustego formularza wstaw komponent CoolBar. Ustaw jego właściwość DockSite na wartość True.
Do komponentu CoolBar wstaw komponent ToolBar. Ustaw właściwość DragKind paska narzędzi na wartość dkDock, a właściwość DragMode na wartość dmAutomatic. Stwórz kilka przycisków paska narzędzi, abyś mógł go lepiej widzieć.
Wstaw kolejny komponent CoolBar do formularza. Zmień jego właściwość Align na wartość alBottom, a właściwość DockSite na True.
Umieść trzeci komponent CoolBar w komponencie. Ustaw jego właściwość Align na wartość alLeft i właściwość DockSite na wartość True. Zmień rozmiar komponentu tak, aby jego szerokość wyniosła około 40 pikseli.
Teraz uruchom swój program. Przeciągnij pasek narzędzi z jednego miejsca dokowania do innego. Zauważ zmianę w orientacji paska po przeciągnięciu go w obszar komponentu CoolBar po lewej stronie formularza.
Poeksperymentuj jeszcze przez chwilę. Ustaw właściwość AutoSize każdego z komponentów CoolBar na wartość True. Spowoduje to zmianę rozmiarów każdego z komponentów CoolBar, zależną od przechowywanych w nim komponentów. Uruchom ponownie swój program i przemieść pasek narzędzi do kolejnych miejsc dokowania. Zauważ, że każdy z komponentów CoolBar jest ledwo widoczny do momentu, kiedy nie zostanie w nim zadokowany pasek narzędzi. Wtedy CoolBar rozszerza się, aby pomieścić w sobie cały komponent.
Swobodne paski narzędzi
Uczynienie paska narzędzi paskiem swobodnym polega na przeciągnięciu go poza miejsce dokowania i upuszczenia gdziekolwiek (pod warunkiem, że nie będzie to inny obszar dokujący). Po tej operacji pasek narzędzi stanie się swobodnym oknem. Nadając właściwości FloatingDockSiteClass nazwę klasy-rodzica swobodnego paska narzędzi można określić typ okna, które służyć będzie jako swobodne miejsce dokowania. Załóżmy przykładowo, że zaprojektowałeś formularz o nazwie MyToolBox, który posiada wszystkie niezbędne cechy użytkowego okna narzędziowego. Żeby uczynić z tego okna właściciela swobodnego paska narzędzi należy posłużyć się następującym przypisaniem:
ToolBar.FloatingDockSiteClass:= TMyToolBox;
Delphi automatycznie utworzy obiekt klasy TMyToolBox i umieści w nim pasek narzędzi, jeśli tylko będzie on niezadokowany, a przyciski myszy zwolnione. Żeby ponownie zadokować pasek, wystarczy upuścić go nad dowolnym miejscem dokowania. Aby miejsce dokowania przyjęło swobodny pasek narzędzi, trzeba odpowiednio zareagować na zdarzenia OnDockOver i OnDockDrop. Wywołanie metody ManualDock paska narzędzi we wnętrzu procedury obsługującej zdarzenie OnDockDrop spowoduje jego zadokowanie.
Paski stanu
Pasek stanu to kolejna cecha, która zapewnia aplikacjom większą atrakcyjność. Wiele aplikacji korzysta z pasków stanu - chociaż są oczywiście takie, które się bez nich obywają. Komponent biblioteki VCL StatusBar, reprezentujący kontrolkę paska stanu biblioteki Win32, znacznie ułatwia tworzenie pasków tego typu. Przyjrzyjmy się na początek najważniejszym właściwościom tego komponentu, zestawionym w tabeli 13.2.
Tabela 13.2. Właściwości komponentu StatusBar
Właściwość |
Opis |
AutoHint |
Kiedy kursor myszy przemieszcza się nad dowolnym komponentem, dla którego ustawiona została właściwość Hint, na pasku stanu automatycznie wyświetlany jest tekst pomocy kontekstowej. |
Panels |
Dotyczy pasków stanu z wydzielonymi panelami. Właściwość ta definiuje indywidualne panele paska. |
SimplePanel |
Określa, czy pasek stanu wyświetla prosty panel, czy zbiór paneli. |
SimpleText |
Tekst przeznaczony dla prostego panelu paska stanu. |
SizeGrip |
Określa, czy w prawym dolnym rogu paska stanu wyświetlany będzie uchwyt zmiany rozmiaru. Jest to mały obszar, który można uchwycić i przeciągnąć, zmieniając w ten sposób rozmiar okna. Brak uchwytu nie jest przeszkodą w modyfikowaniu rozmiaru okna, ale jego obecność ułatwia tę operację. |
UseSystemFont |
Wymusza stosowanie bieżącej czcionki systemowej, bez względu na ustawienia właściwości Font. Właściwość ta przydaje się tym użytkownikom, którzy korzystają z zestawów tematycznych (pack themes) pakietu Plus. |
Pasek prosty czy złożony?
Jak wynika z powyższej tabeli, pasek stanu może być komponentem prostym lub składać się z wielu obszarów. Prosty pasek stanu posiada tylko jeden panel zajmujący całą jego długość. Prosty pasek uzyskuje się nadając właściwości SimplePanel wartość True. SimplePanel działa jak przełącznik, dzięki któremu można przełączać się między prostym, a złożonym paskiem stanu (w trakcie pracy programu) przez zwykłą zmianę wartości tej właściwości na - odpowiednio - True lub False.
Złożony pasek stanu to pasek składający się z wielu paneli. Po wybraniu złożonego paska stanu do ułożenia na nim paneli można wykorzystać Edytor Paneli Paska Stanu (StatusBar Panels Editor). Wywołanie edytora wymaga dwukrotnego kliknięcia na obszarze pola wartości właściwości Panels. Przycisk Add Selected dodaje nowy obszar, przycisk Delete Selected usuwa wybrany obszar. Aby dokonać edycji panelu należy wybrać go w Edytorze Paneli a następnie zmodyfikować jego właściwości w oknie Inspektora Obiektów. Na rysunku 13.5 przedstawiony został Edytor Paneli Paska Stanu oraz Inspektor Obiektów w czasie edycji paneli.
Rysunek 13.5. Edytor Paneli Paska Stanu |
|
|
Indywidualne panele złożonego paska stanu są obiektami klasy TStatusPanel. |
Większość właściwości jest w miarę zrozumiała, ale dwie z nich wymagają szerszego wyjaśnienia. Właściwość Text zawiera tekst, który będzie wyświetlany w określonym panelu paska; właściwość ta może być również modyfikowana w czasie pracy, w efekcie czego zmienia się oczywiście tekst na odpowiednim panelu. Dokładne omówienie ustawiania tekstu dla paska stanu znajduje się w dalszej części tego rozdziału.
Właściwość Style może być ustawiona na wartość psTest lub psOwnerDrawn. W przypadku wartości psText (domyślnej) panel zachowuje się tak, jak należałoby się tego spodziewać - tekst w panelu jest wyrównywany w zależności od wartości właściwości Alignment. Jeżeli wartością Style jest psOwnerDrawn (co nazywane jest potocznie „rysowaniem specyficznym” - ang. owner drawing) odpowiedzialność za rysowanie tekstu i rysunków w obszarze panelu spada na programistę; zagadnienie to jest omawiane dalej w sekcji „Panele paska stanu rysowane ręcznie”.
Właściwości Width, Bevel i Alignment nie wymagają tłumaczenia. Wystarczy poeksperymentować z nimi, aby przekonać się jaki wpływ mają ich ustawienia na wygląd paska stanu.
|
Zmiany wprowadzane do paska stanu poprzez Edytor Paneli są niezwłocznie uwidaczniane w oknie Projektanta Formularzy. Umieść Edytor Paneli Paska Stanu w takim miejscu, abyś mógł widzieć pasek stanu w trakcie jego modyfikacji. Każda zmiana, jakiej dokonasz, będzie niezwłocznie odzwierciedlona w oknie Projektanta Formularzy. |
Po zakończeniu dodawania paneli do paska stanu należy zamknąć okno Edytora Paneli i w ten sposób powrócić do Projektanta Formularzy.
|
Jeżeli właściwość Panels komponentu StatusBar zostanie zmodyfikowana, Projektant Formularzy automatycznie nada właściwości SimplePanel wartość False. Przyjmowane jest tu bowiem założenie, że użycie więcej niż jednego panelu oznacza rezygnację z chęci stosowania prostego paska stanu. |
Zmiana tekstu na pasku stanu
Istnieją dwie metody zmiany tekstu na pasku stanu:
Ręczne modyfikowanie właściwości SimpleText paska stanu (dla pasków prostych) lub właściwości Text poszczególnych paneli (dla pasków złożonych).
Pozwolenie bibliotece VCL na dostarczenie tekstów pomocy kontekstowej dla paska stanu, przez nadanie właściwości AutoHint wartości True.
Samodzielne modyfikowanie tekstu na pasku stanu jest proste, szczególnie jeśli chodzi o pasek prosty. Kiedy właściwość SimplePanel posiada wartość True tekst, który ma się pojawić na pasku, można przypisać właściwości SimpleText:
StatusBar.SimpleText:='Ten tekst pojawi się na pasku stanu';
W przypadku złożonych pasków stanu zmiana tekstu jest tylko odrobinę bardziej złożona. Chcąc zmienić tekst wyświetlany przez pierwszy panel złożonego paska stanu, należałoby użyć następującego kodu:
StatusBar.Panels[0].Text:='Tekst na pasku stanu';
Właściwość Panels komponentu StatusBar posiada pole o nazwie Items będące tablicą paneli paska stanu. Ustawienie właściwości Text dla elementu tej tablicy spowoduje zmianę tekstu w odpowiednim panelu. (Ponieważ Items jest domyślną właściwością tablicową obiektu Panels, nie trzeba odwoływać się do niej w sposób jawny.) Jak widać, tablica jest indeksowana względem zera - pierwszym panelem paska stanu jest element o numerze 0.
Automatyczna pomoc kontekstowa nie wymaga szczególnego tłumaczenia. Cały proces sprowadza się do nadania właściwości AutoHint wartości True. Reszta, jak wskazuje nazwa właściwości, jest wykonywana automatycznie.
|
Nawet jeżeli właściwość AutoHint jest ustawiona, dalej można ręcznie modyfikować teksty pomocy kontekstowej paska stanu. Samodzielne ustawianie tekstu nie napotyka żadnych przeszkód, ale trzeba pamiętać, że przy następnym przesunięciu kursora nad komponentem wprowadzony tekst zostanie zastąpiony tekstem standardowym. |
Panele paska stanu rysowane ręcznie
Wcześniej padło stwierdzenie iż właściwość Style może przyjąć jedną z dwóch wartości: psText lub psOwnerDrawn. Po ustawieniu stylu na psOwnerDrawn trzeba przejąć odpowiedzialność za rysowanie wszystkich rzeczy, jakie wyświetlane mają być w panelu. Raczej niespotykane jest zadawanie sobie kłopotu użytkowania ręcznie rysowanego panelu tylko po to, aby wyświetlać w nim tekst. Zazwyczaj oznacza to chęć wyświetlania na pasku stanu ikon lub bitmap określonego typu. Niezależnie od tego, co będzie rysowane, kolejność czynności jest jednakowa:
Ustawienie właściwości Style panelu na wartość psOwnerDrawn (zazwyczaj poprzez Edytor Paneli Paska Stanu).
Obsłużenie zdarzenia OnDrawPanel.
Oczywiście, prawdziwa praca związana będzie z zaimplementowaniem procedury obsługującej zdarzenie OnDrawPanel. Deklaracja tej funkcji wygląda następująco:
procedure TForm1.StatusBar1DrawPanel
(StatusBar : TStatusBar;
Panel : TStatusPanel; const Rect : TRect);
StatusBar jest wskaźnikiem do paska stanu. Zazwyczaj wskaźnik taki już istnieje (właściwość Name komponentu StatusBar), więc parametr ten nie jest zbyt użyteczny chyba, że korzystamy z wielu ręcznie rysowanych pasków stanu. Argument Panel jest wskaźnikiem do określonego panelu, który w danej chwili wymaga odświeżenia. Parametr ten może zostać wykorzystany do określenia, który z paneli (rysowanych specyficznie) wymaga odświeżenia, jeżeli pasek stanu zawiera ich więcej niż jeden. Parametr Rect określa rozmiar i pozycję panelu. Jest to ważny parametr, ponieważ niesie on ze sobą dokładną informację na temat rozmiarów obszaru rysowania.
Procedura obsługująca zdarzenie OnDrawPanel jest wywoływana pojedynczo dla każdego z paneli, którego właściwość Style ustawiona została na wartość psOwenrDrawn. Jeżeli narysowania wymaga tylko jeden panel, wszystkie parametry, oprócz Rect, przestają mieć znaczenie. Kiedy paneli jest więcej, najpierw trzeba określić, na którym z nich mają być wykonywane operacje, a dopiero potem można przejść do właściwego rysowania. Być może dobrą ilustracją okaże się tutaj przykład. Kod źródłowy związany z książką zawiera program o nazwie StatBar przedstawiający niektóre rzeczy, jakie można robić z paskami stanu. Uruchom ten program, a także przejrzyj jego kod źródłowy w poszukiwaniu wskazówek pomocnych przy implementowaniu własnych pasków stanu w aplikacjach. Pracujący program StatBar przedstawiony został na rysunku 13.6.
Rysunek 13.6. Program StatBar zawierający rysowane specyficznie panele paska stanu |
|
Jak łatwo można zauważyć, pasek stanu w tym programie składa się z kilku paneli. Trzy środkowe są rysowane specyficznie. Panele oznaczone symbolami OVR i EXT symulują pasek stanu edytora tekstu lub edytora kodu. Programy tego typu zazwyczaj posiadają możliwość włączenia lub wyłączenia trybu rozszerzonego zaznaczania (Extended Selection) lub trybu nadpisywania znaków (Overtype). Jeżeli dany tryb jest włączony, tekst na pasku stanu ma kolor czarny, kiedy zaś tryb jest wyłączony tekst wyświetlany jest jako nieaktywny, w charakterystycznej trójwymiarowej formie. Trzeci panel wyświetla jedną ze standardowych ikon Windows ilustrując wykorzystanie grafiki w pasku stanu. Uruchom program i poeksperymentuj z nim aby przekonać się, jak działa.
Listing 13.1 przedstawia procedurę obsługującą zdarzenie OnDrawPanel pochodzącą z programu StatBar. Przejrzyj ją i zwróć uwagę na komentarze aby zrozumieć, co dokładnie dzieje się w kodzie.
Listing 13.1. Metoda StatusBarDrawPanel programu StatBar
procedure TMainForm.StatusBarDrawPanel(StatusBar: TStatusBar;
Panel: TStatusPanel; const Rect: TRect);
var
R : TRect;
Icon : HIcon;
begin
with StatusBar.Canvas do begin
{ Utworzenie tymczasowego obiektu klasy TRect. Parametr Rect }
{ jest stały, więc nie możemy go zmienić. }
R := Rect;
{ Sprawdzenie czy panel 3 jest tym, który wymaga
{ odświeżenia. Jeżeli tak jest w nim rysowana ikona. }
if Panel.Index = 3 then begin
{ Załadowanie jednej z magazynowych ikon Windows. }
{ Czasami łatwiej jest użyć funkcji API niż korzystać }
{ z metod VCL. }
Icon := LoadIcon(0, IDI_HAND);
{ Narysowanie ikony i pomniejszenie jej do rozmiaru }
{ 15 x 15 pikseli. Oprócz tego wycentrowanie ikony }
{ w panelu }
DrawIconEx(Handle, Rect.Left + 6, 3,
Icon, 15, 15, 0, 0, DI_NORMAL);
{ Koniec zadania }
Exit;
end;
{ To raczej długie wyrażenie if sprawdza czy któreś }
{ z pól wyboru Overtype Mode lub Extended Selection }
{ jest zaznaczone. Jeśli nie, musimy dwukrotnie narysować }
{ reprezentujący je tekst. Pierwszy raz na biało, następnie
{ ponownie z przemieszczeniem o jeden piksel w kolorze szarym. }
{ W efekcie powstaje trójwymiarowy tekst nieaktywności }
if ((Panel.Index = 1) and (OvrMode.Checked = False)) or
((Panel.Index = 2) and (ExtendedSel.Checked = False)) then begin
{ Przesunięcie o jeden piksel }
Inc(R.Left);
Inc(R.Top, 2);
{ Zmiana koloru na biały }
Font.Color := clWhite;
{ Ustawienie tła na przezroczyste, w ten sposób }
{ drugi tekst nie zasłoni widniejącego pod nim tekstu }
{ w kolorze białym }
Brush.Style := bsClear;
{ Rysowanie tekstu za pomocą funkcji API DrawText. }
{ Korzystamy z tej funkcji, ponieważ pozwala ona wycentrować }
{ tekst zarówno poziomo jak i pionowo w ramach danego obszaru }
{ prostokątnego. }
DrawText(Handle, PChar(Panel.Text), -1,
R, DT_CENTER or DT_VCENTER or DT_SINGLELINE);
{ Ustawienie koloru na szary, ponieważ za chwilę będziemy }
{ rysować ten sam tekst w tym kolorze. }
Font.Color := clGray;
{ Set the rect back to the original size. }
Dec(R.Left);
Dec(R.Top, 2);
end;
{ Wyświetlenie tekstu. Jeżeli element jest aktywny wtedy }
{ do rysowania tekstu używany jest kolor domyślny (czarny). }
{ Jeżeli element jest nieaktywny, używany jest (ustawiony }
{ przed chwilą) kolor szary. }
DrawText(Handle, PChar(Panel.Text), -1,
R, DT_CENTER or DT_VCENTER or DT_SINGLELINE);
end;
end;
Kod ten może wydawać się trochę zastraszający, ale jego większość stanowią komentarze. Sam kod jest względnie prosty. Komentarze wyjaśniają, co dzieje się w każdym kroku: trójwymiarowy wygląd nieaktywnego tekstu został osiągnięty przez narysowanie tekstu raz w kolorze białym, a następnie jeszcze raz w kolorze szarym z minimalnym przesunięciem. W efekcie wygląda on tak, jakby znajdował się w zagłębieniu. Ikona jest wyświetlana przy użyciu dwóch funkcji API: LoadIcon i DrawIconEx.
Przy pierwszym podejściu ręczne rysowanie paneli paska stanu może okazać się zniechęcające, ale z czasem okaże się, że nie jest to aż takie złe. Być może w swojej pracy w ogóle nie będziesz musiał korzystać z ręcznego sterowania paskami, jeśli jednak kiedyś nadejdzie taki czas będziesz wiedział, że jest to możliwe.
Dodawanie funkcji związanych z udostępnianiem poleceń
Udostępnianie poleceń (ang. command enabling) jest to proces polegający na włączaniu lub wyłączaniu dostępu do przycisków w zależności od bieżących warunków panujących w aplikacji. Przykładowo - nie ma potrzeby, aby aktywne były przyciski Wytnij i Kopiuj (lub odpowiadające im polecenia menu) edytora tekstu w sytuacji, gdy nie jest zaznaczony żaden fragment tekstu; nie ma również sensu użycie przycisku Wklej, gdy Schowek jest pusty.
Udostępnianie poleceń nie jest operacją trudną, szczególnie kiedy wykorzystać można nowy komponent Delphi o nazwie TActionList. Mimo to trzeba poświęcić sporo czasu, aby doprowadzić wszystko do porządku. Czas ten wynika z konieczności zwracania uwagi na detale. (Czasami właśnie zwracanie uwagi na detale odróżnia programy doskonałe od programów miernych.)
Udostępnianie poleceń przy użyciu klas
TActionList i TAction
Klasa TAction zapewnia wygodny sposób przeprowadzania operacji udostępniania poleceń. TActionList, niewidzialny komponent zarządzający akcjami, znajduje się na stronie Additional Palety Komponentów. Jak sama nazwa wskazuje TActionList zawiera listę obiektów klasy TAction. Po utworzeniu akcji jest ona przypisywana do obiektów kontrolnych, które wymagają aktywacji lub dezaktywacji w zależności od niej samej. Przez obiekty kontrolne należy tu rozumieć elementy menu (również menu kontekstowych), przyciski pasków narzędzi itp.
Dla przykładu weźmy po uwagę polecenie menu EditCut (lub EdycjaWytnij). Mogą istnieć przynajmniej trzy obiekty związane z tym zadaniem:
Element menu głównego
Przycisk paska narzędzi
Element menu rozwijalnego (kontekstowego)
Akcje tworzy się za pomocą Edytora Listy Akcji (ActionList Editor). Biorąc pod uwagę poprzedni przykład, należałoby utworzyć obiekt akcji dla polecenia wycięcia (Cut) i nazwać go np. CutAction. Następnie, korzystając z Inspektora Obiektów, obiekt CutAction przypisać trzeba właściwości Action każdego z komponentów związanych jest z operacją wycinania (czyli np. przycisków paska narzędzi lub elementów menu). Jeżeli w czasie pracy programu niezbędne jest udostępnienie operacji wycięcia, można to zrobić przy użyciu jednej linii kodu:
CutAction.Enabled:=True;
Przypisanie to spowoduje udostępnienie wszystkich komponentów, których właściwość Action jest ustawiona na wartość CutAction. Dezaktywacja wszystkich obiektów polecenia Cut sprowadza się do przypisania właściwości Enabled wartości False. Dogodnym miejscem na umieszczenie własnego kodu związanego z udostępnianiem poleceń jest zdarzenie OnUpdate należące do klas TAction i TActionList.
Udostępnianie poleceń za pomocą klasy TAction jest czymś, czego trzeba doświadczyć osobiście, aby móc je w pełni docenić. W następnej sekcji zajmiemy się dodawaniem funkcji udostępniania poleceń w programie ScratchPad.
Implementacja mechanizmu udostępniania poleceń
W tej sekcji zajmiemy się implementacją udostępniania poleceń w programie ScratchPad. Po pierwsze - trzeba skonfigurować obiekt ActionList, a następnie powiązać różnorodne obiekty z listą akcji.
Tworzenie listy akcji
Cały proces zaczyna się od komponentu ActionList, będącego sercem systemu udostępniania poleceń w bibliotece VCL. Na początku dodamy akcje dla elementów menu edycji (Edit). Następnie dodamy akcje dla elementów menu File: Save i Save As. Proces ustawiania komponentu ActionList został przedstawiony poniżej w postaci kolejnych kroków:
Tworzenie akcji dla menu Edit
Poniżej znajduje się opis tworzenia akcji dla elementów menu Edit: Cut, Copy i Paste. Postępuj zgodnie z poniższymi punktami:
Umieść w formularzu komponent ActionList i zmień jego właściwość Name na ActionList.
Dwukrotnym kliknięciem na ikonie komponentu wywołaj okno Edytora Listy Akcji.
Kliknij prawym przyciskiem myszy na obszarze Edytora Listy Akcji i wybierz polecenie New Standard (z otwartego menu kontekstowego). Wybierz akcję o nazwie TEditCopy i kliknij na przycisku OK. Zauważ, że właściwości tej klasy zostały wyświetlone w oknie Inspektora Obiektów.
Zatrzymajmy się na chwilę w tym miejscu, aby dokładniej wyjaśnić zasady działania akcji. Przyjrzyj się zawartości okna Inspektora Obiektów. Zauważ, że akcja TEditCopy posiada kilka znajomych właściwości, z których każda ma przypisaną wartość. Szczególną uwagę zwróć uwagę na właściwości Caption, Hint, ImageIndex i ShortCut, posiadają one wartości odpowiadające elementowi menu Edit o nazwie Copy. Właściwości te przekazywane będą każdemu z obiektów kontrolnych przypisanych zostaną do tej akcji. Powrócimy do tego zagadnienia w następnej sekcji.
Zmień właściwość Name nowej akcji na CopyAction, właściwość Hint na Kopiuj | Kopiuj do Schowka i właściwość ImageIndex na wartość 3.
Stwórz kolejną standardową akcję - TEditCut. Zmień właściwość Name na CutAction, właściwość Hint na Wytnij | Wytnij do Schowka i właściwość ImageIndex na wartość 4.
Stwórz trzecią akcję - TEditPaste. Zmień właściwość Name na PasteAction, właściwość Hint na wartość Wklej | Wklej ze Schowka i właściwość ImageIndex na wartość 5.
W tym miejscu zakończony został proces tworzenia akcji dla elementów menu Edit.
Tworzenie akcji dla menu File
W następnej kolejności należy stworzyć akcje dla elementów menu File: Save i Save As. Postępuj według poniższych kroków:
Kliknij prawym przyciskiem myszy w obszarze Edytora Listy Akcji, wybierz polecenie New Action. Utworzony zostanie nowy obiekt klasy TAction, a jego właściwości wyświetlone zostaną w Inspektorze Obiektów.
Zmień właściwość Name na SaveAction, właściwość Caption na &Zapisz…, właściwość Category na File, właściwość Hint na Zapisz | Zapisz plik, właściwość ImageIndex na wartość 2 i właściwość ShortCut na Ctrl+S.
Stwórz kolejną nową akcję. Zmień właściwość Name na SaveAsAction, właściwość Caption na Zapisz &jako…, właściwość Category na File. Nie ma potrzeby ustawiania właściwości Hint i ImageIndex, ponieważ z akcją tą nie jest skojarzony żaden przycisk na pasku narzędzi. Wygląd Edytora Listy Akcji na obecnym etapie prac przedstawia rysunek 13.7.
Rysunek 13.7. Edytor Listy Akcji po utworzeniu kilku akcji |
|
Zamknij okno Edytora Listy.
|
Jeżeli wymagane jest zarządzanie dziesiątkami różnych akcji, można podzielić je na poszczególne kategorie. W tym celu należy przypisać dowolny tekst właściwości akcji o nazwie Category. Przykładowo, stworzone wcześniej akcje związane z edycją można zgrupować ustawiając właściwość Category każdej z nich na wartość Edit. Kategorie akcji mają jedynie cel organizacyjny i w żaden sposób nie wpływają na sposób działania obiektów akcji. |
Łączenie akcji z komponentami
Kolejnym krokiem w naszej pracy jest powiązanie stworzonych niedawno akcji z różnymi elementami menu oraz przyciskami paska narzędzi, do których wspomniane akcje się odnoszą:
Uruchom Edytor Menu klikając dwukrotnie na komponencie MainMenu.
Wybierz element menu FileSave i zmień jego właściwość Action na SaveAction.
Wybierz element menu FileSave As i zmień jego właściwość Action na SaveAsAction.
Przejdź do menu Edit, wybierz element o nazwie Cut. Zmień jego właściwość Action na CutAction.
Powtórz krok 4 dla poleceń Copy i Paste menu Edit, przypisując właściwości Action wartości - odpowiednio - CopyAction i PasteAction. Zamknij okno Edytora Menu.
W oknie Projektanta Formularzy kliknij na przycisku paska narzędzi o nazwie File Save. Zmień jego właściwość Action na FileSaveAction.
Powtórz krok szósty dla przycisków paska narzędzi o nazwach Cut, Copy i Paste, nadając właściwości Action wartość - odpowiednio - CutAction, CopyAction i PasteAction.
W miarę potrzeby możesz również zmienić wartość właściwości Action elementów menu MemoPopup.
Prawdopodobnie nie zauważyłeś tego, ale po przypisaniu wartości SaveAction właściwości Action komponentu, jego właściwości Caption, Checked, Enabled, HelpContext, Hint, ImageIndex, ShortCut i Visible przyjęły wartości odpowiadających im właściwości w obiekcie SaveAction. Ważne jest zrozumienie, iż właściwości danej akcji zastępują właściwości każdego komponentu, który zostanie do niej przypisany. Należy o tym pamiętać ustawiając właściwości akcji. Dlatego w trakcie tworzenia akcji nakazałem Ci zmieniać właściwości Hint i ItemIndex. Jeżeli byś tego nie zrobił, teksty pomocy kontekstowej oraz ikony przycisków paska narzędzi byłyby nieprawidłowe.
W tej chwili każdy z wymienionych przed chwilą komponentów jest już połączony z odpowiednią akcją. Kiedy określona akcja ulegnie zmianie, zmienią się również wszystkie skojarzone z nią komponenty. Weźmy dla przykładu następującą linię kodu:
SaveAction.Enabled:= False;
Po wykonaniu tego przypisania każdy komponent, którego właściwość Action jest ustawiona na wartość SaveAction (a więc element głównego menu - Save i przycisk paska narzędzi o tej samej nazwie) stanie się nieaktywny. Aby udostępnić wszystkie komponenty skojarzone z tą akcją należy użyć następującego kodu:
SaveAction.Enabled:=True;
Jak widać jest to bardzo proste. Ponieważ oba komponenty Save (polecenie menu i przycisk paska narzędzi) mają właściwość Action ustawioną na SaveAction, poniższe dwa fragmenty kodu są sobie równoważne:
{Jedna operacja przy wykorzystaniu właściwości Action}
SaveAction.Enabled:= False;
…
{Inny sposób }
FileSave.Enabled:= False;
FileSaveBtn.Enabled := False;
W tym przykładzie zaoszczędzona została tylko jedna linia kodu, ale gdy komponentów wymagających aktywacji lub dezaktywacji będzie więcej, zastosowanie akcji oszczędzi nam sporo czasu. Po stworzeniu obiektu akcji i skojarzeniu go z jednym lub kilkoma komponentami proces udostępniania poleceń stanie się tak prosty jak pojedyncza linia kodu. Piękno tego systemu polega na tym, iż nie ma znaczenia liczba komponentów wymagających aktywacji lub dezaktywacji - cały proces wciąż będzie się bowiem zawierał w pojedynczej linii kodu.
Uruchom teraz program ScratchPad. Zauważ, że przyciski Cut i Copy są nieaktywne. Wpisz dowolny tekst w polu komponentu Memo i zaznacz go. Wspomniane dwa przyciski staną się nagle aktywne. Kliknij gdziekolwiek w obszarze komponentu Memo, aby anulować zaznaczenie - przyciski Cut i Copy ponownie staną się nieaktywne. Czy dostępny jest przycisk Paste? Jeśli tak, naciśnij kombinację klawiszy Alt+Print Screen (kombinacja ta spowoduje skopiowanie do Schowka bieżącego okna w formacie bitmapy) - przycisk Paste powinien stać się nieaktywny, ponieważ nie można wkleić bitmapy do komponentu Memo. Zaznacz dowolny tekst i kliknij na przycisku Cut lub Copy. W tym momencie przycisk Paste uaktywni się, ponieważ w Schowku znalazł się tekst - a ten może być wklejony do pola Memo.
Jak to działa? Standardowe akcje TEditCopy, TeditCut i TEditPaste automatycznie „wiedzą”, w jaki sposób włączyć lub wyłączyć dostęp do skojarzonych z nimi komponentów, gdy aktywny jest obiekt edycji dowolnego typu. Nie jest to żadna magia, ale kolejna doskonała zaleta akcji. Wystarczy stworzyć akcję standardową, a reszta dzieje się automatycznie. Aby zadziałał mechanizm udostępniania poleceń menu Edit, nie trzeba było tworzyć jakiegokolwiek kodu. Nie ma lepszego sposobu na realizację tego zadania.
Udostępnianie poleceń menu Save i Save As
W przypadku poleceń menu Edit sprawa była prosta, ponieważ nie trzeba było pisać żadnego kodu. Polecenia menu File, Save i Save As wymagać będą odrobinę więcej pracy, ponieważ nie istnieją dla nich akcje standardowe. Nie ma się jednak czym martwić - dodanie możliwości udostępniania tych elementów menu nie zajmuje zbyt dużo czasu. Aby zaimplementować tę możliwość skorzystamy ze zdarzenia OnUpdate. Wcześniej jednak musisz poznać trochę więcej informacji, aby lepiej uchwycić przeznaczenie tego zdarzenia.
Zdarzenie OnUpdate stanowi dogodne miejsce do umieszczenia kodu związanego z udostępnianiem poleceń. Kiedy aplikacja przetworzy wszystkie oczekujące komunikaty, Windows wyśle do niej komunikat WM_ENTERIDLE. W ten sposób system informuje program o braku komunikatów dla niego i zezwala mu na chwilowy „odpoczynek”. Kiedy komunikat taki otrzyma aplikacja Delphi, wywoła ona w odpowiedzi zdarzenie OnUpdate klasy TAction. Wystarczy stworzyć procedurę obsługującą to zdarzenie i w jej wnętrzu przeprowadzić niezbędne operacje związane z udostępnianiem poleceń. Funkcji tej można użyć do sprawdzenia stanu komponentu Memo i na tej podstawie włączyć lub wyłączyć dostęp do poleceń Save i Save As.
Jedyna rzecz, jaka pozostała jeszcze do wykonania, to stworzenie procedury obsługującej zdarzenie OnUpdate obiektu akcji. Postępuj według poniższych wskazówek:
Klikając dwukrotnie na komponencie ActionList uruchom Edytor Listy Akcji.
Z listy dostępnych akcji wybierz SaveAction. Jeżeli nie widać jej na liście, kliknij na kategorii File lub (All Actions).
W oknie Inspektora Obiektów kliknij podwójnie na kolumnie Value obok zdarzenia OnUpdate. W Edytorze Kodu wyświetlona zostanie procedura obsługująca to zdarzenie -edycją jej kodu zajmiemy się za chwilę.
Zlokalizuj okno Edytora Listy Akcji (jeżeli nie możesz go znaleźć skorzystaj z polecenia menu ViewWindow List). Z listy akcji wybierz SaveAsAction.
W oknie Inspektora Obiektów kliknij na przycisku rozwinięcia listy obok zdarzenia OnUpdate. Wybierz z listy element SaveActionUpdate. Dzięki temu polecenia Save i Save As będą mogły korzystać ze wspólnej procedury obsługującej zdarzenie OnUpdate.
Zamknij okno Edytora Listy Akcji.
Utworzenie procedury zdarzeniowej jest oczywiście prostszą częścią zadania. Bardziej skomplikowane jest napisanie kodu, który powinien znaleźć się między dyrektywami begin i end. Gotowa procedura obsługująca zdarzenie OnUpdate została przedstawiona na listingu 13.2. Przejdź do Edytora Kodu i wpisz fragment kodu przedstawiony poniżej do własnej procedury zdarzeniowej.
Listing 13.2. Procedura obsługująca zdarzenie OnUpdate w programie ScratchPad
procedure TMainForm.SaveActionUpdate(Sender: TObject);
begin
{ Udostępnianie poleceń dla elementów Save i Save As. }
SaveAction.Enabled :=
Memo.Modified and (Length(Memo.Lines.Text) > 0);
SaveAsAction.Enabled := SaveAction.Enabled;
{ Dwa kolejne polecenia nie korzystają z mechanizmu akcji.}
{ Zamiast tego ich właściwość Enabled jest modyfikowana w sposób }
{ bezpośredni. }
{ Udostępnianie polecenia Select All. }
EditSelectAll.Enabled := Memo.Lines.Count > 0;
{ Udostępnianie polecenia Undo. }
EditUndo.Enabled := Memo.Modified;
end;
Właściwość Enabled akcji SaveAction jest ustawiona w oparciu o wynik badania, czy obiekt Memo został zmodyfikowany i czy zawiera tekst. Mówiąc skrótowo - akcja Save jest udostępniana, jeżeli pole Memo zostało zmodyfikowane od momentu ostatniego załadowania i jeżeli nie jest puste. Identyczna wartość jest przypisywana właściwości Enabled akcji SaveAsAction. Dzięki temu możliwe jest udostępnianie obu poleceń - Save i Save As - w tym samym czasie w oparciu o te same kryteria.
Zauważ, że we wnętrzu procedury obsługującej zdarzenie OnUpdate znalazło się jeszcze kilka dodatkowych poleceń. Udostępnianie poleceń menu Edit: Select All i Undo odbywa się nie poprzez obiekt akcji, lecz przez bezpośrednie ustawienie właściwości Enabled. Użycie obiektów akcji w przypadku tych dwóch poleceń byłoby lekką przesadą, ponieważ każde z nich wymaga tylko jednej linii kodu. Posiadając procedurę obsługującą zdarzenie OnUpdate można w jej wnętrzu przeprowadzić proces udostępniania dowolnego typu poleceń. Mówiąc inaczej, funkcja ta nie jest przeznaczona wyłącznie dla elementów menu File: Save i Save As.
|
Testowanie kodu zawartego w procedurze obsługującej zdarzenie OnUpdate może okazać się trudnym zadaniem. Problem polega na tym, że umieszczenie jakichkolwiek punktów przerwań w tej funkcji spowoduje natychmiastowe zatrzymanie programu po jego starcie. Dlatego w tym przypadku trzeba skorzystać z innych metod śledzenia niż zwykłe punkty przerwań. Dwiema takimi metodami są: warunkowe punkty przerwań i użycie funkcji OutputDebugString wysyłającej komunikaty do Dziennika Zdarzeń. |
TCommandList
Lista komponentów posiada pewną dodatkową zaletę, która nie została jeszcze wspomniana. Aby przekonać się o jej istnieniu wykonaj poniższe kroki:
W głównym formularzu programu ScratchPad wybierz ikonę MainMenu.
Zmień właściwość Images na ImageList.
Tę samą czynność powtórz dla ikony PopupMenu.
Uruchom teraz program i przyjrzyj się menu File i Edit - zwróć uwagę na bitmapy. Elementy menu dziedziczą bowiem właściwość ImageIndex od skojarzonych z nimi obiektów akcji. Aby udostępnić te bitmapy wystarczy przypisać listę obrazków używaną przez akcje do właściwości menu o nazwie Images. Reszta dzieje się w sposób automatyczny.
Drukowanie w aplikacjach Delphi
Drukowanie jest nieodzownym elementem codziennej pracy większości użytkowników Windows. Mimo, że wiele programów nie posiada możliwości drukowania, większość aplikacji Windows wyposażona jest w określone funkcje wspomagające drukowanie. W sekcji tej zajmiemy się podstawowymi zagadnieniami drukowania.
Zapewnienie możliwości drukowania w aplikacjach DOS było naprawdę przykrym obowiązkiem. Program taki musiał dostarczać i instalować sterowniki dla wszystkich typów drukarek z jakimi przyszło współpracować danemu programowi. Rozwiązanie takie było ogromnym ciężarem dla twórców oprogramowania - zarówno licencjonowanego jak i typu shareware. Zmiany nadeszły wraz z nastaniem systemu Windows, który przejął na siebie większość ciężarów związanych z różnymi drukarkami, ich sterownikami itp. Teraz wystarczy wysłać dane do drukarki tak, jakby były one przeznaczone dla okna. Zagadnieniem tym zajmiemy się wkrótce.
Najprawdopodobniej będziesz zadowolony kiedy dowiesz się, że w wielu przypadkach funkcje drukowania są wbudowanym elementem biblioteki VCL i wykonują się w sposób niemal automatyczny. Zdarzają się jednak przypadki, kiedy trzeba przeprowadzić specjalistyczny proces drukowania. Zanim jednak zajmiemy się realizacją tego zadania, przyjrzyjmy się powszechnie znanym oknom dialogowym procesu drukowania. Potem przejdziemy do omówienia różnych metod drukowania, możliwych do zastosowania w aplikacjach Delphi.
Standardowe okna dialogowe drukowania
Windows udostępnia standardowe okna dialogowe drukowania (Drukuj) i ustawień drukowania (Ustawienia wydruku) z możliwością wykorzystania ich we własnych aplikacjach. Okno Drukuj jest używane tuż przed przystąpieniem do drukowania, natomiast okno Ustawienia wydruku służy do konfigurowania drukarki. Żeby móc skorzystać z tych komponentów, wcześniej trzeba oczywiście dodać je do formularza.
Okno Drukuj
Jak zostało to wspomniane przed chwilą, okno dialogowe Drukuj jest wyświetlane przed rozpoczęciem drukowania, zazwyczaj po wybraniu przez użytkownika polecenia FilePrint (lub PlikDrukuj). Jeżeli zostanie ono zamknięte przyciskiem OK rozpocznie się proces drukowania; wybranie przycisku Cancel przerywa ten proces. Okno drukowania Windows w swojej najprostszej formie zostało przedstawione na rysunku 13.8.
Rysunek 13.8. Okno drukowania w Windows |
|
Bez wątpienia widziałeś to okno już wcześniej. Rozwijalna lista u góry umożliwia wybór drukarki, na którą skierowany zostanie wydruk. Przycisk Właściwości przywołuje na ekran okno dialogowe specyficzne dla typu wybranej aktualnie drukarki, umożliwiając ustawienie takich parametrów, jak orientacja papieru, rozdzielczość, a także inne właściwości związane z bieżącą drukarką. Sekcja Zakres wydruku pozwala użytkownikowi na wydruk wszystkich stron, stron z określonego zakresu lub fragmentów (ewentualnie obiektów) aktualnie zaznaczonych w aplikacji. Sekcja Liczba kopii do określenia liczby kopii, a także posortowania wydruku (opcja Sortuj).
Okno dialogowe Drukuj jest reprezentowane w bibliotece VCL przez komponent PrintDialog. Podobnie jak w przypadku innych standardowych okienek, również i to jest wyświetlane własną metodą Execute. Nie powinno Cię zdziwić, że Windows wykonuje większość operacji jakie dostępne są w oknie Drukuj. Do rzeczy tych można zaliczyć wybór drukarki, liczby kopii, a także opcje sortowania wydruku. W zależności od typu budowanej aplikacji trzeba będzie udostępnić użytkownikowi możliwość drukowania określonego zakresu stron lub aktualnie zaznaczonego fragmentu dokumentu w aplikacji. Chcąc udostępnić tego typu cechy będziesz musiał dokładnie zapoznać się z właściwościami komponentu PrintDialog przed przystąpieniem do implementowania procesu drukowania.
Komponent PrintDialog nie posiada żadnych zdarzeń i tylko jedną metodę - Execute. Cała funkcjonalność tego komponentu opiera się na jego właściwościach, zestawionych w tabeli 13.3.
Tabela 13.3. Właściwości komponentu PrintDialog
Właściwość |
Opis |
Collate |
Przy wydruku w wielu kopiach ustawia kolejność drukowania poszczególnych kopii i stron. Przy wartości TRUE wydrukowane zostaną najpierw kolejne strony pierwszej kopii, następnie kolejne strony drugiej kopii itp. - otrzymamy więc żądaną kopię pojedynczych egzemplarzy wydruku. Wartość FALSE spowoduje drukowanie każdej ze stron określoną liczbę razy - otrzymamy więc coś w rodzaju pojedynczego egzemplarza wydruku z powielonymi |
Copies |
Określa liczbę kopii do wydrukowania. Właściwość tę można ustawić przed wywołaniem okna Drukuj, jeżeli w naszej aplikacji została zdefiniowana opcja reprezentująca liczbę kopii do wydrukowania. |
FromPage |
Jeżeli aktywna jest opcja zakresu stron, właściwość ta określa numer strony początkowej. Aplikacje umożliwiające drukowanie zakresu stron powinny odczytywać wartość tej właściwości w celu określenia stron, które należy drukować. |
MaxPage |
Określa maksymalny numer strony, jaki można wybrać w polu Do podczas operacji drukowania zakresu stron. |
MinPage |
Określa minimalny numer strony, jaki można wybrać w polu Od podczas operacji drukowania zakresu stron. |
Options |
Zawiera zestaw opcji określających, które z cech okna dialogowego Drukuj są dostępne. Dzięki tej właściwości można dodać do okna przycisk pomocy (Help), wyświetlić opcję drukowania do pliku (Print to File) a także udostępnić opcje zakresu stron lub wyboru rodzaju wydruku. |
PrintRange |
Określa, który z przycisków opcji zakresu drukowania jest dostępny bezpośrednio po wyświetleniu okna Drukuj. |
PrintToFile |
Określa, czy użytkownik wybrał opcję drukowania do pliku. Rzeczywisty zapis danych wyjściowych do pliku leży w gestii aplikacji. |
ToPage |
Podczas drukowania zakresu stron określa numer strony końcowej. Aplikacje umożliwiające drukowanie zakresu stron powinny odczytywać wartość tej właściwości w celu określenia stron, które należy wydrukować. |
Po zamknięciu okna Drukuj aplikacja nie ma zbyt wiele do roboty chyba, że uaktywnione zostały opcje drukowania zakresu stron i drukowania do pliku. Przykładowo, jeżeli aplikacja umożliwia drukowanie zakresu stron, trzeba odczytać właściwości FromPage i ToPage aby określić, które ze stron należy wydrukować. W pozostałych przypadkach drukowanie można rozpocząć w chwili, gdy użytkownik wybierze przycisk OK.
Okno ustawień wydruku
Okno ustawień wydruku (rysunek 13.9) jest stosowane, gdy użytkownik chce dokonać zmiany drukarki, rozmiaru stron, ich źródła i orientacji.
Rysunek 13.9. Okno ustawień wydruku |
|
W większości aplikacji okno ustawień wydruku nie jest bezwzględnie wymagane, ponieważ użytkownik może zmienić ustawienia drukowania po kliknięciu na przycisku Właściwości okna Drukuj (patrz rysunek 13.8). Z drugiej strony, zaimplementowanie okna ustawień wydruku jest tak łatwe, iż nic nie stoi na przeszkodzie, aby dołączyć je do własnej aplikacji. Jak bardzo łatwe? Komponent PrinterSetup nie posiada żadnych specyficznych dla siebie zdarzeń, metod lub właściwości. Podobnie jak w przypadku komponentu PrintDialog, jedyną metodą godną uwagi jest Execute. Okno ustawień wydruku jest całkowicie obsługiwane przez Windows - do tego stopnia, że funkcja Execute nie zwraca nawet żadnej wartości. Dzieje się tak, ponieważ Windows robi wszystko samodzielnie. Jeżeli użytkownik kliknie na przycisku Cancel, żadna czynności nie jest podejmowana. Po kliknięciu na przycisku OK Windows dokonuje odpowiednich zmian w przygotowaniu do drukowania. Zadanie programisty sprowadza się do wywołania okna ustawień wydruku. Typowa funkcja obsługująca polecenie menu FilePrint Setup mogłaby wyglądać następująco:
procedure TMainForm.FilePrintSetupClick(Sender : TObject);
begin
PrinterSetupDialog.Execute;
end;
Jak widać, zaimplementowanie okna ustawień wydruku jest tak proste, że z łatwością można je dodawać do własnych aplikacji.
Drukowanie w prosty sposób
Sposób drukowanie jest operacją zdeterminowaną przez typ aplikacji. Być może nie brzmi to zbyt przekonująco, ale jest to prawda. W zależności od rodzaju budowanej aplikacji proces drukowania może być zawarty w jednej linii kodu lub wymagać może setek linii. Zacznijmy więc od rzeczy prostych, posuwając się w kierunku zagadnień bardziej złożonych.
Metoda Print formularza
Klasa TForm posiada metodę o nazwie Print, która może być wykorzystana do drukowania zawartości formularza. Drukowany jest jedynie obszar klienta z wyłączeniem ramki i menu. Chociaż metoda ta sprawuje się doskonale dla zwykłej zawartości ekranu, to jest ona mimo to ograniczona przez swoje możliwości implementacyjne. Możliwy jest wybór jednej spośród trzech opcji drukowania, kontrolowanych przez właściwość PrintScale. Możliwe opcje skalowania wydruku wraz z ich opisami zawarte zostały w tabeli 13.4.
Tabela 13.4. Opcje właściwości PrintScale
Opcja |
Opis |
poNone |
Nie jest stosowany żaden typ skalowania. Postać wydruku jest silnie uzależniona od używanej drukarki. |
poProportional |
Po ustawieniu tej opcji podejmowane są próby drukowania formularza o rozmiarze w przybliżeniu odpowiadającym jego reprezentacji na ekranie. |
poPrintToFit |
Opcja ta powoduje zwiększenie lub zmniejszenie rozmiaru rysunku tak, aby dopasować go do bieżących ustawień drukarki. |
Właściwość PrintScaled może być ustawiona zarówno w trakcie pracy programu, jak i w fazie jego projektowania. Użycie metody Print jest ograniczone do zwykłych obszarów ekranu i raczej nie znajduje zastosowania przy zaawansowanych operacjach drukowania.
Metoda Print dla komponentu RichEdit
Potęga komponentu RichEdit płynie głównie z ogromu pracy, jaką w jego tle wykonuje komponent Memo. Drukowanie w tym komponencie jest osiągane przez wywołanie metody Print. Metoda ta pobiera jeden parametr - o nazwie Caption - wykorzystywany przez Menedżer Wydruków w czasie wyświetlania zadania drukowania. Drukowanie zawartości komponentu RichEdit jest bardzo proste:
RichEdit.Print('MojaApp.exe - readme.txt');
Programista nie musi się niczym martwić. Zawijanie tekstu i numerowanie stron są implementowane automatycznie. Komponent RichEdit jest doskonałym rozwiązaniem, kiedy potrzebny jest obszar edycji składający się z wielu linii i posiadający możliwości drukowania.
|
W celu wydrukowania pliku tekstowego można skorzystać z funkcji Windows API - ShellExecute. Funkcja ta służy (między innymi) do uruchamiania odpowiedniego programu w zależności od rozszerzenia pliku. Przykładowo, pliki z rozszerzeniem .txt domyślnie skojarzone są z programem Notatnik (Notepad.Exe). Po podwójnym kliknięciu (w Eksploratorze) na pliku o takim rozszerzeniu Windows odszuka w Rejestrze odpowiednie skojarzenie i w efekcie uruchomi w/w program. Zachowanie takie można wykorzystać do własnych celów. Przykładem niech będzie poniższa linia kodu:
ShellExecute(Handle, `print', `readme.txt', nil, nil, Jej wykonanie powoduje załadowanie programu Notatnik, wydrukowanie pliku Readme.txt i opuszczenie programu. Tak naprawdę główne okno Notatnika w ogóle nie zostanie wyświetlone, ponieważ parametrowi Show nadany został styl SW_HIDE. Wykorzystanie tej techniki opiera się na założeniu, że użytkownik nie zmienił domyślnego skojarzenia rozszerzenia .txt i nie usunął z systemu programu Notatnika. Chcąc skorzystać z funkcji ShellExecute należy dodać nazwę ShellApp do listy modułów (uses). |
Drukowanie z wykorzystaniem komponentu QuickReport
Programy „bazodanowe” mogą korzystać z komponentu QuickReport do drukowania raportów. Został on tutaj wspomniany, ponieważ zdecydowanie odnosi się do procesu drukowania, ale ponieważ jest to przede wszystkim komponent baz danych, jego dokładne omówienie zostanie odłożone do rozdziału 18. „Budowanie aplikacji baz danych”.
Drukowanie w trudny sposób
Nie pozwól, aby ten tytuł Cię zniechęcił. Drukowanie nie jest operacją trudną; wymaga jedynie nieco czasu i organizacji. Przyjrzyjmy się najpierw pewnym zagadnieniom, które trzeba znać, aby móc implementować funkcje drukowania w aplikacjach. Później zajmiemy się rzeczywistym kodem.
Czym jest kontekst urządzenia
Zagadnienie kontekstu urządzenia było omawiane szczegółowo w trakcie rozważań na temat klasy TCanvas w poprzednim rozdziale - ale powtórka nikomu nie zaszkodzi. Kontekst urządzenia (ang. device context) to coś w rodzaju tabliczki, na której mogą rysować programy Windows. Lepszym określeniem jest tutaj płótno (ang. canvas). Na płótnie można rysować tekst, linie, bitmapy, prostokąty, elipsy itd. Rodzaj rysowanej linii zależy od pióra aktualnie wybranego dla kontekstu urządzenia. Kolor i wzór wypełnienia wynikają z bieżącego pędzla kontekstu. Zarządzanie kontekstami urządzeń wymaga wielkiej ostrożności. W systemie Windows istnieje ogromna liczba kontekstów urządzeń, z tego powodu trzeba natychmiast zwalniać każdy z nich, gdy tylko przestanie być potrzebny. Ponadto, jeżeli obiekty wybrane dla kontekstu nie zostaną zwolnione w sposób prawidłowy, program może powodować gubienie pamięci, z nawet pozostawić środowisko Windows w stanie niestabilnym. Jak łatwo można sobie wyobrazić, korzystanie z kontekstu urządzenia może być zadaniem bardzo skomplikowanym.
Dobrą nowiną jest jednak to, że VCL chroni nas przed koniecznością posiadania wiedzy na temat każdego szczegółu dotyczącego kontekstów urządzeń. Biblioteka VCL implementuje konteksty urządzeń Windows w klasie TCanvas. Właściwość Canvas tej klasy zwalnia nas z obowiązku martwienia się o wszystkie drobne szczegóły, które potrafią doprowadzić programistę do szaleństwa podczas prób poradzenia sobie z kontekstami urządzeń Windows. VCL dba o przydzielenie kontekstu, wybranie dla niego odpowiednich narzędzi i zwolnienie go kiedy tylko przestanie już być potrzebny. Naszych zadaniem jest jedynie rysowanie na płótnie, resztą zajmuje się VCL.
Cóż jednak ma to wspólnego z drukowaniem? Wygląda to mniej więcej tak: Windows udostępnia kontekst urządzenia drukarki - i wszystkie operacje graficzne (przykładowo - rysowanie figur geometrycznych czy wypisywanie tekstu) wykonywane w odniesieniu do tegoż kontekstu będą miały swe odzwierciedlenie na papierze drukarki. Tak pojęte drukowanie nie różni się więc od wykreślania grafiki na ekranie - nie trzeba więc chyba nikogo przekonywać, jak wielki jest to postęp w porównaniu do drukowania w starym poczciwym DOSie.
Klasa TPrinter i funkcja Printer
Biblioteka VCL wspiera operacje drukowania udostępniając klasę TPrinter. Klasa ta implementuje wszystkie funkcje drukowania w Windows; jej największą zaletą jest właściwość Canvas reprezentująca (w postaci płótna) ni mniej, ni więcej tylko powierzchnię papieru załadowanego bieżąco do drukarki.
Wszystko, co trzeba zrobić, żeby móc drukować w programach Delphi, to dodanie modułu Printers do listy modułów (uses), a następnie zakodować czegoś takiego:
Printer.BeginDoc;
Printer.Canvas.TextOut(20,20, 'Witam!!!');
Printer.EndDoc;
Printer jest nazwą funkcji, zwracającą wskazanie na obiekt klasy TPrinter, reprezentujący bieżąco przydzieloną drukarkę - odpowiednio skonfigurowany i gotowy do użytku; wystarczy tylko przekazać mu impuls do rozpoczęcia pracy.
Przyjrzyjmy się teraz pobieżnie właściwościom i metodom klasy TPrinter. Najważniejsze właściwości tej klasy zostały przedstawione w tabeli 13.5, natomiast w tabeli 13.6 znajdują się jej główne metody.
Klasa TPrinter nie posiada interfejsu środowiskowego dla IDE. Wszelkie operacje na niej odbywają się podczas pracy aplikacji.
Tabela 13.5. Właściwości klasy TPrinter
Właściwość |
Opis |
Aborted |
Wartością tej właściwości jest True, jeżeli proces drukowania został rozpoczęty, a następnie przerwany przed naturalnym zakończeniem. |
Canvas |
Płótno reprezentujące powierzchnię wydruku. |
Capabilities |
Bieżące ustawienia sterownika urządzenia drukarki. |
Copies |
Liczba drukowanych kopii. |
Fonts |
Lista czcionek akceptowalnych przez bieżącą drukarkę. |
Handle |
Uchwyt do kontekstu urządzenia drukarki (HDC). Używaj tej właściwości kiedy zajdzie potrzeba wywołania funkcji Windows API wymagającej uchwytu do kontekstu urządzenia. |
Orientation |
Orientacja drukarki (poPortrait lub poLandscape). Właściwość ta jest modyfikowana automatycznie, gdy użytkownik wybierze drukarkę lub zmodyfikuje jej ustawienia. Można również przypisać jej wartość w sposób bezpośredni. |
PageHeight |
Wysokość bieżącej strony drukarki (w pikselach). Wartość ta jest zależna od typu drukarki i dodatkowo może się zmieniać w zależności od orientacji drukarki. Niektóre drukarki mogą korzystać z więcej niż jednej rozdzielczości, co również jest źródłem wahań tej wartości. |
PageNumber |
Numer aktualnie drukowanej strony. Właściwość ta jest zwiększana za każdym razem, gdy wywołana zostanie funkcja NewPage rozpoczynająca drukowanie nowej strony. |
PageWidth |
Szerokość strony w pikselach. Podobnie jak w przypadku właściwości PageHeight wartość ta zmienia się w zależności od rozdzielczości, orientacji papieru i jego rozmiaru. |
PrinterIndex |
Indeks aktualnie wybranej drukarki na liście drukarek dostępnych w systemie. Wartość -1 reprezentuje drukarkę domyślną. |
Printers |
Lista drukarek dostępnych w systemie. |
Printing |
Właściwość ta posiada wartość True jeżeli drukarka aktualnie drukuje. |
Title |
Tekst, który identyfikuje zadanie drukowania w oknie Menedżera Wydruków. |
Tabela 13.6. Metody klasy TPrinter
Właściwość |
Opis |
Abort |
Stosowana do przerwania procesu drukowania przed jego normalnym |
BeginDoc |
Rozpoczyna proces drukowania. Konfiguruje drukarkę przygotowując ją do wydruku. |
EndDoc |
Kończy proces drukowania. Wymusza faktyczne rozpoczęcie drukowania bieżącej strony i wykonuje operacje czyszczenia wydruku w Windows. |
cd. na następnej stronie
Tabela 13.6. cd. Metody klasy TPrinter
Właściwość |
Opis |
GetPrinter |
Udostępnia charakterystykę bieżącej drukarki. Zamiast tej metody lepiej jest korzystać z właściwości Printers. (Właściwości Printers jest preferowaną metodą dostępu, ponieważ umożliwia ona zarówno pobieranie jak |
NewPage |
Używana jest do wymuszenia drukowania bieżącej strony i rozpoczęcia nowej. Zwiększa wartość właściwości PageNumber. |
SetPrinter |
Ustawia bieżącą drukarkę. Zamiast tej metody lepiej jest stosować właściwość Printers. |
Praktyczne zastosowanie
Nadeszła pora, aby w praktyce wykorzystać zdobytą wiedzę. Jeszcze raz powrócimy do programu ScratchPad i trochę go ulepszymy. W końcu co to za edytor tekstu, który nie potrafi drukować?
Po pierwsze - musisz minimalnie zmodyfikować formularz główny. Elementy menu Print i Print Setup zostały już dodane, ale trzeba je jeszcze udostępnić i umieścić w formularzu reprezentujące je okna dialogowe:
Przywołaj okno Edytora Menu, klikając podwójnie na komponencie MainMenu.
W oknie Edytora Menu wybierz polecenie menu FilePrint programu ScratchPad. Zmień właściwość Enabled na wartość True.
Identyczną operację wykonaj dla elementu menu FilePrint Setup. Zamknij okno Edytora Menu.
Umieść w formularzu komponent PrintDialog i zmień jego właściwość Name na PrintDialog.
Umieść w formularzu komponent PrinterSetupDialog i zmień jego właściwość Name na PrinterSetupDialog.
W tym miejscu zakończone zostały zmiany w formularzu, teraz nadeszła pora aby zająć się kodem źródłowym. Na początek, trzeba dodać kilka elementów do deklaracji głównego formularza. Postępuj według poniższych wskazówek:
Przejdź do Edytora kodu i dodaj moduł Printers do listy uses głównego formularza.
Znajdź deklarację klasy TMainForm w sekcji interface. Dodaj poniższą linię kodu do sekcji private deklaracji klasy.
procedure PrintFooter(var R : TRect; LineHeight : Integer);
Jest to deklaracja funkcji drukującej stopkę u dołu każdej strony.
Wciśnij kombinację klawiszy Ctrl+Shift+C, aby uaktywnić mechanizm uzupełniania klas (class completion), który stworzy procedurę PrintFooter w sekcji implementacyjnej. Wpisaniem kodu zajmiesz się później.
Przejdź do Projektanta Formularzy i wybierz polecenie FilePrint z głównego menu formularza. Wyświetlona zostanie metoda FilePrintClick. Na razie pozostaw ją bez zmian.
Wybierz polecenie menu FilePrint Setup. Wpisz pojedynczą linię kodu (poczynając od miejsca, w którym znajduje się kursor) tak, aby funkcja FilePrintSetup przyjęła następującą postać:
procedure TMainForm.FilePrintSetupClick(Sender: TObject);
begin
PrinterSetupDialog.Execute;
end;
Teraz można przystąpić do wypełnienia kodem metod FilePrintClick i PrintFooter. Metoda FilePrintClick została przedstawiona na listingu 13.3. Możesz ręcznie wpisać kod lub załadować projekt ScratchPad należący do kodu książki i przetestować go w Delphi. Metoda PrintFooter znajduje się na listingu 13.4. Wpisz kod tych metod do pliku SPMain.pas. Możesz oczywiście pominąć komentarze.
Listing 13.3. Metoda FilePrintClick
procedure TMainForm.FilePrintClick(Sender: TObject);
var
I : Integer;
LineHeight : Integer;
LinesPerPage : Integer;
LineCount : Integer;
R : TRect;
S : string;
begin
{ Wyświetlenie okna dialogowego Drukuj. }
if PrintDialog.Execute then begin
{ Ustawienie tytułu dla obiektu Printer.}
Printer.Title := 'ScratchPad - ' + OpenDialog.FileName;
{ Przypisanie obiektowi drukarki czcionki jaką używa }
{ obiekt memo }
Printer.Canvas.Font := Memo.Font;
{ Określenie wysokości linii. Funkcja MulDiv wyznacza }
{ wysokość linii na podstawie rozmiaru (pole Size) czcionki }
{ biorąc pod uwagę aktualną rozdzielczość drukarki. Trzeba }
{ tutaj użyć funkcji Abs, ponieważ rezultat może mieć ujemną }
{ wartość. Następnie do wysokości dodawane jest 40% rozmiaru }
{ jako odstęp między wierszami. }
LineHeight := Abs(
MulDiv(Printer.Canvas.Font.Size,
GetDeviceCaps(Printer.Handle, LOGPIXELSY), 72));
Inc(LineHeight, (LineHeight * 4) div 10);
{ Określenie jak dużo linii zmieści się na jednej stronie. }
{ Obcięcie wyniku o trzy linie, aby zostawić miejsce na }
{ dolny margines. }
LinesPerPage := (Printer.PageHeight div lineHeight) - 4;
{ Wydruk zacznie się w linii 4, a nie w zerowej,
{ aby zostawić miejsce na nagłówek i margines. }
LineCount := 4;
{ Przekazanie Windows informacji o rozpoczęciu drukowania }
{ nagłówka }
Printer.BeginDoc;
R.Top := LineHeight;
R.Left := 20;
R.Right := Printer.PageWidth;
R.Bottom := LineHeight * 2;
DrawText(Printer.Handle,
PChar(OpenDialog.FileName), -1, R, DT_CENTER);
{ Przejście w pętli przez wszystkie linie i drukowanie ich }
for I := 0 to Pred(Memo.Lines.Count) do begin
{ Po osiągnięciu dolnej krawędzi strony, licznik jest }
{ zerowany, strona wysuwana i następuje rozpoczęcie }
{ nowej strony }
Inc(LineCount);
if LineCount = LinesPerPage then begin
PrintFooter(R, LineHeight);
LineCount := 4;
Printer.NewPage;
end;
{ Pobranie następnego łańcucha i wydrukowanie go przy }
{ pomocy funkcji TextOut. }
S := Memo.Lines.Strings[I];
Printer.Canvas.TextOut(0, LineCount * LineHeight, S);
end;
{ Koniec wydruku. }
PrintFooter(R, LineHeight);
Printer.EndDoc;
end;
end;
Listing 13.4. Metoda PrintFooter
procedure TMainForm.PrintFooter(var R: TRect; LineHeight: Integer);
var
S : String;
begin
with Printer do begin
{ Utworzenie łańcucha który posłuży do wyświetlenia }
{ numeru strony. }
S := Format('Page %d', [PageNumber]);
{ Ustawienie obszaru, w którym wydrukowana będzie stopka. }
{ Odszukanie końca strony i cofnięcie się o dwie linie. }
R.Top := PageHeight - (lineHeight * 2);
R.Bottom := R.Top + lineHeight;
{ Wyświetlenie tekstu przy użyciu funkcji DrawText, }
{ dzięki której można bezproblemowo wycentrować tekst. }
DrawText(Handle, PChar(S), -1, R, DT_CENTER);
{ Rysowanie linii w poprzek strony tuż nad tekstem
{ numeru strony }
Canvas.MoveTo(0, R.Top - 2);
Canvas.LineTo(R.Right, R.Top - 2);
end;
end;
Powyższy kod pokazuje raczej, w jaki sposób można drukować bezpośrednio z poziomu Windows, zamiast korzystać z wbudowanych funkcji drukowania udostępnianych przez bibliotekę VCL. Zazwyczaj jestem za tym, aby stosować proste metody (kiedy jest to możliwe), czasami jednak zdarzają się sytuacje, kiedy te proste metody nie są dostatecznie elastyczne. Wtedy przydaje się wiedza umożliwiająca bezproblemowe wykonanie zadania.
Drukowanie bitmap
Drukowanie bitmap jest operacją prostą. Wystarczy stworzyć egzemplarz klasy TBitmap, załadować bitmapę do utworzonego obiektu i wysłać ją do drukarki przy użyciu metody Draw należącej do klasy TCanvas. Oto cały kod tej operacji:
procedure TForm1.Button1Click(Sender : TObject);
var
Bitmap : TBitmap;
begin
Bitmap := TBitmap.Create;
Bitmap.LoadFromFile('test.bmp');
Printer.BeginDoc;
Printer.Canvas.Draw(20, 20, Bitmap);
Printer.EndDoc;
Bitmap.Free;
end;
Drukując bitmapę należy zdawać sobie sprawę z faktu, iż może ona okazać się bardzo mała w zależności od rozdzielczości danej drukarki. Bitmapa może wymagać rozciągnięcia, aby przyjąć poprawny wygląd. Jeżeli zajdzie taka potrzeba, zamiast funkcji Draw należy użyć funkcji StretchDraw.
Korzystanie z kursorów
Użytkowanie kursorów nie jest rzeczą trudną, ale mimo to wspomnę o kilku szczegółach. Niniejsza sekcja dotyczy kursorów, które ulegają zmianom w trakcie działania programu. (Zmiana kursora na poziomie projektowania wymaga ustawienia nowej wartości właściwości Cursor określonego komponentu.) Po krótkim spojrzeniu na podstawy funkcjonowania kursorów przejdziemy do omówienia zasad ładowania kursorów predefiniowanych i użytkowych.
Podstawy kursorów
Po pierwsze, można dokonać zmiany kursora dla określonego komponentu, formularza lub całego obszaru użytkowego aplikacji. Jeżeli kursor ma być zmieniony dla całej aplikacji, trzeba zmodyfikować właściwość Cursor obiektu Screen reprezentującego ekran aplikacji. Po zmianie tej właściwości można być pewnym, że kursor pozostanie niezmieniony niezależnie od tego, nad jakim komponentem się znajduje. Powiedzmy, przykładowo, że chcesz zmienić kształt kursora na klepsydrę. Jeżeli zmienisz właściwość Cursor tylko dla formularza, kursor będzie się zmieniał w klepsydrę za każdym razem, gdy znajdzie się nad formularzem, ale po umieszczeniu go nad dowolnym innym komponentem będzie mu przywracany wygląd pierwotny; zmiana na poziomie obiektu Screen zapewnia jednolitą postać kursora na całym ekranie.
Zarządzanie kursorami leży w gestii obiektu Screen. Wszystkie kursory, z których można korzystać w swoich aplikacjach, znajdują się we właściwości Cursors tego obiektu. Zwróć uwagę, że właściwość ta nosi nazwę Cursors i nie jest tym samym co właściwość Cursor omawiana w poprzednim akapicie. Cursors jest tablicą zawierającą listę kursorów dostępnych dla aplikacji, natomiast Cursor jest właściwością wykorzystywaną do wyświetlenia określonego kursora. Na początku może wydać się to mylące, jednak w miarę nabierania doświadczenia wszystko stanie się oczywiste.
Windows oferuje kilka wbudowanych kursorów z przeznaczeniem do użycia w aplikacjach. Oprócz tych kursorów można skorzystać jeszcze z kilku dodatkowych oferowanych przez bibliotekę VCL. Ogólnie kursory te nazywane są kursorami wbudowanymi (ang. stock cursors). Każdy z takich kursorów posiada reprezentującą go stałą. Na przykład kursor w kształcie strzałki nosi nazwę crArrow, kursor w kształcie klepsydry nazywa się crHourGlass, natomiast kursor przeciągania to crDrag. Wszystkie te kursory są przechowywane w tablicy Cursors i zajmują pozycje od -17 do 0; kursor domyślny ma indeks zerowy (crDefault), brak kursora jest reprezentowany przez indeks -1 (crNone), kursor strzałki to -2 (crArrow) itd. Kompletna lista kursorów wbudowanych wraz z reprezentującymi je stałymi znajduje się w systemie pomocy.
Aby wykorzystać dowolny kursor z tablicy Cursors, trzeba przypisać jego nazwę właściwości Cursor obiektu Screen:
Screen.Cursor := crHourGlass;
W tym miejscu kontrolę przejmuje biblioteka VCL ładując i wyświetlając wskazany kursor. Korzystanie z właściwości Cursors jest dla nas nie widoczne, ponieważ wybierając kursor nie odwołujemy się do niej w sposób jawny. Zamiast tego dokonujemy przypisania właściwości Cursor, a odnalezieniem i wyświetleniem kursora zajmuje się już sama biblioteka. Właściwość Cursor komponentu oraz właściwość Cursors obiektu Screen współpracują ze sobą, aby wyświetlać różne typy kursorów w aplikacji.
Przyczyn do zmiany kursora w aplikacji może być wiele - wyświetlenie kursora oczekiwania (klepsydry), korzystanie ze specjalistycznych kursorów w aplikacji graficznej, implementowanie kursora pomocy w aplikacji.
Ładowanie i korzystanie z kursorów wbudowanych
Windows udostępnia kilka wbudowanych w środowisko kursorów, które można wykorzystać we własnych aplikacjach. Ponadto istnieją też gotowe kursory oferowane przez bibliotekę VCL. Ich użycie nie jest obarczone żadnymi ograniczeniami.
Jedną z oczywistych sytuacji, która wymaga zmiany kursora jest potrzeba wykonania przez aplikację dłuższego procesu. Za złą praktykę uważa się blokowanie programu bez powiadomienia o tym stanie użytkownika. Właśnie do tego celu służy kursor klepsydry (nazywany w Windows kursorem oczekiwania - ang. wait cursor). Załóżmy, że w swojej aplikacji masz pętlę programową, której wykonanie może zająć dłuższy czas. Wtedy powinieneś zrobić coś takiego:
procedure TMainForm.DoItClick(Sender : TObject);
var
I, Iterations : Integer;
OldCursor : TCursor;
begin
Iterations : = StrToInt(Time.Text);
OldCursor := Screen.Cursor;
Screen.Cursor := crHourGlass;
for I := 0 to Interactions do begin
Application.ProcessMessages;
Time.Text := IntToStr(I);
end;
Screen.Cursor : = OldCursor;
end;
Ponieważ nie zawsze wiadomo, jaki kursor jest w danej chwili używany przez aplikację, dobrym rozwiązaniem jest zapisanie bieżącego kursora przed zmodyfikowaniem właściwości Cursor. Kiedy nowy kursor przestanie już być potrzebny, można będzie odtworzyć jego poprzednią postać.
|
Czasami zmiana właściwości Cursor przed przystąpieniem do dłuższego procesu przetwarzania pętli może nie dać żadnego widocznego efektu. Przyczyną jest to, że Windows nie miał szansy zmienić kursora przed wejściem programu w pętlę. Podczas wykonywania pętli aplikacja nie jest w stanie przetwarzać żadnych komunikatów - łącznie z tym nakazującym zmianę kursora. Rozwiązaniem jest wymuszenie na Windows przetwarzania komunikatów oczekujących w pętli komunikatów (ang. message loop) w trakcie wykonywania pętli przez program: Application.ProcessMessages; Teraz aplikacja umożliwia swobodny przepływ komunikatów, a kursor zmienia swój wygląd w chwili, gdy rozpoczyna się pętla. |
Oczywiście, można również zmienić kursor dla indywidualnego komponentu. Przykładowo - program graficzny może zmieniać kursor w obszarze użytkowym (klienta) w zależności od bieżącego narzędzia rysowania. W takim przypadku nie należy zmieniać kursora dla obiektu Screen, ponieważ chcemy, aby kursor przyjmował kształt strzałki gdy znajdzie się nad paskiem narzędzi, paskiem stanu, menu lub dowolnym innym komponentem, jaki występować może w formularzu. Należy wtedy ustawić kursor tylko dla komponentu reprezentującego okno użytkowe aplikacji:
PaintBox.Curosr := crCross;
Kursor zostanie zmieniony tylko dla jednego określonego komponentu, natomiast pozostałe z nich stosują swoje własne predefiniowane kursory.
Ładowanie i korzystanie z kursorów użytkowych
Załadowanie kursora użytkowego wymaga trochę więcej pracy. Jak zostało to wspomniane wcześniej, lista kursorów dostępnych dla aplikacji przechowywana jest przez właściwość Cursors klasy TScreen. Użycie kursora użytkowego wymaga wykonania kilku kroków:
Stwórz kursor w Edytorze Graficznym lub innym edytorze zasobów i zapisz go jako plik .res.
Za pomocą dyrektywy kompilatora $R włącz ten plik do swojego programu.
Załaduj kursor do tablicy Cursors za pomocą funkcji LoadCursor.
Zaimplementuj kursor przypisując odpowiednią wartość indeksu właściwości Cursor formularza, obiektu Screen lub dowolnego innego komponentu.
Sposób wykonania dwóch pierwszych kroków został omówiony w rozdziale jedenastym podczas rozważań na temat Edytora Graficznego i w rozdziale ósmym, w którym mowa była o zasobach. Po dołączeniu kursora do pliku .exe można w prosty sposób załadować go do tablicy Cursors, za pomocą funkcji LoadCursor:
Screen.Cursors[1] := LoadCursor(HInstance, 'MOJKURSOR');
Kod ten działa przy założeniu, że posiadasz kursor o nazwie MOJKURSOR i chcesz mu przypisać indeks 1 w liście kursorów (pamiętaj, że pozycje od -17 do 0 są wykorzystywane przez kursory wbudowane). Załadowanie kursora musi się odbyć tylko raz, dlatego prawdopodobnie najlepszym miejscem na przeprowadzenie tej operacji jest procedura obsługująca zdarzenie OnCreate.
|
Wszystkie kursory są ładowane do właściwości Cursors obiektu Screen niezależnie od tego, czy są one następnie wykorzystywane przez obiekt Screen, formularz, czy komponent. Istnieje tylko jedna tablica Cursors i należy ona do obiektu Screen. |
Aby skorzystać z określonego kursora, wystarczy przypisać go właściwości Cursor obiektu Screen lub dowolnego innego komponentu:
PaintBox.Cursor := 1;
Jeżeli posiadasz kilka własnych kursorów, możesz utworzyć dla nich stałe o odpowiednich nazwach, zamiast stosować wartości liczbowe, które są łatwe do pomylenia. Przy zastosowaniu tej metody poprzedni kod wyglądałby następująco:
const
crHELION = 1;
{w dalszej części kodu …}
Screen.Cursors[crHELION] := LoadCursor(HInstance, 'MOJKURSOR');
{jeszcze w dalszej części kodu…}
PaintBox.Cursor := crHELION;
Jak widać, ładowanie i implementowanie własnych kursorów nie jest trudne, kiedy wiadomo jak to się robi. Kod przeznaczony dla tego rozdziału zawiera przykładowy program o nazwie CursTest, demonstrujący zagadnienia omawiane w tej sekcji.
Podsumowanie
W rozdziale tym poznałeś niektóre cechy decydujące o jakości aplikacji dla Windows, a także przekonałeś się, w jaki sposób można zaimplementować je we własnych programach. Powinienem Cię ostrzec, iż łatwo można popaść w przesadę stosując we własnych aplikacjach zbyt dużo takich cech jak paski kontrolne, paski stanu, czy kursory. Stosuj w swoich aplikacjach wszelkie niezbędne dekoracje, ale nie przesadzaj z nimi. Pod koniec rozdziału zapoznałeś się z mechanizmami drukowania. W niektórych przypadkach mechanizm ten jest wbudowany w komponent, a wtedy drukowanie staje się operacją niezwykle łatwą. W innych sytuacjach niezbędna staje się współpraca z klasą TPrinter - ale nawet wtedy drukowanie pozostaje rzeczą łatwą do zaimplementowania.
Warsztat
Warsztat składa się z pytań kontrolnych oraz ćwiczeń utrwalających i pogłębiających zdobytą wiedzę. Odpowiedzi do pytań możesz znaleźć w dodatku A.
Pytania i odpowiedzi
Czy mogę jednocześnie wyłączyć dostęp do wszystkich komponentów na moim pasku narzędzi?
Tak. Ustaw właściwość Enabled paska narzędzi na wartość False.
Zauważyłem, że komponenty TCoolBar i TControlBar służą w przybliżeniu do tego samego celu. Którego z nich powinienem używać?
Moja rada brzmi: używaj TControlBar. Istnieje wiele wersji biblioteki podstawowych kontrolek firmy Microsoft - COMCTL32.DLL. TCoolBar jest nadbudowany wokół kontrolki cool bar firmy Microsoft, a przez to zależny od wersji biblioteki COMCTL32.DLL jaką użytkownik zainstalował w swojej maszynie. TControlBar działa niezależnie od COMCTL32.DLL, dlatego jego zachowanie w aplikacji nie będzie się zmieniać od systemu do systemu.
Jak mam umieścić bitmapę na pasku stanu?
Stwórz pasek wielopanelowy. Zmień styl panelu, który przechowywać będzie bitmapę, na psOwnerDraw. Następnie, we wnętrzu procedury obsługującej zdarzenie OnDrawPanel, użyj metody Draw klasy TCanvas aby narysować bitmapę na panelu.
W jakim celu powinienem zajmować się czymś takim, jak udostępnianie poleceń elementów menu i przycisków paska zadań?
Ponieważ użytkownicy oczekują konsekwentnego interfejsu. Jeżeli którekolwiek polecenia są niedostępne (w menu lub na pasku), powinny być one przyciemnione. Jest to wizualna wskazówka dla użytkownika, że dane polecenia w ogóle istnieją, ale chwilowo nie mają zastosowania.
Jestem użytkownikiem Delphi od dłuższego czasu. Mam już zaimplementowany system udostępniania poleceń. Jaką korzyść da mi przejście na mechanizm akcji i list akcji?
Akcje stanowią pewnego rodzaju centralną lokalizację, z której można przeprowadzać wszelkie operacje udostępniania poleceń. W przeciwieństwie do własnego kodu udostępniania poleceń, który rozrzucony jest po całym obszarze kodu aplikacji, akcje pozwalają na umieszczenie całego kodu w jednym miejscu.
Chcę aby moja aplikacja była w stanie wyprowadzać dane pochodzące z kontrolki dużego obszaru edycji. Jak najłatwiej to zrobić?
Najprostszym rozwiązaniem jest zastosowanie komponentu RichEdit. Do drukowania jego zawartości można wykorzystać jego metodę Print.
Zauważyłem, że zarówno obiekt Printer, jak i jego właściwość Canvas posiadają właściwość Handle. Jaka różnica występuje między nimi?
W tym przypadku nie ma żadnej różnicy. Jeżeli zaistnieje potrzeba wywołania funkcji Windows API, która wymaga uchwytu do kontekstu urządzenia drukarki, można użyć uchwytu Printer.Handle lub Printer.Canvas.Handle.
Po zmianie kursora w głównym formularzu, jego wygląd jest poprawny nad samym formularzem, ale gdy znajdzie się nad dowolnym z przycisków jest mu przywracany kształt strzałki. Dlaczego?
Zmiany kursora trzeba dokonać w obiekcie Screen, a nie w formularzu. Dokonanie zmiany na poziomie obiektu Screen da nam pewność, że nowy kursor będzie używany nad dowolną częścią aplikacji.
Quiz
W jaki sposób do zdarzenia OnClick przycisku paska narzędzi dołącza się funkcję obsługującą to zdarzenie?
Czy na pasku narzędzi można umieszczać obiekty kontrolne inne niż przyciski?
Jaką nazwę nosi zdarzenie listy TActionList, na które należy zareagować implementując udostępnianie poleceń?
Do czego służy właściwość SimplePanel komponentu StatusBar?
W jaki sposób dokonuje się ręcznej zmiany tekstu na pasku stanu?
W jaki sposób włącza się lub wyłącza dostęp do elementów menu i przycisków?
W jaki sposób w aplikacjach Delphi osiąga się dostęp do drukarki?
Jakiej metody klasy TPrinter należy użyć do rozpoczęcia drukowania?
Jakiej metody klasy TPrinter należy użyć podczas drukowania, aby rozpocząć nową stronę?
W jaki sposób dokonuje się zmiany kursora dla komponentu, podczas pracy programu?
Ćwiczenia
Napisz program zaczynając od samego początku. Dodaj do niego pasek narzędzi i umieść na nim pięć przycisków. Następnie dodaj pasek stanu. Udostępnij mechanizm pomocy kontekstowej dla przycisków paska narzędzi tak, aby wyświetlany był zarówno tekst na pasku stanu jak i podpowiedzi.
Zmodyfikuj pasek stanu programu ScratchPad dodając drugi panel. W panelu tym wyświetl tekst Zapisany lub Zmodyfikowany w zależności od wartości Memo. Modified.
W oknie informacji o programie ScratchPad zmień numer wersji na Version 1.05. Zmiany wprowadź również w polu Title okna opcji projektu i we właściwości Caption głównego formularza. W końcu dodałeś nowe cechy do programu, należy więc to zaznaczyć.
Stwórz swój własny kursor w Edytorze Graficznym. Napisz program, który wyświetla ten kursor gdy naciśnięty zostanie przycisk głównego formularza.
Zadanie dodatkowe: Zmodyfikuj program ScratchPad z ćwiczenia trzeciego, tak aby na pasku stanu wyświetlał różne bitmapy, w zależności od tego, czy bieżący plik jest zachowany. (Wskazówka: przyjrzyj się bitmapom led1on.bmp i led1off.bmp rezydującym w katalogu Shared Files\Images\Buttons.)
Zadanie dodatkowe: Zmodyfikuj program ScratchPad tak, aby użytkownik mógł określić górny, dolny, lewy i prawy margines wydruku.
556 Część II
556 C:\Dokumenty\Roboczy\Delphi 4 dla kazdego\13.doc
C:\Dokumenty\Roboczy\Delphi 4 dla kazdego\13.doc 513
Rozdział 13. ♦ Zagadnienia nieco bardziej skomplikowane 555