FORMULARZ
Tworzenie okien o dowolnych kształtach.
Borland C++ Builder podczas uruchamiania tworzy domyślnie okno formularza o prostokątnym kształcie. Jednak nie jesteśmy skazani tylko na okna w takim kształcie. W prosty sposób można utworzyć formularz w kształcie np. Elipsy.
// Plik źródłowy np. Unit1.cpp |
Można również w podobny sposób utworzyć formularz w kształcie prostokąta o zaokrąglonych rogach.
// Plik źródłowy np. Unit1.cpp |
...lub też w kształcie prostokąta o ostrych rogach.
// Plik źródłowy np. Unit1.cpp |
Jednak w podanych przykładach tworzone są tylko bardzo proste kształty, można jednak tworzyć również kombinacje podanych przykładów tworząc bardziej złożone kształty. W tym celu należy posłużyć się funkcją 'API' - 'CombineRgn()'.
// Plik źródłowy np. Unit1.cpp |
W podanym przykładzie formularz został stworzony z trzech kształtów: elipsy, prostokąta oraz prostokąta z zaokrąglonymi kształtami. Tworzenie dwóch kształtów jest znacznie prostsze.
// Plik źródłowy np. Unit1.cpp |
Formularza o taki kształtach nie można przesuwać, ponieważ nie posiada belki tytułowej, dlatego trzeba dołączyć procedurę przesuwania formularza. Opis takiej procedury został umieszczony w kolejnej poradzie.
Przesuwanie formularza po uchwyceniu go w dowolnym miejscu - sposób 1.
Przesuwanie formularza po uchwyceniu go w dowolnym miejscu opiera się na 'przekonaniu' formularza, że uchwyciliśmy belkę tytułową. Najpierw przechodzimy do pliku nagłówkowego (np. Unit1.h) i umieszczamy w sekcjach 'private:' i 'public:' deklarację funkcji obsługującej komunikat 'WM_NCHITTEST' oraz mapę komunikatu.
// Plik nagłówkowy np. Unit1.h. |
Następnie przechodzimy do pliku źródłowego (np. Unit1.cpp) i dodajemy funkcję obsługującą komunikat.
// Plik źródłowy np. Unit1.cpp |
...no i tyle wystarczy.
...powrót do menu.
Przesuwanie formularza po uchwyceniu go w dowolnym miejscu - sposób 2.
Drugi sposób jest znacznie prostszy, pozwala również na przesuwanie formularza po uchwyceniu obiektów znajdujących się na nim. Najpierw utwórzmy nowy projekt i umieśćmy na nim np. komponent Image, który posłuży nam do zobrazowania przykładu przesuwania formularza po uchwyceniu obiektu znajdującego się na nim. Następnie przejdźmy do pliku nagłówkowego (np. Unit1.h) i w sekcji private umieśćmy definicję funkcji 'void __fastcall Przesun(TMouseButton Button);'.
// Plik nagłówkowy np. Unit1.h. |
Teraz przejdźmy do pliku źródłowego (np. Unit1.cpp) i dodajmy obsługę nowo utworzonej funkcji.
// Plik źródłowy np. Unit1.cpp
ReleaseCapture(); |
Następnie w zdarzeniu formularza - 'OnMouseDown' i w zdarzeniu Image - 'OnMouseDown ' wywołajmy funkcję 'Przesun...'.
// Plik źródłowy np. Unit1.cpp TShiftState Shift, int X, int Y) TShiftState Shift, int X, int Y) |
...i w zasadzie to wszystko, tak jak to pokazano w powyższym przykładzie funkcję 'Przesun(TMouseButton Button) ' można wywołać dla każdego komponentu wyposażonego w zdarzenie 'OnMouseDown'.
Dynamiczne tworzenie okna formularza.
Dynamiczne tworzenie okna formularza polega np. na wywoływaniu obiektu 'Form2' z poziomu obiektu 'Form1'. Dynamiczne - ponieważ obiekt nie jest tworzony w czasie uruchamiania programu, lecz wtedy gdy jest potrzebny i jest usuwany gdy już go nie potrzebujemy. Co to daje? No cóż oszczędza pamięć RAM, jeżeli obiekt nie jest tworzony w czasie uruchamiania programu to nie trzeba mu przydzielać pamięci, natomiast gdy zostanie już utworzony i następnie zamknięty to zwolni pamięć którą zajmował. W prezentowany przykładzie utworzymy dwa formularze - 'Form1' i 'Form2'. 'Form1' będzie oknem głównym programu, 'Form2' natomiast będzie tworzony dynamicznie. Na obydwu formularzach umieszczamy przyciski 'Button1'. Po kliknięciu na przycisk znajdujący się na 'Form1', zostanie
utworzony 'Form2' a 'Form1' zostanie ukryty. Natomiast po kliknięciu na przycisk znajdujący się na 'Form2' - 'Form1' zostanie przywołany a 'Form2' - usunięty z pamięci.
W tym celu w menu programu Borland C++ Builder, w Project|Options (sugeruję się menu BCB 4, w innych wersjach Options może być gdzie indziej), odnajdujemy zakładkę Froms i w oknie Auto-create forms: odnajdujemy wpis Form2, a nastęnie przenosimy go do okna: Available forms:.
Następnie przechodzimy do pliku źródłowego Unit1.cpp i w sekcji include dodajemy wpis: #include "Unit2.h".
// Plik źródłowy Unit1.cpp |
// Plik źródłowy Unit2.cpp |
// Plik źródłowy Unit1.cpp |
// Plik źródłowy Unit2.cpp |
Ukrywanie i wyświetlanie formularza.
W celu ukrycia formularza możemy posłużyć się metodą 'Hide' lub po prostu uczynić formularz niewidocznym poprzez ustawienie jego właściwości 'Visible' na false. Natomiast, żeby wyświetlić formularz można posłużyć się metodą 'Show' lub po prostu uczynić formularz widocznym poprzez ustawienie jego właściwości 'Visible' na true.
W prezentowanym przykładzie sprawimy, żeby po kliknięciu przycisku 'Button1' na formularzu 'Form1' wywołać formularz 'Form2' i ukryć formularz 'Form1'. W tym celu tworzymy dwa formularze 'Form1' i 'Form2'. 'Form1' będzie formularzem głównym więc w Object Inspector w zakładce Properties jego właściwość 'Visible' powinna być ustawiona na true, natomiast właściwość 'Visible'
dla formularza 'Form2' powinna być ustawiona na false (gdyby właściwość 'Visible' była ustawiona dla obydwu formularzy na true, to po uruchomieniu programu obydwa formularze byłyby widoczne).
Trzeba jeszcze "powiadomić" obydwa formularze o własnym istnieniu dlatego w plikach źródłowych Unit1.cpp i Unit2.cpp w sekcji include umieszczamy wpisy: #include "Unit2.h" i w drugim pliku: #include "Unit1.h".
// Plik źródłowy Unit1.cpp |
// Plik źródłowy Unit2.cpp |
// Plik źródłowy Unit1.cpp |
// Plik źródłowy Unit2.cpp |
Rysowanie na pasku tytułowym formularza.
Jedyne co możemy zmienić na pasku tytułowym formularza - TForm to jest właściwość Caption i ikony do minimalizacji, maksymalizacji i zamykania. Istnieje jednak pewien sposób by umieścić tam własny tekst lub rysunek. Służy do tego funkcja API - GetWindowDC i należy ją wywoływać w zdarzeniu OnPaint formularza:
// Plik źródłowy Unit1.cpp |
Trzeba wiedzieć, że funkcja GetWindowDC pobiera uchwyt nie tylko do paska tytułowego, ale do całego formularza dlatego gdybym zrobił tak:
Pierwszy parametr X Drugi parametr Y |
...to tekst został by narysowany na formularzu poniżej paska tytułowego, ponieważ pierwszy parametr określa odległość o lewego brzegu formularza w poziomie, natomiast parametr drugi określa odległość od góry formularza w pionie.
Żeby umieścić na formularzu bitmapę, trzeba najpierw przygotować sobie odpowiedni plik w formacie BMP a następnie należy postąpić tak:
// Plik źródłowy Unit1.cpp |
Przechwyceniu komunikatu o przesunięciu formularza.
Tworząc pewien program stanąłem przed problemem jak sprawdzić w którym momencie formularz jest przesuwany. Spróbujcie zainicjować wykonanie dowolnej instrukcji ale tylko wtedy gdy formularz np. Form1 jest przesuwany, a zobaczycie że nie istnieje żadne zdarzenie, które dałoby zadowalające efekty i OnMouseMove nie przyda się na wiele.
Załóżmy, że potrzebujemy umieścić kod uniemożliwiający przesunięcie formularza poza krawędzie pulpitu. Żeby zrealizować to zadanie przechodzimy do pliku nagłówkowego (np. Unit1.h) i w sekcji private definiujemy funkcję SetMoveWindow, przy czym nazwa funkcji jest dowolna. W sekcji public umieszczamy mapę komunikatu:
// Plik nagłówkowy np. Unit1.h |
Następnie przechodzimy do pliku źródłowego (np. Unit1.cpp) i definiujemy zadeklarowaną funkcję SetMoveWindow:
// Plik źródłowy np. Unit1.cpp |
Tak zdefiniowana funkcja przechwytuje już komunikat przesunięcia formularza Form1 jednak nie wykonuje żadnych instrukcji ponieważ ich tam nie ma. Umieszczę tam kod uniemożliwiający przesunięcie formularza Form1 poza krawędzie pulpitu:
// Plik źródłowy np. Unit1.cpp |
Jak uczynić formularz przeźroczystym?
Żeby
uczynić formularz przeźroczystym należy utworzyć metodę CreateParams.
Przedstawiona tutaj metoda sprawdzi się tylko w nieruchomych formach, ponieważ
po przesunięciu formularza po pulpicie przeźroczystość zostanie utracona:
// Plik nagłówkowy np Unit1.h |
// Plik źródłowy np. Unit1.cpp |
Umieszczanie grafiki na pasku tytułowym.
Żeby umieścić bitmapę na pasku tytułowym formularza należy posłużyć się funkcją API - GetWindowDC:
// Plik nagłówkowy np Unit1.h |
// Plik źródłowy np. Unit1.cpp |
Blokowanie zmiany rozmiaru, przenoszenia i zamykania formularza.
Pokażę kilka funkcji umożliwiających ograniczenie pewnych funkcji formularza.
Posłużę się funkcją API GetSystemMenu, która zwraca uchwyt do kopii menu
systemowego. Jako pierwszy argument funkcja pobiera uchwyt do menu systemowego,
a jako drugi wartość typu bool, jeżeli wartość ta jest ustawiona na FALSE, to
włącza się blokowanie funkcji formularza w przeciwnym razie zablokowane funkcje
zostają odblokowane.
Przykład pierwszy blokuje możliwość zmiany rozmiaru formularza:
// Plik źródłowy np. Unit1.cpp |
Przykład drugi blokuje
możliwość przenoszenia formularza:
// Plik źródłowy np. Unit1.cpp |
Przykład trzeci blokuje
możliwość zamknięcia formularza:
// Plik źródłowy np. Unit1.cpp |
Istnieje jeszcze możliwość blokowania 'Przywracania', 'Minimalizacji' i 'Maksymalizacji' formularza, jednak nie działa to w środowisku Windows XP.
SC_RESTORE - Przywracanie formularza
SC_MINIMIZE - Minimalizacja formularza
SC_MAXIMIZE - Maksymalizacja formularza
Przywracanie funkcji menu systemowego przebiega podobnie, trzeba jedynie zmienić wartość FALSE na TRUE:
// Plik źródłowy np. Unit1.cpp |
Jeżeli chodzi o blokowanie możliwości zamknięcia formularza, to istnieją jeszcze dwa sposoby, które działają podobnie. W pierwszym przypadku należy posłużyć się zdarzeniem OnClose, w drugim OnCloseQuery dla formularza:
Sposób pierwszy:
// Plik źródłowy np. Unit1.cpp |
Sposób drugi:
// Plik źródłowy np. Unit1.cpp |
Tak zablokowany formularz nie pozwoli się zamknąć nawet kombinacją klawiszy Alt+F4, można przywrócić opcję zamykania formularza poprzez zmianę tych parametrów na: Actrion = caFree; i CanClose = true;. Ustawienia te muszą być wprowadzone w tych samych zdarzeniach, a oto przykład:
// Plik źródłowy np. Unit1.cpp |
Podobnie będzie wyglądało to w zdarzeniu OnCloseQuery, należy tylko posłużyć się funkcją CanClose. Na zakończenie jeszcze jedna ciekawostka, otóż można zamiast zamknięcia formularza wymusić na nim minimalizację, tak przynajmniej jest to opisane w pliku pomocy BCB, jednak nie potwierdza się to w praktyce, w środowisku Windows XP to nie zadziałało:
// Plik źródłowy np. Unit1.cpp |
M
ożna oczywiście zminimalizować okno, zamiast je zamykać, jednak trzeba posłużyć się inną funkcją:
// Plik źródłowy np.
Unit1.cpp |
Program zawsze można zamknąć używając funkcji Terminate:
// Plik źródłowy np.
Unit1.cpp |
Ta funkcja powoduje jednak zamknięcie całej aplikacji, a nie wybranego okna.
Tworzenie formy o dowolnych kształtach poprzez nałożenie maski w formie bitmapy.
Przedstawiona tu porada
bazuje na poradzie tworzenie okien o dowolnych kształtach i cała sztuka polega
na traktowaniu pojedynczego piksela jako regionu, a potem połączenie wszystkich
regionów w jeden, żeby zastosować tą poradę należy umieścić na formularzu obiekt
Image1 i wczytać do niego bitampę, która będzie maską dla formularza. Formularz
zostanie obcięty w oparciu o kolor czarny, ale można stosować inne kolory
poprzez modyfikowanie zmiennej kolor:
// Plik źródłowy np. Unit1.cpp |
Opracował: Artur Wojnar.
Przedstawiona metoda ma dość istotny mankament, im większa grafika, tym dłużej trwa obcinanie formularza, dlatego pozwoliłem sobie nieco zmodyfikować ten kod, w oparciu o kod kolegi, poprzez zastosowanie funkcji ScanLine:
// Plik źródłowy np. Unit1.cpp |
Niestety i to tylko w niewielkim stopniu skraca czas obcinania formularza, wpadłem na pomysł, żeby podzielić maskę (bitmapę) na cztery części i każdą część obcinać oddzielnie, jednak obcinanie nawet czterech części w czterech różnych pętlach niczego nie zmieni, ponieważ każda kolejna pętla będzie zaczynała obieg dopiero gdy poprzednia zakończy swój, więc żeby rozwiązać ten problem należy utworzyć cztery wątki, i w każdej umieścić pętlę, oczywiście znacznie lepszym pomysłem jest stworzenie funkcji tworzącej regiony i wywoływanie jej w każdym wątku z innymi parametrami, w ten sposób uniknie się powielania kodu. Oczywiście gdy każdy wątek zakończy obcinanie swojego regionu należy to wszystko jakoś połączyć w całość, początkowo myślałem o zastosowaniu obiektu Timer, jednak lepszym rozwiązaniem jest stworzenie piątego wątku i zrealizowanie tego zadania w nim, a oto kompletny kod:
// Plik nagłówkowy np Unit1.h |
// Plik źródłowy np. Unit1.cpp Form1->Handle, Form1->hRgn1); Form1->Image1->Picture->Bitmap, Form1->Handle, Form1->hRgn2); Form1->Image1->Picture->Bitmap, Form1->Handle,
Form1->hRgn3); Form1->Image1->Picture->Bitmap, Form1->Handle,
Form1->hRgn4); |
Jeśli chodzi o tą poradę to już nic więcej nie wymyślę, żeby skrócić czas obcinania należy zastosować jak największą liczbę wątków dzieląc maskę (bitmapę) na jak największą liczbę części, na zakończenie przykład kombinacji regionów dla dla 8 części:
HRGN hRgn1, hRgn2, hRgn3, hRgn4, hRgn5, hRgn6, hRgn7, hRgn8, hRgn9;
hRgn1 = CreateRectRgn(0, 0, 0, 0); CombineRgn(Form1->hRgn9, Form1->hRgn1, Form1->hRgn2, RGN_OR); |
Jeśli ktoś zna inny sposób na zrealizowanie tego zadania, to chętnie go poznam.
Tworzenie formy o stałej niezmiennej szerokości.
W tej poradzie pokaże jak
za pomocą funkcji przechwytującej komunikaty stworzyć formularz, który nawet
przy maksymalizacji zachowa stałą szerokość, w takim formularzu można zmieniać
rozmiar, ale zmieniała się będzie tylko długość, szerokość zawsze pozostanie
taka sama. W taki sposób skonstruowany jest formularz środowiska BCB, to okno u
góry ekranu na którym znajdują się komponenty:
// Plik nagłówkowy np Unit1.h |
// Plik źródłowy Unit1.cpp |
Komunikat przechwytuje informację o zmianie rozmiaru formularza i zawsze ustawia jego szerokość na 110 pikseli.
...powrót do menu.
Dynamiczne tworzenie formularzy.
Niniejsza porada
stanowi uzupełnienie porady:
dynamiczne tworzenie okna
formularza.
Sposób
dynamicznego tworzenia obiektów jest pewnie wszystkim dobrze znany, w tym także
sposób na tworzenie formularza, dlatego chcę tutaj przedstawić trochę nietypowy
sposób, tzn. ten sposób jest jak najbardziej typowy dla C++ Builder, tylko nie
wszyscy może o nim wiedzą. Prosty sposób na dynamiczne utworzenie formularza
jest taki:
// Plik źródłowy Unit1.cpp |
Proste! W ten sposób utworzymy pusty formularz, taki jaki się tworzy przy uruchamianiu nowego projektu w BCB. Istnieje możliwość dynamicznego utworzenia formularza, będącego kopią już stworzonego formularza, np. gdybyśmy chcieli utworzyć klon formularza Form1:
// Plik źródłowy Unit1.cpp |
W ten sposób powstanie nowy formularz będący dokładną kopią formularza Form jeden ze wszystkimi obiektami, zmiennymi i funkcjami jakie znajdują się na Form1, czyli na MyForm pojawią się dokładnie te same obiekty, odwołanie do tych obiektów odbywa się dokładnie tak samo jak do obiektów na Form1:
// Plik źródłowy Unit1.cpp |
Niejakim problemem może być próba dynamicznego utworzenia formularza o takiej samej nazwie powtórnie, czyli raz utworzyliśmy formularz o nazwie MyForm i próbujemy utworzyć nowy egzemplarz formularza o takiej samej nazwie. W takiej sytuacji program się posypie, ponieważ nie można utworzyć dwóch formularzy o tej samej nazwie. Rozwiązaniem tego problemu jest oczywiście usunięcie formularza przed ponownym jego utworzeniem. Jeżeli tworzymy formularz w oparciu o klasę TForm (nie np. TForm1) wewnątrz jakiegoś zdarzenia to można go usunąć tylko wewnątrz tego zdarzenia, ponieważ poza tym zdarzeniem wydaje się on nie istnieć:
// Plik źródłowy Unit1.cpp |
Rozwiązaniem może być zadeklarowanie formularza w sekcji private lub public pliku nagłówkowego, wtedy będzie możliwe zniszczenie tego obiektu w każdym zdarzeniu:
// Plik nagłówkowy np
Unit1.h |
// Plik źródłowy Unit1.cpp |
Ważnym jest, żeby pamiętać o zniszczeniu formularze, przed ponownym utworzeniem formularza o tej samej nazwie. W sytuacji gdy tworzymy formularz w oparciu o już istniejący (np. Form1) postępujemy tak samo, jednak istnieje sposób na to żeby formularz sam się zniszczył przy zamykaniu. Tworząc dynamicznie formularz w oparciu o istniejący już formularz, np. TForm1 należy w zdarzeniu OnClose dla formularza Form1 dodać kod niszczący:
// Plik źródłowy Unit1.cpp |
W ten sposób każdy dynamicznie utworzony obiekt w oparciu o klasę TForm1
będzie również posiadał kod niszczący go przy zamykaniu, wiąże się to oczywiście
z tym, że po zamknięciu formularza, w tym również Form1 zostanie on zniszczony i
żeby go ponownie uruchomić należy go ponownie utworzyć, nie wystarczy zwykłe
Form1->Show(). Dlatego stosowanie tej metody w odniesieniu do formularza
głównego ma sens ponieważ zamknięcie tego formularza wiąże się z zamknięciem
całej aplikacji.
W przypadku innych formularzy wchodzących w skład aplikacji, ma to również
praktyczne zastosowanie, ale trzeba wyłączyć tworzenie takich formularzy przy
uruchamianiu aplikacji, daje to dodatkowy plus, ponieważ formularz, który będzie
w ten sposób tworzony nie zajmuje miejsca w pamięci dopóki nie zostanie
utworzony, a po zniszczeniu zwalnia pamięć.
Jeżeli w skład aplikacji wchodzą dwa formularze Form1 główny i Form2 dodatkowy i
nie zachodzi potrzeba uruchamiania Form2 przy uruchomieniu aplikacji, to nie
zachodzi również potrzeba tworzenia tego formularza przy uruchomieniu i
niepotrzebnego zajmowania pamięci. Co zrobić? Tworzymy formularz Form2 ze
wszystkim co ma się na nim znaleźć a następnie przechodzimy do menu
Project -> Options na zakładkę Forms i przesuwamy Form2
z okienka Auto-create forms: do okienka Available forms:.
Tak spreparowanego formularza nie da się już wywołać za pomocą funkcji
Form2->Show(), nie da się wogóle wywołać formularza o nazwie Form2,
trzeba zawsze wywoływać formularz pochodzący od klasy TForm2:
// Plik źródłowy Unit1.cpp |
Należy pamiętać o umieszczeniu w zdarzeniu OnCLose dla formularza Form2 metody Action = caFree; celem zniszczenia go przy zamknięciu. Należy oczywiście pamiętać również o włączeniu do pliku źródłowego wpisu:
#include Unit2.cpp - dla Unit2.
Tworzenie przeźroczystego formularza poprzez usunięcie wybranego koloru.
W tej poradzie chcę pokazać sposób nie tylko na utworzenie przeźroczystego
formularza, ale również na utworzenie formularza o dowolnym kształcie
poprzez nałożenie np. grafiki i usunięcie z niej wybranego koloru. W celu
dokładniejszego zrozumienia o co mi chodzi proponuję pobrać
przykład archiwum ZIP 256
KB. Opisywany tutaj problem był już poruszany w poradzie
tworzenie okien o dowolnych kształtach poprzez nałożenie
mapy w formie bitmapy jednak opisany tam sposób jest mało efektywny,
nieekonomiczny i daleki od oczekiwanych rezultatów, jeżeli jednak jesteś
szczęśliwym posiadaczem środowiska Borland C++ Builder w wersji 6
to we właściwościach formularza powinieneś mieć dwie interesujące nas
właściwości: TransparentColor i TransparentColorValue
(wersji 5 nie znam, więc nie wiem czy występują tam takie właściwości).
Ustawienie właściwości TransparentColor na true
sprawia, ze z formularza będzie usuwany, a właściwie czyniony
przeźroczystym kolor określony we właściwości TransparentColorValue. Co
ciekawe przeźroczystym jest ustawiany kolor nie tylko znajdujący się
bezpośrednio na formularzu, ale kolor który znajduje się na wszystkich
obiektach umieszczonych na tym formularzu, czyli jeżeli umieścimy np. na
formularzu obiekt Image i wczytamy do niego obrazek z czarnym tłem i
ustawimy we właściwości TransparentColorValue kolor czarny, to czarne tło
z Image zostanie usunięte razem z tymi częściami formularza, które to tło
przykrywa. Wybranie koloru czarnego jako koloru przeźroczystego nie jest
dobrym pomysłem, ponieważ ten kolor posiadają również czcionki znajdujące
się na obiektach umieszczonych na formie, więc zostaną ustawione jako
przeźroczyste. Proponuje z tym poeksperymentować.
Jeżeli chcemy stworzyć sobie formularz o własnym dowolnym kształcie należy
jeszcze zmienić jeszcze jedną właściwość BorderStyle na
bsNone, po to żeby górna belka i obramowanie formularza nie były
widoczne. W ten sposób można tworzyć formularze o dowolnych kształtach lub
przeźroczyste nie pisząc nawet linijki kodu. Z takim formularzem wiążą się
jednak pewne kłopoty no bo jak go przesuwać jeśli nie ma belki tytułowej,
ten problem da się rozwiązać stosując porady zamieszczone w tym dziale,
inna sprawa to zmiana rozmiaru formularza, na to porady nie ma, ale w
podanym na początku tej porady przykładzie znajduje się kompletny kod
źródłowy, który rozwiązuje ten problem. Podany tam sposób wykorzystuje
przypisanie polimorficzne (o czym było już na stronie) oraz komponent
ApplicationEvents, ale ogólnie cały kod jest krótki prosty i zrozumiały.
Na zakończenie kilka praktycznych porad. Przede wszystkim jako grafiki z tłem do usunięcia można użyć dowolnego formatu graficznego o ile posiadamy odpowiednie biblioteki, czyli nie musi to być wcale bitmapa, lecz może to być również format JPEG, z tym że JPEG jest formatem stratnym, więc należy zwrócić szczególną uwagę na tło, ponieważ po przygotowaniu grafiki w tym formacie i ustawienia koloru jako tła może się okazać, że po zapisaniu zmieniły się parametry tła, i że nie jest ono jednolite, a właściwość TransparentColorValue usuwa dokładnie jeden kolor bez żadnych kolorów pośrednich. Jeżeli w trakcie tworzenia grafiki ustawię jako tło kolor clLime (dobry pomysł, kolor rzadko stosowany), wartość RGB wynosi R = 0, G = 255, B = 0, to po zapisaniu do JPEG może się okazać, że wartości RGB uległy lekkiemu przesunięciu na np. R = 1, G = 255, B = 0 (HEX = 0x0000FF01). Dla oka taka zmiana będzie niezauważalna, ale formularz nie rozpozna już tego koloru i go nie usunie ponieważ będzie miał ustawiony kolor clLime a nie 0x0000FF01, a o taką pomyłkę w przypadku formatu JPEG nie jest trudno, zresztą przy tworzeniu bitmapy w 255 kolorach również może nastąpić przesunięcie koloru. Najlepiej to widać na zamieszczonych poniżej rysunkach:
|
|
![]() |
||
bitmapa, 24 bity |
bitmapa, 8 bitów |
JPEG, 24 bity |
Na pierwszy rzut oka wyglądają identycznie, ale tylko w przypadku bitmap można usunąć zielone tło, ale już w formacie JPEG tło nie jest jednolite pomimo iż na takie wygląda i nie zostanie prawidłowo usunięte. Kolejna sprawa to ustawianie przeźroczystego tła w obiekcie Image, można je ustawiać, jednak kolor zdefiniowany jako przeźroczysty w Image nie może być tym samym kolorem który został zdefiniowany we właściwości TransparentColorValue formularza. Ciekawe efekty można uzyskać umieszczając na formularzu takie obiekty jak np. Panel, Pie czy Shape i nadając im odpowiedni kolor. Można eksperymentować z dowolnym obiektem posiadającym kolor, który można zmienić lub zdefiniować jako ten do usunięcia z formularza razem z formularzem, który przysłania.
Sklejanie formularzy, jednoczesne przesuwanie dwóch formularzy. - opracował: euraziel
Poradę tą
nadesłał euraziel i pokazuje ona jak w prosty sposób przesuwać dwa formularze na
raz, czyli jeżeli przesuwamy formularz pierwszy to jednocześnie przesuwa się
formularz drugi zachowując położenie względem formularza pierwszego. Pozwoliłem
sobie wprowadzić kilka kosmetycznych zmian do kodu, rezygnując z mapy
komunikatów na rzecz klasy TWndMethod, jest to o tyle leprze rozwiązanie, że
wykorzystując jedną funkcję można obsłużyć wiele komunikatów wysyłanych z/do
aplikacji, w przykładzie funkcja OnFormMove będzie przechwytywała tylko
komunikat o przesunięciu formularza WM_MOVING.
// Plik nagłówkowy np Unit1.h |
// Plik źródłowy Unit1.cpp |
Upuszczanie plików na formularz.
Upuszczanie plików
na formularz sprowadza się właściwie tylko do stworzenia obsługi komunikatu
WM_DROPFILES. Na przykładzie wczytywania do obiektu Image1 pliku
upuszczonego na formularz pokażę jak należy to robić. Zanim przystąpimy do
tworzenia kodu umieśćmy na formularzu obiekty Image1 i Label1. Dla obiektu
Image1 zmieniamy jego właściwości Stretch na true i
Proportional również na true. Właściwość Stretch będzie dostosowywała
automatycznie rozmiar wczytanej grafiki do rozmiaru obiektu Image, w właściwość
Proportional pozwoli zachować prawidłowe proporcje obrazka. Do obiektu Label1
zostanie wczytana ścieżka dostępu do upuszczonego pliku. Obiekt Image domyślnie
nie obsługuje plików *.jpg, żeby mu to umożliwić należy w pliku nagłówkowym w
sekcji include dodać bibliotekę #include <JPEG.HPP>.
Przystępujemy do tworzenie kodu, w tym celu w pliku nagłówkowym w sekcji
private umieszczamy następujące deklaracje:
// Plik nagłówkowy np Unit1.h |
Tworzenie obsługi
komunikatów opisywałem już wielokrotnie przy okazji innych porad, więc tym razem
odpuszczę to sobie.
Przechodzimy teraz do pliku źródłowego i w konstruktorze klasy formularza
umieszczamy kod łączący metodę Df z funkcją OnDropFiles, oraz włączamy
akceptowanie przez formularz upuszczanych przez niego plików:
// Plik źródłowy Unit1.cpp |
Następnie tworzymy dla formularza zdarzenie OnClose i umieszczamy kod anulujący akceptowanie upuszczanych plików.
// Plik źródłowy Unit1.cpp |
Teraz tworzymy definicję funkcji OnDropFiles odpowiedzialnej za przechwytywanie komunikatu o upuszczeniu pliku na formularz i realizującej działania na tym pliku:
// Plik źródłowy Unit1.cpp
nFiles = DragQueryFile((HDROP)Msg.WParam,
0xFFFFFFFF, NULL, 0); |
Tak skonstruowany kod będzie przechwytywał ścieżkę dostępu do upuszczonego pliku i będzie go ładował do obiektu Image1 niezależnie od tego gdzie ten obiekt zostanie upuszczony na formularzu, czyli nie tylko na obiekt Image1. Jeżeli chcemy, żeby ładowanie obrazu do Image1 było realizowane tylko w sytuacji gdy plik zostanie upuszczony na ten obiekt, należy nieco zmodyfikować kod:
// Plik źródłowy Unit1.cpp
nFiles = DragQueryFile((HDROP)Msg.WParam,
0xFFFFFFFF, NULL, 0); |
W ten sposób można przechwytywać ścieżkę dostępu do dowolnego upuszczonego pliku na formularzu i zaznaczam tutaj, że upuszczenie pliku na formularzu nie oznacza, że ten plik zostaje włączony do zasobów aplikacji, lecz program przechwytuje tylko ścieżkę dostępu do tego pliku, co w zupełności wystarcza, żeby wykonać dowolne operacje na takim pliku.
...powrót do menu.
Minimalizacja okna głównego bez minimalizacji okien wtórnych.
Każda aplikacja
okienkowa posiada główne okno programu, jeżeli aplikacja posiada więcej niż
jedno okno i wywołamy to okno (wtórne - drugie), i zminimalizujemy okno główne,
to okno wtórne również się zminimalizuje, ponieważ minimalizacja okna głównego
oznacza minimalizację całego programu. Istnieje jednak sposób żeby to obejść,
należy umieścić w formularzu wtórnym funkcję CreateParams, która to
funkcja jest ładowana przy uruchamianiu aplikacji, a konkretnie przy tworzeniu
okna. Funkcję deklarujemy w pliku nagłówkowym i definiujemy ją w pliku
źródłowym, robimy to we wszystkich oknach wtórnych, funkcji nie należy
umieszczać w formularzu głównym.
// Plik nagłówkowy np Unit2.h |
// Plik źródłowy Unit2.cpp |
Tej funkcji się ni wywołuje, jest ona wywoływana automatycznie podczas tworzenia okna programu.
...powrót do menu.
Okna MDI (Child) bez paska tytułowego i ramki - opracował: Paweł Pecio
Ostatnio sporo czasu
potrzebowałem na odnalezienie w Internecie sposobu na uzyskanie okna potomnego
MDI (MDI Child) bez paska tytułowego i ramki. Zwykłe ustawienie BorderStyle na
bsNone nie działa. Po kilku godzinach poszukiwań znalazłem kilka przydatnych
wskazówek i udało mi się sklecić takie rozwiązanie tego problemu:
W pliku nagłówkowym formy mającej być oknem MDI trzeba dodać w sekcji public:
// Plik nagłówkowy np Unit2.h |
W pliku źródłowym:
// Plik źródłowy Unit2.cpp |
W ten sposób można też modyfikować też inne style tworzonych okien.
Struktura Params zawiera też kilka innych ciekawych pozycji jak np.
Caption, ale nie sprawdzałem czy działa, zapewne potem VCL i tak ustawia
tytuł okna na zdefiniowany we właściwościach formularza, oraz StyleEx,
która zawiera dodatkowe style okien.
...powrót do menu.