Prezentowane materiały są przeznaczone dla uczniów szkół ponadgimnazjalnych. Autor artykułu: mgr Jerzy Wałaszek, wersja1.0 |
©2013 mgr
Jerzy Wałaszek
|
Uruchom Code::Blocks, wybierz opcję Create a new project, a następnie w okienku dialogowym New from template wybierz Win32 GUI Project. Resztę zrób standardowo, jak dla aplikacji konsoli. Usuń z edytora program szablonowy i na jego miejsce wstaw poniższy kod:
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Prosty program dla Windows // PRG_004 //----------------------------------- #include <windows.h> int main() { MessageBox(0, "Witaj w świecie okienkowym\nI-LO w Tarnowie", "Pierwszy program dla MS-Windows", MB_OK | MB_ICONINFORMATION); ExitProcess(0); } |
Wybierz z menu opcję Project → Properties. W okienku dialogowym Project/target options wybierz zakładkę Build targets. W sekcji Selected build target options zmień typ na GUI application. Zatwierdź okienko dialogowe klikając na przycisk OK. Zmiana ta spowoduje, że nie będzie tworzone okno konsoli.
Skompiluj i uruchom program. Jeśli wszystko jest OK, to na ekranie pojawi się następujące okienko:
W systemie Windows program musi współpracować ze środowiskiem graficznym. Dlatego programowanie MS-Windows wymaga dobrej znajomości sposobu pracy systemu oraz setek funkcji systemowych (tzw. API – Aplication Program Interface : Interfejs Programowy Aplikacji), za pomocą których komunikujemy się z różnymi składnikami systemu.
W pierwszym programie wykorzystujemy dwie procedury systemowe, które wykonają za nas "czarną robotę". Ich deklaracje są umieszczone w pliku nagłówkowym windows.h, który bezwarunkowo musi być dołączony do aplikacji tworzonych dla środowiska MS-Windows.
Już w tak prostym programie występuje kilka pojęć, które musimy dobrze poznać, aby efektywnie programować okienka:
System MS-Windows jest wielozadaniowym systemem operacyjnym (ang. multitasking operating system). Oznacza to, iż naraz w pamięci komputera może być uruchomione wiele różnych programów. Jeśli komputer posiada jeden procesor, to oczywiście programy te nie wykonują się jednocześnie. System zarządzania wykonaniem zadań (ang. task scheduler) tworzy tzw. kolejkę procesów, na której umieszcza odwołania do uruchomionych programów lub ich procesów – jeden program może utworzyć kilka równoległych procesów, które nazywamy wątkami (ang. threads).
Programy w kolejce uruchamiane są naprzemiennie co pewien czas. Działają przez krótką chwilę, następnie są zamrażane i z kolejki uruchomiony zostaje kolejny program (proces). Dzięki temu użytkownikowi "wydaje" się, iż jednocześnie pracuje wiele programów.
Gdy proces kończy swoje działanie, musi o tym poinformować system. Do tego celu służy właśnie wywołanie funkcji ExitProcess(). Parametrem jest kod wyjścia, czyli 32-bitowa liczba zwracana przez proces przy zakończeniu. Ponieważ procesy mogą być tworzone jako wątki w innych procesach, kod wyjścia służy często jako informacja, czy dany proces zakończył się poprawnie, czy też nie. U nas nie wykorzystujemy kodu wyjścia, zatem funkcję wywołujemy z parametrem 0:
ExitProcess(0);
Ponieważ w pamięci może znajdować się kilka programów, nie mogą nawzajem zakłócać się. W tym celu w systemie MS-Windows istnieje mechanizm ochrony pamięci przydzielanej procesom. Mechanizm ten odnosi się również do danych wewnętrznych systemu MS-Windows. Oznacza to, iż procesy nie mogą odwoływać się bezpośrednio do obszarów danych systemu lub innych procesów – muszą to robić za pomocą odpowiednich funkcji systemowych.
Problem ten rozwiązano za pomocą tzw. uchwytów lub dojść (ang. handle). Są to liczby całkowite, które jednoznacznie identyfikują wewnętrzne struktury danych Windows. Możemy je traktować jak numery identyfikacyjne. Wiele funkcji systemowych wymaga jako jednego z parametrów właśnie uchwytu np. okienka, kontrolki, urządzenia graficznego itp., na którym dana operacja ma zostać przeprowadzona. Elementy systemu identyfikowane przez uchwyty nazywamy zasobami (ang. resources).
Jeśli uchwyt ma wartość 0, to nie identyfikuje żadnego zasobu.
W systemie MS-Windows jest udostępniona dla programisty biblioteka funkcji o nazwie WinAPI (Windows Application Programming Interface – interfejs programowy aplikacji Windows). W naszym programie wykorzystujemy z WinAPI funkcję MessageBox() (okienko wiadomości), która tworzy proste okienko wyświetlając w nim przekazane w parametrach teksty oraz przyciski. Deklaracja tej funkcji jest następująca:
int MessageBox ( HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType );
HWND | – | typ danych dla uchwytów okienek (ang. Handle of WiNDow). |
LPCTSTR | – | typ danych oznaczający adres (wskaźnik) tekstu zakończonego znakiem '\0' (ang. Long Pointer of ConstanT STRing) |
UINT | – | typ danych oznaczający 32-bitową liczbę całkowitą bez znaku (ang. Unsigned INTeger). |
hWnd | – | określa uchwyt okna procesu, z którego wywołana została funkcja MessageBox(). W naszym programie nie utworzyliśmy żadnego okna, dlatego jako uchwyt przekazaliśmy wartość 0. |
lpText | – | wskaźnik tekstu, który zostanie umieszczony w oknie informacyjnym. Tekst może składać się z kilku wierszy zakończonych znakiem '\n'. |
lpCaption | – | wskaźnik tekstu, który pojawi się na pasku tytułowym okienka informacyjnego |
uType | – | określa rodzaj okna. Głównie chodzi tutaj o zestaw
przycisków, które pojawią się w obszarze okna oraz o ikonkę
umieszczaną po lewej stronie tekstu. Możliwych wartości jest
dużo. My zastosowaliśmy stałą MB_OK,
która powoduje wyświetlenie pod tekstem pojedynczego
przycisku OK. Kliknięcie w ten przycisk kończy działanie
funkcji MessageBox(). Typ okna informacyjnego możemy zestawiać z poniższych grup stałych za pomocą operatora C++ | (suma bitowa): Grupa stałych określająca wyświetlane przyciski: MB_ABORTRETRYIGNORE – przyciski Przerwij, Ponów próbę, Ignoruj MB_HELP – oprócz przycisku OK w okienku pojawi się przycisk Pomoc. Kliknięcie w ten przycisk lub naciśnięcie klawisza F1 spowoduje wysłanie przez Windows do właściciela okna informacyjnego wiadomości WM_HELP. Obsługą takich wiadomości zajmujemy się w dalszych przykładach. MB_OK – pojawi się jeden przycisk OK. Jest to wartość standardowa. MB_OKCANCEL – pojawią się przyciski OK i Anuluj. MB_RETRYCANCEL – pojawią się przyciski Ponów próbę i Anuluj MB_YESNO – pojawią się przyciski Tak i Nie MB_YESNOCANCEL – pojawią się przyciski Tak, Nie i Anuluj Do powyższych wartości możemy dołączyć (operator |) stałe określające rodzaj wyświetlanej ikony w okienku informacyjnym: MB_ICONEXCLAMATION – pojawi się ikona znaku z wykrzyknikiem. MB_ICONINFORMATION – pojawi się dymek z małą literką i. MB_ICONQUESTION – pojawi się dymek z pytajnikiem. MB_ICONSTOP – system wygeneruje krótki dźwięk i pojawi się ikona stopu. Kolejna grupa stałych określa, który z przycisków (jeśli jest ich więcej niż 1) okienka będzie przyciskiem standardowym – takim, który jest uaktywniany po naciśnięciu klawisza Enter. MB_DEFBUTTON1 – pierwszy
przycisk okna staje się przyciskiem standardowym. Jest
to wartość domyślna. |
Zwróć uwagę, iż funkcja MessageBox() zwraca wartość typu int. Wartość ta zależy od wybranego typu okna (parametr uType) i może być następująca:
IDABORT – wybrano przycisk Przerwij
IDCONTINUE, IDRETRY, IDTRYAGAIN – wybrano przycisk Ponów próbę
IDCANCEL – wybrano przycisk Anuluj
IDIGNORE – wybrano przycisk Ignoruj
IDNO – wybrano przycisk Nie
IDOK – wybrano przycisk OK
IDYES – wybrano przycisk Tak.
Chociaż MessageBox() służy głównie do powiadamiania użytkownika o jakiś zdarzeniach powstałych w trakcie pracy programu (najczęściej są to komunikaty błędów), to poniżej mamy przykładową grę w odgadywanie liczb od 0 do 15 wykorzystującą tą funkcję. Zasady są bardzo proste – użytkownik wymyśla sobie liczbę od 0 do 15, a następnie odpowiada na pytania komputera. Komputer odgadnie liczbę po 4 pytaniach.
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Prosta gra dla Windows // PRG_005 //----------------------------------- #include <windows.h> #include <sstream> #include <ctime> using namespace std; int main() { char tytul[] = "ZGADYWANKA (C)2013 I-LO w Tarnowie"; int i,j; MessageBox(0, "Wymyśl liczbę z zakresu od 0 do 15 i kliknij OK", tytul, MB_OK | MB_ICONINFORMATION); // generujemy tablicę masek bitowych int maski[] = {1,2,4,8}; srand((unsigned)time(NULL)); for(i = 0; i < 50; i++) { int x = rand() % 4; int y = rand() % 4; int z = maski[x]; maski[x] = maski[y]; maski[y] = z; } // zadajemy 4 pytania, w każdym umieszczamy liczby zawierające // bit z bieżącej maski. Odpowiedź TAK powoduje dodanie maski do // zgadywanej liczby int liczba = 0; for(i = 0; i < 4; i++) { ostringstream sout; sout << "Pytanie nr " << i + 1 << "\n\nCzy twoja liczba jest jedną z liczb : \n\n"; for(j = 1; j < 16; j++) if(j & maski[i]) sout << j << " "; if(MessageBox(0, sout.str().c_str(), tytul, MB_YESNO | MB_ICONQUESTION) == IDYES) liczba += maski[i]; } // Wyświetlamy wynik ostringstream sout; sout << "Twoją liczbą jest liczba " << liczba; MessageBox(0, sout.str().c_str(), tytul, MB_OK | MB_ICONINFORMATION); // Koniec ExitProcess(0); } |
Efekt uruchomienia programu |
---|
|
int maski[] = {1,2,4,8}; srand((unsigned)time(NULL)); for(int i = 0; i < 50; i++) { int x = rand() % 4; int y = rand() % 4; int z = maski[x]; maski[x] = maski[y]; maski[y] = z; } |
Liczbę użytkownika odgadujemy testując, czy zawiera odpowiednie wagi
binarne 1,2,4 i 8. Na przykład liczba 13 w zapisie binarnym postać: Oznacza to, iż zawiera wagi 8, 4 i 1. Do testowania
wag będziemy używali tablicy maski[]. Jednakże, aby zdezorientować
użytkownika, zawartość tablicy pomieszamy losowo. Np. jeśli na początku
ma ona zawartość:
|
int liczba = 0; |
W tej zmiennej będziemy zbierać wagi wskazane przez użytkownika, które występują w jego liczbie. |
for(int i = 0; i < 4; i++) { ostringstream sout; sout << "Pytanie nr " << i + 1 << "\n\nCzy twoja liczba jest jedną z liczb : \n\n"; for(int j = 1; j < 16; j++) if(j & maski[i]) sout << j << " "; if(MessageBox(0, sout.str().c_str(), tytul, MB_YESNO | MB_ICONQUESTION) == IDYES) liczba += maski[i]; } |
Wykonujemy cztery obiegi pętli. W każdym obiegu
zadajemy pytanie, czy liczba wymyślona przez użytkownika znajduje się
wśród liczb zawierających wagę Na uwagę zasługuje typ |
ostringstream sout; sout << "Twoją liczbą jest liczba : " << liczba; MessageBox(0, sout.str().c_str(), tytul, MB_OK | MB_ICONINFORMATION); |
Gdy pętla wykona 4 pełne obiegi, w zmiennej liczba
otrzymamy wynik – liczbę
wymyśloną przez użytkownika. Wykorzystując ponownie klasę |
ExitProcess(0); } |
Kończymy działanie programu. |
// Koło Informatyczne I LO w Tarnowie // (C)2013 mgr Jerzy Wałaszek // Proste okienko w Windows // PRG_006 //----------------------------------- #include <windows.h> // Tutaj umieszczamy nazwę klasy, wg której powstanie nasze okno // Jest to zmienna globalna. //-------------------------------------------------------------- char ClassName[ ] = "SimpleWinClass"; // Procedura obsługująca wiadomości dla okna, wywoływana przez // MS-Windows przy okazji różnych zdarzeń, które należy obsłużyć //-------------------------------------------------------------- LRESULT CALLBACK WndProc(HWND h, UINT uMsg , WPARAM wP, LPARAM lP) { if(uMsg == WM_DESTROY) // Jeśli wiadomość dotyczy zamknięcia okna, { PostQuitMessage(0); return 0; // to je zamykamy i kończymy funkcję } else return DefWindowProc(h,uMsg,wP,lP); // Inaczej każemy systemowi obsłużyć tę wiadomość } // W Windows główna funkcja, od której uruchamiany jest program, // nazywa się WinMain zamiast main. //-------------------------------------------------------------- int WINAPI WinMain (HINSTANCE hinst, HINSTANCE hprevinst, LPSTR cmdLine, int cmdShow) { WNDCLASSEX wc; // struktura klasy okna MSG ms; // przechowuje wiadomości aplikacji HWND h; // Uchwyt naszego okna // Ustawiamy kolejne pola struktury klasy okna wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = wc.cbWndExtra = 0; wc.hInstance = hinst; wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = ClassName; wc.hIcon = wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Rejestrujemy klasę okna w Windows. Jeśli się to nie uda, kończymy program if(!RegisterClassEx(&wc)) return 0; // Gdy klasa jest zarejestrowana, tworzymy na jej podstawie okno h = CreateWindowEx(0, // dodatkowe style okna, 0 oznacza ich brak ClassName, // adres tablicy char z nazwą klasy "Nasze pierwsze okno", // adres łańcucha z napisem tytułowym okna WS_OVERLAPPEDWINDOW, // styl okna CW_USEDEFAULT, // współrzędna X lewego górnego narożnika okna CW_USEDEFAULT, // współrzędna Y lewego górnego narożnika okna 300, // szerokość okna w pikselach, również może być CW_USEDEFAULT 200, // wysokość okna w pikselach, również może być CW_USEDEFAULT 0, // uchwyt nadrzędnego okna, jeśli istnieje 0, // uchwyt menu, jeśli istnieje hinst, // uchwyt programu, który korzysta z okna 0 // adres dodatkowych danych ); // Utworzone okno wyświetlamy na ekranie ShowWindow(h, cmdShow); // Uaktualniamy treść okna UpdateWindow(h); // Rozpoczynamy pętlę obsługi wiadomości napływających do naszego okna. while(GetMessage(&ms, NULL, 0, 0)) // Czekamy na wiadomość { TranslateMessage(&ms); // Przekształcamy wiadomość DispatchMessage(&ms); // Wysyłamy ją do procedury okna } // zwracamy wartość umieszczoną w polu wParam ostatniej wiadomości return ms.wParam; } |
Po skompilowaniu i uruchomieniu ukaże się okienko aplikacji:
Powyższy program wyjaśnia powód nie rozpoczynania nauki programowania od systemu Windows. Jednakże warto dokładnie przeczytać podane tutaj informacje, ponieważ opisane funkcje i struktury występują praktycznie w każdym programie okienkowym. Z drugiej strony nie musimy się wszystkiego dokładnie uczyć na pamięć. Wystarczy, iż wiemy ogólnie, gdzie znaleźć odpowiednie wyjaśnienia i do czego służą opisane funkcje i struktury danych.
Zadaniem naszego programu jest utworzenie jednego prostego, pustego okna aplikacji, które będzie posiadało typowe zachowania okienkowe (na przykład możliwość przesuwania po ekranie, skalowania, sprowadzania na pasek zadań, itp.). W tym celu musimy wykonać następujące kroki:
Do rejestracji klasy okna wykorzystywana jest struktura typu WNDCLASSEX, która posiada następującą deklarację:
typedef struct { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; HICON hIconSm; } WNDCLASSEX, *PWNDCLASSEX;
W definicji pól tej struktury występuje wiele nowych typów danych, zdefiniowanych specjalnie na potrzeby systemu MS-Windows.
UINT – 32-bitowa liczba całkowita bez znaku, odpowiednik C++ to unsigned int. WNDPROC – adres procedury okna, czyli procedury obsługującej napływające dla niego wiadomości. HINSTANCE – uchwyt programu HICON – uchwyt ikony HCURSOR – uchwyt kursora HBRUSH – uchwyt pędzla. LPCTSTR – długi wskaźnik tablicy znaków zakończonych kodem 0
Poszczególne pola struktury WNDCLASSEX posiadają następujące przeznaczenia:
cbSize – określa rozmiar w bajtach struktury WNDCLASSEX. Rozmiar ten otrzymamy z sizeof(WNDCLASSEX). style – określa style okna, które mogą być dowolną kombinacją (za pomocą operatora | ) stałych:
CS_BYTEALIGNCLIENT – Wyrównuje obszar roboczy okna do granicy bajtu w kierunku x. Styl ten wpływa na szerokość okna oraz jego położenie poziome na ekranie monitora.
CS_BYTEALIGNWINDOW – Wyrównuje okno do granicy bajtu w kierunku x. Styl ten wpływa na szerokość okna oraz jego położenie poziome na ekranie monitora.
CS_CLASSDC – Przydziela jeden wspólny kontekst graficzny dla wszystkich okien w tej klasie. Ponieważ klasy okien są zależne od procesów, to możliwe jest utworzenie okien tej samej klasy przez wiele wątków danej aplikacji. Możliwe również jest jednoczesna próba użycia w wątkach tego samego kontekstu graficznego. Gdy tak się stanie, system zezwoli tylko jednemu wątkowi na ukończenie jego operacji graficznych.
CS_DBLCLKS – Wysyła wiadomość o podwójnym kliknięciu myszką do procedury okna, gdy kursor myszki znajduje się w obszarze okna zbudowanego na podstawie tej klasy.
CS_DROPSHADOW – W Windows XP: umożliwia rzucanie cienia przez okno. Efekt ten jest stosowany zwykle przez małe okna, które na krótko pojawiają się na ekranie – np. okna podpowiedzi lub menu, aby stworzyć wrażenie trójwymiarowości.
CS_GLOBALCLASS – Określa, iż dana klasa okien jest globalną klasą aplikacji.
CS_HREDRAW – Przerysowuje całe okno przy zmianie szerokości obszaru roboczego użytkownika.
CS_NOCLOSE – Wyłącza przycisk zamykania okna na pasku tytułowym.
CS_OWNDC – Przydziela oddzielny kontekst graficzny dla każdego okna w tej klasie.
CS_PARENTDC – Ustawia prostokąt obcinania okna potomnego do prostokąta obcinania okna nadrzędnego. Dzięki temu okno potomne może rysować po swoim oknie nadrzędnym.
CS_SAVEBITS – Zachowuje w postaci bitmapy część obrazu ekranu zakrytego przez okno tej klasy. Gdy okno zostanie usunięte, system wykorzysta zapamiętany obraz do przywrócenia ekranu.
Styl ten jest użyteczny dla małych okienek (menu, okna dialogowe, itp.), które są wyświetlane przez krótki okres czasu, a następnie usuwane. Styl ten zwiększa czas wymagany na wyświetlenie okna, ponieważ system musi najpierw przydzielić pamięć na obrazek.
CS_VREDRAW – Przerysowuje całe okno przy zmianie wysokości obszaru roboczego.
lpfnWndProc – adres procedury okna, która przetwarza i obsługuje wiadomości systemowe napływające dla okna utworzonego na podstawie tej klasy.
cbClsExtra – określa liczbę bajtów, którą należy dodatkowo zarezerwować za strukturą klasy okna. System inicjuje te bajty na zero.
cbWndExtra – określa liczbę dodatkowych bajtów, które należy zarezerwować za egzemplarzem okna. System inicjuje te bajty na zero.
hInstance – uchwyt programu zawierającego procedurę okna dla tej klasy.
hIcon – uchwyt ikony klasy. Musi to być uchwyt do zasobu ikon. Jeśli będzie zawierać NULL, to system udostępni standardową ikonę.
hCursor – uchwyt kursora klasy. Musi to być uchwyt do zasobu kursora. Jeśli będzie zawierać NULL, to aplikacja musi osobiście ustawić kształt kursora w momencie, gdy kursor myszki pojawia się ponad okienkiem aplikacji.
hbrBackground – uchwyt pędzla tła klasy. Pole to może być uchwytem do fizycznego pędzla stosowanego do malowania tła lub może być wartością koloru. Stałe koloru wymienione są poniżej. Jeśli stosujemy stałą koloru, to należy dokonać rzutowania typu (HBRUSH):
COLOR_ACTIVEBORDER – kolor aktywnej ramki okna COLOR_ACTIVECAPTION – kolor aktywnego tytułu okna COLOR_APPWORKSPACE – kolor obszaru roboczego aplikacji COLOR_BACKGROUND – kolor tła COLOR_BTNFACE – kolor przycisków COLOR_BTNSHADOW – kolor cienia przycisków COLOR_BTNTEXT – kolor tekstu przycisków COLOR_CAPTIONTEXT – kolor tekstu tytułu okna COLOR_GRAYTEXT – kolor zablokowanego tekstu COLOR_HIGHLIGHT – kolor wyróżnienia COLOR_HIGHLIGHTTEXT – kolor wyróżnionego tekstu COLOR_INACTIVEBORDER – kolor nieaktywnej ramki COLOR_INACTIVECAPTION – kolor nieaktywnego tytułu COLOR_MENU – kolor menu COLOR_MENUTEXT – kolor tekstu menu COLOR_SCROLLBAR – kolor paska przewijania COLOR_WINDOW – kolor okna COLOR_WINDOWFRAME – kolor ramki okna Jeśli pole to zawiera wartość NULL, to aplikacja musi samodzielnie zamalowywać tło okna przy każdym żądaniu malowania obszaru roboczego.
lpszMenuName – wskaźnik nazwy menu klasy. Jeśli pole zawiera NULL, to okna zbudowane na podstawie tej klasy nie będą posiadały żadnego menu.
lpszClassName – wskaźnik nazwy klasy.
hIconSm – wskaźnik małej ikony związanej z klasą okna. Jeśli pole zawiera NULL, system przeszuka zasoby ikon określone przez pole hIcon i tam dobierze odpowiednią ikonę.
Gdy struktura WNDCLASSEX zostanie wypełniona odpowiednimi danymi, używamy jej do rejestracji klasy za pomocą funkcji:
ATOM RegisterClassEx ( CONST WNDCLASSEX *lpwcx );
lpwcx – wskaźnik powyżej opisanej struktury WNDCLASSEX.
Jeśli wywołanie funkcji się powiedzie, to zwracana jest specjalna wartość identyfikująca jednoznacznie rejestrowaną klasę. W przeciwnym razie zwracane jest 0, co właśnie sprawdza nasz program.
Gdy klasa okna zostanie zarejestrowana w systemie Windows, można na jej podstawie tworzyć okna robocze aplikacji. Do tego celu wykorzystujemy funkcję:
HWND CreateWindowEx ( DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam );
dwExStyle – określa rozszerzone style okna dla tworzonego okna. Ponieważ w tym programie nie wykorzystujemy tego parametru, a jest on dosyć rozbudowany, opiszemy go, gdy stanie się potrzebny. lpClassName – wskaźnik tablicy znaków zakończonych kodem 0, zawierających nazwę klasy. lpWindowName – wskaźnik tekstu będącego tytułem okna. dwStyle – określa styl tworzonego okna. Parametr może być dowolną kombinacją następujących styli:
WS_BORDER – tworzy okno z cienką linią ramki WS_CAPTION – tworzy okno z paskiem tytułowym WS_CHILD – tworzy okno podrzędne. Okno zawierające ten styl nie może posiadać paska menu. Również ten styl nie może występować razem ze stylem WS_POPUP. WS_CHILDWINDOW – to samo co WS_CHILD. WS_CLIPCHILDREN – z operacji graficznych okna będą wyłączone obszary przykryte przez okna potomne. WS_CLIPSIBLINGS – powoduje wyłączenie z operacji graficznych dla okien potomnych obszarów zakrytych przez inne okna potomne. Jeśli ten styl nie jest ustawiony, to okna potomne mogą rysować po przykrywającym je obszarze innego okna potomnego. WS_DISABLED – tworzy okno, które jest początkowo zablokowane. Zablokowane okno nie może odczytywać danych od użytkownika. WS_DLGFRAME – tworzy okno posiadające krawędź w stylu typowym dla okienek dialogowych. WS_GROUP – określa pierwszą kontrolkę w grupie kontrolek. WS_HSCROLL – tworzy okno z poziomym paskiem przewijania. WS_ICONIC – tworzy okno, które początkowo jest zminimalizowane do ikony. To samo co WS_MINIMIZE. WS_MAXIMIZE – tworzy okno pierwotnie zmaksymalizowane WS_MAXIMIZEBOX – tworzy okno z przyciskiem maksymalizacji. WS_MINIMIZE – tworzy okno, które początkowo jest zminimalizowane do ikony. To samo co WS_ICONIC. WS_MINIMIZEBOX – tworzy okno z przyciskiem minimalizacji. WS_OVERLAPPED – tworzy okno, które może być przykrywane innymi oknami. Okno takie posiada pasek tytułowy oraz brzeg. WS_OVERLAPPEDWINDOW – tworzy okno zakrywalne z ustawionymi stylami WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, WS_MINIMIZEBOX, oraz WS_MAXIMIZEBOX. WS_POPUP – tworzy wyskakujące okno. WS_POPUPWINDOW – tworzy okno wyskakujące z ustawionymi stylami WS_BORDER, WS_POPUP i WS_SYSMENU. WS_SIZEBOX – tworzy okno z brzegiem zawierającym ikonę skalowania. WS_SYSMENU – tworzy okno z przyciskiem menu systemowego na pasku tytułowym. Styl WS_CAPTION również musi być dołączony. WS_TABSTOP – Określa kontrolkę otrzymującą skupienie przy naciśnięciu klawisza TAB, Naciskanie klawisza TAB zmienia skupienie klawiatury na następną kontrolkę z ustawionym stylem WS_TABSTOP. WS_THICKFRAME – tworzy okienko ze skalowalnym brzegiem – to samo co WS_SIZEBOX. WS_TILED – to samo co WS_OVERLAPPED WS_TILEDWINDOW – to samo co WS_OVERLAPPEDWINDOW WS_VISIBLE – tworzy okno pierwotnie widoczne. Styl ten można włączać i wyłączać wykorzystując funkcje API ShowWindow lub SetWindowPos. WS_VSCROLL – tworzy okno z pionowym paskiem przewijania. x,y – określają początkową pozycję okna na ekranie. Współrzędne x i y odnoszą się do lewego górnego narożnika okna. W obu przypadkach można zastosować stałą CW_USEDEFAULT. nWidth, nHeight – szerokość i wysokość okna. hWndParent – jeśli tworzone okno jest własnością innego okna, to tutaj umieszczamy uchwyt właściciela hMenu – uchwyt menu okna. Jeśli okno nie posiada menu, wpisujemy tutaj NULL. hInstance – uchwyt programu, z którym jest skojarzone to okno lpParam – wskaźnik wartości do przekazania do okna poprzez pole lpCreateParams struktury CREATESTRUCT, która jest wskazywana przez parametr lParam wiadomości WM_CREATE. Wiadomość jest wysyłana do tworzonego okna przez tą funkcję zanim zakończy ona swoje działanie.
Funkcja zwraca uchwyt do utworzonego okna. Uchwyt ten należy zapamiętać w osobnej zmiennej, ponieważ korzystają z niego funkcje obsługujące okno.
Po utworzeniu okna pokazujemy je na ekranie za pomocą funkcji:
BOOL ShowWindow ( HWND hWnd, int nCmdShow );
hWnd – uchwyt okna nCmdShow – określa sposób wyświetlenia okna. Przy pierwszym wywołaniu okna aplikacji parametr ten powinien przyjąć wartość otrzymaną przez funkcję WinMain w parametrze cmdShow. W kolejnych wywołaniach można stosować poniższe stałe:
SW_FORCEMINIMIZE – minimalizuje okno. Tą opcję zwykle stosujemy przy minimalizowaniu okna z innego wątku. SW_HIDE – ukrywa to okno i aktywizuje kolejne okienko na pulpicie SW_MAXIMIZE – maksymalizuje okno SW_MINIMIZE – minimalizuje okno i aktywuje następne okno ze sterty ekranu SW_RESTORE – aktywuje i wyświetla okno. SW_SHOW – aktywuje okno i wyświetla je w bieżących rozmiarach i na bieżącej pozycji. SW_SHOWDEFAULT – ustawia stan pokazywania okna na podstawie wartości SW_... określonej w strukturze STARTUPINFO, przekazanej do funkcji CreateProcess przez program, który uruchomił tą aplikację. SW_SHOWMAXIMIZED – aktywuje okno i wyświetla je jako zmaksymalizowane SW_SHOWMINIMIZED – aktywuje okno i wyświetla je jako zminimalizowane SW_SHOWMINNOACTIVE – wyświetla okno jako zminimalizowane. Różni się od SW_SHOWMINIMIZED tym, iż okno nie jest aktywowane SW_SHOWNA – wyświetla okno w bieżących rozmiarach i na bieżącej pozycji. Nie aktywuje okna. SW_SHOWNOACTIVATE – wyświetla okno w jego ostatnich rozmiarach i na jego ostatniej pozycji. Nie aktywuje okna. SW_SHOWNORMAL – aktywuje i wyświetla okno. Tą stałą aplikacja powinna wybrać przy pierwszym wyświetleniu okna.
Wyświetlone na ekranie okno zwykle uaktualniamy za pomocą funkcji:
BOOL UpdateWindow ( HWND hWnd );
Funkcja powoduje odświeżenie obszaru roboczego (client area) określonego okna przez wysłanie do niego wiadomości WM_PAINT. Wiadomość jest obsługiwana w procedurze okna.
Gdy w systemie Windows pojawi się zdarzenie związane z naszym oknem, zostaje do niego wysłana odpowiednia wiadomość. System magazynuje wiadomości dla różnych okien na specjalnej kolejce. Do pobierania wiadomości wykorzystujemy funkcję:
BOOL GetMessage ( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax );
lpMsg – wskaźnik struktury MSG, w której zostanie umieszczona wiadomość z kolejki wiadomości danego wątku. Struktura MSG jest następująca: typedef struct { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; } MSG, *PMSG;
hwnd – uchwyt okna, którego procedura otrzyma wiadomość. Jeśli wiadomość odnosi się do wątku, to pole zawiera NULL. message – określa identyfikator wiadomości. wParam – dodatkowa informacja związana z wiadomością – pole 16 bitowe lParam – dodatkowa informacja związana z wiadomością – pole 32 bitowe time – określa czas wysłania wiadomości pt – określa pozycję kursora we współrzędnych ekranu w czasie wysłania wiadomości. hWnd – uchwyt okna, dla którego wiadomości należy pobrać. Okno musi należeć do bieżącego wątku. Jeśli hWnd zawiera NULL, to funkcja GetMessage pobierze wiadomości dla wszystkich okien należących do bieżącego wątku. wMsgFilterMin – określa najniższą wartość wiadomości do pobrania. wMsgFilterMax – określa największą wartość wiadomości do pobrania. Jeśli oba parametry mają wartość 0, to GetMessage pobiera wszystkie wiadomości. Parametry te stosuje się zwykle do filtrowania wiadomości pochodzących od klawiatury i myszy.
Funkcja zwraca wartość 0 po odebraniu wiadomości WM_QUIT. W pozostałych przypadkach wartość jest niezerowa.
Naciśnięcie lub zwolnienie dowolnego klawisza powoduje wysłanie wiadomości o tzw. klawiszach wirtualnych. Wiadomości te umożliwiają przetwarzanie dosłownie wszystkich klawiszy. Jeśli w naszym oknie interesują nas jedynie klawisze znakowe (czyli takie, które produkują litery, cyfry i znaki), to możemy przetłumaczyć wiadomości klawiszy wirtualnych na klawisze znakowe za pomocą funkcji:
BOOL TranslateMessage(const MSG *lpMsg);
Pobraną z kolejki wiadomość rozsyłamy do odpowiedniego okna za pomocą funkcji:
LRESULT DispatchMessage(const MSG *lpmsg);
Wiadomość trafia do procedury okna. Wartość zwrotna jest wartością zwróconą przez procedurę okna. Najczęściej się ją ignoruje.
char ClassName[ ] = "SimpleWinClass"; | Nazwa klasy okna, wg której Windows będzie budowało okna naszej aplikacji. |
LRESULT CALLBACK WndProc ( HWND h, // uchwyt okna UINT uMsg, // kod wiadomości WPARAM wP, // parametr 16-bitowy LPARAM lP // parametr 32-bitowy ) { if(uMsg == WM_DESTROY) { PostQuitMessage(0); return 0; } else return DefWindowProc(h,uMsg,wP,lP); } |
Do obsługi zdarzeń musimy przygotować specjalną funkcję zwaną procedurą
okna.
Na podstawie kodu wiadomości procedura okna wykonuje odpowiednie działanie – np. wywołuje inne funkcje w programie użytkownika. W przypadku naszej procedury okna obsługiwana jest tylko jedna
wiadomość: Po otrzymaniu tej wiadomości wywoływana jest funkcja PostQuitMessage() informująca system, iż wątek chce zakończyć swoje działanie. Następnie kończymy działanie zwracając 0. Dla wszystkich pozostałych wiadomości, których nie obsługuje nasza procedura okna, wywołujemy funkcję DefWindowProc przekazując jej wszystkie otrzymane parametry. Funkcja ta dokona standardowej obsługi wiadomości (tzn. większość z nich po prostu zignoruje). |
int WINAPI WinMain ( HINSTANCE hinst, // uchwyt aplikacji HINSTANCE hprevinst, // obecnie prawie zawsze NULL LPSTR cmdLine, // wskaźnik wiersza poleceń // uruchamiającego ten program int cmdShow) // określa początkowy sposób // wyświetlania okna { | Funkcja WinMain jest punktem startowym aplikacji okienkowej. |
WNDCLASSEX wc; // struktura klasy okna MSG ms; // przechowuje wiadomości aplikacji HWND h; // Uchwyt naszego okna | Tutaj definiujemy trzy zmienne używane w naszej aplikacji. |
wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = wc.cbWndExtra = 0; wc.hInstance = hinst; wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = ClassName; wc.hIcon = wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); |
Pierwszą rzeczą w naszej aplikacji jest ustawienie pól struktury WNDCLASSEX, która posłuży do zarejestrowania w systemie Windows nowej klasy okienek – naszej klasy. |
if(!RegisterClassEx(&wc)) return 0; | Rejestrujemy klasę okna wykorzystując wypełnioną strukturę WNDCLASSEX. Jeśli rejestracja się nie uda, to natychmiast kończymy program. |
h = CreateWindowEx(0, ClassName, "Nasze pierwsze okno", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 200, 0, 0, hinst, 0); | Na podstawie zarejestrowanej klasy tworzymy okno. Zwróć uwagę, iż do funkcji CreateWindowEx przekazuje się nazwę klasy oraz różne parametry dla tworzonego okna. Funkcja zwraca uchwyt utworzonego okna, który możemy przekazywać różnym funkcjom operującym na okienku. |
ShowWindow(h, cmdShow); | Gdy okno jest utworzone, możemy je umieścić na ekranie. |
UpdateWindow(h); | Następnie odświeżamy jego zawartość. |
while(GetMessage(&ms, NULL, 0, 0)) { TranslateMessage(&ms); DispatchMessage(&ms); } |
Tworzymy pętlę odczytującą wiadomości z systemu, przeznaczone dla naszej
aplikacji. Pętla wykonywana jest do momentu otrzymania wartości 0 z funkcji
GetMessage – oznacza to, iż
wszystkie wątki aplikacji zakończyły działanie i nie ma dla nich już wiadomości
w kolejce.
Wewnątrz pętli wywołujemy dwie funkcje przetwarzamy otrzymaną wiadomość i przesyłamy ją do procedury okna. |
return ms.wParam; } | Kończąc zwracamy parametr wParam (16-bitowy) zawarty w strukturze otrzymanej wiadomości przez ostatnie wywołanie funkcji GetMessage(). |
W programie wymień funkcję WndProc() na poniższą:
LRESULT CALLBACK WndProc(HWND h, UINT uMsg , WPARAM wP, LPARAM lP) { switch(uMsg) { case WM_DESTROY : PostQuitMessage(0); break; // W procedurze okna obsługujemy wiadomość WM_PAINT, która jest wysyłana przy konieczności // przerysowania obszaru roboczego okna. case WM_PAINT : HDC hdc; // uchwyt kontekstu graficznego PAINTSTRUCT pps; // struktura parametrów rysowania RECT r; // prostokąt obejmujący obszar roboczy okna hdc = BeginPaint(h, &pps); GetClientRect(h, &r); DrawText(hdc, "Witaj w świecie MS-Windows",-1, &r, DT_SINGLELINE | DT_CENTER | DT_VCENTER); EndPaint(h, &pps); break; default: return DefWindowProc(h, uMsg, wP, lP); } return 0; } |
Skompiluj i uruchom program. Otrzymasz następujące okno:
Program w systemie Windows nie może sobie tak po prostu coś narysować na ekranie. Ekran jest zasobem współdzielonym, zatem korzystanie z niego musi się odbywać wg ściśle określonych zasad. Okienko, po którym chcemy rysować może znajdować się w różnych stanach (np. może być zminimalizowane lub częściowo przykryte innym oknem itp.). Dlatego przy operacjach graficznych należy bezwzględnie wykorzystywać funkcje jądra graficznego GDI (ang. Graphic Device Interface – interfejs grafiki).
Jeśli chcemy coś narysować na obszarze roboczym okienka (ang. client area), najpierw musimy poprosić system Windows o udostępnienie dla tego obszaru uchwytu odpowiedniego kontekstu graficznego (ang. DC – device context). Uchwyt kontekstu graficznego identyfikuje pewną wewnętrzną strukturę danych w systemie Windows, w której są umieszczone różne parametry graficzne. Uchwyt DC można otrzymać na kilka sposobów. W naszym programie wykorzystujemy do tego celu funkcję:
HDC BeginPaint ( HWND hwnd, LPPAINTSTRUCT lpPaint );
HDC | – | typ danych określający uchwyt do kontekstu graficznego | ||||||||||||
HWND | – | typ danych dla uchwytu okna | ||||||||||||
LPPAINTSTRUCT | – | długi wskaźnik struktury
PAINTSTRUCT |
||||||||||||
hwnd | – | uchwyt okna, po którym będziemy rysować | ||||||||||||
lpPaint | – | wskaźnik struktury PAINTSTRUC,
w której funkcja BeginPaint zapisze informacje graficzne.
Struktura ta posiada następującą definicję:typedef struct tagPAINTSTRUCT { HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; BYTE rgbReserved[32]; } PAINTSTRUCT, *PPAINTSTRUCT;
|
Jeśli wywołanie funkcji się powiedzie, to zwrócony zostanie uchwyt kontekstu graficznego, który należy zachować w osobnej zmiennej, ponieważ będzie niezbędny przy operacjach graficznych na okienku. Jeśli wystąpi błąd, to funkcja zwróci wartość NULL.
Po pobraniu uchwytu kontekstu graficznego ustalamy obszar rysowania. W naszym programie nie bawimy się w szczegóły i pobieramy do struktury RECT współrzędne całego obszaru roboczego okna przy pomocy funkcji GetClientRect. W rzeczywistości w PAINTSTRUC.rcPaint system Windows umieszcza prostokąt obejmujący ten fragment obszaru roboczego okna, który uległ zmianie (np. przesunięto zakrywające go inne okno) i powinien zostać przerysowany (taki prostokąt nosi nazwę nieaktualnego – ang. invalid rectangle). Wykorzystanie tej informacji jest czasami dużo efektywniejsze od przerysowywania całego obszaru roboczego okna, lecz również i trudniejsze, gdyż musimy odpowiednio przeliczać położenia wszystkich obiektów graficznych.
Do wyrysowania tekstu na obszarze roboczym wykorzystujemy funkcję:
int DrawText ( HDC hdc, LPCTSTR lpString, int nCount, LPRECT lpRect, UINT uFormat );
hdc | – | uchwyt kontekstu graficznego | |||||||||||||||||||||||||||||||||
lpString | – | wskaźnik tekstu do wyświetlenia. | |||||||||||||||||||||||||||||||||
nCount | – | określa liczbę znaków w wyświetlanym tekście. Jeśli parametr nCount ma wartość -1, to tekst musi być zakończony znakiem o kodzie 0. | |||||||||||||||||||||||||||||||||
lpRect | – | wskaźnik struktury RECT
zawierającej współrzędne prostokąta, w którym tekst ma zostać
sformatowany.typedef struct _RECT { LONG left; LONG top; LONG right; LONG bottom; } RECT, * PRECT;
|
|||||||||||||||||||||||||||||||||
uFormat | – | określa sposób formatowania tekstu. Parametr
ten może być złożony (za pomocą operatora | )
z poniższych stałych (opisujemy tylko niektóre
wartości, resztę znajdziesz na stronach Microsoftu):
|
Przed zakończeniem obsługi wiadomości WM_PAINT musimy zwolnić uchwyt kontekstu graficznego. W tym celu wywołujemy funkcję EndPaint().
W tym miejscu możesz wypróbować różne funkcje graficzne API Win32.
COLORREF SetPixel ( HDC hdc, int X, int Y, COLORREF crColor );
Funkcja rysuje punkt na ekranie.
hdc | – | uchwyt kontekstu graficznego |
X,Y | – | współrzędne punktu na obszarze graficznym okna. |
crColor | – | kolor piksela. Do utworzenia koloru posługujemy
się makrem RGB, które przyjmuje trzy parametry określające
kolejno składową czerwoną, zieloną i niebieską koloru. Każda ze
składowych ma zakres od 0 (brak) do 255
(maksimum). Np.: RGB(255,0,0) – kolor intensywnie czerwony RGB(0,255,0) – kolor intensywnie zielony RGB(255,0,255) – kolor fioletowy |
Funkcja zwraca kod koloru ustawionego piksela. Kod ten może się różnić od parametru crColor, jeśli w bieżącym trybie graficznym nie istnieje dokładny odpowiednik. Również szybkość działania nie jest specjalnie imponująca.
LRESULT CALLBACK WndProc(HWND h, UINT uMsg , WPARAM wP, LPARAM lP) { switch(uMsg) { case WM_DESTROY : PostQuitMessage(0); break; // W procedurze okna obsługujemy wiadomość WM_PAINT, która jest wysyłana przy konieczności // przerysowania obszaru roboczego okna. case WM_PAINT : HDC hdc; // uchwyt kontekstu graficznego PAINTSTRUCT pps; // struktura parametrów rysowania RECT r; // prostokąt obejmujący obszar roboczy okna int cw,ch; hdc = BeginPaint(h, &pps); GetClientRect(h, &r); cw = r.right - r.left; // szerokość obszaru klienta ch = r.bottom - r.top; // wysokość obszaru klienta for(int y = 0; y < ch; y++) for(int x = 0; x < cw; x++) { COLORREF c = RGB(255-255*x/cw,(255-255*y/ch)/2,255*y/ch); SetPixel(hdc,x,y,c); } EndPaint(h, &pps); break; default: return DefWindowProc(h, uMsg, wP, lP); } return 0; } |
WINGDIAPI BOOL WINAPI MoveToEx ( HDC hdc, int X, int Y, LPPOINT lpPoint );
Funkcja ustawia początek linii.
hdc | – | uchwyt kontekstu graficznego |
X,Y | – | współrzędne nowej pozycji, od której rozpocznie się rysowanie linii. |
lpPoint | – | Adres struktury POINT, w której zostanie
zapisana stara pozycja. Jeśli pole to zawiera NULL, to stara
pozycja nie będzie zwracana.typedef struct tagPOINT { LONG x; LONG y; } POINT; |
Funkcja zwraca 0, jeśli operacja się nie powiodła.
WINGDIAPI BOOL WINAPI LineTo ( HDC hdc, int X, int Y );
Funkcja rysuje linię od bieżącej pozycji (np. ustawionej przez wcześniejsze wywołanie MoveToEx lub LineTo) do punktu X,Y, który jednakże nie jest wypełniany kolorem. Po narysowaniu linii punkt X,Y staje się punktem startowym dla następnej linii. Funkcja zwraca 0, jeśli operacja się nie powiodła. Kolor linii jest zdefiniowany przez kolor bieżącego pióra.
hdc | – | uchwyt kontekstu graficznego |
X,Y | – | współrzędne pozycji, do której będzie rysowana linia |
HPEN CreatePen ( int fnPenStyle, int nWidth, COLORREF crColor );
Funkcja tworzy pióro.
fnPenStyle | – | styl pędzla. Możliwe są trzy wartości:
PS_SOLID – piórko
będzie ciągłe. |
nWidth | – | określa szerokość pióra. Dla wartości 0 piórko ma szerokość 1 piksela. |
crColor | – | kolor pióra. Do utworzenia koloru posługujemy się makrem RGB. |
Funkcja zwraca uchwyt do piórka. Jeśli zwróci wartość NULL, to operacja się nie powiodła.
HGDIOBJ SelectObject ( HDC hdc, HGDIOBJ hgdiobj );
Funkcja wybiera obiekt do użytku w kontekście graficznym.
hdc | – | uchwyt kontekstu graficznego |
hgdiobj | – | uchwyt obiektu, który ma zostać wybrany w danym kontekście. Tutaj przekazujemy wynik funkcji CreatePen.. |
Funkcja zwraca uchwyt do poprzedniego obiektu danego typu.
BOOL DeleteObject ( HGDIOBJ hObject );
Funkcja usuwa poprzednio utworzony obiekt, np. pióro. Jednakże nie wolno usuwać obiektu, który został wybrany w danym kontekście graficznym.
hObject | – | uchwyt usuwanego obiektu |
LRESULT CALLBACK WndProc(HWND h, UINT uMsg , WPARAM wP, LPARAM lP) { switch(uMsg) { case WM_DESTROY : PostQuitMessage(0); break; // W procedurze okna obsługujemy wiadomość WM_PAINT, która jest wysyłana przy konieczności // przerysowania obszaru roboczego okna. case WM_PAINT : HDC hdc; // uchwyt kontekstu graficznego PAINTSTRUCT pps; // struktura parametrów rysowania RECT r; // prostokąt obejmujący obszar roboczy okna HGDIOBJ previous; int cw,ch,x,y; hdc = BeginPaint(h, &pps); GetClientRect(h, &r); previous = SelectObject(hdc,CreatePen(PS_SOLID,0,0)); // tworzymy nowe pióro i wybieramy je cw = r.right - r.left - 1; // szerokość obszaru klienta ch = r.bottom - r.top - 1; // wysokość obszaru klienta for(int i = 0; i < 30; i++) { DeleteObject(SelectObject(hdc,CreatePen(PS_SOLID,0,RGB(255 * i / 30, 0, 255 - 255 * i / 30)))); x = i * cw / 30; y = i * ch / 30; MoveToEx(hdc,0,y,NULL); LineTo(hdc,x,ch); MoveToEx(hdc,0,ch-y,NULL); LineTo(hdc,x,0); MoveToEx(hdc,cw,y,NULL); LineTo(hdc,cw-x,ch); MoveToEx(hdc,cw,ch-y,NULL); LineTo(hdc,cw-x,0); } DeleteObject(SelectObject(hdc,previous)); // usuwamy pióro EndPaint(h, &pps); break; default: return DefWindowProc(h, uMsg, wP, lP); } return 0; } |
W oknie Object Inspector dla Form1 ustaw własności:
Na okienku umieść panel.
W oknie Object Inspector dla panelu ustaw własności:
Skompiluj i uruchom program. Otrzymasz:
Osobnym problemem jest tworzenie aplikacji przenośnych na inne platformy. Jeśli napiszesz program dla Windows, to jego przeróbka dla systemu Linux może być dosyć kosztowna czasowo. Z tego powodu opracowano różne dodatkowe biblioteki, które pozwalają tworzyć aplikacje niezależne od systemu operacyjnego komputera (oczywiście do pewnego stopnia niezależne). Istnieje tutaj kilka możliwości, np.:
I Liceum Ogólnokształcące |
Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl
W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe