![]() |
Wyjście Spis treści Poprzedni Następny
Autor:
©Iczelion |
©2008 mgr
Jerzy Wałaszek
|
Na tej lekcji nauczymy się, jak "maluje" się tekst na obszarze roboczym okna. Dowiemy się również o kontekście urządzenia (device context).

Kod źródłowy możesz pobrać {z tego archiwum}.
W systemie Windows tekst jest pewnym typem obiektu GUI (Graphic User Inteface - Graficzny Interfejs Użytkownika). Każda litera zbudowana jest z licznych pikseli (punktów), które razem tworzą rozróżnialny kształt. Dlatego umieszczanie tekstu na ekranie nazywamy "malowaniem" zamiast "wypisywaniem". Zwykle malujesz tekst w swoim własnym obszarze roboczym (właściwie mógłbyś malować poza tym obszarem, lecz to już inna historia). Umieszczanie tekstu na ekranie w systemie Windows różni się zasadniczo od takiej samej operacji w systemie DOS. Tam ekran można było potraktować jak matrycę 80 kolumn na 25 wierszy. Lecz w systemie Windows ekran jest współdzielony przez kilka programów. Muszą być wymuszone pewne reguły dostępu do tego zasobu, aby programy nie zapisywały sobie nawzajem ekranu. Windows rozwiązuje ten problem przez ograniczenie obszaru rysunkowego każdego okna tylko do jego obszaru roboczego (client area). Rozmiar tego obszaru roboczego również nie jest stały. Użytkownik może go zmienić w każdej chwili. Należy więc dynamicznie określać rozmiary własnego obszaru roboczego.
Zanim będziesz mógł namalować coś na obszarze roboczym, musisz uzyskać na to pozwolenie od systemu Windows. Zgadza się, nie masz już całkowitej kontroli nad ekranem, tak jak w systemie DOS. Musisz prosić Windows o pozwolenia na malowanie po własnym obszarze roboczym. System Windows określi rozmiar twojego obszaru roboczego, czcionkę, kolory oraz inne atrybuty GDI (Graphic Device Interface - Interfejs Urządzenia Graficznego) i prześle uchwyt do kontekstu urządzenia z powrotem do twojego programu. Możesz następnie wykorzystać ten kontekst urządzenia jako paszport do rysowania po swoim własnym obszarze roboczym okna.
Co to jest kontekst urządzenia? Jest to po prostu pewna struktura danych zarządzana wewnętrznie przez system Windows. Kontekst urządzenia powiązany jest z określonym urządzeniem, takim jak drukarka lub ekran wideo. W przypadku tego ostatniego kontekst urządzenia związany jest z określonym oknem na ekranie.
Niektóre z wartości w kontekście urządzenia są atrybutami graficznymi, takimi jak kolory, czcionki, itp. Są to standardowe wartości, które możesz zmieniać wedle woli. Istnieją one po to, aby zmniejszyć ilość danych, które należałoby każdorazowo przesyłać do wywołań funkcji GDI.
Kontekst urządzenia możesz potraktować jak standardowe środowisko przygotowane dla ciebie przez system Windows. Później będziesz mógł zmienić niektóre standardowe wartości, jeśli będziesz sobie tak życzył.
Gdy program chce malować, musi otrzymać uchwyt kontekstu urządzenia. Zwykle można tego dokonać na kilka sposobów.
Musisz pamiętać o jednej rzeczy - po wykorzystaniu uchwytu kontekstu urządzenia musisz go zwolnić w czasie przetwarzania pojedynczej wiadomości. Nie pobieraj tego uchwytu w odpowiedzi na jedną wiadomość i zwalniaj w odpowiedzi na inną.
System Windows wysyła wiadomości WM_PAINT do okna w celu powiadomienia go, iż nadszedł czas przemalowania jego obszaru roboczego. Zawartość tego obszaru nie jest zapamiętywana. Zamiast tego, jeśli powstanie sytuacja wymagająca przemalowania obszaru roboczego (np. gdy okno było zakryte przez inne a teraz zostało odkryte), Windows umieszcza w kolejce wiadomości dla tego okna wiadomość WM_PAINT. Okno powinno się zatroszczyć o odświeżenie swojej powierzchni. Musisz zebrać wszystkie informacje o sposobie zamalowania powierzchni roboczej okna w sekcji WM_PAINT procedury twojego okna, aby procedura ta mogła przemalować obszar roboczy, gdy nadejdzie wiadomość WM_PAINT.
Następnym pojęciem, z którym powinieneś się zaznajomić, jest nieaktualny prostokąt (invalid rectangle). System Windows definiuje go jako najmniejszy prostokątny obszar w polu roboczym, który musi zostać przemalowany. Gdy Windows wykryje nieaktualny prostokąt w obszarze roboczym okna, wysyła wiadomość WM_PAINT do tego okna. W odpowiedzi na wiadomość WM_PAINT okno może otrzymać strukturę PAINTSTRUCT, która zawiera m.in. współrzędne nieaktualnego prostokąta. W odpowiedzi na wiadomość WM_PAINT wywołujesz BeginPaint, aby zatwierdzić ten prostokąt. Jeśli nie przetwarzasz wiadomości WM_PAINT, to powinieneś co najmniej wywołać DefWindowProc lub ValidateRect, aby zatwierdzić nieaktualny prostokąt, w przeciwnym razie Windows będzie cyklicznie wysyłać wiadomość WM_PAINT.
Poniżej podaję kolejne kroki, które powinieneś wykonać w odpowiedzi na wiadomość WM_PAINT:
Zauważ, iż nie musisz jawnie zatwierdzać nieaktualnego prostokąta. Jest to robione automatycznie przez wywołanie BeginPaint. Pomiędzy parą BeginPaint-EndPaint możesz wywoływać dowolne funkcje GDI, aby malować po swoim obszarze roboczym. Większość z nich wymaga jako parametru uchwytu kontekstu urządzenia.
Napiszemy program wyświetlający łańcuch tekstu "Asembler Win32 jest wspaniały i łatwy" na środku obszaru roboczego.
.386
.MODEL FLAT, STDCALL
OPTION CASEMAP:NONE
WinMain PROTO :DWORD, :DWORD, :DWORD, :DWORD
INCLUDE \masm32\include\windows.inc
INCLUDE \masm32\include\user32.inc
INCLUDE \masm32\include\kernel32.inc
INCLUDELIB \masm32\lib\user32.lib
INCLUDELIB \masm32\lib\kernel32.lib
.DATA
ClassName DB "SimpleWinClass",0
AppName DB "Nasze okno z tekstem",0
OurText DB "Asembler Win32 jest wspaniały i łatwy!",0
.DATA?
hInstance HINSTANCE ?
CommandLine LPSTR ?
.CODE
start:
INVOKE GetModuleHandle, NULL
mov hInstance, eax
INVOKE GetCommandLine
mov CommandLine, eax
INVOKE WinMain, hInstance, NULL, CommandLine, SW_SHOWDEFAULT
INVOKE ExitProcess, eax
WinMain PROC hInst: HINSTANCE,\
hPrevInst: HINSTANCE,\
CmdLine: LPSTR,\
CmdShow: DWORD
LOCAL wc: WNDCLASSEX
LOCAL msg: MSG
LOCAL hwnd: HWND
mov wc.cbSize, SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW OR CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra, NULL
mov wc.cbWndExtra, NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground, COLOR_WINDOW+1
mov wc.lpszMenuName, NULL
mov wc.lpszClassName, OFFSET ClassName
INVOKE LoadIcon, NULL, IDI_APPLICATION
mov wc.hIcon, eax
mov wc.hIconSm, 0
INVOKE LoadCursor, NULL, IDC_ARROW
mov wc.hCursor, eax
INVOKE RegisterClassEx, ADDR wc
INVOKE CreateWindowEx, NULL, ADDR ClassName, ADDR AppName,\
WS_OVERLAPPEDWINDOW,\
CW_USEDEFAULT, CW_USEDEFAULT,\
CW_USEDEFAULT, CW_USEDEFAULT,\
NULL, NULL, hInst, NULL
mov hwnd, eax
INVOKE ShowWindow, hwnd, SW_SHOWNORMAL
INVOKE UpdateWindow, hwnd
.WHILE TRUE
INVOKE GetMessage, ADDR msg, NULL, 0, 0
.BREAK .IF (!eax)
INVOKE TranslateMessage, ADDR msg
INVOKE DispatchMessage, ADDR msg
.ENDW
mov eax, msg.wParam
ret
WinMain ENDP
WndProc PROC hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
LOCAL hdc: HDC
LOCAL ps: PAINTSTRUCT
LOCAL rect: RECT
.IF uMsg==WM_DESTROY
INVOKE PostQuitMessage, NULL
.ELSEIF uMsg==WM_PAINT
INVOKE BeginPaint, hWnd, ADDR ps
mov hdc, eax
INVOKE GetClientRect, hWnd, ADDR rect
INVOKE DrawText, hdc, ADDR OurText, -1, ADDR rect,\
DT_SINGLELINE OR DT_CENTER OR DT_VCENTER
INVOKE EndPaint, hWnd, ADDR ps
.ELSE
INVOKE DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.ENDIF
xor eax, eax
ret
WndProc ENDP
END start
Większość kodu jest taka sama jak w rozdziale 3. Wyjaśnię tylko ważne różnice.
LOCAL hdc: HDC LOCAL ps: PAINTSTRUCT LOCAL rect: RECT
Są to zmienne lokalne używane przez funkcje GDI w sekcji
WM_PAINT.
RECT STRUCT
left LONG ?
top LONG ?
right LONG ?
bottom LONG ?
RECT ENDS
Pola left i top są współrzędnymi
lewego górnego rogu prostokąta.
right
i bottom są współrzędnymi prawego dolnego rogu.
Zapamiętaj jedną rzecz. Początek układu współrzędnych znajduje się w lewym
górnym rogu obszaru roboczego, a oś y jest zwrócona w dół. Zatem punkt o
współrzędnej y=10 jest PONIŻEJ punktu o
współrzędnej y=0.
INVOKE BeginPaint, hWnd, ADDR ps
mov hdc, eax
INVOKE GetClientRect, hWnd, ADDR rect
INVOKE DrawText, hdc, ADDR OurText, -1, ADDR rect, \
DT_SINGLELINE OR DT_CENTER OR DT_VCENTER
INVOKE EndPaint, hWnd, ADDR ps
W odpowiedzi na wiadomość WM_PAINT wywołujesz
BeginPaint z uchwytem okna, które chcesz malować
oraz z niezainicjowaną strukturą
PAINTSTRUCT
jako parametrami. Jeśli wywołanie powiodło się, to rejestr
eax zawiera uchwyt kontekstu urządzenia. Następnie
wywołujesz GetClientRect, aby pobrać wymiary
obszaru roboczego. Wymiary zostają zwrócone w zmiennej
rect, którą przekazujesz do
DrawText jako jeden z parametrów. Składnia
DrawText jest następująca:
DrawText PROTO hdc: HDC,\
lpString: DWORD,\
nCount: DWORD,\
lpRect: DWORD,\
uFormat: DWORDDrawText jest wysokopoziomową funkcją API wyprowadzania tekstu. Abyś mógł się skoncentrować na malowanym łańcuchu tekstowym, obsługuje ona niektóre niewygodne szczegóły, takie jak przenoszenie wyrazów, wyśrodkowywanie, itp. Jej niskopoziomowa siostra, TextOut, zostanie omówiona w następnej lekcji. DrawText formatuje łańcuch tekstowy, aby dopasować go do granic prostokąta. Wykorzystuje ona bieżąco wybraną czcionkę, kolor oraz tło (w kontekście urządzenia) do narysowania tekstu. Wiersze są zawijane, aby zmieścić się w tym prostokącie. Zwraca wysokość wyjściowego tekstu w jednostkach urządzenia, w naszym przypadku będą to piksele. Przyjrzyjmy się jej parametrom:
Po zakończeniu malowania na obszarze roboczym okienka musisz wywołać funkcję EndPaint, aby zwolnić uchwyt kontekstu urządzenia.
I to wszystko. Podsumujmy główne punkty tutaj:
Ta sama aplikacja w Pascalu:
{********************************
** I Liceum Ogólnokształcące **
** w Tarnowie **
** mgr Jerzy Wałaszek **
********************************}
program TextPaint;
uses Windows;
function WndProc(h:HWND;uMsg:UINT;wP:WPARAM;lP:LPARAM) : longint;
const
OurText = 'FreePascal jest wspaniały i łatwy';
var
HDevCnt: HDC;
ps : PAINTSTRUCT;
r : RECT;
begin
WndProc := 0;
case uMsg of
WM_DESTROY: PostQuitMessage(0);
WM_PAINT : begin
HDevCnt := BeginPaint(h,ps);
GetClientRect(h,r);
DrawText(HDevCnt,OurText,-1,r,DT_SINGLELINE or DT_CENTER or DT_VCENTER);
EndPaint(h,ps);
end;
else WndProc := DefWindowProc(h,uMsg,wP,lP);
end;
end;
function WinMain(hI,hPI:HINST;CmdLine:LPSTR;cmdShow:longint) : longint;
const
ClassName = 'SimpleWinClass';
var
wc : WndClassEx;
ms : msg;
h : HWnd;
begin
wc.cbSize := SizeOf(WndClassEx);
wc.style := CS_HREDRAW or CS_VREDRAW;
wc.lpfnWndProc := @WndProc;
wc.cbClsExtra := 0;
wc.cbWndExtra := 0;
wc.hInstance := hI;
wc.hbrBackground := COLOR_WINDOW + 1;
wc.lpszMenuName := 0;
wc.lpszClassName := ClassName;
wc.hIcon := LoadIcon(0,IDI_APPLICATION);
wc.hIconSm := wc.hIcon;
wc.hCursor := LoadCursor(0,IDC_ARROW);
RegisterClassEx(wc);
h := CreateWindowEx(0,ClassName,'Nasze okno z tekstem',
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,0,0,hI,0);
ShowWindow(h,CmdShow);
UpdateWindow(h);
while GetMessage(ms,0,0,0) do
begin
TranslateMessage(ms);
DispatchMessage(ms);
end;
WinMain := ms.wParam;
end;
begin
ExitProcess(WinMain(GetModuleHandle(0),0,0,SW_SHOWDEFAULT));
end.
|
Autorem kursu jest
Iczelion. Kurs
programowania Windows znalazł się na serwerze I LO w
Tarnowie za pisemną zgodą autora. |
![]() | 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