Tải bản đầy đủ (.pdf) (281 trang)

hoa cuong có thì sử dụng – thích thì lao vào

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (2.77 MB, 281 trang )

<span class='text_page_counter'>(1)</span><div class='page_container' data-page=1></div>
<span class='text_page_counter'>(2)</span><div class='page_container' data-page=2>

ĐẠI HỌC TÔN ĐỨC THẮNG





























TÓM TẮT BÀI GIẢNG



</div>
<span class='text_page_counter'>(3)</span><div class='page_container' data-page=3>

1. Toång quan... 1



1.1

Windows 95 và lập trình trong mơi trường Windows... 1



1.2

Chương trình Windows đầu tiên ... 3



1.3

Thủ tục cửa sổ...11



1.4

Môi trường Visual C++...13



2. Tổng quan về Giao Diện Thiết Bị Đồ Hoạ (GDI) ...15



2.1

Giới thiệu ...15



2.2

Giao diện người dùng...15



2.3

Ngữ cảnh thiết bị (device context)...16



2.4

Quy trình vẽ ...18



2.5

Vẽ từng điểm ...19




2.6

Tạo Marker...21



2.7

Vẽ và vẽ lại ...23



2.8

BeginPaint và EndPaint ...23



2.9

Vùng hợp lệ và không hợp lệ...24



2.10 InvertPixel vaø DrawMarker...25



2.11 GetDC vaø ReleaseDC...26



3. Vẽ đường ...28



3.1

Đường ...28



3.2

Các hàm vẽ đường ...28



3.3

Thuộc tính vẽ đường ...33



3.4

Bút vẽ (pen)...34



</div>
<span class='text_page_counter'>(4)</span><div class='page_container' data-page=4>

5. Xuất Văn bản...60



5.1

Tổng quan ...60



5.2

Các hàm xuất văn bản ...60



TextOut ...60




ExtTextOut...61



TabbedTextOut ...61



DrawText ...64



GrayString ...66



5.3

Các thuộc tính xuất văn bản...67



5.4

Fonts ...70



CreateFont...72



LOGFONT ...72



CreateFontIndirect ...74



5.5

Các thông số văn bản...75



SIZE ...75



GetTextExtentPoint32...75



TEXTMETRIC...75



GetTextMetrics ...76



5.6

Font vaø Path ...79




6. Bộ định thời ...81



6.1

Giới thiệu ...81



6.2

Các hàm thao tác bộ định thời ...81



</div>
<span class='text_page_counter'>(5)</span><div class='page_container' data-page=5>

ShowCursor...115



SetCursor...115



7.3

Phím taét (Keyboard Accelerators) ...115



7.4

Menu...117



7.5

Bitmap ...124



LoadBitmap...125



CreateCompatibleBitmap...126



BitBlt ...126



StretchBlt...126



8. Custom Menu ...134



8.1

Các hàm thao tác trên menu ...134



LoadMenu ...135




SetMenu ...136



DrawMenuBar...136



AppendMenu...136



InsertMenuItem...137



8.2

Thay đổi menu trong chương trình...138



8.3

Menu do người sử dụng tự vẽ (Owner Draw Menu)...143



8.4

Bimap Menu ...174



9. Cửa Sổ Và Nút Điều Khiển ...183



9.1

Đăng ký lớp cửa sổ (tư tưởng OOP) ...183



9.2

Tạo cửa sổ...184



</div>
<span class='text_page_counter'>(6)</span><div class='page_container' data-page=6>

10.5 Một số ví dụ về sử dụng hộp hội thoại modal ...218



11.

Tương tác với người sử dụng...94



11.1 SetCapture, GetCapture vaø ReleaseCapture...94



SetCapture...94



ReleaseCapture...94




GetCapture...95



11.2 Kéo lên đối tượng bằng cách xoá và vẽ lại...95



11.3 Kéo lên đối tượng bằng chọn chế độ vẽ R2_NOTXORPEN ...98



SetROP2...98



12.

Biến đổi toạ độ ...232



12.1 Các thuộc tính GDI ...232



12.2 Các chế độ toạ độ GDI ...232



SetMapMode...233



GetMapMode ...233



12.3 Tịnh tiến và nhân tỉ leä...234



DPtoLP ...234



LPtoDP ...234



SetViewportOrgEx ...235



GetViewportOrgEx ...235



SetWindowOrgEx ...235




GetWindowOrgEx...235



12.4 Chế độ thiết bị (Device Mapping Mode)...236



</div>
<span class='text_page_counter'>(7)</span><div class='page_container' data-page=7>

13.4 Các thao tác trên cửa sổ trượt ...252



SetScrollRange...252



SetScrollPos ...253



ScrollWindow...253



SCROLLINFO...253



SetScrollInfo ...254



GetScrollInfo...254



ShowScrollBar...255



13.5 Các ví dụ sử dụng cửa sổ trượt ...255



</div>
<span class='text_page_counter'>(8)</span><div class='page_container' data-page=8>

Lời nói đầu



Hệ điều hành Windows với giao diện theo hướng đồ hoạ đã trở thành một môi trường làm


việc chuẩn cho những người sử dụng máy vi tính cá nhân. Việc xây dựng các chương trình


hoạt động trong mơi trường Windows đã trở thành một yêu cầu thiết yếu cho những người xây


dựng phần mềm khi môi trường DOS đã hồn thành nhiệm vụ lịch sử của nó.




Để theo kịp với trào lưu thế giới, khoa Công nghệ thơng tin và Tốn ứng dụng trường Đại học


Tôn Đức Thắng đã cập nhật và bổ sung môn Lập trình Windows vào chương trình đào tạo của


khoa.



Tài liệu này được biên soạn nhằm phục vụ cho các sinh viên chuyên ngành Công nghệ thông


Tin của trường. Nội dung bao gồm các kỹ thuật cốt yếu nhất về lập trình trong mơi trường


Windows sử dụng ngôn ngữ C/C++.



Windows cung cấp hơn một ngàn hàm để các lập trình viên tạo các ứng dụng khác nhau, từ


soạn thảo văn bản, vẽ các đối tượng đồ hoạ, hiển thị hình ảnh, phim video, tạo âm thanh,


truyền dữ liệu…. Phạm vi tài liệu chỉ giới hạn trong các nội dung chính bao gồm vẽ hình, xuất


văn bản, xây dựng giao diện, tạo hộp hội thoại… Từ những kiến thức căn bản trên, sinh viên có


thể tham khảo thêm các thư viện do Windows cung cấp để có thể xây dựng các ứng dụng lớn


theo yêu cầu như một luận văn tốt nghiệp hoặc một phần mềm thực sự.



Tài liệu mang tính chất tóm tắt kiến thức và khơng thể tránh khỏi sai sót. Rất mong nhận được


sự phê bình, đóng góp ý kiến của các bạn đồng nghiệp và các sinh viên.



</div>
<span class='text_page_counter'>(9)</span><div class='page_container' data-page=9>

<b>1. </b>

Toång quan



<b>1.1 </b>

Windows 95 và lập trình trong mơi trường Windows


• Hệ điều hành 32 bit



• Giao diện người dùng kiểu đồ hoạ (GUI)


− Giao diện trực quan (Visual Interface).



− WYSIWYG, người sử dụng tương tác với chương trình



− Các ứng dụng Windows có giao diện thống nhất với cùng dáng vẻ : Một cửa sổ


hình chữ nhật, có thanh tiêu đề, menu, hộp hội thoại, thanh trượt …




• Đa nhiệm



− Mỗi chương trình chiếm một phần tài nguyên của hệ thống và có một phần bộ nhớ


thường trú.



− Nhiều chương trình có thể được kích hoạt và chạy cùng một lúc



− Các phiên bản Windows 16 bít hoạt động đa nhiệm theo cơ chế nonpreemtive.


− Windows 32 bít hoạt động theo cơ chế preemtive. Mỗi chương trình có thể được



tách thành các thread thực hiện cùng lúc.


•Quản lý bộ nhớ



− Mã chương trình và dữ liệu có thể dịch chuyển trong bộ nhớ vật lý.


− Khả năng sử dụng bộ nhớ phụ.



</div>
<span class='text_page_counter'>(10)</span><div class='page_container' data-page=10>

− Chương trình viết cho Windows khơng điều khiển trực tiếp thiết bị xuất như màn


hình hay máy in mà thơng qua một ngơn ngữ lập trình đồ hoạ gọi là Giao Diện


Thiết Bị Đồ Hoạ (Graphics Device Interface: GDI).



− Các phần mềm MS-DOS cần có các tập tin điều khiển thiết bị đi kèm (màn hình,


máy in). Chương trình viết cho Windows không cần quan tâm đến vấn đề này.


• Kiến trúc hướng thơng điệp (message driven)



− Windows và các ứng dụng Windows hoạt động theo cơ chế truyền, nhận thông


điệp, các hoạt động của chương trình thay đổi tuỳ theo thơng điệp mà nó nhận


được, thông điệp được gởi qua lại giữa ứng dụng và Windows, giữa các ứng dụng


với nhau.




− Cửa sổ ứng dụng tự động được vẽ lại mỗi khi có sự thay đổi kích thước hay vùng bị


che. Điều này được thực hiện nhờ hệ điều hành gởi thơng điệp cho chương trình.


• Thủ tục cửa sổ



− Hệ điều hành ra lệnh cho ứng dụng nhờ thủ tục cửa sổ (Window Procedure). Hàm


cửa sổ cho biết phản ứng của chương trình với các tác động bên ngoài như user


input…



− Hệ điều hành Windows gởi thông điệp cho một chương trình ứng dụng bằng cách


gọi hàm cửa sổ của ứng dụng đó, với tham số là thơng điệp.



• Tài nguyên.



− Window cho phép người sử dụng đưa vào chương trình các tài nguyên thuộc các


loại biểu tượng (icons), con trỏ (cursors), bitmap, bảng các chuỗi hằng (string


tables), bảng phím tắt (Accelerator), hộp hội thoại (Dialog), menu, thanh công cụ


(toolbar) và siêu văn bản (văn bản loại HTML).



</div>
<span class='text_page_counter'>(11)</span><div class='page_container' data-page=11>

Hình 1-1 – Biểu tượng chuẩn trong Windows


<b>1.2 </b>

Chương trình Windows đầu tiên


1.2.1 Các tập tin chương trình nguồn



sdafx.h, stdafx.cpp, hello.cpp


<b>stdafx.h </b>



#include <windows.h>
// C RunTime Header Files
#include <stdlib.h>



#include <malloc.h>
#include <memory.h>
#include <tchar.h>


<b>stdafx.cpp </b>



</div>
<span class='text_page_counter'>(12)</span><div class='page_container' data-page=12>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY WinMain(HINSTANCE hInstance,


HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{


MSG msg;


// Initialize global strings
MyRegisterClass(hInstance);


// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{


return FALSE;
}


// Main message loop:


while (GetMessage(&msg, NULL, 0, 0))
{



TranslateMessage(&msg);
DispatchMessage(&msg);
}


return msg.wParam;
}


// FUNCTION: MyRegisterClass()


// PURPOSE: Registers the window class.
ATOM MyRegisterClass(HINSTANCE hInstance)
{


WNDCLASSEX wcex;


wcex.cbSize = sizeof(WNDCLASSEX);


wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;


wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;


wcex.hInstance = hInstance;


</div>
<span class='text_page_counter'>(13)</span><div class='page_container' data-page=13>

hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)



return FALSE;


ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);


return TRUE;
}


// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
// WM_PAINT - Paint the main window


// WM_DESTROY - post a quit message and return


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{


PAINTSTRUCT ps;
HDC hdc;


LPSTR szHello = "Hello, world";
switch (message)


{


case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);



// TODO: Add any drawing code here...
RECT rt;


GetClientRect(hWnd, &rt);


DrawText(hdc, szHello, strlen(szHello), &rt, DT_SINGLELINE
| DT_CENTER | DT_VCENTER);


EndPaint(hWnd, &ps);
break;


case WM_DESTROY:


PostQuitMessage(0);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


return 0;
}


</div>
<span class='text_page_counter'>(14)</span><div class='page_container' data-page=14>

CW

Create Window option



DT

DrawText option



Ví dụ các hằng có sử dụng trong chương trình




CS_HREDRAW

DT_SINGLELINE

WM_CREATE



CS_VREDRAW

IDC_ARROW

WM_DESTROY



CW_USEDEFAULT DT_VCENTER

WM_PAINT



DT_CENTER

IDI_APPLICATION WS_OVERLAPPEDWINDOW



1.2.3 Kiểu dữ liệu


• Đơn



WORD

BYTE

DWORD

INT



PSTR

LPSTR

UINT



WPARAM

LPARAM

LRESULT



APIENTRY CALLBACK

(__stdcall)


• Cấu trúc



MSG

Cấu trúc thơng điệp


WNDCLASSEX Cấu trúc lớp cửa sổ


PAINSTRUCT

Cấu trúc paint



RECT

Cấu trúc hình chữ nhật


• Handle (chỉ điểm)



HINSTANCE

Handle đến thể hiện của chương trình


HWND

Handle đến một cửa sổ




HDC

Handle đến ngữ cảnh thiết bị (device context)


HICON

Handle đến một biểu tượng



</div>
<span class='text_page_counter'>(15)</span><div class='page_container' data-page=15>

hdc

handle to device context


hwnd handle to window



i

index



l

LONG

(long int)


lp

long pointer



n

int


pt

POINT



by

BYTE



i

int


b hoặc f : BOOL



w

WORD



sz

chuỗi kết thúc bởi ‘\0’


1.2.5 Hoạt động của chương trình



• Chương trình có hai hàm chính WinMain, WndProc và hai hàm phục vụ là


MyRegisterClass và InitInstance.



• Trình tự thực hiện một chương trình Windows là


− Đăng ký lớp cửa sổ,




− Tạo cửa sổ hiển thị cửa sổ



− Xử lý các thơng điệp nhờ vịng lặp message và thủ tục cửa sổ.



− WinMain không trực tiếp gọi hàm WndProc, hàm được gọi bởi Windows


• Các hàm trong Windows



− Windows cung cấp hơn một ngàn hàm thư viện


− Các hàm sử dụng trong chương trình trên bao gồm



</div>
<span class='text_page_counter'>(16)</span><div class='page_container' data-page=16>

TranslateMesage – Chuyển đổi thơng điệp bàn phím.


DispatchMesage – Gởi thông điệp đến thủ tục cửa sổ.


BeginPaint, EndPaint – Khởi động và kt việc vẽ cửa sổ.


GetClientRect – Lấy kích thước của vùng client của cửa sổ.


DrawText – Hiển thị chuỗi ký tự.



PostQuitMessage –

Đưa thơng điệp “quit : thốt khỏi chương trình” vào hàng đợi


thông điệp.



DefWindowProc – Thực hiện xử lý mặc nhiên các thơng điệp khơng được chương


trình xử lý.



1.2.6 Hàm chính (WinMain)



Gồm các phần chính đăng ký lớp, tạo cửa sổ, hiển thị cửa sổ, xử lý thông điệp


int APIENTRY WinMain(HINSTANCE hInstance,



HINSTANCE hPrevInstance,


LPSTR lpCmdLine,



int nCmdShow)


hInstance : Handle đến chương trình hiện hành



hPrevInstance : Handle đến chương trình trước đó (Windows 95 : ln ln là NULL).


Trong Windows 32 bit, hPrevInstance ln ln NULL, để chỉ có tối đa một phiên bản, ta


dùng hàm FindWindow.



hwndPrev = FindWindow(“HelloAppClass”, 0);
if (hwndPrev)


{


// switch to prev ver
}


lpCmdLine : Tham số dòng lệnh



nCmdShow : tuỳ chọn cách hiển thị cửa sổ



</div>
<span class='text_page_counter'>(17)</span><div class='page_container' data-page=17>

int cbWndExtra;
HANDLE hInstance;
HICON hIcon;
HCURSOR hCursor;


HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
HICON hIconSm;


} WNDCLASSEX;



Đăng ký



Ta đăng ký lớp cửa sổ bằng cách đưa thông tin vào cấu trúc cửa sổ và gọi hàm


RegisterClassEx.



WNDCLASSEX wcex;


wcex.cbSize = sizeof(WNDCLASSEX);


wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;


wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;


wcex.hInstance = hInstance;


wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;


wcex.lpszClassName = szWindowClass;


wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION);
RegisterClassEx(&wcex);


1.2.8 Tạo cửa sổ




</div>
<span class='text_page_counter'>(18)</span><div class='page_container' data-page=18>

<i><b> HMENU hMenu, // handle to menu or child-window identifier </b></i>
<i><b> HANDLE hInstance, // handle to application instance </b></i>


<i><b> LPVOID lpParam // pointer to window-creation data </b></i>
<b>); </b>


<b>HWND CreateWindowEx( </b>


<i><b> DWORD dwExStyle, // extended window style </b></i>


<i><b> LPCTSTR lpClassName, // pointer to registered class name </b></i>
<i><b> LPCTSTR lpWindowName, // pointer to window name </b></i>


<i><b> DWORD dwStyle, // window style </b></i>


<i><b> int x, // horizontal position of window </b></i>
<i><b> int y, // vertical position of window </b></i>
<i><b> int nWidth, // window width </b></i>


<i><b> int nHeight, // window height </b></i>


<i><b> HWND hWndParent, // handle to parent or owner window </b></i>


<i><b> HMENU hMenu, // handle to menu, or child-window identifier </b></i>
<i><b> HINSTANCE hInstance, // handle to application instance </b></i>


<i><b> LPVOID lpParam // pointer to window-creation data </b></i>
<b>); </b>


Ví duï



hWnd = CreateWindow(


szWindowClass, // Window class name
“Hello program”, // Window name


WS_OVERLAPPEDWINDOW, // window style


CW_USEDEFAULT, // horizontal position of window
0, // vertical position of window
CW_USEDEFAULT, // window width


0, // window height


NULL, // parent window handle


NULL, // handle to menu, or child-window identifier
hInstance, // handle to application instance


NULL); // pointer to window-creation data


</div>
<span class='text_page_counter'>(19)</span><div class='page_container' data-page=19>

while (GetMessage(&msg, NULL, 0, 0))
{


TranslateMessage(&msg);
DispatchMessage(&msg);
}


msg laø biến có kiểu MSG


typedef struct tagMSG



{



HWND hwnd;


UINT message;


WPARAM wParam;


LPARAM lParam;


DWORD time;


POINT pt;


}MSG;



<b>1.3 </b>

Thủ tục cửa sổ


1.3.1 Xử lý các thông điệp



Thủ tục cửa sổ là hàm xử lý thông điệp tương ứng một lớp cửa sổ. Thủ tục cửa sổ chỉ rỏ phản


ứng của của chương trình với biến cố nhập, qui định cách vẽ lại phần cửa sổ trước đó bị che


khuất.



switch (message)
{


case WM_CREATE:


// Xu ly thong diep WM_CREATE
break;


</div>
<span class='text_page_counter'>(20)</span><div class='page_container' data-page=20>

WM_PAINT : Thông điệp được gởi mỗi khi cần vẽ lại cửa sổ.


WM_CHAR : Thông điệp ký tự được bấm.



WM_TIMER : Thông điệp của bộ định thời.




WM_LBUTTONDOWN : Thông điệp được gởi mỗi khi mắt trái chuột được bấm.



WM_DESTROY: Thông điệp chỉ rõ Windows đang huỷ cửa sổ sau khi nhận lệnh của


người dùng. Các ứng dụng xử lý thông điệp này theo cách chuẩn là gọi


PostQuitMessage(0);



1.3.2 Thông điệp WM_DESTROY và cách kết thúc chương trình



• Chương trình kết thúc bình thường phải bảo đảm dọn dẹp và giải phóng lại bộ nhớ


cũng như các tài nguyên khác mà chương trình đã dùng. Chương trình kết thúc khi


vịng lặp thơng điệp nhận được thơng điệp WM_QUIT.



• Chương trình có thể được kết thúc bằng nhiều cách: Bằng [System menu] Close, bấm


Alt-F4 hoặc biểu tượng X ở góc trên bên phải. Mỗi cách trên đều dẫn đến một loạt các


thông điệp được gởi cho hàm cửa sổ. Hàm cửa sổ có thể xử lý các thông điệp này hoặc


gởi cho hàm xử lý cửa sổ mặc nhiên.



Một trong số các thơng điệp được hàm cửa sổ quan tâm xử lý là WM_DESTROY. Xử lý



chuẩn cho thông điệp này là gọi PostQuitMessage(0). Hàm này chèn WM_QUIT vào


hàng đợi thông điệp. GetMessage sẽ trả về 0 khi nhận WM_QUIT và kết thúc vịng lặp


thơng điệp.



• Dãy thơng điệp sau đây được gởi cho hàm cửa sổ khi người sử dụng bấm tổ hợp phím


tắt

Alt-F4



WM_SYSKEYDOWN [Alt] được bấm


WM_SYSKEYDOWN [F4] được bấm



</div>
<span class='text_page_counter'>(21)</span><div class='page_container' data-page=21>

1.3.3 Xử lý mặc nhiên các thông điệp




− Các ứng dụng Windows đều có giao diện chung khá giống nhau. Điều này được


thực hiện nhờ hàm xử lý thông điệp mặc nhiên.



− Thông điệp không được WndProc quan tâm xử lý sẽ được gởi cho DefWindowProc


xử lý. DefWindowProc là hàm xử lý mặc nhiên các thơng điệp, nó hoạt động giống


nhau cho tất cả các ứng dụng Windows.



− WndProc có thể gởi thơng điệp cho DefWindowProc và ngược lại. Ví dụ, khi người


sử dụng nhấn tổ hợp phím Alt-F4 để tắt ứng dụng, một loạt các thông điệp được gởi


cho WinProc.



− Thông điệp bao giờ cũng được gởi cho hàm cửa sổ trước. Hàm cửa sổ sẽ gọi


DefWindowProc để xử lý các thơng điệp nó khơng quan tâm.



− Người sử dụng có thể thay đổi một vài hoạt động mặc nhiên bằng cách tự xử lý một


số thơng điệp nào đó.



<b>1.4 </b>

Mơi trường Visual C++



</div>
<span class='text_page_counter'>(22)</span><div class='page_container' data-page=22>

Hình 1-2 – Taïo project trong VC++ 6.0


</div>
<span class='text_page_counter'>(23)</span><div class='page_container' data-page=23>

<b>2. </b>

Tổng quan về Giao Diện Thiết Bị Đồ Hoạ (GDI)



Giao diện đồ hoạ là một trong các đặc tính nổi bật của hệ điều hành Windows. Trong chương


này ta sẽ trình bày tổng quan các vấn đề liên quan đến các thao tác tạo tạo xuất liệu đồ hoạ,


các thao tác vẽ có thể dùng GDI của Windows.



<b>2.1 </b>

Giới thiệu




• GDI là thư viện đồ hoạ của Windows, nó xử lý các thao tác xuất kết quả dạng đồ hoạ


lên màn hình cũng như lên các thiết bị xuất khác như máy in, máy vẽ. GDI có thể tạo


ra các điểm vẽ, đường, chữ… Windows cũng dùng chính GDI để tạo giao diện người


dùng như các cửa sổ, biểu tượng, menu, hộp hội thoại.



• GDI còn cho phép vẽ trên thiết bị logic hay còn gọi là thiết bị ảo như bitmaps và


metafiles.



• GDI tạo khả năng đồ hoạ độc lập thiết bị, nó cung cấp cùng một thủ tục vẽ cho tất cả


các loại thiết bị thay vì 4 cho 4 loại thiết bị khác nhau là màn hình, máy in, bitmaps và


metafile.



Các thiết bị GDI



• GDI có thể vẽ trên các thiết bị khác nhau nhờ các bộ điều khiển thiết bị.



• Bộ điều khiển thiết bị cho GDI biết khả năng vẽ có sẵn của thiết bị (nhờ device


capability bits). Có 5 cờ cho biết khả năng của thiết bị: vẽ cung, đường thẳng, đa giác,


bitmaps và text.



• Mỗi bộ điều khiển thiết bị phải cung cấp ít nhất hai thủ tục, vẽ từng điểm và vẽ đường.


• Bitmap ln ln có hình chữ nhật, nó là hình ảnh được lưu trữ trong bộ nhớ giống như



</div>
<span class='text_page_counter'>(24)</span><div class='page_container' data-page=24>

2.2.2 Các đối tượng vẽ logic



GDI dùng đối tượng vẽ logic để có khả năng độc lập thiết bị. Đối tượng vẽ logic mô tả xuất


liệu ở mức cao, độc lập với thiết bị. Các đối tượng vẽ bao gồm pens (bút), brushes (chổi vẽ),


fonts (kiểu chữ) và logical colors (màu luận lý).



<b>2.3 Ngữ cảnh thiết bị (device context) </b>




• Ngữ cảnh thiết bị là tập hợp các thuộc tính vẽ trong đó có một pen để vẽ đường, một


brush để tô màu và một font để hiển thị văn bản.



• Nhờ ngữ cảnh thiết bị, các hàm vẽ trở nên đơn giản hơn với các tham số được giảm


đến mức tối thiểu vì các thuộc tính vẽ được lấy mặc nhiên từ ngữ cảnh thiết bị. Ta


tham chiếu đến ngữ cảnh thiết bị nhờ một handle đến ngữ cảnh thiết bị (hdc).



Ví dụ :



hdc = BeginPaint(hWnd, &ps);


RECT rt;



GetClientRect(hWnd, &rt);



DrawText(hdc,

szHello,

strlen(szHello),

&rt,



DT_CENTER);



EndPaint(hWnd, &ps);



</div>
<span class='text_page_counter'>(25)</span><div class='page_container' data-page=25></div>
<span class='text_page_counter'>(26)</span><div class='page_container' data-page=26>

Hình 2-2 – Các vùng xén tạo nên các hàng rào không cho phép ứng dụng
này vẽ lên cửa sổ ứng dụng khác


• Vai trò thứ ba của ngữ cảnh thiết bị là ngăn cách xuất liệu của nhiều chương trình truy


xuất thiết bị cùng lúc (vai trò permission slip). Đối với máy in dùng cơ chế spooling và


đối với các cửa sổ ứng dùng, dùng clipping.



• Khi một ứng dụng muốn vẽ, nó phải mượn DC từ Windows


Borrowing routines

Returning routines Description



BeginPaint



GetDC



EndPaint


ReleaseDC



</div>
<span class='text_page_counter'>(27)</span><div class='page_container' data-page=27>

• Thực hiện các thao tác vẽ


• Trả lại DC cho Windows



Hai hàm mượn và trả DC thông dụng là BeginPaint và EndPaint, BeginPaint cho handle đến


ngữ cảnh thiết bị của phần đã được xén trong vùng client.



Việc vẽ trong Windows thường theo khuôn mẫu sau:


case WM_PAINT:



hdc = BeginPaint(hWnd, &ps);


// Các thao tác vẽ



EndPaint(hWnd, &ps);


break;



Có thể dùng GetDC và RealeaseDC để thực hiện thao tác vẽ ngay khi có biến cố nhập như


chuột hay bàn phím được bấm. GetDC trả về handle đến tồn bộ vùng client của cửa sổ ứng


dụng.



case WM_CHAR:


hdc = GetDC(hWnd);



// Thao tác vẽ



ReleaseDC(hWnd, hdc);
break;


Ta cũng có thể dùng GetWindowDC để vẽ lên cả phần non-client của cửa sổ ứng dụng.


<b>2.5 Vẽ từng điểm </b>



Trong phần này ta sẽ tìm hiểu chi tiết qui trình vẽ trên vùng client của một cửa sổ ứng dụng,


và các thao tác vẽ bằng cách trực tiếp bật tắt một pixel nào đó trên màn hình. Các chương tiếp


theo sẽ trình bày các thao tác vẽ đường, hình có tơ màu, vẽ chữ…



2.5.1 Chương trình Pixel



Chương trình pixel sau đây khi được thực hiện sẽ hiển thị một chấm đen ở giữa vùng client


của cửa sổ ứng dụng. Chương trình pixel gồm các tập tin chương trình nguồn sau:



stdafx.h
stdafx.cpp
pixel.cpp


// Trich doan pixel.cpp


</div>
<span class='text_page_counter'>(28)</span><div class='page_container' data-page=28>

return DefWindowProc(hWnd, message, wParam, lParam);
}


return 0;
}


2.5.2 SetPixel vaø GetPixel




Để vẽ từng điểm, ta dùng hàm SetPixel, để lấy giá trị của một pixel xác định trên cửa sổ


ứng dụng, ta dùng hàm GetPixel. Trong chương trình trên, ta còn dùng hàm


GetClientRect. Khai báo của các hàm trên như sau:



Hàm SetPixel đặt giá trị đỏ, xanh lá cây, xanh dương (RGB) cho một pixel tại một toạ độ qui


định trước.



<b>COLORREF SetPixel( </b>



<i><b>HDC hdc, </b></i>

// handle of device context



<i><b>int X, </b></i>

// x-coordinate of pixel



<i><b>int Y, </b></i>

// y-coordinate of pixel



<i><b>COLORREF crColor </b></i>

// pixel color



<b> ); </b>



Hàm GetPixel trả về giá trị đỏ, xanh lá cây, xanh dương của một pixel tại một vị trí qui định


trước.



<b>COLORREF GetPixel( </b>



<i><b>HDC hdc, </b></i>

// handle of device context



<i><b>int XPos, </b></i>

// x-coordinate of pixel



<i><b>int nYPos </b></i>

// y-coordinate of pixel




<b> ); </b>



COLORREF là một số DWORD lưu trữ 3 thành phần R,G,B biểu diễn màu. Ta có thể dùng


macro RGB để tạo ra màu mong muốn.



</div>
<span class='text_page_counter'>(29)</span><div class='page_container' data-page=29>

<b>BOOL GetClientRect( </b>



<i><b>HWND hWnd, </b></i>

// handle of window



<i><b>LPRECT lpRect // address of structure for client coordinates </b></i>


<b>); </b>



LPRECT là con trỏ đến cấu trúc RECT



typedef struct _RECT { // rc
LONG left;


LONG top;
LONG right;
LONG bottom;
} RECT;



<b>2.6 Tạo Marker </b>



Chương trình Marker sử dụng SetPixel và GetPixel để đánh dấu hình chữ thập nhỏ (+) mỗi


khi người sử dụng bấm mắt chuột trái. Các tập tin chương trình nguồn gồm.



</div>
<span class='text_page_counter'>(30)</span><div class='page_container' data-page=30>

#define MAX_MARKERS 100
#define MARKER_SIZE 5



void DrawMarker(HDC hdc, POINT pt, int size);


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{


PAINTSTRUCT ps;
HDC hdc;


int i;


static POINT apt[MAX_MARKERS];
static int nMarkers = 0;


switch (message)
{


case WM_LBUTTONDOWN:


if (nMarkers <= MAX_MARKERS)
{


apt[nMarkers].x = LOWORD(lParam);
apt[nMarkers].y = HIWORD(lParam);
nMarkers++;


InvalidateRect(hWnd, NULL, TRUE);
}



break;


case WM_RBUTTONDOWN:
nMarkers = 0;


InvalidateRect(hWnd, NULL, TRUE);
break;


case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
for (i = 0; i < nMarkers; i++)


DrawMarker(hdc, apt[i], MARKER_SIZE);
EndPaint(hWnd, &ps);


break;
case WM_DESTROY:


PostQuitMessage(0);
break;


default:


</div>
<span class='text_page_counter'>(31)</span><div class='page_container' data-page=31>

for (i = 1; i <= size; i++)
{


InvertPixel(hdc, x-i, y);
InvertPixel(hdc, x+i, y);


InvertPixel(hdc, x, y-i);
InvertPixel(hdc, x, y+i);
}


}


2.6.2 Hoạt động của chương trình



Hàm cửa sổ của chương trình xử lý bốn thơng điệp WM_LBUTTONDOWN, WM_RBUTTONDOWN,


WM_PAINT và WM_DESTROY. WM_LBUTTONDOWN và WM_RBUTTONDOWN là thông điệp mắt


chuột, nó được gởi tới cho hàm cửa sổ mỗi khi mắt trái (hoặc phải) của chuột được bấm. Trong


Marker, ta xử lý thông điệp WM_LBUTTONDOWN bằng cách thêm một điểm vào mảng lưu trữ


các điểm và gởi thông báo cần vẽ lại nhờ hàm InvalidateRect.



Tham số lParam gởi kèm theo thơng điệp cho biết vị trí của con trỏ chuột tại thời điểm bấm


(hoặc nhả).



<b>2.7 Vẽ và vẽ lại </b>



Cửa sổ trong Windows thường bị che một phần bởi hộp hội thoại, menu, con trỏ chuột hay một


cửa sổ khác. Khi không còn bị che, phần này cần được phục hồi. Trong một số trường hợp đặc


biệt khi phần bị che tương đối nhỏ như con trỏ chuột di chuyển hay một hộp hội thoại được mở


ra, Windows tìm cách lưu phần bị che để dễ phục hồi sau này.



Trong đa số các trường hợp còn lại, cơ chế phục hồi phần cửa sổ bị che là bản thân cửa sổ tự


nó vẽ lại phần hư. Điều này được thực hiện mỗi khi cửa sổ nhận được thông điệp WM_PAINT.


Windows gởi thông điệp WM_PAINT này đến cửa sổ bị che trong các trường hợp sau:



• Cửa sổ che đóng lại hoặc di chuyển.




• Cửa sổ thay đổi kích thước (lớp cửa sổ phải được đăng ký với cờ CS_HREDRAW và


CS_VREDRAW được bật).



• Chương trình gián tiếp gởi WM_PAINT thơng qua hàm InvalidateRect.


• Cửa sổ được cuốn.



</div>
<span class='text_page_counter'>(32)</span><div class='page_container' data-page=32>

Hàm mượn ngữ cảnh thiết bị là BeginPaint, hàm trả là EndPaint. Hai hàm này sử dụng cấu


trúc PAINTSTRUCT. Khai báo cấu trúc PAINSTRUCT và các hàm trên như sau.



typedef struct tagPAINTSTRUCT
{ // ps


HDC hdc;
BOOL fErase;
RECT rcPaint;
BOOL fRestore;
BOOL fIncUpdate;
BYTE rgbReserved[32];
} PAINTSTRUCT;


<b>HDC BeginPaint( </b>



<i><b>HWND hwnd, </b></i>

// handle to window



<b> </b>

<i><b>LPPAINTSTRUCT lpPaint </b></i>

//

pointer

to

structure

for

paint


information



<b> ); </b>



<b>BOOL EndPaint( </b>




<i><b>HWND hWnd, </b></i>

// handle to window



<i><b>CONST PAINTSTRUCT *lpPaint // pointer to structure for paint data </b></i>


<b> ); </b>



BeginPaint mượn Windows ngữ cảnh thiết bị (DC) và đưa các thông tin vẽ vào cấu trúc


PAINSTRUCT. BeginPaint được sử dụng để hoạt động cùng với thông điệp WM_PAINT.


BeginPaint cung cấp thông tin về các vùng xén, vùng không hợp lệ.



Hàm EndPaint trả lại DC cho Windows. Khi hàm này được gọi Windows khôi phục lại các


thuộc tính vẽ sẵn sàng cho chương trình cần vẽ ở lần kế tiếp. Đồng thời EndPaint cũng


thông báo rằng vùng không hợp lệ đã được sửa chữa.



</div>
<span class='text_page_counter'>(33)</span><div class='page_container' data-page=33>

<b>); </b>



Trong marker ta gọi với các tham số



<b>InvalidateRect(hWnd, NULL, TRUE); </b>



Tham số thứ ba là cờ xoá, giá trị TRUE cho biết sẽ xoá phần nền trước khi vẽ. Tham số thứ


hai là vùng cửa sổ cần vẽ lại. Giá trị NULL cho biết cần phải vẽ lại toàn bộ vùng client.


Vẽ lại tồn bộ vùng client như trên là khơng hiệu quả. Thông thường, ta chỉ cần vẽ lại một


vùng nhỏ bị hư hay cần cập nhật, ta gọi vùng này là vùng không hợp lệ. Trong marker, mỗi


lần thêm một dấu thập mới, ta chỉ cần vẽ lại vùng đủ chứa marker này.



Ví dụ sau minh hoạ việc vẽ lại tồn bộ đúng dấu thập tại vị trí mắt trái chuột được bấm.



// Marker ver 1.1 : Chi ve lai phan hinh chu nhat vua du chua marker
case WM_LBUTTONDOWN:



if (nMarkers <= MAX_MARKERS)
{


apt[nMarkers].x = LOWORD(lParam);
apt[nMarkers].y = HIWORD(lParam);
nMarkers++;


r.bottom = HIWORD(lParam) + MARKER_SIZE + 1;
r.top = HIWORD(lParam) - MARKER_SIZE - 1;
r.left = LOWORD(lParam) - MARKER_SIZE - 1;
r.right = LOWORD(lParam) + MARKER_SIZE + 1;
InvalidateRect(hWnd, &r, TRUE);


}
break;


<b>2.10 InvertPixel vaø DrawMarker </b>



Sử dụng SetPixel để vẽ dấu thập có thể khơng có tác dụng (vơ hình) nếu màu nền trùng với


màu của dấu thập. Để bảo đảm việc đánh dấu trên mọi nền, ta phủ định giá trị pixel của màu


nền. Điều này được thực hiện nhờ GetPixel theo sau bởi SetPixel với giá trị đã được phủ định


từng bit.



void InvertPixel(HDC hdc, int x, int y)
{


</div>
<span class='text_page_counter'>(34)</span><div class='page_container' data-page=34>

}


Để có thể đánh dấu trên nền có màu khác nhau, DrawMarker phải bảo đảm vẽ dấu chữ thập



bằng gọi InvertPixel, InvertPixel đảo ngược giá trị pixel trên nền cách phủ định giá trị pixel


đang có.



<b>2.11 GetDC và ReleaseDC </b>



Mỗi khi mắt chuột được bấm, thay vì gián tiếp ra lệnh việc vẽ lại thông qua InvalidateRect để


gởi WM_PAINT. Ta có thể vẽ trực tiếp dấu thập mỗi khi nhận được thông điệp mắt trái chuột


được bấm. Điều này có thể được thực hiện bằng GetDC và ReleaseDC. Hàm GetDC cho


mượn ngữ cảnh thiết bị đến toàn bộ vùng client, ReleaseDC trả lại ngữ cảnh này. Giữa hai


thao tác mượn và trả này, ta có thể thực hiện các thao tác vẽ.



// Marker.cpp : Defines the entry point for the application.
// Version 1.2: Dung GetDC de ve ngay khi bam mat trai chuot
// Trich doan phan WndProc


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{


#define MAX_MARKERS 100
#define MARKER_SIZE 5


HDC hdc;


TCHAR szHello[MAX_LOADSTRING];
int i;


static POINT apt[MAX_MARKERS];
static int nMarkers = 0;



switch (message)
{


case WM_LBUTTONDOWN:


if (nMarkers <= MAX_MARKERS)
{


apt[nMarkers].x = LOWORD(lParam);
apt[nMarkers].y = HIWORD(lParam);
nMarkers++;


hdc = GetDC(hWnd);


</div>
<span class='text_page_counter'>(35)</span><div class='page_container' data-page=35>

EndPaint(hWnd, &ps);
break;


case WM_DESTROY:


PostQuitMessage(0);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


</div>
<span class='text_page_counter'>(36)</span><div class='page_container' data-page=36>

<b>3. </b>

Vẽ đường




<b>3.1 </b>

Đường



• Đường là một đối tượng hình học có được bằng cách nối liên tiếp các điểm.


• Đường là loại đối tượng hình học mở. Mỗi đường đều có điểm đầu và điểm cuối



• Đường được vẽ theo giải thuật inclusive/exclusive, nghĩa là điểm đầu thuộc đường,


nhưng điểm cuối không thuộc đường.



<b>3.2 Các hàm vẽ đường </b>



GDI cung cấp hàm vẽ các đường thẳng, cung, đường cong bezier, polyline.


Một số hàm vẽ đường sử dụng cấu trúc POINT



typedef struct tagPOINT
{


LONG x;
LONG y;
} POINT;


3.2.1 MoveToEx vaø LineTo



BOOL MoveToEx(


<i>HDC hdc, </i>
<i>int X, int Y, </i>
LPPOINT lpPoint
);


BOOL LineTo(



</div>
<span class='text_page_counter'>(37)</span><div class='page_container' data-page=37>

<i><b>CONST POINT *lppt, </b></i> // pointer to array containing endpoints


<i><b>int cPoints </b></i> // number of points in the array


<b>); </b>


<b>BOOL PolylineTo( </b>


<i><b>HDC hdc, </b></i> // handle to device context


<i><b>CONST POINT *lppt, </b></i> // pointer to array of points


<i><b>DWORD cCount </b></i> // number of points in array


<b>); </b>


<b>BOOL PolyPolyline( </b>


<i><b>HDC hdc, </b></i> // handle of a device context


<i><b>CONST POINT *lppt, </b></i> // address of an array of points


<i><b>CONST DWORD *lpdwPolyPoints, </b></i> // address of an array of values


<i><b>DWORD cCount </b></i> // number of counts in the second array


<b>); </b>


Linedrag: Ví dụ minh hoạ MoveTo và LineTo




</div>
<span class='text_page_counter'>(38)</span><div class='page_container' data-page=38>

#include "stdafx.h"
#include "resource.h"
#define MAX_SEGS 100
#define MAX_POINTS 10000


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{


int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;


TCHAR szPrompt[MAX_LOADSTRING];


LoadString(hInst, IDS_PROMPT, szPrompt, MAX_LOADSTRING);
int i,j;


static int nSegs, nPoints;
static int anCounts[MAX_SEGS+1];
static BOOL bMouseDown;


static POINT oldPoint, apt[MAX_POINTS];
switch (message)


{


case WM_LBUTTONDOWN:



if (nSegs < MAX_SEGS && nPoints < MAX_POINTS)
{


bMouseDown = TRUE;
SetCapture(hWnd);


anCounts[nSegs] = nPoints;


apt[nPoints].x = LOWORD(lParam);
apt[nPoints++].y = HIWORD(lParam);
nSegs++;


}
break;


case WM_MOUSEMOVE:
if (bMouseDown)
{


if (nPoints < MAX_POINTS)
{


</div>
<span class='text_page_counter'>(39)</span><div class='page_container' data-page=39>

case WM_RBUTTONDOWN:
nSegs = nPoints = 0;


InvalidateRect(hWnd, NULL, TRUE);
break;


case WM_PAINT:



hdc = BeginPaint(hWnd, &ps);
RECT rt;


GetClientRect(hWnd, &rt);


DrawText(hdc, szPrompt, strlen(szPrompt), &rt, DT_CENTER);
for (i = 0; i < nSegs; i++)


{


j = anCounts[i];


MoveToEx(ps.hdc, apt[j].x, apt[j].y, &oldPoint);
for (; j < anCounts[i+1]; j++)


LineTo(ps.hdc, apt[j].x, apt[j].y);
}


EndPaint(hWnd, &ps);
break;


case WM_DESTROY:


PostQuitMessage(0);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);


}


return 0;
}


Chương trình trên có thể cải tiến dùng Polyline hoặc PolyPolyline


3.2.3 Arc



Hàm Arc vẽ một cung là một phần của hình Ellipse


<b>BOOL Arc( </b>


<i><b>HDC hdc, </b></i> // handle to device context


<i><b>int nLeftRect, </b></i> // x-coord : UPL


<i><b>int nTopRect, </b></i> // y-coord : UPL


<i><b>int nRightRect, </b></i> // x-coord : LWR


</div>
<span class='text_page_counter'>(40)</span><div class='page_container' data-page=40>

<b>BOOL ArcTo( </b>


<i><b>HDC hdc, </b></i> <b>// handle to device context </b>


<i><b>int nLeftRect, </b></i> <b>// x-coord : UPL </b>


<i><b>int nTopRect, </b></i> <b>// y-coord : UPL </b>


<i><b>int nRightRect, </b></i> <b>// x-coord : LWR </b>


<i><b>int nBottomRect, </b></i> <b>// y-coord : LWR </b>



<i><b>int nXRadial1, </b></i> <b>// x-coord of the first radial ending point </b>


<i><b>int nYRadial1, </b></i> <b>// y-coord of the first radial ending point </b>


<i><b>int nXRadial2, </b></i> <b>// x-coord of the second radial ending point </b>


<i><b>int nYRadial2 </b></i> // y-coord of the second radial ending point


<b>); </b>


3.2.5 AngleArc


<b>BOOL AngleArc( </b>


<i><b>HDC hdc, </b></i> <b>// handle to device context </b>


<i><b> int X, </b></i> // x-coordinate of circle's center


<i><b> int Y, </b></i> // y-coordinate of circle's center


<i><b> DWORD dwRadius, </b></i> // circle's radius


<i><b> FLOAT eStartAngle, </b></i> // arc's start angle


<i><b> FLOAT eSweepAngle // arc's sweep angle </b></i>
<b> ); </b>


3.2.6 PolyBezier vaø PolyBezierTo



Hàm PolyBezier vẽ một hay nhiều đường cong Bézier.



<b>BOOL PolyBezier( </b>


<i><b>HDC hdc, // handle to device context </b></i>


<i><b>CONST POINT *lppt, // pointer to endpoints and control points </b></i>
<i><b>DWORD cPoints // count of endpoints and control points </b></i>


<b> ); </b>


</div>
<span class='text_page_counter'>(41)</span><div class='page_container' data-page=41></div>
<span class='text_page_counter'>(42)</span><div class='page_container' data-page=42>

Thuộc tính

Ý nghóa


Màu nền (background color)



Chế độ nền (background mode)


Vị trí hiện hành (curent position)


Chế độ vẽ (drawing mode)


Bút vẽ (pen)



Màu thứ hai khi không phải bút vẽ PS_SOLID


Tắt/mở màu nền



Điểm hiện hành cho các thao tác LineTo, ArcTo…


Thao tác vẽ boolean giữa điểm vẽ và nền



Màu, độ rộng và kiểu vẽ đường


Hàm để thay đổi màu nền và chế độ nền là



<i><b>COLORREF SetBkColor(HDC hdc, COLORREF crColor); </b></i>
<i><b>int SetBkMode(HDC hdc, int iBkMode); </b></i>


<i>iBkMode có thể là OPAQUE hoặc TRANSPARENT</i>




<b>3.4 Bút vẽ (pen) </b>



• Bút vẽ là thuộc tính chỉ rõ cách vẽ đường, bút vẽ có ba thành phần là màu, độ rộng và


kiểu.



• Bút vẽ có thể được chia sẻ giữa các các chương trình và giữa các thiết bị.



• GDI cung cấp sẵn một số bút vẽ. Người sử dụng có thể tự tạo thêm các bút vẽ khác.


• Bút vẽ có thể được dùng độc lập thiết bị vì thực chất là bút vẽ luận lý. Khi GDI vẽ, nó



yêu cầu thiết bị nhận biết bút vẽ và bộ điều khiển thiết bị sẽ vẽ đường theo chất lượng


yêu cầu. Nhờ vậy bút vẽ có thể được chia sẻ trên các thiết bị khác nhau.



3.4.1 Taïo và dùng bút vẽ có sẵn



Windows cung cấp sẵn một tập các bút vẽ (stock pens). Đó là các bút vẽ đen, trắng và rỗng.


<b>Hàm GetStockObject </b>



Bút vẽ được tham chiếu đến bằng handle. Hàm GetStockObject cho phép lấy handle của


một bút vẽ có sẵn.



</div>
<span class='text_page_counter'>(43)</span><div class='page_container' data-page=43>

hpenOld = SelectObject(hdc, hPenNew);


…// Các thao tác vẽ đường



SelectObject(hdc, hPenOld);


3.4.2 Tạo mới các bút vẽ




Người sử dụng có thể tự tạo các bút vẽ khác hơn các bút vẽ do Windows cung cấp, có hai cách


để tạo bút vẽ.



<b>Haøm CreatePen </b>



HPEN hpen;


hpen = CreatePen (nPenStyle, nWidth, crColor);


Để tạo bút vẽ đặc, độ rộng 4, màu đỏ, dùng



hpen = CreatePen (PS_SOLID, R, RGB(255,0,0));


nPenStype có thể có các giá trị được định nghĩa sẵn như sau:



PS_SOLID Pen is solid.
PS_DASH Pen is dashed.
PS_DOT Pen is dotted.


PS_DASHDOT Pen has alternating dashes and dots.
PS_DASHDOTDOT Pen has dashes and double dots.
PS_NULL Pen is invisible.


PS_INSIDEFRAME Pen is solid, to be drawn inside a filled figure


<b>Haøm CreatePenIndirect </b>



Dùng cấu trúc LOGPEN để tạo bút vẽ



typedef struct tagLOGPEN { // lgpn


UINT lopnStyle;


POINT lopnWidth;
COLORREF lopnColor;
} LOGPEN;


</div>
<span class='text_page_counter'>(44)</span><div class='page_container' data-page=44>

3.4.3 Ví dụ minh hoạ các thuộc tính bút vẽ



Hình 3-3 – Pen


// Pen.cpp : Defines the entry point for the application.
// Trich doan ham WndProc


#define dim(a) sizeof(a)/sizeof(a[0])
struct PenData


{


char *Name;


int nWidth, nStyle;
COLORREF crColor;
};


PenData aPenTab[] =
{


</div>
<span class='text_page_counter'>(45)</span><div class='page_container' data-page=45>

int i;
HPEN hpen



switch (message)
{


case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);


for (i = 0; i < dim(aPenTab); i++)
{


TextOut(hdc, 10, i*30, aPenTab[i].Name,
lstrlen(aPenTab[i].Name));


hpen = CreatePen(aPenTab[i].nStyle,
aPenTab[i].nWidth,


aPenTab[i].crColor);


SelectObject(hdc, hpen);


MoveToEx(hdc, 150, i*30+10, NULL);
LineTo(hdc, 500, i*30+10);


DeleteObject(hpen);
}


EndPaint(hWnd, &ps);
break;


case WM_DESTROY:



PostQuitMessage(0);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


return 0;
}


<b>3.5 Các chế độ vẽ (Drawing Modes) ảnh hưởng đến vẽ đường </b>



Chế độ vẽ là thao tác Boolean điểu khiển cách GDI vẽ điểm, đường, hình. Chế độ vẽ cịn


được gọi là chế độ trộn vì nó trộn logic với giá trị có sẵn trên nền. Hàm SetRop2 cho phép


thay đổi chế độ vẽ. Hàm GetRop2 cho biết chế độ vẽ hiện hành.



<b>int SetROP2( </b>


<i><b>HDC hdc, </b></i> // handle of device context


<b> </b> <i><b>int fnDrawMode </b></i> // drawing mode


<b> ); </b>


</div>
<span class='text_page_counter'>(46)</span><div class='page_container' data-page=46>

R2_MASKPEN Pixel is a combination of the colors common to both the pen and the screen.
R2_MASKPENNOT Pixel is a combination of the colors common to both the pen and the inverse of


the screen.



R2_MERGENOTPEN Pixel is a combination of the screen color and the inverse of the pen color.
R2_MERGEPEN Pixel is a combination of the pen color and the screen color.


R2_MERGEPENNOT Pixel is a combination of the pen color and the inverse of the screen color.
R2_NOP Pixel remains unchanged.


R2_NOT Pixel is the inverse of the screen color.
R2_NOTCOPYPEN Pixel is the inverse of the pen color.


R2_NOTMASKPEN Pixel is the inverse of the R2_MASKPEN color.
R2_NOTMERGEPEN Pixel is the inverse of the R2_MERGEPEN color.
R2_NOTXORPEN Pixel is the inverse of the R2_XORPEN color.
R2_WHITE Pixel is always 1.


</div>
<span class='text_page_counter'>(47)</span><div class='page_container' data-page=47>

<b>4. </b>

Vẽ miền



<b>4.1 </b>

Miền



• Miền hay hình kín là đối tượng hình học có hai phần: miền trong và đường biên bao quanh


miền đó. Ta cịn gọi các hình này là hình có tơ màu.



• GDI dùng hai cách diễn dịch toạ độ là tâm điểm (pixel-centered) và giao điểm lưới


(grid-intersection) để vẽ các miền. Toạ độ tâm điểm được dùng để vẽ đường còn toạ độ giao


điểm lưới được dùng để xén (clipping). Một trong những ảnh hưởng của toạ độ giao điểm


lưới là hình vẽ có kích thước nhỏ hơn một pixel so với toạ độ tâm điểm.



• Toạ độ tâm điểm được dùng với các hàm vẽ đa giác, toạ độ giao điểm lưới được dùng để


vẽ hình chữ nhật, ellipse, chord, pie.




• GDI cung cấp các hàm vẽ hình với cách diễn dịch toạ độ tương ứng như sau:



Hàm

Toạ độ

Ý nghĩa



Polygon

tâm điểm

đa giác



PolyPolygon

tâm điểm

nhiều ña giaùc



Chord

giao điểm lưới

một phần cung được khép kín bằng đường thẳng


Ellipse

giao điểm lưới

ellipse



Pie

giao điểm lưới

hình cánh quạt


Rectangle

giao điểm lưới

hình chữ nhật



RoundRect

giao điểm lưới

hình chữ nhật có góc trịn


<b>4.2 Các hàm vẽ miền </b>



</div>
<span class='text_page_counter'>(48)</span><div class='page_container' data-page=48>

4.2.1 Vẽ đa giác – Polygon vaø PolyPolygon



Hàm Polygon vẽ một đa giác bằng cách nối các điểm dùng bút vẽ sẵn có trong ngữ cảnh thiết


bị. Nếu điểm đầu và điểm cuối không trùng nhau, một đoạn thẳng sẽ được thêm vào để nối.


Miền bên trong được tơ bằng chổi vẽ có sẵn trong ngữ cảnh thiết bị.



<b>BOOL Polygon( </b>


<i><b> HDC hdc, // handle to device context </b></i>


<i><b> CONST POINT *lpPoints, // pointer to polygon's vertices </b></i>
<i><b> int nCount // count of polygon's vertices </b></i>



<b>); </b>


Hàm PolyPolygon vẽ nhiều đa giác với chỉ một lệnh gọi. Chương trình vẽ nhiều đa giác dùng


hàm này sẽ chạy nhanh hơn gọi nhiều lần hàm vẽ đa giác.



<b>BOOL PolyPolygon( </b>


<i><b> HDC hdc, // handle to device context </b></i>


<i><b> CONST POINT *lpPoints, // pointer to array of vertices for polygons </b></i>
<i><b> CONST INT *lpPolyCounts, // pointer to array with count of vertices </b></i>
<i><b> int nCount // count of polygons </b></i>


<b>); </b>


4.2.2 Vẽ hình chữ nhật – Rectangle, RoundRect, FrameRect và InvertRect


Vẽ hình chữ nhật



<b>BOOL Rectangle( </b>


<i><b> HDC hdc, // handle to device context </b></i>


</div>
<span class='text_page_counter'>(49)</span><div class='page_container' data-page=49>

Hình 4-1 Rectangle


Vẽ hình chữ nhật có góc trịn


<b>BOOL RoundRect( </b>


<i><b> HDC hdc, // handle to device context </b></i>


</div>
<span class='text_page_counter'>(50)</span><div class='page_container' data-page=50>

Hình 4-2 RoundRect



Vẽ khung chữ nhật


<b>int FrameRect( </b>


<i><b> HDC hDC, // handle to device context </b></i>


<i><b> CONST RECT *lprc, // pointer to rectangle coordinates </b></i>
<i><b> HBRUSH hbr // handle to brush </b></i>


<b>); </b>


Tô chữ nhật


<b>int FillRect( </b>


<i><b> HDC hDC, // handle to device context </b></i>


<i><b> CONST RECT *lprc, // pointer to structure with rectangle </b></i>
<i><b> HBRUSH hbr // handle to brush </b></i>


<b>); </b>


Đảo màu hình chữ nhật


<b>BOOL InvertRect( </b>


<i><b> HDC hDC, // handle to device context </b></i>


<i><b> CONST RECT *lprc // pointer to structure with rectangle </b></i>
<b>); </b>


4.2.3 Ellipse, Pie vaø Chord



<b>BOOL Ellipse( </b>


<i><b> HDC hdc, // handle to device context </b></i>


</div>
<span class='text_page_counter'>(51)</span><div class='page_container' data-page=51>

Hình 4-3 Ellipse


<b>BOOL Pie( </b>


<i><b> HDC hdc, // handle to device context </b></i>


<i><b> int nLeftRect, // x-coord of bounding rectangle's upper-left corner </b></i>
<i><b> int nTopRect, // y-coord of bounding rectangle's upper-left corner </b></i>
<i><b> int nRightRect, // x-coord of bounding rectangle's lower-right corner </b></i>
<i><b> int nBottomRect, // y-coord of bounding rectangle's lower-right corner </b></i>
<i><b> int nXRadial1, // x-coord of first radial's endpoint </b></i>


</div>
<span class='text_page_counter'>(52)</span><div class='page_container' data-page=52>

<b>Hình 4-4 Pie </b>


<b>BOOL Chord( </b>


<i><b> HDC hdc, // handle to device context </b></i>


<i><b> int nLeftRect, // x-coord of the upper-left corner of bounding rectangle </b></i>
<i><b> int nTopRect, // y-coord of the upper-left corner of bounding rectangle </b></i>
<i><b> int nRightRect, // x-coord of the lower-right corner of bounding rectangle </b></i>
<i><b> int nBottomRect, // y-coord of the lower-right corner of bounding rectangle </b></i>
<i><b> int nXRadial1, // x-coord of the first radial's endpoint </b></i>


<i><b> int nYRadial1, // y-coord of the first radial's endpoint </b></i>
<i><b> int nXRadial2, // x-coord of the second radial's endpoint </b></i>


<i><b> int nYRadial2 // y-coord of the second radial's endpoint </b></i>
<b>); </b>


Hình 4-5 Chord


<b>4.3 Các thuộc tính tô màu </b>



</div>
<span class='text_page_counter'>(53)</span><div class='page_container' data-page=53>

Thuộc tính vẽ

Ý nghóa



Màu nền (Background Color):

Màu thứ hai của bút vẽ và chổi vẽ đường gạch


Chế độ nền (Background Color):

Bật / Tắt màu nền



Chổi vẽ (Brush):

Màu để tơ miền trong của hình


Gốc của chổi vẽ (Brush Origin):

Vị trí gốc để cho chổi vẽ đường gạch.


Chế độ vẽ(Drawing Mode)

Thao tác Boolean với nền



Bút vẽ (Pen)

Màu, độ rộng và kiểu của đườngbiên


Chế độ tô đa giác (Polygon Fill Mode) Để tô màu đa giác



Chế độ tơ màu có thể là ALTERNATE hoặc WINDING.


Để thay đổi chế độ tô màu, ta dùng hàm PolyFillMode



<b>int SetPolyFillMode( </b>


<i><b>HDC hdc, </b></i> // handle of device context


<i><b>int iPolyFillMode </b></i> // polygon fill mode


<b>); </b>



Hàm để thay đổi màu nền là SetBkColor


<b>COLORREF SetBkColor( </b>


<i><b>HDC hdc, </b></i>


<i><b>COLORREF crColor </b></i>
<b>); </b>


<b>Và hàm để thay đổi chế độ nền là SetBkMode </b>


<b>int SetBkMode( </b>


<i><b>HDC hdc, </b></i>
<i><b>int iBkMode </b></i>
<b>); </b>


</div>
<span class='text_page_counter'>(54)</span><div class='page_container' data-page=54>

Hình 4-6 – Chế độ tơ màu ALTERNATE và WINDING


<b>4.4 Chổi vẽ (</b>

Brushes

)



Chổi vẽ là thuộc tính để tơ. Chổi vẽ được tạo bởi 3 thành phần: Kiểu (Style), màu (color) và


mẫu (Pattern). Kích thước của chỗi vẽ tính là 8 pixel x 8 pixel. Chổi vẽ có thể được chia sẻ


giữa các chương trình và các thiết bị. Windows cung cấp sẵn một số chổi vẽ (stock brushes).


Ngoài ra, người sử dụng có thể tự tạo các chổi vẽ riêng. Ta quản lý chổi vẽ bằng một handle


đến chổi vẽ gọi là HBRUSH.



4.4.1 Tạo và sử dụng chổi vẽ có sẵn



</div>
<span class='text_page_counter'>(55)</span><div class='page_container' data-page=55>

GRAY_BRUSH


HOLLOW_BRUSH


LTGRAY_BRUSH



NULL_BRUSH


WHITE_BRUSH



Hollow brush (equivalent to NULL_BRUSH).


Light gray brush.



Null brush (equivalent to HOLLOW_BRUSH).


White brush.



Ví dụ



HBRUSH hbr;


hbr = GetStockObject(GRAY_BRUSH);
SelectObject(hdc, hbr);


</div>
<span class='text_page_counter'>(56)</span><div class='page_container' data-page=56>

SelectObject để chọn chổi vẽ vừa tạo. Sau khi dùng xong dùng DeleteObject để giải phóng


tài nguyên Windows cấp cho chổi vẽ đó.



Đoạn chương trình sau tạo một chổi vẽ gạch chéo 45 độ có màu xanh dương và vẽ hình ellipse


bằng chổi vẽ đó.



case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
RECT rt;


HBRUSH hbr;


GetClientRect(hWnd, &rt);



<b>hbr = CreateHatchBrush(HS_BDIAGONAL, RGB(0,0,0xff)); </b>
<b>SelectObject(hdc, hbr); </b>


Ellipse(hdc, rt.right/8, rt.bottom/8, 7*rt.right/8, 7*rt.bottom/8);
EndPaint(hWnd, &ps);


<b>DeleteObject(hbr); </b>


break;


Chổi vẽ có thể được tạo sẵn khi cửa sổ vừa được tạo và được giải phóng khi cửa sổ bị huỷ.



case WM_CREATE:


<b>hbr = CreateHatchBrush(HS_BDIAGONAL, RGB(0,0,0xff)); </b>


break;
case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
RECT rt;


GetClientRect(hWnd, &rt);


<b>SelectObject(hdc, hbr); </b>


Ellipse(hdc, rt.right/8, rt.bottom/8, 7*rt.right/8, 7*rt.bottom/8);
EndPaint(hWnd, &ps);



break;
case WM_DESTROY:


<b>DeleteObject(hbr); </b>


</div>
<span class='text_page_counter'>(57)</span><div class='page_container' data-page=57>

4.4.3 Tạo trực tiếp các chổi vẽ


<i><b>Hàm CreateSolidBrush </b></i>



Hàm CreateSolidBrush tạo một chổi vẽ luận lý đặc với màu được quy định bởi tham


số crColor



<b>HBRUSH CreateSolidBrush( </b>


<i><b> COLORREF crColor // brush color value </b></i>
<b>); </b>


<b>Haøm CreateHatchBrush </b>



Hàm CreateSolidBrush tạo một chổi vẽ luận lý là những đường gạch được quy định


bởi tham số fnStyle và có màu clrref.



<b>HBRUSH CreateHatchBrush( </b>
<i><b> int fnStyle, // hatch style </b></i>
<i><b> COLORREF clrref // color value </b></i>
<b>); </b>


Xem hàm CreateBrushIndirect ở bên dưới để biết các giá trị hợp lệ cho


fnStyle.



Haøm CreatePatternBrush




Hàm CreatePatternBrush tạo một chổi vẽ từ mẫu xác định bởi một bitmap trong bộ


nhớ.



<b>HBRUSH CreatePatternBrush( </b>
<i><b> HBITMAP hbmp // handle to bitmap </b></i>
<b>); </b>


4.4.4 Taïo gián tiếp chổi vẽ


Hàm CreateBrushIndirect



Hàm CreateBrushIndirect kết hợp tất cả các hàm tạo chổi vẽ kể trên, nó cho phép tạo


chổi vẽ với kiểu, màu, mẫu từ một cấu trúc chổi vẽ luận lý cho trước.



</div>
<span class='text_page_counter'>(58)</span><div class='page_container' data-page=58>

LONG lbHatch;
} LOGBRUSH;


lbStyle



Kiểu của brush. Thành phần lbStyle có các giá trị sau:



<b>Giá trị </b>

<b>Ý nghóa </b>



BS_DIBPATTERN

Mẫu chổi vẽ định nghĩa bằng bitmap có kích thước 8x8


pixels thuộc loại DIB.



BS_DIBPATTERN8X8

Tương tự BS_DIBPATTERN.


BS_HATCHED

Chổi vẽ Hatched.



BS_HOLLOW

Chổi vẽ rỗng.




BS_NULL

Chổi vẽ rỗng, giống như BS_HOLLOW.



BS_PATTERN

Chỗi vẽ có mẫu định nghĩa bởi bitmap trong bộ nhớ.


BS_PATTERN8X8

Tương tự BS_PATTERN.



BS_SOLID

Chổi vẽ đặc.


lbColor



Màu của chổi vẽ. Nếu lbStyle là BS_HOLLOW hay BS_PATTERN, lbColor không


có ý nghóa.



lbHatch



Khi lbStyle là BS_HATCHED, lbHatch chỉ rõ cách thức gạch, nó quy định hướng của


đường gạch.



Giá trị

<i><b>Ý nghóa </b></i>



HS_BDIAGONAL Gạch theo góc 45 độ từ dưới lên và từ trái sang


phải



</div>
<span class='text_page_counter'>(59)</span><div class='page_container' data-page=59>

<b>4.5 Ví dụ về chổi vẽ </b>



Chương trình minh hoạ Brush tạo các chổi vẽ do người sử dụng định nghĩa.


Tập tin chương trình nguồn brush.cpp như sau:



#define MAX_BRUSHES 30


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM


lParam)


{


int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;


int i;


static nBrushes, nCurBrush;
static HBRUSH ahbr[MAX_BRUSHES];
static TCHAR *aszName[MAX_BRUSHES];
static BYTE acPattern1[] = {


0xff, 0,
0xe7, 0,
0xc3, 0,
0x99, 0,
0x3c, 0,
0x7e, 0,
0xff, 0,
0xff, 0,
};


static BYTE acPattern2[] = {
0x81, 0,


0x82, 0,
0x84, 0,


0xf8, 0,
0x0, 0,
0x0, 0,
0x0, 0,
0x0, 0,
};


switch (message)
{


</div>
<span class='text_page_counter'>(60)</span><div class='page_container' data-page=60>

aszName[nBrushes++] = "Hatch HS_FDIAGONAL";


ahbr[nBrushes] = CreateHatchBrush(HS_HORIZONTAL, RGB(0,0,0xff));
aszName[nBrushes++] = "Hatch HS_HORIZONTAL";


ahbr[nBrushes] = CreateHatchBrush(HS_VERTICAL, RGB(0,0,0xff));
aszName[nBrushes++] = "Hatch HS_VERTICAL";


hBitmap = CreateBitmap(8,8,1,1, acPattern1);
ahbr[nBrushes] = CreatePatternBrush(hBitmap);
aszName[nBrushes++] = "Pattern Brush 1";
DeleteObject(hBitmap);


hBitmap = CreateBitmap(8,8,1,1, acPattern2);
ahbr[nBrushes] = CreatePatternBrush(hBitmap);
aszName[nBrushes++] = "Pattern Brush 2";
DeleteObject(hBitmap);


hBitmap = LoadBitmap(hInst, (LPCTSTR)IDB_BITMAP1);
ahbr[nBrushes] = CreatePatternBrush(hBitmap);


aszName[nBrushes++] = "RC Pattern Brush 1";
DeleteObject(hBitmap);


hBitmap = LoadBitmap(hInst, (LPCTSTR)IDB_BITMAP2);
ahbr[nBrushes] = CreatePatternBrush(hBitmap);
aszName[nBrushes++] = "RC Pattern Brush 2";
DeleteObject(hBitmap);


break;


case WM_COMMAND:


wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)


{


case IDM_ABOUT:


DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
break;


case IDM_EXIT:


DestroyWindow(hWnd);
break;


default:



</div>
<span class='text_page_counter'>(61)</span><div class='page_container' data-page=61>

Rectangle(hdc, 180, i*35+10, 600, i*35+39);
}


EndPaint(hWnd, &ps);
break;


case WM_DESTROY:


for (i = 0; i < nBrushes; i++)
DeleteObject(ahbr[i]);
PostQuitMessage(0);


break;
default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


</div>
<span class='text_page_counter'>(62)</span><div class='page_container' data-page=62>

<b>4.6 Miền và thuộc tính bút vẽ PS_INSIDEFRAME </b>



Bút vẽ có độ rộng và thuộc tính PS_INSIDEFRAME khi kết hợp với vẽ miền cho biết bút vẽ


sẽđược tô đậm vào phần trong của miền. Hình vẽ minh hoạ bút vẽ có thuộc tính trên. Đoạn


chương trình tương ứng như sau:



case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rt);



hpen = CreatePen(PS_SOLID, 16, RGB(0,255,0));
SelectObject(hdc, hpen);


Ellipse(hdc, 0, 0, 4*rt.right/7, 4*rt.bottom/7);
DeleteObject(hpen);


hpen = CreatePen(PS_INSIDEFRAME, 16, RGB(0,255,0));
SelectObject(hdc, hpen);


Ellipse(hdc, 3*rt.right/7, 3*rt.bottom/7, rt.right, rt.bottom);
DeleteObject(hpen);


SelectObject(hdc, GetStockObject(HOLLOW_BRUSH));
hpen = CreatePen(PS_SOLID, 1, RGB(255,0,0));
SelectObject(hdc, hpen);


Ellipse(hdc, 0, 0, 4*rt.right/7, 4*rt.bottom/7);


Ellipse(hdc, 3*rt.right/7, 3*rt.bottom/7, rt.right, rt.bottom);
DeleteObject(hpen);


rt.left = 3*rt.right/7;
rt.top = 3*rt.bottom/7;


DrawText(hdc, szPrompt, strlen(szPrompt), &rt,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);


EndPaint(hWnd, &ps);
break;



</div>
<span class='text_page_counter'>(63)</span><div class='page_container' data-page=63>

Hình 4-10 – PS_INSIDEFRAME style


<b>4.7 Con đường (Paths) </b>



Con đường là tập hợp các đường thẳng và cung được GDI lưu trữ nội bộ. Con đường có thể


được xem là mở rộng của miền. Sau khi tạo con đường có thể vẽ đường viền và tơ màu.



4.7.1 BeginPath vaø EndPath



Để định nghĩa một con đường ta theo trình tự: Mở đường, thực hiện các thao tác vẽ, đóng


đường.



</div>
<span class='text_page_counter'>(64)</span><div class='page_container' data-page=64>

4.7.2 Các hàm thao tác con đường.



Các hàm thao tác trên con đường bao gồm StrokePath, FillPath và


StrokeAndFillPath.



<b>Hàm StrokePath vẽ đường viền của con đường hiện hành bằng bút vẽ sẵn có trong ngữ cảnh </b>


thiết bị



<i><b>BOOL StrokePath(HDC hdc); </b></i>


<b>Hàm FillPath đóng tất cả mọi con đường con đang mở bên trong con đường hiện hành và tô </b>


miền bên trong của con đường bằng chổi vẽ hiện hành và chế độ tô đa giác hiện hành.



<i><b>BOOL FillPath(HDC hdc); </b></i>


<b>Hàm StrokeAndFillPath làm công việc của cả hai hàm trên, nó đóng tất cả mọi con </b>


đường con đang mở bên trong con đường hiện hành, vẽ đường viền của con đường với bút


vẽ hiện hành và tô miền bên trong của con đường bằng chổi vẽ hiện hành và chế độ tô đa



giác hiện hành.



<i><b>BOOL StrokeAndFillPath(HDC hdc ); </b></i>


4.7.3 Ví dụ sử dụng đường – Vẽ và tơ hoa bằng PolyBezier



Chương trình minh hoạ vẽ các cánh hoa bằng đường PolyBezier nội bộ vào trong một con


đường. Sau đó dùng hàm StrokeAndFillPath để vẽ đường viền và tơ màu các cánh hoa.



</div>
<span class='text_page_counter'>(65)</span><div class='page_container' data-page=65>

Hình 4-11 – Minh hoạ Path


// Bezier_Flower.cpp : Defines the entry point for the application.
// Ve va to dung ham PolyBezier, Path


// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//


void DrawMarker(HDC hdc, POINT pt, int size);


void CreateFlowerPoints(POINT ptCenter, POINT pt, POINT aptFlowers[]);
#define MAX_FLOWERS 100


#define MAX_MARKERS (2*MAX_FLOWERS)
#define MARKER_SIZE 5


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{



int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;


TCHAR szPrompt[] = TEXT("Bezier Flowers");
static POINT apt[MAX_MARKERS];


static int nMarkers;
int i;


POINT aptFlowers[12];
static HPEN hpen;
static HBRUSH hbrush;
static int nPenWidth = 2;


static COLORREF crPen = RGB(0xff,0,0);
switch (message)


{


case WM_CREATE:


hbrush = CreateSolidBrush(RGB(0, 0xff, 0)); // Green brush
hpen = CreatePen(PS_SOLID, nPenWidth, crPen); // Red pen
break;


case WM_LBUTTONDOWN:


if (nMarkers < MAX_MARKERS)
{



</div>
<span class='text_page_counter'>(66)</span><div class='page_container' data-page=66>

GetClientRect(hWnd, &rt);


DrawText(hdc, szPrompt, strlen(szPrompt), &rt, DT_CENTER);
SelectObject(hdc, hbrush);


SelectObject(hdc, hpen);


for (i = 0; i < nMarkers; i++)
{


DrawMarker(hdc, apt[i], MARKER_SIZE);
if (i%2 == 1)


{


aptFlowers[0] = apt[i-1];


CreateFlowerPoints(apt[i-1], apt[i], aptFlowers);
BeginPath(hdc);


MoveToEx(hdc, apt[i-1].x, apt[i-1].y, NULL);
PolyBezierTo(hdc, aptFlowers, 12);


CloseFigure(hdc);
EndPath(hdc);
StrokeAndFillPath(hdc);
}
}
EndPaint(hWnd, &ps);


break;
case WM_DESTROY:
DeleteObject(hpen);
DeleteObject(hbrush);
PostQuitMessage(0);
break;
default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


return 0;
}


void CreateFlowerPoints(POINT ptCenter, POINT pt, POINT aptFlowers[])
{


aptFlowers[0] = aptFlowers[1] = pt;
aptFlowers[1].x = 2*ptCenter.x - pt.x;
aptFlowers[2] = ptCenter;


aptFlowers[3].x = aptFlowers[0].x;
aptFlowers[4].x = aptFlowers[1].x;


</div>
<span class='text_page_counter'>(67)</span><div class='page_container' data-page=67>

mới với điểm đầu là tâm, điểm cuối sẽ được dùng để tạo các điểm điều khiển cho


PolyBezierTo.



</div>
<span class='text_page_counter'>(68)</span><div class='page_container' data-page=68>

<b>5. </b>

Xuaát Văn bản.



<b>5.1 </b>

Tổng quan




Văn bản (text) là một phương tiện xuất cơ bản. Cơ chế xuất văn bản của GDI trong Windows


hoàn toàn khác với các cách tiếp cận cổ điển.



• Ở giai đoạn đầu lập trình, chương trình dùng cách tiếp cận xuất văn bản theo định hướng


dòng phù hợp với các thiết bị xuất tương tự như máy đánh chữ.



cout << “Nhap vao hai so nguyen: ”;
cin >> i >> j;


• Cách tiếp cận kế tiếp là xuất văn bản định hướng màn hình. Có thể định vị xuất liệu đến vị


trí bất kỳ trên màn hình theo dịng và cột.



gotoxy(x,y);


cout << “Hello, world\n”;


• Cách tiếp cận của GDI trong Windows là định hướng pixel. GDI cho phép định vị văn bản


xuất đến từng pixel. Người sử dụng có thể dễ dàng trộn lẫn văn bản với các xuất liệu hình


học khác, và cũng dễ dàng trộn lẫn văn bản có kích thước khác nhau.



• Vì giao diện Windows là giao diện đồ hoạ, văn bản được xuất đến đơn vị nhỏ nhất là


pixel, qui trình xuất khơng khác gì vẽ các đối tượng hình học khác nên ta cịn nói xuất văn


bản là vẽ text.



• Font được dùng để vẽ văn bản. Một font bao gồm các mẫu mơ tả hình dạng và kích thước


của từng ký tự (chữ, số, dấu…). Mỗi thiết bị GDI hỗ trợ một hay nhiều font.



<b>5.2 Các hàm xuất văn bản </b>




</div>
<span class='text_page_counter'>(69)</span><div class='page_container' data-page=69>

<b>ExtTextOut </b>



Hàm ExtTextOut vẽ một dòng ký tự tại điểm qui định, dùng font hiện hành, màu nền hiện


hành, và màu chữ hiện hành. Ngồi ra có thể cung cấp một hình chữ nhật để xén, hoặc làm tối


(opaque), hoặc kết hợp cả hai.



<b>BOOL ExtTextOut( </b>


<i><b> HDC hdc, // handle to device context </b></i>


<i><b> int X, // x-coordinate of reference point </b></i>
<i><b> int Y, // y-coordinate of reference point </b></i>
<i><b> UINT fuOptions, // text-output options </b></i>


<i><b> CONST RECT *lprc, // optional clipping and/or opaquing rectangle </b></i>
<i><b> LPCTSTR lpString, // points to string </b></i>


<i><b> UINT cbCount, // number of characters in string </b></i>


<i><b> CONST INT *lpDx // pointer to array of intercharacter spacing </b></i>


// values


<b>); </b>




fuOptions có thể lấy là kết hơp của các giá trị



Giá trị

Ý nghóa




ETO_CLIPPED

Văn bản bị xén vào trong hình chữ nhật.



ETO_OPAQUE

Màu nền hiện hành sẽ được dùng để tơ hình chữ

<sub>nhật. </sub>



lpDx là tuỳ chọn, trỏ đến một mảng các giá trị qui định khoảng cách giữa các ký tự kế tiếp


nhau trong chuỗi.



<b>TabbedTextOut </b>



</div>
<span class='text_page_counter'>(70)</span><div class='page_container' data-page=70>

<b>); </b>


<b>Hình 5-1 - TabledTextOut </b>


Ví dụ: Sắp theo cột bằng TabledTextOut



// TabbedText.cpp : Defines the entry point for the application.
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)


#define MAX_FONTS 50


#define dim(a) sizeof(a)/sizeof(a[0])
#define N 2


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


</div>
<span class='text_page_counter'>(71)</span><div class='page_container' data-page=71>

int nItems = dim(aTextData);
int i;



SIZE textSize;
int aTab[N];
LOGFONT lf;


static char *aFaceName[] =
{


"Arial",


"Times New Roman",
"Bookman Old Style",
"BankGothic Lt BT"
};


const int nFaces = sizeof(aFaceName)/sizeof(aFaceName[0]);
static HFONT ahf[MAX_FONTS];


static int nFonts, nCurFont;


LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
switch (message)


{


case WM_CREATE:


memset(&lf, 0, sizeof(lf));
for (i = 0; i < nFaces; i++)
{



lf.lfHeight = 32;
lf.lfWidth = 12;


lstrcpy(lf.lfFaceName, aFaceName[i]);
ahf[nFonts++] = CreateFontIndirect(&lf);
}


break;


case WM_RBUTTONDOWN:


nCurFont = ++nCurFont % nFonts;
InvalidateRect(hWnd, NULL, TRUE);
break;


case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
SelectObject(hdc, ahf[nCurFont]);
RECT rt;


GetClientRect(hWnd, &rt);


</div>
<span class='text_page_counter'>(72)</span><div class='page_container' data-page=72>

break;
default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


return 0;


}


<b>DrawText </b>



Hàm DrawText xuất văn bản có định dạng bên trong một hình chữ nhật. Văn bản có thể được


định dạng theo tab, xuống dịng, canh đều các ký tự.



<b>int DrawText( </b>


<i><b> HDC hDC, // handle to device context </b></i>
<i><b> LPCTSTR lpString, // pointer to string to draw </b></i>
<i><b> int nCount, // string length, in characters </b></i>


<i><b> LPRECT lpRect, // pointer to struct with formatting dimensions </b></i>
<i><b> UINT uFormat // text-drawing flags </b></i>


<b>); </b>




<i>uFormat </i>



Qui định cách định dạng. Có thể là sự kết hợp của các cờ sau:



Giá trị

Ý nghóa



DT_BOTTOM

Dùng với DT_SINGLELINE để canh dưới.



DT_CALCRECT

Tính kích thước (chiều rộng và cao) của text nhưng

không vẽ. Trả về chiều cao của văn bản được định


dạng




DT_CENTER

Canh giữa.



DT_EXPANDTABS

Dùng tab, mặc nhiên tab stop là 8 ký tự.



</div>
<span class='text_page_counter'>(73)</span><div class='page_container' data-page=73>

DT_WORDBREAK

Ngắt dòng.



Hình 5-2 - Minh hoạ DrawText với DT_CENTER




Hình 5-3 - Minh hoạ DrawText với DT_CENTER | DT_WORDBREAK


Ví dụ minh hoạ – Chương trình WordBreak



// Text_WordBreak.cpp :


// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


</div>
<span class='text_page_counter'>(74)</span><div class='page_container' data-page=74>

{


case WM_LBUTTONDOWN:


bWordBreak = !bWordBreak;


InvalidateRect(hWnd, NULL, TRUE);
break;



case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
RECT rt;


GetClientRect(hWnd, &rt);
if (bWordBreak)


DrawText(hdc, szHello, strlen(szHello), &rt,
DT_CENTER | DT_WORDBREAK);


else


DrawText(hdc, szHello, strlen(szHello), &rt,
DT_CENTER);


EndPaint(hWnd, &ps);
break;


case WM_DESTROY:


PostQuitMessage(0);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);
}



return 0;
}


<b>GrayString </b>



Hàm GrayString vẽ văn bản màu xám.


<b>BOOL GrayString( </b>


<i><b> HDC hDC, </b></i> // handle to the device context


<i><b> HBRUSH hBrush, </b></i> // handle to the brush for graying


<i><b> GRAYSTRINGPROC lpOutputFunc, // pointer to the callback function </b></i>
<i><b> LPARAM lpData, </b></i> // pointer to application-defined data


<i><b> int nCount, </b></i> // number of characters to output


<i><b> int X, </b></i> // horizontal position


<i><b> int Y, </b></i> // vertical position


</div>
<span class='text_page_counter'>(75)</span><div class='page_container' data-page=75>

Hình 5-4 – GrayString


Ví dụ: Đoạn chương trình sau cho xuất liệu là một graystring.



// GrayString.cpp :


// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//



LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{


int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;


TCHAR szHello[] = "Hello. This is a string created by a call to
GrayString";


switch (message)
{


case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);


GrayString(hdc, (HBRUSH)GetStockObject(BLACK_BRUSH), NULL,
(LPARAM)szHello, strlen(szHello), 10, 50, 0, 0);
EndPaint(hWnd, &ps);


break;
case WM_DESTROY:


PostQuitMessage(0);
break;


default:



</div>
<span class='text_page_counter'>(76)</span><div class='page_container' data-page=76>

Khoảng cách ký tự

Số pixel thêm vào giữa các ký tự để canh lề


Canh lề (trái, phải,…)

Quan hệ với điểm mốc



Màu chữ

Màu của bản thân chữ trong văn bản.


5.3.1 Màu văn bản



Ba thuộc tính ngữ cảnh liên quan đến màu văn bản là màu nền, chế độ nền và màu của bản


thân chữ trong văn bản.



Chế độ OPAQUE (mặc nhiên) vẽ màu nền lên phần character cell của mỗi chữ trong văn bản.


Ta dùng hàm SetColor để thay đổi màu chữ của văn bản.



<b>COLORREF SetTextColor( </b>


<i><b>HDC hdc, </b></i> // handle of device context


<b> </b> <i><b>COLORREF crColor </b></i> // text color


<b> </b> <b>); </b>


5.3.2 Canh haøng (text alignment)



Canh hàng văn bản cho phép thay đổi vị trí tương đối của văn bản so với điểm mốc.


Hàm SetTextAlign cho phép canh hàng văn bản.



<b>UINT SetTextAlign( </b>


<i><b> HDC hdc, </b></i> // handle of device context



<i><b> UINT fMode // text-alignment flag </b></i>
<b> </b> <b>); </b>


<i>fMode coù thể có các giá trị </i>



Canh theo chiều ngang: TA_LEFT, TA_CENTER, TA_RIGHT


Canh theo chiều dọc:

TA_TOP, TA_BASELINE, TA_BOTTOM


Ví dụ, để canh trên, ở giữa ta gọi:



</div>
<span class='text_page_counter'>(77)</span><div class='page_container' data-page=77>

HDC hdc;


TCHAR szHello[MAX_LOADSTRING];


LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
int i;


static TextAttrData aTextAttrTab[] =
{


"Top Left Black", TA_LEFT | TA_TOP, RGB(0,0,0),
"Top Right Red", TA_RIGHT | TA_TOP, RGB(0xff,0,0),
"Top Center Blue", TA_CENTER | TA_TOP, RGB(0,0,0xff),
"Bottom Left Gray", TA_LEFT | TA_BOTTOM, RGB(0,0,0),


"Bottom Right Green", TA_RIGHT | TA_BOTTOM, RGB(0,0xff,0),
"Bottom Center Blue", TA_CENTER | TA_BOTTOM, RGB(0,0,0xff),
"Baseline Left Black", TA_LEFT | TA_BASELINE, RGB(0,0,0),


"Baseline Center Yellow", TA_CENTER | TA_BASELINE, RGB(0xff,0xff,0),
"Baseline Right Blue", TA_RIGHT | TA_BASELINE, RGB(0xff,0,0),



};


static HBRUSH hbr;
switch (message)
{


case WM_CREATE:


hbr = CreateHatchBrush(HS_BDIAGONAL, RGB(0,0,0xff));
break;


case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
POINT pt;


SelectObject(hdc, hbr);


Ellipse(hdc, 50,10, 200, 400);
Ellipse(hdc, 250,10, 400, 400);
Ellipse(hdc, 450,10, 600, 400);


for (i = 0; i < dim(aTextAttrTab); i++)
{


SetTextAlign(hdc, aTextAttrTab[i].nAlign);
SetTextColor(hdc, aTextAttrTab[i].crColor);
pt.x = 100, pt.y = i*40+20;



TextOut(hdc, 100, i*40+20, aTextAttrTab[i].Name,
lstrlen(aTextAttrTab[i].Name));


DrawMarker(hdc, pt, 10);
}


SetBkColor(hdc, RGB(0xc0,0xc0,0xc0));
for (i = 0; i < dim(aTextAttrTab); i++)
{


</div>
<span class='text_page_counter'>(78)</span><div class='page_container' data-page=78>

TextOut(hdc, 500, i*40+20, aTextAttrTab[i].Name,
lstrlen(aTextAttrTab[i].Name));
DrawMarker(hdc, pt, 10);


}


EndPaint(hWnd, &ps);
break;


case WM_DESTROY:


PostQuitMessage(0);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


</div>
<span class='text_page_counter'>(79)</span><div class='page_container' data-page=79>

Font VNI-Times nghieâng




<b>Font Courier New Bold </b>



Font Wingdings : 



Font Symbol : αβχδεφγηιϕκλµνοπθρστυϖωξψζ←↑→↓°±≥ΣΩΦ


5.4.1 Các loại font



GDI cung cấp 3 loại font ứng với ba kỹ thuật lưu trữ khác nhau là raster font, vector font và


truetype font.



Raster font, còn gọi là Bitmap Font, là font mà các ký tự được lưu trữ thành bitmap các pixel.


Một font raster được thiết kế với một kích thước cố định và một tỉ lệ ngang dọc nhất định.


Bitmap font khơng có khả năng phóng to thu nhỏ.



Vector Font được định nghĩa bởi dãy các đoạn thẳng và cung nối với nhau. Vector Font có thể


phóng to thu nhỏ, có thể sử dụng trên các thiết bị có độ phân giải khác nhau, có thể chuyển


qua bitmap nhưng khơng bị mất thơng tin. Vector khơng tốt ở kích thước nhỏ cũng như lớn.


TrueType Fonts được sử dụng từ Windows 3.1. Trong TrueType Font, mỗi ký tự gồm dữ liệu


là đường nét thuộc loại thẳng hay cung, thêm vào đó phần chương trình điều khiển để bảo


đảm chất lượng font khi phóng to thu nhỏ (phần chương trình này được gọi là hints). Khả năng


này đặc biệt hữu dụng để sửa sai khi font được sử dụng ở kích thước nhỏ và rất nhỏ. Nhờ đó


TrueType Font có thể được phóng to thu nhỏ đến mọi kích thước. Kết quả là dữ liệu thuộc


dạng bitmap có thể đưa lên màn hình hay máy in, TrueType font tạo điều kiện dễ dàng để đạt


đến WYSIWYG.



Windows 95 có 13 font chuẩn:


Courier New




<b>Courier New Bold </b>



<i>Courier New Italic </i>



<i><b>Courier New Bold Italic </b></i>


Times New Roman



<i>Times New Roman Italic </i>



</div>
<span class='text_page_counter'>(80)</span><div class='page_container' data-page=80>

Point là đơn vị đo kích thước cho font, bằng 1/72 inch


5.4.2 Tạo và sử dụng font



Có thể tạo font trực tiếp hoặc gián tiếp.



Để tạo trực tiếp font, ta dùng hàm CreateFont


<b>CreateFont </b>



Hàm CreateFont tạo một font luận lý với các đặc trưng được quy định bởi các tham số. Font


sau khi được tạo có thể được dùng cho bất kỳ thiết bị nào.



<b>HFONT CreateFont( </b>


<i><b> int nHeight, // logical height of font </b></i>


<i><b> int nWidth, // logical average character width </b></i>
<i><b> int nEscapement, // angle of escapement </b></i>
<i><b> int nOrientation, // base-line orientation angle </b></i>
<i><b> int fnWeight, // font weight </b></i>


<i><b> DWORD fdwItalic, // italic attribute flag </b></i>



<i><b> DWORD fdwUnderline, // underline attribute flag </b></i>
<i><b> DWORD fdwStrikeOut, // strikeout attribute flag </b></i>
<i><b> DWORD fdwCharSet, // character set identifier </b></i>
<i><b> DWORD fdwOutputPrecision, // output precision </b></i>
<i><b> DWORD fdwClipPrecision, // clipping precision </b></i>
<i><b> DWORD fdwQuality, // output quality </b></i>
<i><b> DWORD fdwPitchAndFamily, // pitch and family </b></i>


<i><b> LPCTSTR lpszFace // pointer to typeface name string </b></i>
<b>); </b>


Xem cấu trúc font luận lý LOGFONT để biết ý nghĩa các tham số.



Để tạo gián tiếp font ta dùng hàm CreateFontIndirect và cấu trúc LOGFONT


<b>LOGFONT </b>



</div>
<span class='text_page_counter'>(81)</span><div class='page_container' data-page=81>

BYTE lfCharSet;
BYTE lfOutPrecision;
BYTE lfClipPrecision;
BYTE lfQuality;


BYTE lfPitchAndFamily;


TCHAR lfFaceName[LF_FACESIZE];
} LOGFONT;


<b>lfHeight </b>



Chiều cao tính theo đơn vị luận lý của character cell bao gồm cả internal-leading



nhưng không tính external leading. Khi có giá trị > 0, nó qui định khoảng cách dòng


(line spacing). Khi bằng 0, GDI dùng chiều cao mặc nhiên để tìm font khớp nhất. Khi


giá trị của lfHeight > 0 GDI chuyển sang đơn vị toạ độ thiết bị và lấy giá trị tuyệt đối


của giá trị này để chọn font có chiều cao khớp nhất.



Trong chế độ MM_TEXT ta có thể tính chiều cao theo point size:



lfHeight = -MulDiv(PointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);


lfWidth



Chiều rộng tính theo đơn vị luận lý của ký tự trong font. Thơng thường lfWidth có giá


trị 0.



lfWeight



Cho biết "độ mập" của font, có giá trị từ 0 đến 1000. Giá trị 400 là font bình thường và


700 là bold.



Giá trị Weight



</div>
<span class='text_page_counter'>(82)</span><div class='page_container' data-page=82>

Giá trị TRUE qui định gạch dưới.


lfStrikeOut



Giá trị TRUE qui định gạch ngang.


lfCharSet



Qui định bộ chữ, có các giá trị sau:



ANSI_CHARSET


BALTIC_CHARSET
CHINESEBIG5_CHARSET
DEFAULT_CHARSET
EASTEUROPE_CHARSET
GB2312_CHARSET
GREEK_CHARSET
HANGUL_CHARSET
MAC_CHARSET
OEM_CHARSET
RUSSIAN_CHARSET
SHIFTJIS_CHARSET
SYMBOL_CHARSET
TURKISH_CHARSET


lfQuality



Qui định chất lượng xuất. Có thể có các giá trị



DEFAULT_QUALITY
DRAFT_QUALITY
PROOF_QUALITY


lfPitchAndFamily



Qui định họ font và pitch. Hai bit thấp qui định picth của font.



DEFAULT_PITCH
FIXED_PITCH
VARIABLE_PITCH



Bít 4 đến 7 qui định họ font



FF_DECORATIVE

Old English là một ví dụ

FF_DONTCARE



FF_MODERN

Pica, Elite, and CourierNew.


</div>
<span class='text_page_counter'>(83)</span><div class='page_container' data-page=83>

<b>); </b>


<b>5.5 Các thông số văn bản </b>



Windows cung cấp một số hàm tiện ích cung cấp các thơng tin về văn bản. Cấu trúc SIZE


thường được dùng trong các hàm tính tốn về văn bản.



<b>SIZE </b>



Cấu trúc SIZE định nghĩa chiều rộng và chiều cao một hình chữ nhật.



typedef struct tagSIZE { // siz
LONG cx;


LONG cy;
} SIZE;


<b>GetTextExtentPoint32 </b>



Haøm GetTextExtentPoint32 tính chiều rộng và chiều cao của một chuỗi văn baûn.


<b>BOOL GetTextExtentPoint32( </b>



<i><b> HDC hdc, // handle to device context </b></i>
<i><b> LPCTSTR lpString, // pointer to text string </b></i>
<i><b> int cbString, // number of characters in string </b></i>
<i><b> LPSIZE lpSize // pointer to structure for string size </b></i>
<b>); </b>


<b>TEXTMETRIC </b>



Cấu trúc TEXTMETRIC chứa các thông tin cơ bản của một font vật lý. Tất cả các kích thước


dùng đơn vị luận lý, nghĩa là phụ thuộc vào chế độ toạ độ hiện hành.



typedef struct tagTEXTMETRIC { // tm
LONG tmHeight;


</div>
<span class='text_page_counter'>(84)</span><div class='page_container' data-page=84>

BCHAR tmLastChar;
BCHAR tmDefaultChar;
BCHAR tmBreakChar;
BYTE tmItalic;


BYTE tmUnderlined;
BYTE tmStruckOut;
BYTE tmPitchAndFamily;
BYTE tmCharSet;
} TEXTMETRIC;


<b>GetTextMetrics </b>



Hàm GetTextMetrics cho biết các thông tin metric của font hiện hành đang được chọn.


<b>BOOL GetTextMetrics( </b>



<i><b> HDC hdc, // handle to device context </b></i>


</div>
<span class='text_page_counter'>(85)</span><div class='page_container' data-page=85>

// Font_Create.cpp : Defines the entry point for the application.
//


HFONT MyCreateFont (HDC hdc, char * szFaceName, int nPointSize,
BOOL bBold = FALSE, BOOL bItalic = FALSE)
{


HFONT hFont ;
LOGFONT lf ;


memset(&lf, 0, sizeof(lf));


lf.lfHeight = -MulDiv(nPointSize, GetDeviceCaps(hdc, LOGPIXELSY),
72);


</div>
<span class='text_page_counter'>(86)</span><div class='page_container' data-page=86>

int nPointSize;


BOOL bBold, bItalics;
};


#define MAX_FONTS 20


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)
{
PAINTSTRUCT ps;
HDC hdc;
LOGFONT lf;


int i;


static FONTDATA aFontData[] =
{


"Arial size 20", "Arial", 20, FALSE, FALSE,


"Times New Roman Bold size 30", "Times New Roman", 30, TRUE, FALSE,
"Font Việt Nam VNI-Times co 24 nghiêng", "VNI-Times", 24, FALSE, TRUE,
"VNI-Bodon-Poster co 24 maäp", "VNI-Bodon-Poster", 24, TRUE, FALSE,
"VNI-Hobo co 40 maäp", "VNI-Hobo", 40, TRUE, FALSE


};


const int nFonts = sizeof(aFontData)/sizeof(aFontData[0]);
static HFONT ahf[MAX_FONTS];


switch (message)
{


case WM_CREATE:


memset(&lf, 0, sizeof(lf));
hdc = GetDC(hWnd);


for (i = 0; i < nFonts; i++)


ahf[i] = MyCreateFont(hdc, aFontData[i].szFaceName,
aFontData[i].nPointSize, aFontData[i].bBold,
aFontData[i].bItalics);



ReleaseDC(hWnd, hdc);
break;


case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
for (i = 0; i < nFonts; i++)
{


SelectObject(hdc, ahf[i]);


TextOut(hdc, 30, 50*(i+1), aFontData[i].szPrompt,
lstrlen(aFontData[i].szPrompt));


</div>
<span class='text_page_counter'>(87)</span><div class='page_container' data-page=87>

<b>5.6 Font vaø Path </b>



Các hàm xuất văn bản như TextOut, DrawText… dùng font hiện hành để xuất chữ. Các hàm


này định nghĩa điểm bên trong một path. Để các đường trong mỗi ký tự của font tạo nên điểm


trong path, ta dùng chế độ nền là TRANSPARENT.



Hình 5-7 – Font và Path


Sau đây là trích đoạn hàm WndProc trong chương trình tạo nên xuất liệu trên.



// Font_Path.cpp
int nPenWidth = 2;


COLORREF crPen = RGB(0xff,0,0);



</div>
<span class='text_page_counter'>(88)</span><div class='page_container' data-page=88>

switch (message)
{


case WM_CREATE:


memset(&lf, 0, sizeof(lf));
lf.lfHeight = 120;


lf.lfWidth = 0;


lstrcpy(lf.lfFaceName, "Times New Roman");
hfont = CreateFontIndirect(&lf);


hbrush = CreateSolidBrush(RGB(0, 0xff, 0)); // Green brush
hpen = CreatePen(PS_SOLID, nPenWidth, crPen); // Red pen
crText = RGB(0xc0, 0xc0, 0xc0); // Gray text


break;
case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
RECT rt;


GetClientRect(hWnd, &rt);
rt.top += 10;


SelectObject(hdc, hfont);
SelectObject(hdc, hbrush);
SelectObject(hdc, hpen);
BeginPath(hdc);



DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
EndPath(hdc);


StrokeAndFillPath(hdc);
SetBkMode(hdc, TRANSPARENT);
BeginPath(hdc);


DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER | DT_VCENTER |
DT_SINGLELINE);


EndPath(hdc);


StrokeAndFillPath(hdc);
SetTextColor(hdc, crText);


</div>
<span class='text_page_counter'>(89)</span><div class='page_container' data-page=89>

<b>6. </b>

Bộ định thời



<b>6.1 </b>

Giới thiệu



Bộ định thời là một thiết bị xuất đều đặn “lưu ý” một ứng dụng sau một khoảng thời gian qui


định trước. Chương trình có thể định khoảng thời gian và Windows sẽ gởi thông điệp


WM_TIMER để báo hiệu khoảng thời gian trên đã trôi qua. Bộ định thời cho phép tạo các ứng


dụng có tính động, chẳng hạn như đồng hồ để hiển thị thời gian. Bộ định thời cịn Windows có


được một số tính năng như:



• Đa nhiệm – Chương trình có thể tự cắt ra các chức năng để xử lý riêng thông qua bộ


định thời, nhờ đó nó nhanh chóng trả quyền điều khiển cho Windows.



• Duy trì và cập nhật các thơng báo trạng thái hoạt động của máy.



• Kết thúc các chương trình demo tự chạy



• Cài đặt chức năng autosave.



• Cài đặt các chương trình đa phương tiện.


• Các chương trình trò chôi.



Bộ định thời cũng bảo đảm cho phép chương trình giành lại quyền điều khiển sau khi thốt


khỏi thủ tục cửa sổ.



<b>6.2 Các hàm thao tác bộ định thời </b>



<i><b>Ta cấp phát một bộ định thời bằng cách dùng hàm SetTimer và giải phóng bộ định thời bằng </b></i>



<i><b>KillTimer</b></i>

.



<b>UINT_PTR SetTimer( </b>



</div>
<span class='text_page_counter'>(90)</span><div class='page_container' data-page=90>

<i><b> HWND hWnd, // handle to window </b></i>


<b> UINT_PTR</b>

<i> uIDEvent // timer identifier </i>


<b>); </b>



<b>6.3 Hoạt động của bộ định thời </b>



Khi lập trình trong mơi trường DOS, ta tạo một bộ định thời bằng cách bẫy các ngắt cứng (như


Int 8 hay Int 13). Các ngắt này xảy ra sau mỗi tick (mỗi tick bằng 3600/65536 tức khoảng


1/18,2 giây hay 55 mili giây).



Các ứng dụng Windows không bẫy các ngắt cứng. Chính hệ điều hành Windows xử lý các



ngắt cứng thay cho các ứng dụng. Windows duy trì một biến đếm cho mỗi bộ định thời và


giảm biến đếm này sau mỗi tick. Thông điệp WM_TIMER được gởi vào hàng đợi thông điệp


khi biến đếm giảm đến 0.



Cơ chế hoạt động trên bảo đảm bộ định thời khơng ngắt ngang q trình xử lý của bất kỳ một


thơng điệp nào vì thơng điệp định thời WM_TIMER được đưa vào hàng đợi trước khi được xử


lý.



Trong Windows Me, 98, 97, 95 độ phân giải của bộ định thời là 55 ms (mili giây) trong khi


Windows NT, Windows 2000, Windows XP, độ phân giải là 10 ms. Các ứng dụng không thể


nhận thông điệp định thời nhanh hơn độ phân giải nêu trên. Và khoảng thời gian để gởi thông


<i><b>điệp cũng khơng hồn tồn là giá trị đã được quy định trong hàm SetTimer . Ví dụ, khi gọi </b></i>


SetTimer với khoảng cách định thời là 1000 ms thì trong Windows Me, 98, 95 sau mỗi 989


ms, thông điệp được gởi một lần (tại sao). Tuy nhiên bộ định thời khơng đồng bộ.



<b>6.4 Tính khơng đồng bộ của bộ định thời. </b>



Thông điệp WM_TIMER được đưa vào hàng đợi thông điệp cùng với các thông điệp khác theo


thứ tự thời điểm phát sinh thông điệp. Vì vậy nếu ta qui định khoảng cách giữa hai thơng điệp


là 1000 ms thì khơng bảo đảm sẽ nhận được thông điệp sau mỗi 1000 ms, hay ngay cả 989 ms.


Nếu ứng dụng còn bận xử lý các thông điệp khác trong một khoảng thời gian dài hơn 1 giây,


nó sẽ khơng nhận được thông điệp WM_TIMER trong khoảng thời gian đó. Cần lưu ý


WM_TIMER là thơng điệp có độ ưu tiên thấp.



</div>
<span class='text_page_counter'>(91)</span><div class='page_container' data-page=91>

Tham số nIDEvent có ý nghĩa khi ta cần tạo nhiều bộ định thời trong cùng một ứng dụng.


Tham số lpTimerFunc là con trỏ đến thủ tục định thời để xử lý thông điệp. Nếu tham số này


bằng NULL, thông điệp WM_TIMER sẽ được gởi đến cho thủ tục cửa sổ.



<i><b>Ta có thể sử dụng bộ định thời bằng cách gọi hàm SetTimer với thủ tục định thời có giá trị </b></i>


NULL và xử lý thông điệp WM_TIMER trong thủ tục cửa sổ.




Ví dụ 1: Chương trình bounce 1



Chương trình bounce sau đây vẽ một hình trịn nhỏ di chuyển tự động bên trong vùng client.


Ta dùng hàm SetTimer và xử lý thơng điệp WM_TIMER. Hình tròn nhỏ tự động thay đổi màu


sau một khoảng thời gian qui định trước (1 giây). Tham số TimerProc với giá trị NULL sẽ làm


phát sinh thông điệp WM_TIMER. Cách gọi như sau:



SetTimer(hWnd, ID_MOVE_TIMER, 10, NULL);


SetTimer(hWnd, ID_CHANGECOLOR_TIMER, 1000, NULL);


Chương trình gồm các tập tin chương trình nguồn:


Stdafx.h

stdafx.cpp

Bounce.cpp



</div>
<span class='text_page_counter'>(92)</span><div class='page_container' data-page=92>

Hình 6-2 Cửa sổ chương trình


Sau đây là trích đoạn chương trình nguồn tập tin bounce.cpp



// bounce.cpp : Defines the entry point for the application.
//


//


// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//


// PURPOSE: Processes messages for the main window.
//



// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window


// WM_DESTROY - post a quit message and return
//


//


enum {ID_MOVE_TIMER = 1, ID_CHANGECOLOR_TIMER = 2};


</div>
<span class='text_page_counter'>(93)</span><div class='page_container' data-page=93>

SetTimer(hWnd, ID_MOVE_TIMER, 10, NULL);


SetTimer(hWnd, ID_CHANGECOLOR_TIMER, 1000, NULL);
break;


case WM_COMMAND:


wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId)


{


case IDM_ABOUT:


DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd,
(DLGPROC)About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);


break;
default:


return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_TIMER:
switch(wParam)
{
case ID_MOVE_TIMER:
RECT rClient, r;


GetClientRect(hWnd, &rClient);
r.left = x - rBall - abs(dx);
r.right = x + rBall + abs(dx);
r.top = y - rBall - abs(dy);
r.bottom = y + rBall + abs(dy);
x += dx;


y += dy;


if (x + rBall > rClient.right && dx > 0) dx = -dx;
if (x - rBall < 0 && dx < 0) dx = -dx;


if (y + rBall > rClient.bottom && dy > 0) dy = -dy;
if (y - rBall < 0 && dy < 0) dy = -dy;


InvalidateRect(hWnd, &r, TRUE);
break;



case ID_CHANGECOLOR_TIMER:
++nCrIdx %= nColors;
break;


</div>
<span class='text_page_counter'>(94)</span><div class='page_container' data-page=94>

KillTimer(hWnd, ID_CHANGECOLOR_TIMER);
PostQuitMessage(0);


break;
default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


return 0;
}


Ví dụ 2: Chương trình bounce 2



Chương trình hoạt động tương tự như chương trình của ví dụ trên nhưng ta qui định hàm xử lý


timer như sau:



SetTimer(hWnd, ID_MOVE_TIMER, 10, TimerProc);


SetTimer(hWnd, ID_CHANGECOLOR_TIMER, 1000, TimerProc);


Sau đây là trích đoạn chương trình nguồn tập tin bounce.cpp



// bounce.cpp : Defines the entry point for the application.
//



enum {ID_MOVE_TIMER = 1, ID_CHANGECOLOR_TIMER = 2};


VOID CALLBACK TimerProc (HWND hWnd, UINT message, UINT wParam, DWORD lParam)
{


static int x = 40,y = 50,rBall=10,dx=5,dy=5, nCrIdx;


static COLORREF aColorTab [] = {RGB(0xff,0,0), RGB(0,0xff,0),
RGB(0,0,0xff), RGB(0,0,0), RGB(0xff,0xff,0xff),RGB(0xc0,0xc0,0xc0)};


static int nColors = sizeof(aColorTab)/sizeof(aColorTab[0]);
switch(wParam)


{


case ID_MOVE_TIMER:
RECT rClient, r;


</div>
<span class='text_page_counter'>(95)</span><div class='page_container' data-page=95>

if (y - rBall < 0 && dy < 0) dy = -dy;
SelectObject(hdc, hbr);


SelectObject(hdc, GetStockObject(BLACK_PEN));


Ellipse(hdc, x - rBall, y - rBall, x + rBall, y + rBall);
DeleteObject(hbr);


ReleaseDC(hWnd, hdc);
break;


case ID_CHANGECOLOR_TIMER:


++nCrIdx %= nColors;
break;


}
}
//


// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//


// PURPOSE: Processes messages for the main window.
//


// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window


// WM_DESTROY - post a quit message and return
//


//


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{


int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;



TCHAR szHello[MAX_LOADSTRING];


LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
switch (message)


{


case WM_CREATE:


SetTimer(hWnd, ID_MOVE_TIMER, 10, TimerProc);


SetTimer(hWnd, ID_CHANGECOLOR_TIMER, 1000, TimerProc);
break;


case WM_COMMAND:


</div>
<span class='text_page_counter'>(96)</span><div class='page_container' data-page=96>

hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);


break;


case WM_DESTROY:


KillTimer(hWnd, ID_MOVE_TIMER);


KillTimer(hWnd, ID_CHANGECOLOR_TIMER);
PostQuitMessage(0);


break;
default:



return DefWindowProc(hWnd, message, wParam, lParam);
}


return 0;
}


Ví dụ 3: Chương trình bounce 3



</div>
<span class='text_page_counter'>(97)</span><div class='page_container' data-page=97>

// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
//
struct BALL
{
int x,y,r,dx,dy;
COLORREF crColor;
};
BALL CreateNewBall()
{
BALL b;


b.x = 0, b.y = 0, b.r = 10, b.dx = b.dy = 5;
return b;


};


BALL CreateNewBall(int x, int y, int r, int dx, int dy, COLORREF color)
{


BALL b;



b.x = x, b.y = y, b.r = r, b.dx = dx; b.dy = dy;
b.crColor = color;


return b;
};


const int MAX_BALLS = 100;


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{


int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;


TCHAR szHello[MAX_LOADSTRING];
static BALL aBalls[MAX_BALLS];
static int nBalls;


static COLORREF aColorTab [] = {RGB(0xff,0,0), RGB(0,0xff,0),
RGB(0,0,0xff), RGB(0,0,0), RGB(0xff,0xff,0xff),RGB(0xc0,0xc0,0xc0)};


static int nColors = sizeof(aColorTab)/sizeof(aColorTab[0]);
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);


RECT rt;



switch (message)
{


case WM_CREATE:


</div>
<span class='text_page_counter'>(98)</span><div class='page_container' data-page=98>

break;
default:


return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_CHAR:
switch(toupper(TCHAR(wParam)))
{
case 'N':
GetClientRect(hWnd, &rt);


aBalls[nBalls] = CreateNewBall(rand()%rt.right,
rand()%rt.bottom, 10, 5, 5, aColorTab[nBalls%nColors]);


SetTimer(hWnd, nBalls++, 10, NULL);
break;


case 'R':


if (nBalls > 0)


KillTimer(hWnd, --nBalls);
InvalidateRect(hWnd, NULL, TRUE);
break;



}
break;
case WM_TIMER:


RECT rClient, r;
BALL *pb;


pb = &aBalls[wParam];


GetClientRect(hWnd, &rClient);


r.left = pb->x - pb->r - abs(pb->dx);
r.right = pb->x + pb->r + abs(pb->dx);
r.top = pb->y - pb->r - abs(pb->dy);
r.bottom = pb->y + pb->r + abs(pb->dy);
pb->x += pb->dx;


pb->y += pb->dy;


if (pb->x + pb->r > rClient.right && pb->dx > 0) pb->dx = -pb->dx;
if (pb->x - pb->r < 0 && pb->dx < 0) pb->dx = -pb->dx;


if (pb->y + pb->r > rClient.bottom && pb->dy > 0) pb->dy = -pb->dy;
if (pb->y - pb->r < 0 && pb->dy < 0) pb->dy = -pb->dy;


InvalidateRect(hWnd, &r, TRUE);
break;


case WM_PAINT:



</div>
<span class='text_page_counter'>(99)</span><div class='page_container' data-page=99>

break;


case WM_DESTROY:


PostQuitMessage(0);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


return 0;
}


Ví dụ 4: Chương trình QuaDat



</div>
<span class='text_page_counter'>(100)</span><div class='page_container' data-page=100>

VOID VeVongTron(HDC hdc, int tx, int ty, int bk, HBRUSH hbr)
{


SelectObject(hdc, hbr);


Ellipse(hdc, tx-bk, ty-bk, tx+bk, ty+bk);
}


//


// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//



// PURPOSE: Processes messages for the main window.
//


// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window


// WM_DESTROY - post a quit message and return
//


//


const float PI = 3.1415926F;


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{


int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;


TCHAR szHello[MAX_LOADSTRING];


static int nBkQuiDao = 160, nBkMatTroi = 20, nBkQuaDat = 8;


static COLORREF crMatTroi = RGB(255,0,0), crQuaDat = RGB(0,255,0);
static HBRUSH hbrMatTroi, hbrQuaDat;



// static int txQuaDat, tyQuaDat;
static HPEN hpenQuiDao;


static float fGoc = -PI/4, fStep = 0.1f;


LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);


</div>
<span class='text_page_counter'>(101)</span><div class='page_container' data-page=101>

{


case IDM_ABOUT:


DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
break;


case IDM_EXIT:


DestroyWindow(hWnd);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


break;
case WM_TIMER:


hdc = GetDC(hWnd);


SetROP2(hdc, R2_NOTXORPEN);


GetClientRect(hWnd, &rt);


VeVongTron(hdc, rt.right/2 + int(nBkQuiDao*cos(fGoc)),


rt.bottom/2 + int(nBkQuiDao*sin(fGoc)), nBkQuaDat, hbrQuaDat);
fGoc -= fStep;


VeVongTron(hdc, rt.right/2 + int(nBkQuiDao*cos(fGoc)),


rt.bottom/2 + int(nBkQuiDao*sin(fGoc)), nBkQuaDat, hbrQuaDat);
ReleaseDC(hWnd, hdc);


break;
case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);


// TODO: Add any drawing code here...
GetClientRect(hWnd, &rt);


DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);


VeDuongTron(hdc, rt.right/2, rt.bottom/2, nBkQuiDao, hpenQuiDao);
SelectObject(hdc, GetStockObject(BLACK_PEN));


VeVongTron(hdc, rt.right/2, rt.bottom/2, nBkMatTroi, hbrMatTroi);
SetROP2(hdc, R2_NOTXORPEN);


VeVongTron(hdc, rt.right/2 + int(nBkQuiDao*cos(fGoc)),



</div>
<span class='text_page_counter'>(102)</span><div class='page_container' data-page=102>

<b>7. </b>

Tương tác với người sử dụng



Windows là hệ điều hành có giao diện trực quan thân thiện với người sử dụng. Một trong các


đặc điểm của Windows là khả năng WYSIWYG, trong đó người sử dụng tương tác với chương


trình.



Một trong các thao tác tương tác cơ bản trong môi trường Windows là kéo lê (dragging).


Người sử dụng có thể kéo lê để vẽ, có thể kéo lê để thay đổi kích thước cửa sổ, kéo lê để thay


đổi vị trí, kích thước hay quay đối tượng đồ hoạ.



<b>7.1 </b>

SetCapture, GetCapture vaø ReleaseCapture



Kéo lê trong môi trường Windows được thực hiện nhờ cơ chế bắt giữ con chuột.


<b>SetCapture </b>



Hàm SetCapture bắt giữ con trỏ chuột thành sở hữu của một cửa sổ. Khi cửa sổ đã bắt giữ


được chuột mọi nhập liệu từ chuột đều được gởi cho cửa sổ đó, dù vị trí con trỏ chuột ở trong


hay ở ngồi đường biên của cửa sổ. Tại mỗi thời điểm, chỉ có một cửa sổ duy nhất có thể bắt


giữ con chuột.



Nếu con trỏ chuột đi ra khỏi phạm vi của cửa sổ đang bắt giữ con chuột, nhập liệu từ chuột chỉ


được gởi cho cửa sổ này nếu mắt chuột được bấm.



<b>HWND SetCapture( </b>


<i><b> HWND hWnd // handle of window to receive mouse capture </b></i>
<b>); </b>


Tham số hWnd cho biết cửa sổ sẽ bắt giữ con chuột. Hàm trên trả về handle đến cửa sổ trước


đó đang bắt giữ chuột, hoặc NULL nếu trước đó chưa có cửa sổ nào bắt giữ chuột.




</div>
<span class='text_page_counter'>(103)</span><div class='page_container' data-page=103>

<b>BOOL ReleaseCapture(VOID) </b>


<b>GetCapture </b>



Hàm GetCapture cho biết cửa sổ nào đang bắt giữ con chuột (nếu có).


<b>HWND GetCapture(VOID) </b>


Ta dùng hàm GetCapture để xác định người sử dụng có đang trong q trình tương tác với


chương trình bằng cách kéo lê chuột để thao tác (di chuyển, thay đổi kích thước, quay…) trên


đối tượng hay không.



<b>7.2 Kéo lên đối tượng bằng cách xố và vẽ lại </b>


Ví dụ ứng dụng – Chương trình ObjectDrag V1



Chương trình sau minh hoạ sử dụng các hàm kể trên để thao tác trên đối tượng đồ hoạ.


Chương trình ban đầu vẽ một hình ellipse, người sử dụng sau đó có thể thay đổi vị trí của hình


ellipse này bằng cách đưa con trỏ chuột vào bên trong hình, bấm mắt trái và kéo lê.



Khi người sử dụng bấm mắt trái chuột bên trong hình ellipse, cursor đổi sang hình dạng khác


cho biết người sử dụng đang trong quá trình kéo lê. Khi con trỏ chuột được kéo lê đến đâu,


hình ellipse di chuyển theo đến đó, điều này được thực hiện nhờ xử lý thông điệp


WM_MOUSEMOVE, hàm GetCapture được gọi khi xử lý thông điệp này để xác định con


trỏ chuột có đang bị bắt giữ khơng, nói cách khác, chuột đang được kéo lên không. Ta di


chuyển hình bằng cách cập nhật vị trí, sau đó xố hình cũ và vẽ lại hình ở vị trí mới. Trước đó


con chuột đã bị bắt giữ bởi cửa sổ nhờ gọi hàm SetCapture khi xử lý thông điệp


WM_LBUTTONDOWN. Việc bắt giữ chuột bảo đảm trong quá trình người sử dụng kéo lê,


nếu con trỏ chuột đi ra ngồi phạm vi của cửa sổ thơng điệp chuột vẫn được chuyển cho cửa


sổ đã bắt giữ chuột. Người sử dụng kết thúc việc di chuyển hình bằng cách bng mắt chuột.


Hàm ReleaseCapture được gọi để giải phóng chuột.




Chương trình gồm một số tập tin tài nguyên



small.ico
ObjectDrag.ico


resource.h


Và một số tập tin chương trình nguoàn



</div>
<span class='text_page_counter'>(104)</span><div class='page_container' data-page=104></div>
<span class='text_page_counter'>(105)</span><div class='page_container' data-page=105>

// ObjectDrag.cpp : Defines the entry point for the application.
// ...


bool IsInsideEllipse(int x1, int y1, int x2, int y2, int x, int y)
{


double a = (double(x2)-x1)/2, b = (double(y2)-y1)/2;
if (!a || !b) return false;


double xx = x - (double(x1)+x2)/2, yy = y - (double(y1)+y2)/2;
double xr = xx/a, yr = yy/b;


return xr*xr+yr*yr <= 1;
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{



int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;


TCHAR szHello[MAX_LOADSTRING];
static HCURSOR hCursorMove;
static x1,y1,x2,y2, px, py;
static HBRUSH hbr;


LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
switch (message)


{


case WM_CREATE:
RECT rt;


GetClientRect(hWnd, &rt);


x1 = rt.right/4; x2 = 3*rt.right/4;
y1 = rt.bottom/4; y2 = 3*rt.bottom/4;
HBITMAP hBitmap;


hBitmap = LoadBitmap(hInst, (LPCTSTR)IDB_HEART);
hbr = CreatePatternBrush(hBitmap);


DeleteObject(hBitmap);


hCursorMove = LoadCursor(NULL, IDC_SIZEALL);
break;



case WM_LBUTTONDOWN:
int x, y;


px = x = LOWORD(lParam);
py = y = HIWORD(lParam);


</div>
<span class='text_page_counter'>(106)</span><div class='page_container' data-page=106>

{


SetCursor(hCursorMove);
if (GetCapture() == hWnd)
{


int dx = x-px, dy = y-py;
x1 += dx; y1 += dy;


x2 += dx; y2 += dy;
px = x;


py = y;


InvalidateRect(hWnd, NULL, TRUE);
}


}
break;
case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);



DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
SelectObject(hdc, hbr);


Ellipse(hdc,x1,y1,x2,y2);
EndPaint(hWnd, &ps);
break;


case WM_DESTROY:


PostQuitMessage(0);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


return 0;
}


<b>7.3 Kéo lên đối tượng bằng chọn chế độ vẽ R2_NOTXORPEN </b>



Trong cách tiếp cận trên, ta di chuyển hình bằng cách xố cũ vẽ mới. Điều này làm cho cửa


sổ client bị chớp trong quá trình kéo lê. Hơn nữa nếu hình phức tạp địi hỏi thời gian để vẽ thì


hoạt động của chương trình sẽ khơng hiệu quả. Một cách tiếp cận hiện đại hơn là vẽ một đối


tượng tạm, thông thường là đường biên của đối tượng thực sự, hay đơn giản hơn là hình chữ


nhật ngoại tiếp đối tượng, sau đó kéo lê để di chuyển đối tượng tạm này. Khi người sử dụng


buông mắt chuột để kết thúc quá trình kéo lê, mới vẽ lại đối tượng thực sự.



</div>
<span class='text_page_counter'>(107)</span><div class='page_container' data-page=107>

Có 16 chế độ vẽ, ta chỉ quan tâm đến hai chế độ vẽ là R2_XORPEN và R2_NOTXORPEN.



Cả hai chế độ vẽ này đều có chung đặc điểm là nếu một hình được vẽ hai lần, nó sẽ khơi phục


lại nền về trạng thái trước khi vẽ. Với một trong hai chế độ vẽ kể trên, khi một đối tượng tạm


được kéo lê. Ta vẽ lại nó một lần nữa ở hình dáng và vị trí cũ, thao tác này sẽ khôi phục lại


phần nền như khi chưa vẽ, sau đó ta vẽ lại đối tượng ở hình dáng, vị trí mới. Chế độ


R2_XORPEN phù hợp với cửa sổ có nền đen và R2_NOTXORPEN phù hợp với cửa sổ có nền


trắng.



Ví dụ ứng dụng – Chương trình ObjectDrag V2



Chương trình ObjectDrag V2 sau minh hoạ sử dụng các hàm SetCapture, GetCapture,


ReleaseCapture kết hợp với chế độ vẽ R2_NOTXORPEN kể trên để thao tác trên đối tượng


đồ hoạ. Chương trình ban đầu vẽ một hình ellipse, người sử dụng sau đó có thể thay đổi vị trí


của hình ellipse này bằng cách đưa con trỏ chuột vào bên trong hình, bấm mắt trái và kéo lê.


Khi người sử dụng bấm mắt trái chuột bên trong hình ellipse, cursor đổi sang hình dạng khác


cho biết người sử dụng đang trong quá trình kéo lê. Đồng thời một hình ellipse tạm với nét vẽ


PS_DOT, chổi vẽ rỗng được vẽ chồng lên hình ellipse đang có trong chế độ vẽ


R2_NOTXORPEN. Khi con trỏ chuột được kéo lê đến đâu, hình ellipse tạm này di chuyển


theo đến đó, điều này được thực hiện nhờ xử lý thông điệp WM_MOUSEMOVE, hàm


GetCapture được gọi khi xử lý thông điệp này để xác định con trỏ chuột có đang bị bắt giữ


khơng, nói cách khác, chuột đang được kéo lên khơng. Ta di chuyển hình elippse tạm bằng


cách vẽ lại để khơi phục phần nền bị hình này vẽ lên, cập nhật vị trí, sau đó vẽ lại hình ellipse


tạm này, cũng với chế độ vẽ R2_NOTXORPEN để chuẩn bị cho lần xử lý sau. Khi người sử


dụng bng mắt chuột để kết thúc q trình kéo lê, mới vẽ lại đối tượng thực ở vị trí mới.


Chương trình gồm một số tập tin tài nguyên



small.ico
ObjectDrag.ico


resource.h



Vaø một số tập tin chương trình nguồn



ObjectDrag.rc
StdAfx.h
StdAfx.cpp


</div>
<span class='text_page_counter'>(108)</span><div class='page_container' data-page=108></div>
<span class='text_page_counter'>(109)</span><div class='page_container' data-page=109></div>
<span class='text_page_counter'>(110)</span><div class='page_container' data-page=110>

//////////////////////////////////////////////////////////////////
bool IsInsideEllipse(int x1, int y1, int x2, int y2, int x, int y)
{


double a = (double(x2)-x1)/2, b = (double(y2)-y1)/2;
if (!a || !b) return false;


double xx = x - (double(x1)+x2)/2, yy = y - (double(y1)+y2)/2;
double xr = xx/a, yr = yy/b;


return xr*xr+yr*yr <= 1;
}


//////////////////////////////////////////////////////////////////////////
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{


int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;



TCHAR szHello[MAX_LOADSTRING];
static int x1,y1,x2,y2,xc,yc,px,py;
static HBRUSH hbr;


static HCURSOR hCursorMove;
static HPEN hPenDot;


int dx, dy;
RECT rt;


LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
switch (message)


{


case WM_CREATE:


GetClientRect(hWnd, &rt);


x1 = rt.right/4; x2 = 3*rt.right/4;
y1 = rt.bottom/4; y2 = 3*rt.bottom/4;


hbr = CreateHatchBrush(HS_DIAGCROSS, RGB(255,0,0));
hCursorMove = LoadCursor(NULL, IDC_SIZEALL);


hPenDot = CreatePen(PS_DOT, 1, RGB(0,0,255));
break;


case WM_COMMAND:



</div>
<span class='text_page_counter'>(111)</span><div class='page_container' data-page=111>

case WM_LBUTTONDOWN:


xc = LOWORD(lParam); yc = HIWORD(lParam);
if (IsInsideEllipse(x1,y1,x2,y2,xc,yc))
{


hdc = GetDC(hWnd);


SelectObject(hdc,hPenDot);
SetROP2(hdc, R2_NOTXORPEN);
Ellipse(hdc,x1,y1,x2,y2);
SetCursor(hCursorMove);
SetCapture(hWnd);


}
break;


case WM_MOUSEMOVE:


if (GetCapture() == hWnd)
{


hdc = GetDC(hWnd);


SelectObject(hdc,hPenDot);
SetROP2(hdc, R2_NOTXORPEN);
Ellipse(hdc,x1,y1,x2,y2);
px = xc; py = yc;



xc = LOWORD(lParam); yc = HIWORD(lParam);
dx = xc-px, dy = yc - py;


x1 += dx, x2 += dx,
y1 += dy, y2 += dy;


Ellipse(hdc,x1,y1,x2,y2);
}


break;


case WM_LBUTTONUP:


if (GetCapture() == hWnd)
{


ReleaseCapture();


InvalidateRect(hWnd, NULL, TRUE);
}


break;
case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rt);


</div>
<span class='text_page_counter'>(112)</span><div class='page_container' data-page=112>

Ví dụ ứng dụng – Chương trình ObjectDrag V6



Chương trình ObjectDrag V6 cho phép vẽ và di chuyển hình ellipse. Người sử dụng bấm chuột



và kéo lê. Nếu điểm bấm không thuộc một hình ellipse nào đã vẽ trước đó thì vẽ hình ellipse


mới. Nếu vị trí chuột được bấm ở trong một trong các hình ellipse đã vẽ thì di chuyển hình


ellipse đó theo chiều di chuyển của chuột. Người sử dụng có thể thay đổi thuộc tính tơ và màu


tơ của hình ellipse sẽ vẽ tiếp theo.



</div>
<span class='text_page_counter'>(113)</span><div class='page_container' data-page=113>

Hình 7-8 Cửa sổ chương trình sau khi di chuyển ba hình đầu và vẽ thêm 4 hình khác.


// ObjectDrag.cpp : Defines the entry point for the application.
//...


class CEllipse
{


int x1,y1,x2,y2;
COLORREF crColor;
int hatch;


public:


CEllipse(){Set(0,0,0,0); SetColor(RGB(0,0,255)); SetHatch(HS_DIAGCROSS);}
CEllipse(int _x1, int _y1, int _x2, int _y2) {


</div>
<span class='text_page_counter'>(114)</span><div class='page_container' data-page=114>

};


void CEllipse::Draw(HDC hdc)
{


HBRUSH hbr;


hbr = CreateHatchBrush(hatch, crColor);


SelectObject(hdc, hbr);


Ellipse(hdc,x1,y1,x2,y2);
DeleteObject(hbr);


}


bool CEllipse::IsInside(int x, int y)
{


double a = (double(x2)-x1)/2, b = (double(y2)-y1)/2;
if (!a || !b) return false;


double xx = x - (double(x1)+x2)/2, yy = y - (double(y1)+y2)/2;
double xr = xx/a, yr = yy/b;


return xr*xr+yr*yr <= 1;
}


void CEllipse::Translate(int dx, int dy)
{


x1 += dx; y1 += dy;
x2 += dx; y2 += dy;
}


int FindEllipse(CEllipse ae[], int n, int x, int y)
{


for (int i = n-1; i >= 0; i--)


if (ae[i].IsInside(x,y))


return i;
return -1;
}


//////////////////////////////////////////////////////////
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)


//


// PURPOSE: Processes messages for the main window.
//


</div>
<span class='text_page_counter'>(115)</span><div class='page_container' data-page=115>

static COLORREF aCrTab[] =
{


RGB(0,255,0), RGB(0, 255,255),RGB(0,0,0), RGB(255,0,0), RGB(0,0,255),
RGB(255,255,0),


};


static int aHatch[] =
{
HS_HORIZONTAL,
HS_VERTICAL,
HS_FDIAGONAL,
HS_BDIAGONAL,
HS_CROSS,
HS_DIAGCROSS


};


int dx, dy, i;


static int curColor, curHatch = 5;


static INT flag = DRAW, nEllipse,nCur = -1;
RECT rt;


LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
switch (message)


{


case WM_CREATE:


GetClientRect(hWnd, &rt);


hCursorMove = LoadCursor(NULL, IDC_SIZEALL);
hCursorCross = LoadCursor(NULL, IDC_CROSS);
hPenDot = CreatePen(PS_DOT, 1, RGB(0,0,255));
break;


case WM_COMMAND:


wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId)


{



case IDM_ABOUT:


DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
break;


case IDM_EXIT:


DestroyWindow(hWnd);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


</div>
<span class='text_page_counter'>(116)</span><div class='page_container' data-page=116>

break;


case WM_LBUTTONDOWN:


xc = LOWORD(lParam); yc = HIWORD(lParam);


if ((nCur = FindEllipse(ae, nEllipse, xc, yc)) >= 0)
{


flag = MOVE;


hdc = GetDC(hWnd);


SelectObject(hdc,hPenDot);


SetROP2(hdc, R2_NOTXORPEN);
ae[nCur].Draw(hdc);
SetCursor(hCursorMove);
SetCapture(hWnd);
}


else if (nEllipse < MAX_ELLIPSE)// DRAW
{


flag = DRAW;


ae[nEllipse].Set(xc,yc,xc,yc,aCrTab[curColor],aHatch[curHatch]);
hdc = GetDC(hWnd);


SetROP2(hdc, R2_NOTXORPEN);
ae[nEllipse].Draw(hdc);
SetCursor(hCursorCross);
SetCapture(hWnd);
nEllipse++;
}
else


flag = -1;
break;


case WM_MOUSEMOVE:


if (GetCapture() == hWnd)
{



if (flag == MOVE)
{


hdc = GetDC(hWnd);


SelectObject(hdc,hPenDot);
SetROP2(hdc, R2_NOTXORPEN);
ae[nCur].Draw(hdc);


px = xc; py = yc;


xc = LOWORD(lParam); yc = HIWORD(lParam);
dx = xc-px, dy = yc - py;


</div>
<span class='text_page_counter'>(117)</span><div class='page_container' data-page=117>

{


ReleaseCapture();


InvalidateRect(hWnd, NULL, TRUE);
}


break;
case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rt);
for (i = 0; i < nEllipse; i++)


ae[i].Draw(hdc);
EndPaint(hWnd, &ps);


char buf[128];


wsprintf(buf, "Number of Ellipses: %d", nEllipse);
DrawText(hdc, buf, lstrlen(buf), &rt, DT_CENTER);
break;


case WM_DESTROY:


PostQuitMessage(0);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


</div>
<span class='text_page_counter'>(118)</span><div class='page_container' data-page=118>

<b>8. </b>

Sử dụng tài nguyên



Window cho phép người sử dụng đưa vào chương trình các tài nguyên thuộc các loại biểu


tượng (icons), con trỏ (cursors), bitmap, bảng các chuỗi hằng (string tables), bảng phím tắt


(Accelerator), hộp hội thoại (Dialog), thực đơn (menu), thanh công cụ (toolbar) và một siêu


văn bản (văn bản loại HTML). Một số tài nguyên thuộc các loại này có thể được Windows


cung cấp sẵn gọi là tài nguyên chuẩn. Người sử dụng có thể tự tạo các tài ngun khác thơng


qua một tập tin tài ngun có phần đuôi .RC (Resource Script).



Tập tin tài nguyên là một văn bản viết theo cú pháp của một ngôn ngữ lập trình để mơ tả các


tài ngun sẽ cấp cho chương trình.



</div>
<span class='text_page_counter'>(119)</span><div class='page_container' data-page=119>

• Biểu tượng chuẩn (standard icons) là những biểu tượng do Windows cung cấp sẵn,


những biểu tượng này được xác định bằng các hằng có phần tiếp đầu ngữ IDI_.



Windows 95 cung cấp các biểu tượng chuẩn sau:



IDI_APPLICATION IDI_HAND


IDI_QUESTION IDI_EXCLAMATION
IDI_ASTERISK IDI_WINLOGO
IDI_WARNING IDI_EXCLAMATION
IDI_ERROR IDI_INFORMATION
IDI_ASTERISK


• Custom Icons là những biểu tượng do người sử dụng định nghĩa bằng cách tạo tập tin


biểu tượng có phần đi .ICO và mơ tả trong tập tin tài nguyên bằng một tham chiếu


đến tập tin này. Ví dụ sau là trích đoạn một tập tin .RC phần định nghĩa biểu tượng



IDI_SCROLL ICON DISCARDABLE "Scroll.ICO"
IDI_SMALL ICON DISCARDABLE "SMALL.ICO"


</div>
<span class='text_page_counter'>(120)</span><div class='page_container' data-page=120>

8.1.2 Các thao tác trên biểu tượng


<b>LoadIcon </b>



Hàm LoadIcon nạp tài nguyên icon từ một tập tin thực thi và trả về kết quả là chỉ điểm đến


biểu tượng được nạp.



<b>HICON LoadIcon( </b>


<i><b> HINSTANCE hInstance, // handle to application instance </b></i>
<i><b> LPCTSTR lpIconName // icon-name string or icon resource </b></i>


// identifier



<b>); </b>


Để nạp biểu tượng chuẩn ta dùng LoadIcon với hInstance có giá trị NULL.


<b>DrawIcon </b>



Hàm DrawIcon vẽ biểu tượng tại vị trí qui định bởi tham số X,Y.


<b>BOOL DrawIcon( </b>


<i><b> HDC hDC, // handle to device context </b></i>
<i><b> int X, // x-coordinate of upper-left corner </b></i>
<i><b> int Y, // y-coordinate of upper-left corner </b></i>
<i><b> HICON hIcon // handle to icon to draw </b></i>
<b>); </b>


Ví dụ:



Để nạp biểu tượng do người sử dụng định nghĩa



wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_SCROLL);


Để nạp biểu tượng chuẩn



HICON hIcon2 = LoadIcon(NULL, IDI_APPLICATION);


Để lấy thơng tin về kích thước của biểu tượng, ta dùng hàm


<b>int GetSystemMetrics( </b>


<i><b> int nIndex // system metric or configuration setting to retrieve </b></i>
<b>);</b>



Với nIndex lấy các giá trị sau đây tương ứng với biểu tượng.



</div>
<span class='text_page_counter'>(121)</span><div class='page_container' data-page=121>

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{


int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;


TCHAR szHello[MAX_LOADSTRING];


TCHAR szPrompt[] = "Standard Icons";
static ICONDATA aIconTab[] =


{
"IDI_APPLICATION", IDI_APPLICATION,
"IDI_ASTERISK", IDI_ASTERISK,
"IDI_ERROR", IDI_ERROR,
"IDI_EXCLAMATION", IDI_EXCLAMATION,
"IDI_HAND", IDI_HAND,
"IDI_INFORMATION", IDI_INFORMATION,
"IDI_QUESTION", IDI_QUESTION,
"IDI_WARNING", IDI_WARNING,
"IDI_WINLOGO", IDI_WINLOGO
};


int x, y, i;
switch (message)


{


case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
RECT rt;


int cxIcon, cyIcon;


GetClientRect(hWnd, &rt);


DrawText(hdc, szPrompt, strlen(szPrompt), &rt, DT_CENTER);
HICON hIcon;


hIcon = LoadIcon(NULL, (LPCTSTR)IDI_APPLICATION);
cxIcon = GetSystemMetrics(SM_CXICON);


cyIcon = GetSystemMetrics(SM_CYICON);
y = 20;


SetTextAlign(hdc, TA_CENTER);


for (i = 0, x = 70; i < dim(aIconTab); i++, x += XSTEP)
{


if (x > rt.right - 50)
x = 70, y += YSTEP;


DrawIcon(hdc, x, y, LoadIcon(NULL, aIconTab[i].iconId));
TextOut(hdc, x+cxIcon/2, y+40, aIconTab[i].szName,



</div>
<span class='text_page_counter'>(122)</span><div class='page_container' data-page=122>

<b>8.2 Con troû (cursor) </b>


8.2.1 Tổng quan



Một con trỏ (cursor) là một hình ảnh kích thước nhỏ để chỉ rõ vị trí hiện hành của một thiết bị


trỏ (pointing device) như chuột, bút vẽ hay track ball.



Hình 8-4 Con trỏ chuẩn


• Khi người sử dụng di chuyển chuột, con trỏ chuột di chuyển tương ứng.



• Người sử dụng có thể qui định hình dạng của con trỏ cho ứng dụng. Khi người sử dụng


di chuyển chuột đến một ứng dụng nào đó, con trỏ sẽ đổi sang hình dạng tương ứng.


• Người sử dụng cũng có thể qui định con trỏ khác nhau ở các vùng khác nhau của ứng



duïng.



</div>
<span class='text_page_counter'>(123)</span><div class='page_container' data-page=123>

• Custom Cursors là những con trỏ do người sử dụng định nghĩa bằng cách tạo tập tin


con trỏ có phần đi .ICO và mơ tả trong tập tin tài nguyên bằng một tham chiếu đến


tập tin này. Cách định nghĩa tương tự như biểu tượng.



8.2.2 Các thao tác trên con trỏ


<b>LoadCursor </b>



Hàm LoadCursor nạp tài nguyên con trỏ từ một tập tin thực thi và trả về kết quả là chỉ điểm


đến biểu tượng được nạp.



<b>HCURSOR LoadCursor( </b>


<i><b> HINSTANCE hInstance, // handle to application instance </b></i>



<i><b> LPCTSTR lpCursorName // name string or cursor resource identifier </b></i>
<b>); </b>


<b>ShowCursor </b>



Hàm ShowCursor hiển thị hay làm biến mất curosr.


<b>int ShowCursor( </b>


<i><b> BOOL bShow // cursor visibility flag </b></i>
<b>); </b>


<b>SetCursor </b>



Hàm SetCursor định con trỏ mới cho ứng dụng.


<b>HCURSOR SetCursor( </b>


<i><b> HCURSOR hCursor // handle to cursor </b></i>
<b>); </b>


Ví dụ:



SetCursor(LoadCursor(NULL, IDC_WAIT));


<b>8.3 Phím tắt (Keyboard Accelerators) </b>



Phím tắt là một phương tiện để người sử dụng điều khiển chương trình bằng bàn phím, phím


tắt ánh xạ một thao tác trên bàn phím thành một thơng điệp định nghĩa sẵn,


WM_COMMAND với thông số kèm theo cho biết chi tiết thông điệp, phím tắt có thể được


dùng thay thế cho việc chọn một chức năng trong menu bằng chuột.




</div>
<span class='text_page_counter'>(124)</span><div class='page_container' data-page=124>

IDC_HELLO ACCELERATORS MOVEABLE PURE
BEGIN


"/", IDM_ABOUT, ASCII, ALT, NOINVERT
"?", IDM_ABOUT, ASCII, ALT, NOINVERT
"A", ID_LUACHON_TIENGANH, VIRTKEY, ALT, NOINVERT
"C", ID_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT
"N", ID_FILE_NEW, VIRTKEY, CONTROL, NOINVERT
"O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT
"S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT
"V", ID_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT
"V", ID_OPTION_VIETNAMESE, VIRTKEY, ALT, NOINVERT
"X", ID_EDIT_CUT, VIRTKEY, CONTROL, NOINVERT
END


Có thể sử dụng các phím do windows định nghĩa sẵn như: VK_F1, VK_F2,…


Để có thể sử dụng phím tắt, dùng LoadAccelerator và TranslateAccelerator



int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{


// TODO: Place code here.
MSG msg;


HACCEL hAccelTable;



// Initialize global strings


LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_HELLO, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);


// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{


return FALSE;
}


hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_HELLO);
// Main message loop:


while (GetMessage(&msg, NULL, 0, 0))
{


</div>
<span class='text_page_counter'>(125)</span><div class='page_container' data-page=125>

<b>8.4 Menu </b>


8.4.1 Toång quan



Menu là một phương tiện để ánh xạ một thao tác chọn xử lý với một xử lý thực sự. Một menu


là một danh sách các mục qui định các tuỳ chọn hoặc một nhóm các tuỳ chọn (một submenu)


của ứng dụng. Bấm vào một mục menu hay một menu con sẽ kích hoạt ựng dụng thực thi lệnh


tương ứng.



Windows có ba loại menu chính là system menu – top level menu – popup menu.


Menu được tạo với những dấu hiệu nhận diện sau (Visual clues):




• Arrow : nested menu


• Seperator : group menu


• Check mark



• Ellipsis (…)



</div>
<span class='text_page_counter'>(126)</span><div class='page_container' data-page=126>

8.4.2 Menu template



Người sử dụng có thể định nghĩa menu trong tập tin tài nguyên (resource)



IDC_MENU MENU DISCARDABLE
BEGIN


POPUP "&File"
BEGIN


MENUITEM "&Open ...\tCtrl+O", ID_FILE_OPEN
MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE
MENUITEM "Save &As ...", ID_FILE_SAVEAS
MENUITEM "E&xit", IDM_EXIT


END


POPUP "&Help"
BEGIN


MENUITEM "&About ...", IDM_ABOUT
END


MENUITEM "Quit !", ID_QUIT



MENUITEM "BreakCol", 65535, MENUBREAK
MENUITEM "Break ?", 65535


END


Dạng tổng quát



POPUP text [, optionlist]
BEGIN


MENUITEM text, result-code [optionlist]
END


POPUP …
BEGIN


MENUITEM "&About ...", IDM_ABOUT
END


Có 5 lựa chọn



CHECKED
GRAYED
INACTIVE


MENUBREAK – toplevel menu
MENUBARBREAK – popup menu


8.4.3 Menu Handles:




</div>
<span class='text_page_counter'>(127)</span><div class='page_container' data-page=127>

1. Thiết kế mẫu menu trong tập tin tài ngun. Có thể qui định phím tắt tương ứng cho


mỗi mục menu. Định nghĩa ID nhận diện cho các mục menu.



2. Xử lý thơng điệp WM_COMMAND phát sinh do kích hoạt một mục menu, trong đó ta


xử lý cho trường hợp wParam có giá trị tương ứng với ID của mục menu được bấm.


Chương trình sau minh hoạ sử dụng menu đơn giản.



Chương trình bounce sau đây cho phép tạo một hình trịn nhỏ di chuyển bên trong vùng client


bằng cách vào menu File – New Ball hoặc loại bớt hình bằng vào menu File – Remove Ball



Sau đây là trích đoạn chương trình nguồn:



struct BALL
{


</div>
<span class='text_page_counter'>(128)</span><div class='page_container' data-page=128>

b.crColor = color;
return b;


};


const int MAX_BALLS = 100;


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{


int wmId, wmEvent;
PAINTSTRUCT ps;


HDC hdc;


TCHAR szHello[MAX_LOADSTRING];
static BALL aBalls[MAX_BALLS];
static int nBalls;


static COLORREF aColorTab [] = {RGB(0xff,0,0), RGB(0,0xff,0),
RGB(0,0,0xff), RGB(0,0,0), RGB(0xff,0xff,0xff),RGB(0xc0,0xc0,0xc0)};


static int nColors = sizeof(aColorTab)/sizeof(aColorTab[0]);
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);


RECT rt;


switch (message)
{


case WM_COMMAND:


wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId)


{


case ID_FILE_NEWBALL:


GetClientRect(hWnd, &rt);


aBalls[nBalls] = CreateNewBall(rand()%rt.right,


rand()%rt.bottom, 10, 5, 5, aColorTab[nBalls%nColors]);


SetTimer(hWnd, nBalls++, 10, NULL);
break;


case ID_FILE_REMOVEBALL:
if (nBalls > 0)


KillTimer(hWnd, --nBalls);
InvalidateRect(hWnd, NULL, TRUE);
break;


case IDM_ABOUT:


DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
break;


</div>
<span class='text_page_counter'>(129)</span><div class='page_container' data-page=129>

r.bottom = pb->y + pb->r + abs(pb->dy);
pb->x += pb->dx;


pb->y += pb->dy;


if (pb->x + pb->r > rClient.right && pb->dx > 0) pb->dx = -pb->dx;
if (pb->x - pb->r < 0 && pb->dx < 0) pb->dx = -pb->dx;


if (pb->y + pb->r > rClient.bottom && pb->dy > 0) pb->dy = -pb->dy;
if (pb->y - pb->r < 0 && pb->dy < 0) pb->dy = -pb->dy;


InvalidateRect(hWnd, &r, TRUE);
break;



case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
RECT rt;


int i;


GetClientRect(hWnd, &rt);


DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
HBRUSH hbr;


for (i = 0; i < nBalls; i++)
{


hbr = CreateSolidBrush(aBalls[i].crColor);
SelectObject(hdc, hbr);


Ellipse(hdc, aBalls[i].x - aBalls[i].r, aBalls[i].y -
aBalls[i].r, aBalls[i].x + aBalls[i].r, aBalls[i].y + aBalls[i].r);


DeleteObject(hbr);
}
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:



return DefWindowProc(hWnd, message, wParam, lParam);
}


return 0;
}


8.4.5 Sử dụng checkmark



</div>
<span class='text_page_counter'>(130)</span><div class='page_container' data-page=130>

Hình 8-6 Menu với checkmark
// Trich doan resource.h


#define ID_BRUSHCOLOR_WHITE 32771
#define ID_BRUSHCOLOR_RED 32772
#define ID_BRUSHCOLOR_BLUE 32773
#define ID_BRUSHCOLOR_GREEN 32774
#define ID_BRUSHCOLOR_LTGRAY 32775
#define ID_BRUSHCOLOR_GRAY 32776
#define ID_BRUSHCOLOR_BLACK 32777


// Ellipse.cpp : Defines the entry point for the application.
#define MAX_MARKERS 100


#define MARKER_SIZE 5


void DrawMarker(HDC hdc, POINT pt, int size);


</div>
<span class='text_page_counter'>(131)</span><div class='page_container' data-page=131>

switch (message)
{



case WM_COMMAND:


hMenu = GetMenu(hWnd);
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId)
{
case ID_BRUSHCOLOR_WHITE:
case ID_BRUSHCOLOR_RED:
case ID_BRUSHCOLOR_BLUE:
case ID_BRUSHCOLOR_GREEN:
case ID_BRUSHCOLOR_LTGRAY:
case ID_BRUSHCOLOR_GRAY:
case ID_BRUSHCOLOR_BLACK:


CheckMenuItem (hMenu, nBrushColor, MF_UNCHECKED);
nBrushColor = LOWORD (wParam) ;


CheckMenuItem (hMenu, nBrushColor, MF_CHECKED) ;
InvalidateRect (hWnd, NULL, TRUE) ;


return 0 ;
default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


break;


case WM_LBUTTONDOWN:



if (nMarkers <= MAX_MARKERS)
{


apt[nMarkers].x = LOWORD(lParam);
apt[nMarkers].y = HIWORD(lParam);
nMarkers++;


InvalidateRect(hWnd, NULL, TRUE);
}


break;


case WM_RBUTTONDOWN:
nMarkers = 0;


InvalidateRect(hWnd, NULL, TRUE);
break;


case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);


hbr = CreateHatchBrush(HS_BDIAGONAL,


aColorTab[nBrushColor - ID_BRUSHCOLOR_WHITE]);
RECT rt;


GetClientRect(hWnd, &rt);



</div>
<span class='text_page_counter'>(132)</span><div class='page_container' data-page=132>

SelectObject(hdc, hbr);


Ellipse(hdc, apt[i-1].x, apt[i-1].y,
apt[i].x, apt[i].y);
}


}


DeleteObject(hpen);
DeleteObject(hbr);
EndPaint(hWnd, &ps);
break;


case WM_DESTROY:


PostQuitMessage(0);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


return 0;
}


<b>8.5 Bitmap </b>


8.5.1 Toång quan



Một bitmap là một đối tượng đồ hoạ dùng để tạo, thao tác và lưu trữ hình ảnh vào bộ nhớ hay



tập tin trên đĩa. Các thao tác trên bimap có thể là phóng to, trượt, quay, vẽ…



Windows sẵn cung cấp một số bitmap gọi là bitmap chuẩn. Người sử dụng có thể tự định nghĩa


các bitmap riêng gọi là custom bitmap.



Bitmap là một trong các loại đối tượng đồ hoạ có thể được chọn cho một ngữ cảnh thiết bị


(tượng tự như bút vẽ, chổi vẽ, font…).



Về mặt lưu trữ nội bộ, cấu trúc của một bitmap bao gồm:



• Phần đầu (header) mơ tả độ phân giải của thiết bị trên đó ma trận các pixel sẽ được


tạo, kích thước hình chữ nhật, kích thước của mảng các bít biểu diễn các pixel…



• Bảng palette luận lý.



• Mảng các bit định nghĩa mối quan hệ giữa các pixel trên ảnh được bitmapped với các


phần tử trong bảng palette.



</div>
<span class='text_page_counter'>(133)</span><div class='page_container' data-page=133>

<b>CreateBitmap</b> Creates a bitmap.


<b>CreateBitmapIndirect</b> Creates a bitmap.


<b>CreateCompatibleBitmap</b> Creates a bitmap compatible with a device.


<b>CreateDIBitmap</b> Creates a device-dependent bitmap (DDB) from a DIB.


<b>CreateDIBSection</b> Creates a DIB that applications can write to directly.


<b>ExtFloodFill</b> Fills an area of the display surface with the current brush.



<b>GetBitmapDimensionEx</b> Gets the dimensions of a bitmap.


<b>GetDIBColorTable</b> Retrieves RGB color values from a DIB section bitmap.


<b>GetDIBits</b> Copies a bitmap into a buffer.


<b>GetPixel</b> Gets the RGB color value of the pixel at a given coordinate.


<b>GetStretchBltMode</b> Gets the current stretching mode.


<b>GradientFill</b> Fills rectangle and triangle structures.


<b>LoadBitmap</b> Loads a bitmap from a module's executable file.


<b>MaskBlt</b> Combines the color data in the source and destination bitmaps.


<b>PlgBlt</b> Performs a bit-block transfer.


<b>SetBitmapDimensionEx</b> Sets the preferred dimensions to a bitmap.


<b>SetDIBColorTable</b> Sets RGB values in a DIB.


<b>SetDIBits</b> Sets the pixels in a bitmap using color data from a DIB.


<b>SetDIBitsToDevice</b> Sets the pixels in a rectangle using color data from a DIB.


<b>SetPixel</b> Sets the color for a pixel.


<b>SetPixelV</b> Sets a pixel to the best approximation of a color.



<b>SetStretchBltMode</b> Sets the bitmap stretching mode.


<b>StretchBlt</b> Copies a bitmap and stretches or compresses it.


<b>StretchDIBits</b> Copies the color data in a DIB.


</div>
<span class='text_page_counter'>(134)</span><div class='page_container' data-page=134>

Hàm CreateBitmap tạo một bitmap với độ rộng, chiều cao, và định dạng màu.


<b>HBITMAP CreateBitmap( </b>



<i><b> int </b></i>

<i><b>nWidth</b></i>

<b><sub>, // bitmap width, in pixels </sub></b>



<b> int</b>

<i>nHeight</i>

<b>, </b>

// bitmap height, in pixels


<b> UINT</b>

<i>cPlanes</i>

<b>, </b>

// number of color planes


<b> UINT</b>

<i>cBitsPerPel</i>

<b>, </b>

// number of bits to identify color


<b> CONST VOID</b>

<b>*</b>

<i>lpvBits</i> // color data array


<b>); </b>



<b>CreateCompatibleBitmap </b>



Hàm CreateCompatibleBitmap tạo một bitmap tương thích với thiết bị liên kết với ngữ


cảnh thiết bị hdc



<b>HBITMAP CreateCompatibleBitmap( </b>
<i><b> HDC </b>hdc</i><b>, // handle to DC </b>


<i><b> int </b>nWidth</i><b>, // width of bitmap, in pixels </b>


<i><b> int </b>nHeight</i> // height of bitmap, in pixels


<b>); </b>


<b>BitBlt </b>



Hàm BitBlt chuyển dịch một khối bit các dữ liệu màu của một hình chữ nhật các pixels từ một


ngữ cảnh thiết bị này đến một ngữ cảnh thiết bị khác.



<b>BOOL BitBlt( </b>



<i><b> HDC </b></i>

<i><b>hdcDest</b></i>

<b><sub>, // handle to destination DC </sub></b>



<b> int</b>

<i>nXDest</i>

<b>, </b>

// x-coord of destination upper-left corner


<b> int</b>

<i>nYDest</i>

<b>, </b>

// y-coord of destination upper-left corner


<b> int</b>

<i>nWidth</i>

<b>, </b>

// width of destination rectangle


<b> int</b>

<i>nHeight</i>

<b>, </b>

// height of destination rectangle


<b> HDC</b>

<i>hdcSrc</i>

<b>, </b>

// handle to source DC


<b> int</b>

<i>nXSrc</i>

<b>, </b>

// x-coordinate of source upper-left corner


<b> int</b>

<i>nYSrc</i>

<b>, </b>

// y-coordinate of source upper-left corner


<b> DWORD</b>

<i>dwRop</i> // raster operation code


</div>
<span class='text_page_counter'>(135)</span><div class='page_container' data-page=135>

<i><b> int </b>nHeightDest</i><b>, // height of destination rectangle </b>


<i><b> HDC </b>hdcSrc</i><b>, // handle to source DC </b>


<i><b> int </b>nXOriginSrc</i><b>, // x-coord of source upper-left corner </b>
<i><b> int </b>nYOriginSrc</i><b>, // y-coord of source upper-left corner </b>
<i><b> int </b>nWidthSrc</i><b>, // width of source rectangle </b>


<i><b> int </b>nHeightSrc</i><b>, // height of source rectangle </b>
<i><b> DWORD </b>dwRop</i> // raster operation code


<b>); </b>


8.5.3 Một số ví dụ sử dụng bitmap


Ví dụ 1: Chương trình ShowBitmap



Chương trình ShowBitmap cho phép hiển thị một ảnh bitmap trên cửa sổ client. Bitmap được


được nhúng vào tập tin tài nguyên. Hàm LoadBitmap nạp bitmap vào bộ nhớ, hàm


DrawBitmap sử dụng BitBlt để chuyển các bit từ một ngữ cảnh thiết bị loại bộ nhớ ra cửa sổ


client.



Chương trình gồm các tập tin tài nguyên:



Campanula.BMP CampanulaSM.BMP
ShowBitmap.ICO SMALL.ICO


Và các tập tin chương trình nguồn:



</div>
<span class='text_page_counter'>(136)</span><div class='page_container' data-page=136>

Hình 8-7 Cửa sổ client của chương trình


Bản thân một tài nguyên bitmap cũng có thể được dùng trong hộp hội thoại




Hình 8-8 Hộp hội thoại của chương trình


Sau đây là trích đoạn chương trình nguồn


Trích đoạn Resource.rc



#include "resource.h"


/////////////////////////////////////////////////////////////////////////////
//


// Icon
//


// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.


IDI_SHOWBITMAP ICON DISCARDABLE "ShowBitmap.ICO"
IDI_SMALL ICON DISCARDABLE "SMALL.ICO"


/////////////////////////////////////////////////////////////////////////////
//


</div>
<span class='text_page_counter'>(137)</span><div class='page_container' data-page=137>

/////////////////////////////////////////////////////////////////////////////
//


// Bitmap
//


IDB_FLOWER BITMAP DISCARDABLE "Campanula.BMP"
IDB_FLOWER_SM BITMAP DISCARDABLE "CampanulaSM.BMP"



Trích đoạn ShowBitmap.cpp



// ShowBitmap.cpp : Defines the entry point for the application.
//


#include "stdafx.h"
#include "resource.h"
// ...


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
//...


// DrawBitmap


void DrawBitmap(HDC hdc, HBITMAP hBitmap, int xStart, int yStart)
{


BITMAP bmp;
HDC hdcMem;


hdcMem = CreateCompatibleDC(hdc);
SelectObject(hdcMem, hBitmap);


GetObject(hBitmap, sizeof(bmp), (LPVOID) &bmp);


BitBlt(hdc, xStart, yStart, bmp.bmWidth, bmp.bmHeight, hdcMem, 0, 0,
SRCCOPY);



DeleteDC(hdcMem);
}


//


</div>
<span class='text_page_counter'>(138)</span><div class='page_container' data-page=138>

{


case WM_CREATE:


hBitmap = LoadBitmap(hInst, (LPCTSTR)IDB_FLOWER);
BITMAP bmp;


GetObject(hBitmap, sizeof(bmp), (LPVOID) &bmp);
sizeDoc.cx = bmp.bmWidth;


sizeDoc.cy = bmp.bmHeight;
RECT rtWnd, rtClient;


GetWindowRect(hWnd, &rtWnd);
GetClientRect(hWnd, &rtClient);


MoveWindow(hWnd, 0, 0,
sizeDoc.cx+rtWnd.right-rtWnd.left-rtClient.right,


sizeDoc.cy+rtWnd.bottom-rtWnd.top-rtClient.bottom, TRUE);
break;


case WM_COMMAND:


wmId = LOWORD(wParam);


wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)


{


case IDM_ABOUT:


DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
break;


case IDM_EXIT:


DestroyWindow(hWnd);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


break;
case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
DrawBitmap(hdc, hBitmap, 0, 0);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);


break;
default:


</div>
<span class='text_page_counter'>(139)</span><div class='page_container' data-page=139>

Chương trình gồm các tập tin tài nguyên:



CampanulaSM.BMP ShowBitmap.ICO SMALL.ICO


Và các tập tin chương trình nguồn:



StdAfx.h StdAfx.cpp
resource.h ShowBitmap.rc
ShowBitmap.h ShowBitmap.cpp


Hình 8-9 Cửa sổ client của chương trình


Sau đây là trích đoạn tập tin ShowBitmap.cpp



</div>
<span class='text_page_counter'>(140)</span><div class='page_container' data-page=140>

HDC hdcMem;


hdcMem = CreateCompatibleDC(hdc);
SelectObject(hdcMem, hBitmap);


GetObject(hBitmap, sizeof(bmp), (LPVOID) &bmp);


BitBlt(hdc, xStart, yStart, bmp.bmWidth, bmp.bmHeight, hdcMem, 0, 0,
SRCCOPY);


DeleteDC(hdcMem);
}



//


// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//


// PURPOSE: Processes messages for the main window.
//


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{


int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;


TCHAR szHello[MAX_LOADSTRING];
static SIZE sizeBmp;


static HBITMAP hBitmap;
static int nGap = 1;


LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
switch (message)


{


case WM_CREATE:



hBitmap = LoadBitmap(hInst, (LPCTSTR)IDB_FLOWER_SM);
BITMAP bmp;


GetObject(hBitmap, sizeof(bmp), (LPVOID) &bmp);
sizeBmp.cx = bmp.bmWidth;


sizeBmp.cy = bmp.bmHeight;
break;


case WM_SIZE:


</div>
<span class='text_page_counter'>(141)</span><div class='page_container' data-page=141>

break;
default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


break;
case WM_PAINT:


RECT rt;


int i, nx, ny;
SIZE size;


hdc = BeginPaint(hWnd, &ps);
DrawBitmap(hdc, hBitmap, 0, 0);
GetClientRect(hWnd, &rt);


size = sizeBmp;


size.cx += nGap;
size.cy += nGap;


nx = (rt.right-1)/size.cx+1;
ny = (rt.bottom-1)/size.cy+1;
for (i = 1; i < nx; i++)


BitBlt(hdc, size.cx*i, 0, sizeBmp.cx, sizeBmp.cy, hdc, 0, 0,
SRCCOPY);


for (i = 1; i < ny; i++)


BitBlt(hdc, 0, size.cy*i, rt.right, sizeBmp.cy, hdc, 0, 0,
SRCCOPY);


EndPaint(hWnd, &ps);
break;


case WM_DESTROY:


PostQuitMessage(0);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


return 0;
}



</div>
<span class='text_page_counter'>(142)</span><div class='page_container' data-page=142>

<b>9. </b>

Custom Menu



Sử dụng menu ở mức đơn giản, ta có thể dễ dàng ánh xạ một thao tác chọn chức năng trên


menu với một thao tác thực hiện thực sự. Bên cạnh đó Windows cịn cho phép người sử dụng


tạo các menu phong phú nhờ hỗ trợ bitmap cho các mục menu cung như cung cấp khả năng


cho người sử dụng tự vẽ menu.



<b>9.1 </b>

Các hàm thao tác trên menu



Windows cung cấp một loạt hàm cho phép thao tác trên menu. Bảng dưới đây liệt kê các hàm


thông dụng hỗ trợ lập trình trên menu.



Function

Description



AppendMenu

Thêm một mục mới vào menu hay menu con.



CheckMenuItem

Đánh dấu hoặc xoá dấu của một mục menu.



CheckMenuRadioItem

Đánh dấu một mục menu và làm cho nó trở thành một



nút radio.



CreateMenu

Tạo menu.



CreatePopupMenu

Tạo popup menu.



DeleteMenu

Xố một menu Item.



DestroyMenu

Xoá hẳn một menu.




</div>
<span class='text_page_counter'>(143)</span><div class='page_container' data-page=143>

GetMenuItemCount

Lấy số mục trong một menu.



GetMenuItemID

Lấy số nhận diện của một menu item.



GetMenuItemInfo

Lấy thông tin về một menu.



GetMenuItemRect

Lấy kích thước hình chữ nhật bao menu.



GetMenuState

Lấy giá trị các cờ trạng thái của một menu item.



GetMenuString

Lấy nội dung văn bản của một menu item.



GetSubMenu

Lấy handle đến menu con hoặc drop-down menu được



kích hoạt bởi một menu item.



GetSystemMenu

Sao chép hoặc thay đổi menu hệ thống.



InsertMenu

Cheøn menu item vào một menu.



InsertMenuItem

Chèn menu item vào một menu.



IsMenu

Xác định một handle có phải là menu handle không.



LoadMenu

Nạp tài nguyên resource.



ModifyMenu

Thay đổi menu item.



RemoveMenu

Xoá một menu item.




SetMenu

Gắn menu với một cửa sổ.



SetMenuDefaultItem

Định menu item mặc nhiên cho một menu.



SetMenuInfo

Định lại thông tin cho một menu.



SetMenuItemBitmaps

Liên kết một bitmap với một menu item.



SetMenuItemInfo

Thay đổi thông tin của một menu item.



</div>
<span class='text_page_counter'>(144)</span><div class='page_container' data-page=144>

<b>HMENU LoadMenu( </b>


<i><b> HINSTANCE hInstance, // handle to application instance </b></i>


<i><b> LPCTSTR lpMenuName // menu name string or menu-resource </b></i>


// identifier


<b>); </b>


<b>SetMenu </b>



Hàm SetMenu định menu cho ứng dụng.


<b>BOOL SetMenu( </b>


<i><b> HWND hWnd, // handle to window </b></i>
<i><b> HMENU hMenu // handle to menu </b></i>
<b>); </b>



<b>DrawMenuBar </b>



Hàm DrawMenuBar vẽ lại thanh menu của ứng dụng.


<b>BOOL DrawMenuBar( </b>


<i><b> HWND hWnd // handle to window with menu bar to redraw </b></i>
<b>); </b>


<b>AppendMenu </b>



Hàm AppendMenu thêm một menu vào cuối của một thanh menu, menu drop-down menu,


menu con, hay menu shortcut. Ta có thể dùng hàm này để qui định nội dung, dáng vẻ và hoạt


động của mục menu.



<b>BOOL AppendMenu( </b>


<i><b> HMENU </b>hMenu</i><b>, // handle to menu </b>
<i><b> UINT </b>uFlags</i><b>, // menu-item options </b>


<i><b> UINT_PTR </b>uIDNewItem</i><b>, // identifier, menu, or submenu </b>
<i><b> LPCTSTR </b>lpNewItem // menu-item content </i>


<b>); </b>


lpNewItem



Con trỏ chỉ đến nội dung, nội dung có ý nghĩa thay đổi tuỳ theo giá trị của uFlags có


bao hàm các giá trị MF_BITMAP, MF_OWNERDRAW, hay MF_STRING.



Value

Description




</div>
<span class='text_page_counter'>(145)</span><div class='page_container' data-page=145>

Value

Description



MF_BITMAP

Dùng bitmap làm menu item. lpNewItem chứa một handle đến


bitmap.



MF_CHECKED

Đặt một dấu check bên cạnh menu item. Nếu ứng dụng cung


cấp một bitmaps check-mark, cờ này sẽ làm hiển thị một dấu


check bitmap bên cạnh menu item



MF_DISABLED

Làm menu không hiệu lực, nhưng không làm mờ.


MF_ENABLED

Làm menu có hiệu lực.



MF_GRAYED

Làm menu mờ và không hiệu lực.



MF_MENUBARBREAK Đối với thanh menu, tương đương cờ MF_MENUBREAK Đối


với các trường hợp khác, dùng một được đường gạch đứng để


ngăn cách các cột.



MF_MENUBREAK

Mục menu mới được đặt vào hàng mới (đối với menu bar) or


hoặc cột mới (đối với drop-down menu, submenu, hoặc


shortcut menu), khơng có đường gạch đứng ngăn cách.



MF_OWNERDRAW

Người sử dụng tự vẽ menu.


MF_POPUP

Menu thuộc loại popup.



MF_SEPARATOR

Vẽ một đường ngang để ngăn cách.


MF_STRING

Menu có nội dung là một chuỗi.


MF_UNCHECKED




<b>InsertMenuItem </b>



Hàm InsertMenuItem chèn một phần tử mới vào một menu.


<b>BOOL InsertMenuItem( </b>


</div>
<span class='text_page_counter'>(146)</span><div class='page_container' data-page=146>

<b>9.2 Thay đổi menu trong chương trình </b>



Ta có thể thay đổi menu trong chương trình bằng cách nạp menu mới bằng hàm LoadMenu


và chọn menu đó bằng SetMenu. Chương trình sau cho phép thay đổi menu trong chương


trình, người sử dụng có thể chọn menu tiếng Anh hoặc tiếng Việt không dấu. Việc này được


thực hiện bằng cách nạp menu khi chương trình đang thực thi.



</div>
<span class='text_page_counter'>(147)</span><div class='page_container' data-page=147>

#define IDD_ABOUTBOX 103
#define IDS_APP_TITLE 103
#define IDM_ABOUT 104
#define IDM_EXIT 105
#define IDS_HELLO 106
#define IDI_HELLO 107
#define IDS_HELLOVN 107
#define IDI_SMALL 108
#define IDC_HELLO 109
#define IDC_HELLOVN 110
#define IDR_MAINFRAME 128
#define ID_FILE_NEW 32771
#define ID_FILE_OPEN 32772
#define ID_FILE_SAVE 32773
#define ID_FILE_PRINT 32774
#define ID_EDIT_COPY 32775
#define ID_EDIT_CUT 32776
#define ID_EDIT_PASTE 32777


#define ID_OPTION_VIETNAMESE 32778
#define ID_LUACHON_TIENGANH 32779
#define IDC_STATIC -1
// Next default values for new objects
//


#ifdef APSTUDIO_INVOKED


#ifndef APSTUDIO_READONLY_SYMBOLS


#define _APS_NEXT_RESOURCE_VALUE 132
#define _APS_NEXT_COMMAND_VALUE 32780
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 110
#endif


#endif


<b>// Hello.rc </b>


#include "resource.h"
#include "windows.h"


/////////////////////////////////////////////////////////////////////////////
//


</div>
<span class='text_page_counter'>(148)</span><div class='page_container' data-page=148>

POPUP "&File"
BEGIN


MENUITEM "&New\tCtrl+N", ID_FILE_NEW


MENUITEM "&Open\tCtrl+O", ID_FILE_OPEN
MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE
MENUITEM SEPARATOR


MENUITEM "&Print", ID_FILE_PRINT
MENUITEM "E&xit", IDM_EXIT
END


POPUP "&Edit"
BEGIN


MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY
MENUITEM "Cut\tCtrl+X", ID_EDIT_CUT
MENUITEM "&Paste\tCtrl+V", ID_EDIT_PASTE
END


POPUP "&Option"
BEGIN


MENUITEM "&Vietnamese\tAlt+V", ID_OPTION_VIETNAMESE
END


POPUP "&Help"
BEGIN


MENUITEM "&About ...", IDM_ABOUT
END


END



IDC_HELLOVN MENU DISCARDABLE
BEGIN


POPUP "&Ho So"
BEGIN


MENUITEM "Ho so &Moi\tCtrl+N", ID_FILE_NEW
MENUITEM "Mo &Ho so\tCtrol+O", ID_FILE_OPEN
MENUITEM "&Luu Ho so\tCtrl+S", ID_FILE_SAVE
MENUITEM SEPARATOR


MENUITEM "&In Ho so", ID_FILE_PRINT
MENUITEM "&Thoat", IDM_EXIT
END


POPUP "&Soan Thao"
BEGIN


</div>
<span class='text_page_counter'>(149)</span><div class='page_container' data-page=149>

//


IDC_HELLO ACCELERATORS MOVEABLE PURE
BEGIN


"/", IDM_ABOUT, ASCII, ALT, NOINVERT
"?", IDM_ABOUT, ASCII, ALT, NOINVERT
"A", ID_LUACHON_TIENGANH, VIRTKEY, ALT, NOINVERT
"C", ID_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT
"N", ID_FILE_NEW, VIRTKEY, CONTROL, NOINVERT
"O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT
"S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT


"V", ID_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT
"V", ID_OPTION_VIETNAMESE, VIRTKEY, ALT, NOINVERT
"X", ID_EDIT_CUT, VIRTKEY, CONTROL, NOINVERT
END


/////////////////////////////////////////////////////////////////////////////
//


// String Table
//


STRINGTABLE DISCARDABLE
BEGIN


IDS_APP_TITLE "Hello"


IDS_HELLO "Hello World! Press Alt + V to change to Vietnamese"
IDS_HELLOVN "Xin chao! Bam Alt + A de chuyen sang tieng Anh"
IDC_HELLO "HELLO"


IDC_HELLOVN "HELLOVN"
END


// Hello.cpp : Defines the entry point for the application.
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{



int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;


TCHAR szHello[MAX_LOADSTRING];
HMENU hMenu;


</div>
<span class='text_page_counter'>(150)</span><div class='page_container' data-page=150>

hMenu = LoadMenu(hInst, (LPCSTR)IDC_HELLOVN);
SetMenu(hWnd, hMenu);


DrawMenuBar(hWnd);
bVietNamese = TRUE;


InvalidateRect(hWnd, NULL, TRUE);
break;


case ID_LUACHON_TIENGANH:


hMenu = LoadMenu(hInst, (LPCSTR)IDC_HELLO);
SetMenu(hWnd, hMenu);


DrawMenuBar(hWnd);
bVietNamese = FALSE;


InvalidateRect(hWnd, NULL, TRUE);
break;


case ID_FILE_NEW:



MessageBox(hWnd, "File New", "Message", MB_OK);
break;


case ID_FILE_OPEN:


MessageBox(hWnd, "File Open", "Message", MB_OK);
break;


case ID_FILE_SAVE:


MessageBox(hWnd, "File Save", "Message", MB_OK);
break;


case ID_EDIT_COPY:


MessageBox(hWnd, "Edit Copy", "Message", MB_OK);
break;


case ID_EDIT_CUT:


MessageBox(hWnd, "Edit Cut", "Message", MB_OK);
break;


case ID_EDIT_PASTE:


MessageBox(hWnd, "Edit Paste", "Message", MB_OK);
break;


case IDM_EXIT:



DestroyWindow(hWnd);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


break;
case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
RECT rt;


</div>
<span class='text_page_counter'>(151)</span><div class='page_container' data-page=151>

<b>9.3 Menu do người sử dụng tự vẽ (Owner Draw Menu) </b>



Người sử dụng có thể tạo menu bằng cách dùng hàm CreateMenu, sau đó sử dụng hàm


AppendMenu để tạo nên các thành phần của menu.



Để có thể tạo được menu tự vẽ tham số uFlag trong hàm AppendMenu có bao gồm thuộc tính


MF_OWNERDRAW.



<b>BOOL AppendMenu( </b>


<i><b> HMENU hMenu, // handle to menu to be changed </b></i>
<i><b> UINT uFlags, // menu-item flags </b></i>


<i><b> UINT uIDNewItem, // menu-item identifier or handle to drop-down menu or </b></i>


submenu



<i><b> LPCTSTR lpNewItem // menu-item content </b></i>
<b>); </b>


Trình tự tạo một mục menu tự vẽ như sau:



<b>1) Gọi hàm AppendMenu với tham số uFlags bao gồm giá trị MF_OWNERDRAW. </b>



MF_OWNERDRAW

Qui định rằng mục menu sẽ được người sử dụng tự vẽ. Trước khi


menu được hiển thị lần đầu tiên, cửa sổ sở hữu menu sẽ nhận được


thông điệp

WM_MEASUREITEM

để lấy chiều dài và chiều rộng


của mục menu. Thơng điệp

WM_DRAWITEM

sau đó được gởi cho


thủ tục cửa sổ mỗi khi dáng vẻ của menu cần được cập nhật.



2) Xử lý thông điệp

WM_MEASUREITEM

.



Thông điệp

WM_MEASUREITEM

sử dụng cấu trúc

MEASUREITEMSTRUCT
typedef struct tagMEASUREITEMSTRUCT { // mis


UINT CtlType; // type of control


UINT CtlID; // combo box, list box, or button identifier
UINT itemID; // menu item, variable-height list box,
// or combo box identifier


UINT itemWidth; // width of menu item, in pixels


UINT itemHeight; // height of single item in list box menu,
// in pixels



DWORD itemData; // application-defined 32-bit value
} MEASUREITEMSTRUCT;


Thông điệp

WM_MEASUREITEM

có hai tham số đi kèm wParam là giá trị nhận dạng của



</div>
<span class='text_page_counter'>(152)</span><div class='page_container' data-page=152>

Thông điệp

WM_DRAWITEM

được gởi cho cửa sổ chứa menu mỗi khi một mục menu


với thuộc tính MF_OWNERDRAW cần được cập nhật.



WM_DRAWITEM


idCtl = (UINT) wParam; // control identifier
lpdis = (LPDRAWITEMSTRUCT) lParam; // item-drawing information


Tham số lParam cho biết các thông tin mà cửa sổ chứa menu cần phải nắm để có thể


vẽ được mục menu này. Các thơng tin đó được lưu trong cấu trúc

DRAWITEMSTRUCT

.


typedef struct tagDRAWITEMSTRUCT { // dis


UINT CtlType;
UINT CtlID;
UINT itemID;
UINT itemAction;
UINT itemState;
HWND hwndItem;
HDC hDC;
RECT rcItem;
DWORD itemData;
} DRAWITEMSTRUCT;


Người sử dụng xử lý thông điệp kể trên để vẽ menu vào ngữ cảnh thiết bị, thành phần


hDC của cấu trúc trên.




</div>
<span class='text_page_counter'>(153)</span><div class='page_container' data-page=153></div>
<span class='text_page_counter'>(154)</span><div class='page_container' data-page=154></div>
<span class='text_page_counter'>(155)</span><div class='page_container' data-page=155>

help.bmp

hinhve.bmp



file.bmp

brush.bmp



small.ico

VnMenu.ico



Và các tập tin chương trình nguồn



resource.h

StdAfx.h



VnMenu.h

StdAfx.cpp



VnMenu.rc

VnMenu.cpp



Trích đoạn chương trình nguồn resource.h



// resource.h


// Used by VnMenu.rc
//


</div>
<span class='text_page_counter'>(156)</span><div class='page_container' data-page=156>

#define ID_BRUSH_CROSS 32792
#define ID_BRUSH_HORIZONTAL 32793
#define ID_BRUSH_VERTICAL 32794
#define ID_FIGURE_ELLIPSE 32795
#define ID_FIGURE_RECTANGLE 32796
#define ID_FIGURE_ROUNDRECT 32797
#define ID_FIGURE_CHORD 32798
#define ID_FIGURE_PIE 32799



Trích đoạn chương trình nguồn vnmenu.cpp



// VnMenu.cpp : Defines the entry point for the application.
//


#include "stdafx.h"
#include "resource.h"


HFONT MyCreateFont (HDC hdc, char * szFaceName, int nPointSize,
BOOL bBold = FALSE, BOOL bItalic = FALSE)
{


HFONT hFont ;
LOGFONT lf ;


memset(&lf, 0, sizeof(lf));


lf.lfHeight = -MulDiv(nPointSize, GetDeviceCaps(hdc,
LOGPIXELSY), 72);


lf.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
lf.lfItalic = bItalic;


strcpy (lf.lfFaceName, szFaceName) ;
hFont = CreateFontIndirect (&lf) ;
return hFont ;


}
//



// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//


// PURPOSE: Processes messages for the main window.
//


</div>
<span class='text_page_counter'>(157)</span><div class='page_container' data-page=157>

struct M_POPUP
{


char *name;
UINT bitmapId;
int nItems;


M_ITEM aItems[MAX_ITEMS];
};


static M_POPUP aVnMenu[] =
{


"&Hoà sô", IDB_FILE, 5,
{


{"Hồ sơ mới", ID_FILE_NEW, MF_OWNERDRAW},
{"Mở hồ sơ", ID_FILE_OPEN, MF_OWNERDRAW},
{"Lưu hồ sơ", ID_FILE_SAVE, MF_OWNERDRAW},
{"", 0, MF_SEPARATOR},


{"In", ID_FILE_PRINT, MF_OWNERDRAW},
{"Thoát", IDM_EXIT, MF_OWNERDRAW},


},


"&Soạn Thảo", IDB_EDIT, 3,
{


{"Sao chép", ID_EDIT_COPY, MF_OWNERDRAW},
{"Cắt", ID_EDIT_CUT, MF_OWNERDRAW},


{"Daùn", ID_EDIT_PASTE, MF_OWNERDRAW},
},


"Đổi Menu Font", IDB_FONT, 4,
{


{"VNI-Times", ID_FONT_VNI_TIMES, MF_OWNERDRAW},
{"VNI-Courier", ID_FONT_VNI_COURIER, MF_OWNERDRAW},
{"VNI-Aptima", ID_FONT_VNI_APTIMA, MF_OWNERDRAW},
{"VNI-Cooper", ID_FONT_VNI_COOPER, MF_OWNERDRAW}
},


"&Hình", IDB_FIGURE, 5,
{


{"Hình Bầu dục", ID_FIGURE_ELLIPSE, MF_OWNERDRAW},
{"Hình Chữ Nhật", ID_FIGURE_RECTANGLE, MF_OWNERDRAW},
{"Hình chữ nhật trịn", ID_FIGURE_ROUNDRECT, MF_OWNERDRAW},
{"Hình Vành khun", ID_FIGURE_CHORD, MF_OWNERDRAW},


</div>
<span class='text_page_counter'>(158)</span><div class='page_container' data-page=158>

{



{"Thoâng tin", IDM_ABOUT, MF_OWNERDRAW},
}


};


struct FONTMENUDATA
{


UINT id;


TCHAR szFaceName[32];
int nPointSize;


BOOL bBold, bItalics;
};


static FONTMENUDATA aFontData[] =
{


{ID_FONT_VNI_TIMES, "VNI-Times", 12},


{ID_FONT_VNI_COURIER, "VNI-Couri", 12, TRUE},
{ID_FONT_VNI_APTIMA, "VNI-Aptima", 12},


{ID_FONT_VNI_COOPER, "VNI-Cooper", 12, TRUE, TRUE},
};


#define dim(a) (sizeof(a)/sizeof(a[0]))
const int nPopups = dim(aVnMenu);



const int nFonts = dim(aFontData);


HMENU AddMenuPopup(HMENU hMenu, M_POPUP mp)
{


HMENU hMenuPopup = CreatePopupMenu();


AppendMenu (hMenu, MF_POPUP|MF_BITMAP, (UINT)hMenuPopup,
(LPSTR)LoadBitmap (hInst, (LPSTR)mp.bitmapId));


for (int i = 0; i < mp.nItems; i++)


AppendMenu (hMenuPopup, mp.aItems[i].attr, mp.aItems[i].id,
mp.aItems[i].name);


return hMenuPopup;
}


M_ITEM *SearchItem(int n, M_POPUP *a, UINT id)
{


</div>
<span class='text_page_counter'>(159)</span><div class='page_container' data-page=159>

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{


int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;



TCHAR szName[][MAX_LOADSTRING] = {"Hình Ellipse", "Hình Chữ Nhật", "Hình
Chữ nhật trịn", "Chord", "Pie"};


static HMENU hMenuVn;
static HFONT ahf[nFonts];


static int cxCheck, cyCheck, nBrush;


static WORD nBrushIdx, nFigIdx, nCurFont = 1;
static COLORREF crHighlight, crHighlightText;
static HBRUSH ahbr[7];


M_ITEM *pItem;
switch (message)
{


case WM_CREATE:
int i;


LOGFONT lf;


crHighlight = GetSysColor(COLOR_HIGHLIGHT);


crHighlightText = GetSysColor(COLOR_HIGHLIGHTTEXT);
cxCheck = GetSystemMetrics(SM_CXMENUCHECK);


cyCheck = GetSystemMetrics(SM_CYMENUCHECK);
ZeroMemory(&lf, sizeof(lf));


hdc = GetDC(hWnd);



for (i = 0; i < nFonts; i++)


ahf[i] = MyCreateFont(hdc, aFontData[i].szFaceName,
aFontData[i].nPointSize, aFontData[i].bBold,
aFontData[i].bItalics);


ahbr[0] = CreateSolidBrush(RGB(0,0, 255));


ahbr[1] = CreateHatchBrush(HS_BDIAGONAL, RGB(0,0,255));
ahbr[2] = CreateHatchBrush(HS_FDIAGONAL, RGB(0,0,255));
ahbr[3] = CreateHatchBrush(HS_DIAGCROSS, RGB(0,0,255));
ahbr[4] = CreateHatchBrush(HS_CROSS, RGB(0,0,255));
ahbr[5] = CreateHatchBrush(HS_HORIZONTAL, RGB(0,0,255));
ahbr[6] = CreateHatchBrush(HS_VERTICAL, RGB(0,0,255));
nBrush = 7;


</div>
<span class='text_page_counter'>(160)</span><div class='page_container' data-page=160>

lpmi = (LPMEASUREITEMSTRUCT)lParam;
HFONT hfont, hfontOld;


// switch(lpmi->itemID)


pItem = SearchItem(nPopups, aVnMenu, lpmi->itemID);
if (pItem)


{


hdc = GetDC (hWnd);


for (i = 0; i < dim(aFontData); i++)


if (lpmi->itemID == aFontData[i].id)


break;


if (i < dim(aFontData))
hfont = ahf[i];
else


hfont = ahf[0];


SelectObject (hdc, hfont);


GetTextExtentPoint32(hdc, pItem->name, lstrlen(pItem->name),
&sizeText);


ReleaseDC (hWnd, hdc);


lpmi->itemWidth = sizeText.cx + cxCheck;
lpmi->itemHeight = max(sizeText.cy, cyCheck);
}


break;


case WM_DRAWITEM:


LPDRAWITEMSTRUCT lpdi;


lpdi = (LPDRAWITEMSTRUCT)lParam;
COLORREF crTextOld, crBackOld;
BOOL bSwap;



int xText, yText;
char *szText;
HBRUSH hbrOld;


pItem = SearchItem(nPopups, aVnMenu, lpdi->itemID);
if (pItem)


{


for (i = 0; i < dim(aFontData); i++)
if (lpdi->itemID == aFontData[i].id)


break;


</div>
<span class='text_page_counter'>(161)</span><div class='page_container' data-page=161>

cxCheck = GetSystemMetrics(SM_CXMENUCHECK);
xText = lpdi -> rcItem.left + cxCheck;
yText = lpdi -> rcItem.top;


ExtTextOut(lpdi->hDC, xText, yText, ETO_OPAQUE, &lpdi->rcItem,
szText, strlen(szText), NULL);


if (lpdi->itemID - ID_FONT_VNI_TIMES == nCurFont ||
lpdi->itemID - ID_FIGURE_ELLIPSE == nFigIdx ||
lpdi->itemID - ID_BRUSH_SOLID == nBrushIdx)
{


hbrOld = (HBRUSH)SelectObject(lpdi->hDC, ahbr[0]);
Circle>hDC, lpdi->rcItem.left+cxCheck/2,



(lpdi->rcItem.top + lpdi->rcItem.bottom)/2, 5);
SelectObject(lpdi->hDC, hbrOld);


}


SelectObject (lpdi->hDC, hfontOld);
if (bSwap)
{
SetTextColor(lpdi->hDC, crTextOld);
SetBkColor(lpdi->hDC, crBackOld);
}
}
break;
case WM_COMMAND:


wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId)
{
case ID_FONT_VNI_TIMES:
case ID_FONT_VNI_COURIER:
case ID_FONT_VNI_APTIMA:
case ID_FONT_VNI_COOPER:


</div>
<span class='text_page_counter'>(162)</span><div class='page_container' data-page=162>

break;


case IDM_ABOUT:


DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
break;



case IDM_EXIT:


DestroyWindow(hWnd);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


break;
case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
RECT rt;


GetClientRect(hWnd, &rt);


SelectObject (hdc, ahf[nCurFont]);


DrawText(hdc, szName[nFigIdx], strlen(szName[nFigIdx]), &rt,
DT_CENTER);


SelectObject(hdc, ahbr[nBrushIdx]);
switch(ID_FIGURE_ELLIPSE + nFigIdx)
{


case ID_FIGURE_ELLIPSE:



Ellipse(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4,
3*rt.bottom/4);


break;


case ID_FIGURE_RECTANGLE:


Rectangle(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4,
3*rt.bottom/4);


break;


case ID_FIGURE_ROUNDRECT:


RoundRect(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4,
3*rt.bottom/4, 30, 30);


break;


case ID_FIGURE_PIE:


Pie(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4, 3*rt.bottom/4,
rt.right, rt.bottom/2, 0, 0);


break;


case ID_FIGURE_CHORD:


Chord(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4,



</div>
<span class='text_page_counter'>(163)</span><div class='page_container' data-page=163>

}


Ví dụ 2. Chương trình VnMenu V2 tạo menu có tiếng Việt bằng cách tự vẽ các mục menu


tương tự như ví dụ trên nhưng có thêm chức năng cho phép lựa chọn giữa menu tiếng Việt và


menu tiếng Anh.



</div>
<span class='text_page_counter'>(164)</span><div class='page_container' data-page=164>

Hình 9-8 VnMenu với lựa chọn tiếng Anh


Ta có thể chuyển qua lại giữa tiếng Anh và tiếng Việt nhờ duy trì hai menu riêng rẽ. Menu


tiếng Anh được định nghĩa từ tập tin tài nguyên, menu tiếng Việt có được bằng cách tự vẽ các


mục trên menu.



Trích đoạn chương trình nguồn sau đây minh hoạ việc chuyển đổi qua lại giữa hai menu.



// VnMenu.cpp : Defines the entry point for the application.
//


#include "stdafx.h"
#include "resource.h"


</div>
<span class='text_page_counter'>(165)</span><div class='page_container' data-page=165>

};


static M_POPUP aVnMenu[] =
{


"&Hồ sơ", IDB_FILE, 5,
{


{"Hồ sơ mới", ID_FILE_NEW, MF_OWNERDRAW},
{"Mở hồ sơ", ID_FILE_OPEN, MF_OWNERDRAW},


{"Lưu hồ sơ", ID_FILE_SAVE, MF_OWNERDRAW},
{"", 0, MF_SEPARATOR},


{"In", ID_FILE_PRINT, MF_OWNERDRAW},
{"Thoát", IDM_EXIT, MF_OWNERDRAW},
},


"&Soạn Thảo", IDB_EDIT, 3,
{


{"Sao cheùp", ID_EDIT_COPY, MF_OWNERDRAW},
{"Cắt", ID_EDIT_CUT, MF_OWNERDRAW},


{"Dán", ID_EDIT_PASTE, MF_OWNERDRAW},
},


"Đổi Menu Font", IDB_FONT, 4,
{


{"VNI-Times", ID_FONT_VNI_TIMES, MF_OWNERDRAW},
{"VNI-Courier", ID_FONT_VNI_COURIER, MF_OWNERDRAW},
{"VNI-Aptima", ID_FONT_VNI_APTIMA, MF_OWNERDRAW},
{"VNI-Cooper", ID_FONT_VNI_COOPER, MF_OWNERDRAW}
},


"&Hình", IDB_FIGURE, 5,
{


{"Hình Chữ Nhật", ID_FIGURE_RECTANGLE, MF_OWNERDRAW},
{"Hình Bầu dục", ID_FIGURE_ELLIPSE, MF_OWNERDRAW},



{"Hình chữ nhật trịn", ID_FIGURE_ROUNDRECT, MF_OWNERDRAW},
{"Hình Vành khun", ID_FIGURE_CHORD, MF_OWNERDRAW},


{"Hình bánh", ID_FIGURE_PIE, MF_OWNERDRAW},
},


"&Chổi vẽ", IDB_BRUSH, 7,
{


</div>
<span class='text_page_counter'>(166)</span><div class='page_container' data-page=166>

{"Thoâng tin", IDM_ABOUT, MF_OWNERDRAW},
}


};


struct FONTMENUDATA
{


UINT id;


TCHAR szFaceName[32];
int nPointSize;


BOOL bBold, bItalics;
};


static FONTMENUDATA aFontData[] =
{


{ID_FONT_VNI_TIMES, "VNI-Times", 12},



{ID_FONT_VNI_COURIER, "VNI-Couri", 12, TRUE},
{ID_FONT_VNI_APTIMA, "VNI-Aptima", 12},


{ID_FONT_VNI_COOPER, "VNI-Cooper", 12, TRUE, TRUE},
};


#define dim(a) (sizeof(a)/sizeof(a[0]))
const int nPopups = dim(aVnMenu);


const int nFonts = dim(aFontData);


HMENU AddMenuPopup(HMENU hMenu, M_POPUP mp)
{


HMENU hMenuPopup = CreatePopupMenu();


AppendMenu (hMenu, MF_POPUP|MF_BITMAP, (UINT)hMenuPopup,
(LPSTR)LoadBitmap (hInst, (LPSTR)mp.bitmapId));


for (int i = 0; i < mp.nItems; i++)


AppendMenu (hMenuPopup, mp.aItems[i].attr, mp.aItems[i].id,
mp.aItems[i].name);


return hMenuPopup;
}


M_ITEM *SearchItem(int n, M_POPUP *a, UINT id)
{



</div>
<span class='text_page_counter'>(167)</span><div class='page_container' data-page=167>

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{


int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;


TCHAR* szName[][5] =
{


{"Ellipse", "Rectangle", "RoundRect", "Chord", "Pie"},


{"Hình Bầu dục", "Hình Chữ Nhật", "Hình Chữ nhật trịn", "Hình
vành khun", "Hình chiếc bánh"}


};


static HMENU hMenu, hMenuVn;
static HFONT ahf[nFonts];


static int cxCheck, cyCheck, nBrush;


static WORD nBrushIdx, nFigIdx, nCurFont = 0;
static COLORREF crHighlight, crHighlightText;
static HBRUSH ahbr[7];


static bool bVietNamese = true;


M_ITEM *pItem;
switch (message)
{
case WM_CREATE:
int i;
LOGFONT lf;


crHighlight = GetSysColor(COLOR_HIGHLIGHT);


crHighlightText = GetSysColor(COLOR_HIGHLIGHTTEXT);
cxCheck = GetSystemMetrics(SM_CXMENUCHECK);


cyCheck = GetSystemMetrics(SM_CYMENUCHECK);
ZeroMemory(&lf, sizeof(lf));


hdc = GetDC(hWnd);


for (i = 0; i < nFonts; i++)


ahf[i] = MyCreateFont(hdc, aFontData[i].szFaceName,
aFontData[i].nPointSize, aFontData[i].bBold,
aFontData[i].bItalics);


ahbr[0] = CreateSolidBrush(RGB(0,0, 255));


</div>
<span class='text_page_counter'>(168)</span><div class='page_container' data-page=168>

hMenuPopup = CreatePopupMenu();
SetMenu(hWnd, hMenuVn);


break;



case WM_MEASUREITEM:
SIZE sizeText;


LPMEASUREITEMSTRUCT lpmi;


lpmi = (LPMEASUREITEMSTRUCT)lParam;
HFONT hfont, hfontOld;


pItem = SearchItem(nPopups, aVnMenu, lpmi->itemID);
if (pItem)


{


hdc = GetDC (hWnd);


for (i = 0; i < dim(aFontData); i++)
if (lpmi->itemID == aFontData[i].id)


break;


if (i < dim(aFontData))
hfont = ahf[i];
else


hfont = ahf[0];


SelectObject (hdc, hfont);


GetTextExtentPoint32(hdc, pItem->name, lstrlen(pItem->name),
&sizeText);



ReleaseDC (hWnd, hdc);


lpmi->itemWidth = sizeText.cx + cxCheck;
lpmi->itemHeight = max(sizeText.cy, cyCheck);
}


break;


case WM_DRAWITEM:


LPDRAWITEMSTRUCT lpdi;


lpdi = (LPDRAWITEMSTRUCT)lParam;
COLORREF crTextOld, crBackOld;
BOOL bSwap;


int xText, yText;
char *szText;
HBRUSH hbrOld;


</div>
<span class='text_page_counter'>(169)</span><div class='page_container' data-page=169>

crBackOld = SetBkColor (lpdi->hDC, crHighlight);
bSwap = TRUE;


}
else


bSwap = FALSE;


cxCheck = GetSystemMetrics(SM_CXMENUCHECK);


xText = lpdi -> rcItem.left + cxCheck;
yText = lpdi -> rcItem.top;


ExtTextOut(lpdi->hDC, xText, yText, ETO_OPAQUE, &lpdi->rcItem,
szText, strlen(szText), NULL);


if (lpdi->itemID - ID_FONT_VNI_TIMES == nCurFont ||
lpdi->itemID - ID_FIGURE_ELLIPSE == nFigIdx ||
lpdi->itemID - ID_BRUSH_SOLID == nBrushIdx)
{


hbrOld = (HBRUSH)SelectObject(lpdi->hDC, ahbr[0]);
Circle>hDC, lpdi->rcItem.left+cxCheck/2,


(lpdi->rcItem.top + lpdi->rcItem.bottom)/2, 5);
SelectObject(lpdi->hDC, hbrOld);


}


SelectObject (lpdi->hDC, hfontOld);
if (bSwap)


{


SetTextColor(lpdi->hDC, crTextOld);
SetBkColor(lpdi->hDC, crBackOld);
}


}
break;



case WM_COMMAND:


wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId)


{


case ID_FONT_VNI_TIMES:
case ID_FONT_VNI_COURIER:
case ID_FONT_VNI_APTIMA:
case ID_FONT_VNI_COOPER:


CheckMenuItem(hMenu, ID_FONT_VNI_TIMES + nCurFont,
MF_UNCHECKED);


</div>
<span class='text_page_counter'>(170)</span><div class='page_container' data-page=170>

CheckMenuItem(hMenu, ID_BRUSH_SOLID + nBrushIdx,
MF_UNCHECKED);


nBrushIdx = wmId - ID_BRUSH_SOLID;
CheckMenuItem(hMenu, wmId, MF_CHECKED);
InvalidateRect(hWnd, NULL, TRUE);


break;


case ID_FIGURE_ELLIPSE:
case ID_FIGURE_RECTANGLE:
case ID_FIGURE_ROUNDRECT:
case ID_FIGURE_CHORD:


case ID_FIGURE_PIE:


CheckMenuItem(hMenu, ID_FIGURE_ELLIPSE + nFigIdx,
MF_UNCHECKED);


nFigIdx = wmId - ID_FIGURE_ELLIPSE;
CheckMenuItem(hMenu, wmId, MF_CHECKED);
InvalidateRect(hWnd, NULL, TRUE);


break;


case ID_OPTION_ENGLISH:
bVietNamese = false;
SetMenu(hWnd, hMenu);
break;


case ID_OPTION_TIENGVIET:
SetMenu(hWnd, hMenuVn);
bVietNamese = true;
break;


case IDM_ABOUT:


DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
break;


case IDM_EXIT:


DestroyWindow(hWnd);
break;



default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


</div>
<span class='text_page_counter'>(171)</span><div class='page_container' data-page=171>

case ID_FIGURE_RECTANGLE:


Rectangle(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4,
3*rt.bottom/4);


break;


case ID_FIGURE_ROUNDRECT:


RoundRect(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4,
3*rt.bottom/4, 30, 30);


break;


case ID_FIGURE_PIE:


Pie(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4, 3*rt.bottom/4,
rt.right, rt.bottom/2, 0, 0);


break;


case ID_FIGURE_CHORD:


Chord(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4,



3*rt.bottom/4, rt.right, rt.bottom/3, 0, rt.bottom);
break;


}


EndPaint(hWnd, &ps);
break;


case WM_DESTROY:


for (i = 0; i < nBrush; i++)
DeleteObject(ahbr[i]);
PostQuitMessage(0);


break;
default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


return 0;
}


</div>
<span class='text_page_counter'>(172)</span><div class='page_container' data-page=172></div>
<span class='text_page_counter'>(173)</span><div class='page_container' data-page=173>

// VnMenu.cpp : Defines the entry point for the application.
//


// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//



#define MAX_ITEMS 10
struct M_ITEM


{


char *name;
UINT id;
UINT attr;
};


struct M_POPUP
{


char *name;
UINT bitmapId;
int nItems;


M_ITEM aItems[MAX_ITEMS];
};


static M_POPUP aVnMenu[] =
{


"&Hồ sơ", IDB_FILE, 5,
{


{"Hồ sơ mới", ID_FILE_NEW, MF_OWNERDRAW},
{"Mở hồ sơ", ID_FILE_OPEN, MF_OWNERDRAW},
{"Lưu hồ sơ", ID_FILE_SAVE, MF_OWNERDRAW},
{"", 0, MF_SEPARATOR},



{"In", ID_FILE_PRINT, MF_OWNERDRAW},
{"Thoát", IDM_EXIT, MF_OWNERDRAW},
},


"&Soạn Thảo", IDB_EDIT, 3,
{


{"Sao chép", ID_EDIT_COPY, MF_OWNERDRAW},
{"Cắt", ID_EDIT_CUT, MF_OWNERDRAW},


{"Dán", ID_EDIT_PASTE, MF_OWNERDRAW},
},


"Đổi Menu Font", IDB_FONT, 4,
{


</div>
<span class='text_page_counter'>(174)</span><div class='page_container' data-page=174>

},


"&Chổi vẽ", IDB_BRUSH, 7,
{


{"Đặc", ID_BRUSH_SOLID, MF_OWNERDRAW},
{"Chéo ngược", ID_BRUSH_BWDIAGONAL, MF_OWNERDRAW},
{"Chéo xuôi", ID_BRUSH_FWDIAGONAL, MF_OWNERDRAW},
{"Chéo", ID_BRUSH_DIAGCROSS, MF_OWNERDRAW},


{"Ngang doïc", ID_BRUSH_CROSS, MF_OWNERDRAW},
{"Ngang", ID_BRUSH_HORIZONTAL, MF_OWNERDRAW},
{"Doïc", ID_BRUSH_VERTICAL, MF_OWNERDRAW},


},


"&Tùy Chọn", IDB_OPTION, 1,
{


{"English", ID_OPTION_ENGLISH, MF_OWNERDRAW},
},


"&Hướng dẫn", IDB_HELP, 1,
{


{"Thoâng tin", IDM_ABOUT, MF_OWNERDRAW},
}


};


struct FONTMENUDATA
{


UINT id;


TCHAR szFaceName[32];
int nPointSize;


BOOL bBold, bItalics;
};


static FONTMENUDATA aFontData[] =
{



{ID_FONT_VNI_TIMES, "VNI-Times", 12},


{ID_FONT_VNI_COURIER, "VNI-Couri", 12, TRUE},
{ID_FONT_VNI_APTIMA, "VNI-Aptima", 12},


</div>
<span class='text_page_counter'>(175)</span><div class='page_container' data-page=175>

}


M_ITEM *SearchItem(int n, M_POPUP *a, UINT id)
{


for (int i = 0; i < n; i++)
{


for (int j = 0; j < a[i].nItems; j++)
if (a[i].aItems[j].id == id)


return &a[i].aItems[j];
}


return NULL;
}


BOOL Circle(HDC hdc, int x, int y, int r)
{


return Ellipse(hdc, x-r, y-r, x+r, y+r);
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)



{


int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;


TCHAR* szName[][5] =
{


{"Ellipse", "Rectangle", "RoundRect", "Chord", "Pie"},


{"Hình Bầu dục", "Hình Chữ Nhật", "Hình Chữ nhật trịn", "Hình
vành khuyên", "Hình cánh quạt"}


};


static HMENU hMenu, hMenuVn;
static HFONT ahf[nFonts];


static int cxCheck, cyCheck, nBrush;


static WORD nBrushIdx, nFigIdx, nCurFont = 0;
static COLORREF crHighlight, crHighlightText;
static HBRUSH ahbr[7];


static bool bVietNamese = true;
M_ITEM *pItem;


</div>
<span class='text_page_counter'>(176)</span><div class='page_container' data-page=176>

ahf[i] = MyCreateFont(hdc, aFontData[i].szFaceName,


aFontData[i].nPointSize, aFontData[i].bBold,


aFontData[i].bItalics);


ahbr[0] = CreateSolidBrush(RGB(0,0, 255));


ahbr[1] = CreateHatchBrush(HS_BDIAGONAL, RGB(0,0,255));
ahbr[2] = CreateHatchBrush(HS_FDIAGONAL, RGB(0,0,255));
ahbr[3] = CreateHatchBrush(HS_DIAGCROSS, RGB(0,0,255));
ahbr[4] = CreateHatchBrush(HS_CROSS, RGB(0,0,255));
ahbr[5] = CreateHatchBrush(HS_HORIZONTAL, RGB(0,0,255));
ahbr[6] = CreateHatchBrush(HS_VERTICAL, RGB(0,0,255));
nBrush = 7;


ReleaseDC(hWnd, hdc);
hMenu = GetMenu(hWnd);
hMenuVn = CreateMenu();


for (i = 0; i < nPopups; i++)


AddMenuPopup(hMenuVn, aVnMenu[i]);
HMENU hMenuPopup;


hMenuPopup = CreatePopupMenu();
SetMenu(hWnd, hMenuVn);


break;


case WM_MEASUREITEM:
SIZE sizeText;



LPMEASUREITEMSTRUCT lpmi;


lpmi = (LPMEASUREITEMSTRUCT)lParam;
HFONT hfont, hfontOld;


switch(lpmi->itemID)
{


case ID_FIGURE_ELLIPSE:
case ID_FIGURE_RECTANGLE:
case ID_FIGURE_ROUNDRECT:
case ID_FIGURE_CHORD:
case ID_FIGURE_PIE:


lpmi->itemWidth = 80;
lpmi->itemHeight = 30;
break;


</div>
<span class='text_page_counter'>(177)</span><div class='page_container' data-page=177>

for (i = 0; i < dim(aFontData); i++)
if (lpmi->itemID == aFontData[i].id)


break;


if (i < dim(aFontData))
hfont = ahf[i];
else


hfont = ahf[nCurFont];
SelectObject (hdc, hfont);



GetTextExtentPoint32(hdc, pItem->name, lstrlen(pItem->name),
&sizeText);


ReleaseDC (hWnd, hdc);


lpmi->itemWidth = sizeText.cx + cxCheck;
lpmi->itemHeight = max(sizeText.cy, cyCheck);
}
break;
}
break;
case WM_DRAWITEM:
LPDRAWITEMSTRUCT lpdi;


lpdi = (LPDRAWITEMSTRUCT)lParam;
COLORREF crTextOld, crBackOld;
BOOL bSwap;


int xText, yText;
char *szText;
switch(lpdi->itemID)
{
case ID_FIGURE_ELLIPSE:
case ID_FIGURE_RECTANGLE:
case ID_FIGURE_ROUNDRECT:
case ID_FIGURE_CHORD:
case ID_FIGURE_PIE:
HBRUSH hbrOld;
int orgRop;


bSwap = false;


hbrOld = (HBRUSH)SelectObject(lpdi->hDC, ahbr[nBrushIdx]);
if (lpdi->itemState & ODS_SELECTED)


{


xText = lpdi -> rcItem.left;
yText = lpdi -> rcItem.top;


</div>
<span class='text_page_counter'>(178)</span><div class='page_container' data-page=178>

lpdi->rcItem.right, lpdi->rcItem.bottom-2);
break;


case ID_FIGURE_RECTANGLE:


Rectangle(>hDC, >rcItem.left+cxCheck,
lpdi->rcItem.top + 2,


lpdi->rcItem.right, lpdi->rcItem.bottom-2);
break;


case ID_FIGURE_ROUNDRECT:


RoundRect(>hDC, >rcItem.left+cxCheck,
lpdi->rcItem.top + 2,


lpdi->rcItem.right, lpdi->rcItem.bottom-2, 5, 5);
break;


case ID_FIGURE_CHORD:



Chord(>hDC, >rcItem.left+cxCheck,


lpdi->rcItem.top + 2, lpdi->rcItem.right, lpdi->rcItem.bottom-2,
>rcItem.right, >rcItem.bottom-10,


lpdi->rcItem.left, (lpdi->rcItem.top+lpdi->rcItem.bottom)/2);
break;


case ID_FIGURE_PIE:


Pie(lpdi->hDC, lpdi->rcItem.left+cxCheck, lpdi->rcItem.top
+ 2, lpdi->rcItem.right, lpdi->rcItem.bottom+20,


lpdi->rcItem.right,
(lpdi->rcItem.top+lpdi->rcItem.bottom)/2,


lpdi->rcItem.left, lpdi->rcItem.top);
break;


}


if (lpdi->itemID - ID_FIGURE_ELLIPSE == nFigIdx)
{


SelectObject(lpdi->hDC, ahbr[0]);


Circle>hDC, lpdi->rcItem.left+cxCheck/2,
(lpdi->rcItem.top + lpdi->rcItem.bottom)/2, 5);



}


if (bSwap)
{


SetBkColor(lpdi->hDC, crBackOld);
SetROP2(lpdi->hDC, orgRop);


}


</div>
<span class='text_page_counter'>(179)</span><div class='page_container' data-page=179>

xText = lpdi -> rcItem.left;
yText = lpdi -> rcItem.top;


crBackOld = SetBkColor (lpdi->hDC, crHighlight);
orgRop = SetROP2(lpdi->hDC, R2_XORPEN);


bSwap = true;
}


cxCheck = GetSystemMetrics(SM_CXMENUCHECK);


ExtTextOut(lpdi->hDC, xText, yText, ETO_OPAQUE, &lpdi->rcItem,
"", 0, NULL);


Rectangle(lpdi->hDC, lpdi->rcItem.left+cxCheck, lpdi->rcItem.top
+ 2,


lpdi->rcItem.right, lpdi->rcItem.bottom-2);
if (lpdi->itemID - ID_BRUSH_SOLID == nBrushIdx)



{


SelectObject(lpdi->hDC, ahbr[0]);


Circle>hDC, lpdi->rcItem.left+cxCheck/2,
(lpdi->rcItem.top + lpdi->rcItem.bottom)/2, 5);


}


if (bSwap)
{


SetBkColor(lpdi->hDC, crBackOld);
SetROP2(lpdi->hDC, orgRop);


}


SelectObject(lpdi->hDC, hbrOld);
break;


default:


pItem = SearchItem(nPopups, aVnMenu, lpdi->itemID);
if (pItem)


{


for (i = 0; i < dim(aFontData); i++)
if (lpdi->itemID == aFontData[i].id)



break;


if (i < dim(aFontData))
hfont = ahf[i];
else


hfont = ahf[nCurFont];
szText = pItem->name;


</div>
<span class='text_page_counter'>(180)</span><div class='page_container' data-page=180>

ExtTextOut(lpdi->hDC, xText, yText, ETO_OPAQUE, &lpdi->rcItem,
szText, strlen(szText), NULL);


if (lpdi->itemID - ID_FONT_VNI_TIMES == nCurFont ||
lpdi->itemID - ID_FIGURE_ELLIPSE == nFigIdx ||
lpdi->itemID - ID_BRUSH_SOLID == nBrushIdx)
{


hbrOld = (HBRUSH)SelectObject(lpdi->hDC, ahbr[0]);
Circle>hDC, lpdi->rcItem.left+cxCheck/2,
(lpdi->rcItem.top + lpdi->rcItem.bottom)/2, 5);


SelectObject(lpdi->hDC, hbrOld);
}


SelectObject (lpdi->hDC, hfontOld);
if (bSwap)


{


SetTextColor(lpdi->hDC, crTextOld);


SetBkColor(lpdi->hDC, crBackOld);
}


}
break;
}


break;


case WM_COMMAND:


wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId)


{


case ID_FONT_VNI_TIMES:
case ID_FONT_VNI_COURIER:
case ID_FONT_VNI_APTIMA:
case ID_FONT_VNI_COOPER:


CheckMenuItem(hMenu, ID_FONT_VNI_TIMES + nCurFont,
MF_UNCHECKED);


nCurFont = wmId - ID_FONT_VNI_TIMES;
CheckMenuItem(hMenu, wmId, MF_CHECKED);
InvalidateRect(hWnd, NULL, TRUE);


break;



</div>
<span class='text_page_counter'>(181)</span><div class='page_container' data-page=181>

case ID_FIGURE_ROUNDRECT:
case ID_FIGURE_CHORD:
case ID_FIGURE_PIE:


CheckMenuItem(hMenu, ID_FIGURE_ELLIPSE + nFigIdx,
MF_UNCHECKED);


nFigIdx = wmId - ID_FIGURE_ELLIPSE;
CheckMenuItem(hMenu, wmId, MF_CHECKED);
InvalidateRect(hWnd, NULL, TRUE);


break;


case ID_OPTION_ENGLISH:
bVietNamese = false;
SetMenu(hWnd, hMenu);
break;


case ID_OPTION_TIENGVIET:
SetMenu(hWnd, hMenuVn);
bVietNamese = true;
break;


case IDM_ABOUT:


DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
break;


case IDM_EXIT:



DestroyWindow(hWnd);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


break;
case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
RECT rt;


GetClientRect(hWnd, &rt);


SelectObject (hdc, ahf[nCurFont]);


DrawText(hdc, szName[bVietNamese][nFigIdx],


strlen(szName[bVietNamese][nFigIdx]), &rt, DT_CENTER);
SelectObject(hdc, ahbr[nBrushIdx]);


switch(ID_FIGURE_ELLIPSE + nFigIdx)
{


</div>
<span class='text_page_counter'>(182)</span><div class='page_container' data-page=182>

Pie(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4, 3*rt.bottom/4,
rt.right, rt.bottom/2, 0, 0);



break;


case ID_FIGURE_CHORD:


Chord(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4,


3*rt.bottom/4, rt.right, rt.bottom/3, 0, rt.bottom);
break;


}


EndPaint(hWnd, &ps);
break;


case WM_DESTROY:


for (i = 0; i < nBrush; i++)
DeleteObject(ahbr[i]);
PostQuitMessage(0);


break;
default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


return 0;
}


<b>9.4 Bimap Menu </b>




Người sử dụng có thể tạo menu bằng cách dùng hàm CreateMenu, sau đó sử dụng hàm


AppendMenu để tạo nên các thành phần của menu.



Để có thể có được bitmap menu, ta gọi hàm AppendMenu với uFlag có bao gồm thuộc tính


MF_BITMAP.



MF_BITMAP

Dùng bitmap để vẽ menu item. Tham số lpNewItem chứa handle


đến bitmap.



</div>
<span class='text_page_counter'>(183)</span><div class='page_container' data-page=183></div>
<span class='text_page_counter'>(184)</span><div class='page_container' data-page=184>

Hình 9-13 Các menu item làbitmap


Trích đoạn chương trình nguồn vnmenu.cpp



// VnMenu.cpp : Defines the entry point for the application.
//


#include "stdafx.h"
#include "resource.h"
//...


//


// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//


#define MAX_ITEMS 10
struct M_ITEM


{



</div>
<span class='text_page_counter'>(185)</span><div class='page_container' data-page=185>

{


"&Hồ sơ", IDB_FILE, 6,
{


{"Hồ sơ mới", ID_FILE_NEW, MF_BITMAP, IDB_FILE_NEW},
{"Mở hồ sơ", ID_FILE_OPEN, MF_BITMAP, IDB_FILE_OPEN},
{"Lưu hồ sơ", ID_FILE_SAVE, MF_BITMAP, IDB_FILE_SAVE},
{"", 0, MF_SEPARATOR},


{"In", ID_FILE_PRINT, MF_BITMAP, IDB_FILE_PRINT},
{"Thoát", IDM_EXIT, MF_BITMAP, IDB_FILE_EXIT},
},


"&Soạn Thảo", IDB_EDIT, 3,
{


{"Sao cheùp", ID_EDIT_COPY, MF_BITMAP, IDB_EDIT_COPY},
{"Cắt", ID_EDIT_CUT, MF_BITMAP, IDB_EDIT_CUT},


{"Dán", ID_EDIT_PASTE, MF_BITMAP, IDB_EDIT_PASTE},
},


"Đổi Menu Font", IDB_FONT, 4,
{


{"VNI-Times", ID_FONT_VNI_TIMES, MF_BITMAP, IDB_FONT_TIMES},


{"VNI-Courier", ID_FONT_VNI_COURIER, MF_BITMAP, IDB_FONT_COURIER},


{"VNI-Aptima", ID_FONT_VNI_APTIMA, MF_BITMAP, IDB_FONT_APTIMA},
{"VNI-Cooper", ID_FONT_VNI_COOPER, MF_BITMAP, IDB_FONT_COOPER}
},


"&Hình", IDB_FIGURE, 5,
{


{"Hình Chữ Nhật", ID_FIGURE_ELLIPSE, MF_BITMAP,
IDB_FIGURE_ELLIPSE},


{"Hình Bầu dục", ID_FIGURE_RECTANGLE, MF_BITMAP,
IDB_FIGURE_RECTANGLE},


{"Hình chữ nhật trịn", ID_FIGURE_ROUNDRECT, MF_BITMAP,
IDB_FIGURE_ROUNDRECT},


{"Hình Vành khuyên", ID_FIGURE_CHORD, MF_BITMAP, IDB_FIGURE_CHORD},
{"Hình bánh", ID_FIGURE_PIE, MF_BITMAP, IDB_FIGURE_PIE},


},


"&Chổi vẽ", IDB_BRUSH, 8,
{


{"Đặc", ID_BRUSH_SOLID, MF_BITMAP, IDB_BRUSH_SOLID},
{"...", ID_BRUSH_SOLID, MF_SEPARATOR},


</div>
<span class='text_page_counter'>(186)</span><div class='page_container' data-page=186>

{


{"Thoâng tin", IDM_ABOUT, MF_BITMAP, IDB_HELP_THONGTIN},


}


};


struct FONTMENUDATA
{


UINT id;


TCHAR szFaceName[32];
int nPointSize;


BOOL bBold, bItalics;
};


static FONTMENUDATA aFontData[] =
{


{ID_FONT_VNI_TIMES, "VNI-Times", 12},


{ID_FONT_VNI_COURIER, "VNI-Couri", 12, TRUE},
{ID_FONT_VNI_APTIMA, "VNI-Aptima", 12},


{ID_FONT_VNI_COOPER, "VNI-Cooper", 12, TRUE, TRUE},
};


#define dim(a) (sizeof(a)/sizeof(a[0]))
const int nPopups = dim(aVnMenu);


const int nFonts = dim(aFontData);



HMENU AddMenuPopup(HMENU hMenu, M_POPUP mp)
{


HMENU hMenuPopup = CreatePopupMenu();


AppendMenu (hMenu, MF_POPUP|MF_BITMAP, (UINT)hMenuPopup,


(LPSTR)LoadBitmap (hInst, (LPSTR)mp.bitmapId));
for (int i = 0; i < mp.nItems; i++)


if (mp.aItems[i].attr & MF_BITMAP)


AppendMenu (hMenuPopup, mp.aItems[i].attr, mp.aItems[i].id,
(LPSTR)LoadBitmap(hInst, (LPSTR)mp.aItems[i].bitmapId));
else


AppendMenu (hMenuPopup, mp.aItems[i].attr, mp.aItems[i].id,
mp.aItems[i].name);


</div>
<span class='text_page_counter'>(187)</span><div class='page_container' data-page=187>

BOOL Circle(HDC hdc, int x, int y, int r)
{


return Ellipse(hdc, x-r, y-r, x+r, y+r);
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{



int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;


TCHAR* szName[][5] =
{


{"Ellipse", "Rectangle", "RoundRect", "Chord", "Pie"},


{"Hình Bầu dục", "Hình Chữ Nhật", "Hình Chữ nhật trịn", "Hình
vành khun", "Hình chiếc bánh"}


};


static HMENU hMenu, hMenuVn;
static HFONT ahf[nFonts];


static int cxCheck, cyCheck, nBrush;


static WORD nBrushIdx, nFigIdx, nCurFont = 0;
static COLORREF crHighlight, crHighlightText;
static HBRUSH ahbr[7];


static bool bVietNamese = true;
switch (message)


{


case WM_CREATE:


int i;


LOGFONT lf;


crHighlight = GetSysColor(COLOR_HIGHLIGHT);


crHighlightText = GetSysColor(COLOR_HIGHLIGHTTEXT);
cxCheck = GetSystemMetrics(SM_CXMENUCHECK);


cyCheck = GetSystemMetrics(SM_CYMENUCHECK);
ZeroMemory(&lf, sizeof(lf));


hdc = GetDC(hWnd);


for (i = 0; i < nFonts; i++)


ahf[i] = MyCreateFont(hdc, aFontData[i].szFaceName,
aFontData[i].nPointSize, aFontData[i].bBold,


aFontData[i].bItalics);


</div>
<span class='text_page_counter'>(188)</span><div class='page_container' data-page=188>

for (i = 0; i < nPopups; i++)


AddMenuPopup(hMenuVn, aVnMenu[i]);
HMENU hMenuPopup;


hMenuPopup = CreatePopupMenu();
SetMenu(hWnd, hMenuVn);


break;



case WM_COMMAND:


wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId)


{


case ID_FONT_VNI_TIMES:
case ID_FONT_VNI_COURIER:
case ID_FONT_VNI_APTIMA:
case ID_FONT_VNI_COOPER:


CheckMenuItem(hMenu, ID_FONT_VNI_TIMES + nCurFont,
MF_UNCHECKED);


nCurFont = wmId - ID_FONT_VNI_TIMES;
CheckMenuItem(hMenu, wmId, MF_CHECKED);
InvalidateRect(hWnd, NULL, TRUE);


break;


case ID_BRUSH_SOLID:
case ID_BRUSH_BWDIAGONAL:
case ID_BRUSH_FWDIAGONAL:
case ID_BRUSH_DIAGCROSS:
case ID_BRUSH_CROSS:
case ID_BRUSH_HORIZONTAL:
case ID_BRUSH_VERTICAL:



CheckMenuItem(hMenu, ID_BRUSH_SOLID + nBrushIdx,
MF_UNCHECKED);


nBrushIdx = wmId - ID_BRUSH_SOLID;
CheckMenuItem(hMenu, wmId, MF_CHECKED);
InvalidateRect(hWnd, NULL, TRUE);


break;


</div>
<span class='text_page_counter'>(189)</span><div class='page_container' data-page=189>

case ID_OPTION_TIENGVIET:
SetMenu(hWnd, hMenuVn);
bVietNamese = true;
break;


case IDM_ABOUT:


DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
break;


case IDM_EXIT:


DestroyWindow(hWnd);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);
}



break;
case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
RECT rt;


GetClientRect(hWnd, &rt);


SelectObject (hdc, ahf[nCurFont]);


DrawText(hdc, szName[bVietNamese][nFigIdx],


strlen(szName[bVietNamese][nFigIdx]), &rt, DT_CENTER);
SelectObject(hdc, ahbr[nBrushIdx]);


switch(ID_FIGURE_ELLIPSE + nFigIdx)
{


case ID_FIGURE_ELLIPSE:


Ellipse(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4,
3*rt.bottom/4);


break;


case ID_FIGURE_RECTANGLE:


Rectangle(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4,
3*rt.bottom/4);



break;


case ID_FIGURE_ROUNDRECT:


RoundRect(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4,
3*rt.bottom/4, 30, 30);


break;


case ID_FIGURE_PIE:


Pie(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4, 3*rt.bottom/4,
rt.right, rt.bottom/2, 0, 0);


</div>
<span class='text_page_counter'>(190)</span><div class='page_container' data-page=190>

break;
default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


return 0;
}


</div>
<span class='text_page_counter'>(191)</span><div class='page_container' data-page=191>

<b>10. </b>

Cửa Sổ Và Nút Điều Khiển



Nhu cầu tạo cửa sổ con là rất lớn: Cửa sổ log, debug, text…, trong các ứng dụng MDI. Qui


trình tạo cửa sổ bao gồm đăng ký lớp và gọi hàm tạo cửa sổ



<b>10.1 Đăng ký lớp cửa sổ (tư tưởng OOP) </b>


Dùng hàm RegisterClass




typedef struct _WNDCLASS {
UINT style;


WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HANDLE hInstance;
HICON hIcon;
HCURSOR hCursor;


HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
} WNDCLASS;


Ví dụ để có thể nhận thơng điệp bấm mắt chuột hai lần liên tiếp, dùng

CS_DBLCLKS.

Để tạo



private DC, duøng

CS_OWNDC


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{


// ...


</div>
<span class='text_page_counter'>(192)</span><div class='page_container' data-page=192>

break;
case WM_PAINT:



hdc = BeginPaint(hWnd, &ps);
// …


EndPaint(hWnd, &ps);
break;


case WM_DESTROY:


PostQuitMessage(0);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


return 0;
}


<b>10.2 Tạo cửa sổ </b>



Dùng CreateWindow hoặc CreateWindowEx


<b>HWND CreateWindowEx( </b>


<i><b> DWORD dwExStyle, // extended window style </b></i>


<i><b> LPCTSTR lpClassName, // pointer to registered class name </b></i>
<i><b> LPCTSTR lpWindowName, // pointer to window name </b></i>


<i><b> DWORD dwStyle, // window style </b></i>



<i><b> int x, // horizontal position of window </b></i>
<i><b> int y, // vertical position of window </b></i>
<i><b> int nWidth, // window width </b></i>


<i><b> int nHeight, // window height </b></i>


<i><b> HWND hWndParent, // handle to parent or owner window </b></i>


<i><b> HMENU hMenu, // handle to menu, or child-window identifier </b></i>
<i><b> HINSTANCE hInstance, // handle to application instance </b></i>


</div>
<span class='text_page_counter'>(193)</span><div class='page_container' data-page=193>

WS_VISIBLE

Tạo cửa sổ được hiển thị ngay từ đầu.


WS_EX_MDICHILD

Tạo cửa sổ con MDI.



WS_EX_TOPMOST

Tạo cửa sổ topmost.


Cửa sổ có thể có các trạng thái ban đầu sau:



WS_VISIBLE, WS_MAXIMIZED, WS_MINIMIZED, WS_DISABLE


WS_OVERLAPPED, WS_OVERLAPPEDWINDOW



Hàm GetSystemMetrics cho biết các thơng số kích thước về cửa sổ.


<b>int GetSystemMetrics( </b>


<i><b> int nIndex // system metric or configuration setting to retrieve </b></i>
<b>);</b>


<b>10.3 Tạo cửa sổ con </b>



Đăng ký và tạo cửa sổ con, mỗi lớp đăng ký cho cửa sổ con phải có hàm xử lý.



10.3.1 Chương trình MDI V1



</div>
<span class='text_page_counter'>(194)</span><div class='page_container' data-page=194>

Hình 10-1 Cửa sổ con


// Mdi.cpp : Defines the entry point for the application.
//


#include "stdafx.h"
#include "resource.h"


#define MAX_LOADSTRING 100


// Global Variables:


HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING];// The title bar text
TCHAR szTextWndClass[MAX_LOADSTRING];// The title bar text
TCHAR szBallWndClass[MAX_LOADSTRING];// The title bar text


// Foward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);


ATOM TextWndRegisterClass(HINSTANCE hInstance);
ATOM BallWndRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK TextWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK BallWndProc(HWND, UINT, WPARAM, LPARAM);


LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);


</div>
<span class='text_page_counter'>(195)</span><div class='page_container' data-page=195>

BallWndRegisterClass(hInstance);


// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))


return FALSE;


hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_MDI);


// Main message loop:


while (GetMessage(&msg, NULL, 0, 0))
{


if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{


TranslateMessage(&msg);
DispatchMessage(&msg);
}


}


return msg.wParam;
}


// FUNCTION: MyRegisterClass()



ATOM MyRegisterClass(HINSTANCE hInstance)
{


WNDCLASSEX wcex;


</div>
<span class='text_page_counter'>(196)</span><div class='page_container' data-page=196>

wcex.lpszClassName = szWindowClass;


wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);


return RegisterClassEx(&wcex);
}


ATOM TextWndRegisterClass(HINSTANCE hInstance)
{


WNDCLASSEX wcex;


LoadString(hInstance, IDS_TEXTCLASS, szTextWndClass, MAX_LOADSTRING);


wcex.cbSize = sizeof(WNDCLASSEX);


wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)TextWndProc;
wcex.cbClsExtra = 0;


wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;


wcex.hCursor = LoadCursor(NULL, IDC_ARROW);


wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;


wcex.lpszClassName = szTextWndClass;


wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);


</div>
<span class='text_page_counter'>(197)</span><div class='page_container' data-page=197>

wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;


wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;


wcex.lpszClassName = szBallWndClass;


wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);
return RegisterClassEx(&wcex);


}


// FUNCTION: InitInstance(HANDLE, int)


BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{


HWND hWnd;



hInst = hInstance; // Store instance handle in our global variable


hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);


if (!hWnd)
{


return FALSE;
}


</div>
<span class='text_page_counter'>(198)</span><div class='page_container' data-page=198>

PAINTSTRUCT ps;
HDC hdc;


TCHAR szParent[MAX_LOADSTRING];
HWND hWndChild;


LoadString(hInst, IDS_PARENT, szParent, MAX_LOADSTRING);
static int xx, yy;


switch (message)
{


case WM_COMMAND:


wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
switch (wmId)


{



case ID_FILE_NEWTEXT:


hWndChild = CreateWindow(szTextWndClass, szTitle,
WS_OVERLAPPEDWINDOW | WS_CHILD | WS_CLIPSIBLINGS,
xx, yy, 300, 150, hWnd, NULL, hInst, NULL);


if (!hWndChild)
return TRUE;
xx += 20; yy += 20;


if (xx > 70) xx = yy = 0;


ShowWindow(hWndChild, SW_SHOW);
UpdateWindow(hWndChild);


break;


case ID_FILE_NEWBALL:


</div>
<span class='text_page_counter'>(199)</span><div class='page_container' data-page=199>

DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd,
(DLGPROC)About);


break;
case IDM_EXIT:


DestroyWindow(hWnd);
break;


default:



return DefWindowProc(hWnd, message, wParam, lParam);
}


break;
case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
RECT rt;


GetClientRect(hWnd, &rt);


DrawText(hdc, szParent, strlen(szParent), &rt, DT_CENTER);
EndPaint(hWnd, &ps);


break;


case WM_DESTROY:


PostQuitMessage(0);
break;


default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


return 0;
}



LRESULT CALLBACK TextWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{


</div>
<span class='text_page_counter'>(200)</span><div class='page_container' data-page=200>

break;
case WM_PAINT:


hdc = BeginPaint(hWnd, &ps);
RECT rt;


GetClientRect(hWnd, &rt);


DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
EndPaint(hWnd, &ps);


break;
default:


return DefWindowProc(hWnd, message, wParam, lParam);
}


return 0;
}


LRESULT CALLBACK BallWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM
lParam)


{



PAINTSTRUCT ps;
HDC hdc;


static int xPos, yPos, dx = 5, dy = 5;
static int r = 20;


RECT rt;


switch (message)
{


case WM_CREATE:


SetTimer(hWnd, 0, 8, NULL);
GetClientRect(hWnd, &rt);
xPos = rt.right/2;


</div>

<!--links-->

×