1. Trang chủ
  2. » Kỹ Năng Mềm

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

281 5 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 281
Dung lượng 2,77 MB

Nội dung

• Cöûa soå che ñoùng laïi hoaëc di chuyeån. • Chöông trình giaùn tieáp gôûi WM_PAINT thoâng qua haøm InvalidateRect. • Cöûa soå ñöôïc cuoán. • Menu popup môû ra roài ñoùng laïi.. Haø[r]

(1)(2)

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

 



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

(3)

1 Toång quan

1.1 Windows 95 lập trình mơi trường Windows

1.2 Chương trình Windows

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

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

2 Tổng quan 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ẽ điểm 19

2.6 Taïo Marker 21

2.7 Vẽ vẽ lại 23

2.8 BeginPaint vaø EndPaint 23

2.9 Vùng hợp lệ 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

(4)

5 Xuất Văn 60

5.1 Tổng quan 60

5.2 Các hàm xuất văn 60

TextOut 60

ExtTextOut 61

TabbedTextOut 61

DrawText 64

GrayString 66

5.3 Các thuộc tính xuất văn 67

5.4 Fonts 70

CreateFont 72

LOGFONT 72

CreateFontIndirect 74

5.5 Các thông số vă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 định thời 81

(5)

ShowCursor 115

SetCursor 115

7.3 Phím tắ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 menu 134

LoadMenu 135

SetMenu 136

DrawMenuBar 136

AppendMenu 136

InsertMenuItem 137

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

8.3 Menu 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

(6)

10.5 Một số ví dụ 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 cách xoá vẽ lại 95

11.3 Kéo lên đối tượ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 nhân tỉ lệ 234

DPtoLP 234

LPtoDP 234

SetViewportOrgEx 235

GetViewportOrgEx 235

SetWindowOrgEx 235

GetWindowOrgEx 235

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

(7)

13.4 Các thao tác 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

(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ôi trường làm việc chuẩn cho người sử dụng máy vi tính cá nhân Việc xây dựng chương trình hoạt động môi trường Windows trở thành yêu cầu thiết yếu cho người xây dựng phần mềm mơi trường DOS hồn thành nhiệm vụ lịch sử

Để theo kịp với trào lưu giới, khoa Công nghệ thông tin Tốn ứng dụng trường Đại học Tơn Đức Thắng cập nhật bổ sung môn Lập trình Windows vào chương trình đào tạo khoa

Tài liệu biên soạn nhằm phục vụ cho sinh viên chuyên ngành Công nghệ thông Tin trường Nội dung bao gồm kỹ thuật cốt yếu lập trình mơi trường Windows sử dụng ngôn ngữ C/C++

Windows cung cấp ngàn hàm để lập trình viên tạo ứng dụng khác nhau, từ soạn thảo văn bản, vẽ đối tượng đồ hoạ, hiển thị hình ảnh, phim video, tạo âm thanh, truyền liệu… Phạm vi tài liệu giới hạn nội dung 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ừ kiến thức trên, sinh viên có thể tham khảo thêm thư viện Windows cung cấp để xây dựng ứng dụng lớn theo yêu cầu luận văn tốt nghiệp phần mềm thực

Tài liệu mang tính chất tóm tắt kiến thức khơng thể tránh khỏi sai sót Rất mong nhận sự phê bình, đóng góp ý kiến bạn đồng nghiệp sinh viên

(9)

1 Toång quan

1.1 Windows 95 lập trình 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 với dáng vẻ : Một cửa sổ hình chữ nhật, có tiêu đề, menu, hộp hội thoại, trượt …

• Đa nhiệm

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

− Nhiều chương trình kích hoạt chạy lúc

− Các phiên Windows 16 bít hoạt động đa nhiệm theo chế nonpreemtive − Windows 32 bít hoạt động theo chế preemtive Mỗi chương trình

tách thành thread thực lúc • •Quản lý nhớ

− Mã chương trình liệu dịch chuyển nhớ vật lý − Khả sử dụng nhớ phụ

(10)

− Chương trình viết cho Windows khơng điều khiển trực tiếp thiết bị xuất hình hay máy in mà thơng qua ngơn ngữ lập trình đồ hoạ gọi Giao Diện Thiết Bị Đồ Hoạ (Graphics Device Interface: GDI)

− Các phần mềm MS-DOS cần có tập tin điều khiển thiết bị 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 đề • Kiến trúc hướng thông điệp (message driven)

− Windows ứng dụng Windows hoạt động theo chế truyền, nhận thông điệp, hoạt động chương trình thay đổi tuỳ theo thơng điệp mà nhận được, thông điệp gởi qua lại ứng dụng Windows, ứng dụng với

− Cửa sổ ứng dụng tự động vẽ lại có thay đổi kích thước hay vùng bị che Điều thực 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 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 chương trình với tác động bên user input…

− Hệ điều hành Windows gởi thơng điệp cho chương trình ứng dụng cách gọi hàm cửa sổ ứng dụng đó, với tham số thơng điệp

• Tài nguyên

− Window cho phép người sử dụng đưa vào chương trình tài nguyên thuộc loại biểu tượng (icons), trỏ (cursors), bitmap, bảng chuỗi (string tables), bảng phím tắt (Accelerator), hộp hội thoại (Dialog), menu, công cụ (toolbar) siêu văn (văn loại HTML)

(11)

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

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

sdafx.h, stdafx.cpp, hello.cpp stdafx.h

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

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

stdafx.cpp

(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;

(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; }

(14)

CW Create Window option

DT DrawText option

Ví dụ có sử dụng 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 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ể chương trình HWND Handle đến cửa sổ

HDC Handle đến ngữ cảnh thiết bị (device context) HICON Handle đến biểu tượng

(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 f : BOOL

w WORD

sz chuỗi kết thúc ‘\0’ 1.2.5 Hoạt động chương trình

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

• Trình tự thực chương trình Windows − Đăng ký lớp cửa sổ,

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

− Xử lý thông điệp nhờ vòng lặp message thủ tục cửa sổ

− WinMain không trực tiếp gọi hàm WndProc, hàm gọi Windows • Các hàm Windows

− Windows cung cấp ngàn hàm thư viện − Các hàm sử dụng chương trình bao gồm

(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 kt việc vẽ cửa sổ GetClientRect – Lấy kích thước vùng client cửa sổ DrawText – Hiển thị chuỗi ký tự

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

DefWindowProc – Thực xử lý thông điệp khơng chương trình xử lý.

1.2.6 Hàm (WinMain)

Gồm phần đă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 hành

hPrevInstance : Handle đến chương trình trước (Windows 95 : ln ln NULL) Trong Windows 32 bit, hPrevInstance luôn NULL, để có tối đa 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ổ

(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ổ cách đưa thông tin vào cấu trúc cửa sổ 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ổ

(18)

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

LPVOID lpParam // pointer to window-creation data );

HWND CreateWindowEx(

DWORD dwExStyle, // extended window style

LPCTSTR lpClassName, // pointer to registered class name LPCTSTR lpWindowName, // pointer to window name

DWORD dwStyle, // window style

int x, // horizontal position of window int y, // vertical position of window int nWidth, // window width

int nHeight, // window height

HWND hWndParent, // handle to parent or owner window

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

LPVOID lpParam // pointer to window-creation data );

Ví dụ

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

(19)

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

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

msg biến có kiểu MSG typedef struct tagMSG {

HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; }MSG;

1.3 Thủ tục cửa sổ 1.3.1 Xử lý thông điệp

Thủ tục cửa sổ hàm xử lý thông điệp tương ứng lớp cửa sổ Thủ tục cửa sổ rỏ phản ứng 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;

(20)

WM_PAINT : Thông điệp gởi cần vẽ lại cửa sổ WM_CHAR : Thông điệp ký tự bấm

WM_TIMER : Thông điệp định thời

WM_LBUTTONDOWN : Thông điệp gởi mắt trái chuột bấm

WM_DESTROY: Thông điệp rõ Windows huỷ cửa sổ sau nhận lệnh người dùng Các ứng dụng xử lý thông điệp theo cách chuẩn gọi PostQuitMessage(0);

1.3.2 Thông điệp WM_DESTROY 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 giải phóng lại nhớ cũng tài nguyên khác mà chương trình dùng Chương trình kết thúc vịng lặp thơng điệp nhận thơng điệp WM_QUIT

• Chương trình kết thúc nhiều cách: Bằng [System menu] Close, bấm Alt-F4 biểu tượng X góc bên phải Mỗi cách dẫn đến loạt thông điệp gởi cho hàm cửa sổ Hàm cửa sổ xử lý thơng điệp gởi cho hàm xử lý cửa sổ

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

chuẩn cho thông điệp gọi PostQuitMessage(0) Hàm chèn WM_QUIT vào hàng đợi thông điệp GetMessage trả nhận WM_QUIT kết thúc vịng lặp thơng điệp

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

WM_SYSKEYDOWN [Alt] bấm WM_SYSKEYDOWN [F4] bấm

(21)

1.3.3 Xử lý thông điệp

− Các ứng dụng Windows có giao diện chung giống Điều thực nhờ hàm xử lý thông điệp

− Thông điệp không WndProc quan tâm xử lý gởi cho DefWindowProc xử lý DefWindowProc hàm xử lý thơng điệp, hoạt động giống nhau cho tất ứng dụng Windows

− WndProc gởi thơng điệp cho DefWindowProc ngược lại Ví dụ, người sử dụng nhấn tổ hợp phím Alt-F4 để tắt ứng dụng, loạt thông điệp gởi cho WinProc

− Thông điệp gởi cho hàm cửa sổ trước Hàm cửa sổ gọi DefWindowProc để xử lý thơng điệp khơng quan tâm

− Người sử dụng thay đổi vài hoạt động cách tự xử lý số thơng điệp

1.4 Môi trường Visual C++

(22)

Hình 1-2 – Tạo project VC++ 6.0

(23)

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

Giao diện đồ hoạ đặc tính bật hệ điều hành Windows Trong chương này ta trình bày tổng quan vấn đề liên quan đến thao tác tạo tạo xuất liệu đồ hoạ, các thao tác vẽ dùng GDI Windows

2.1 Giới thiệu

• GDI thư viện đồ hoạ Windows, xử lý thao tác xuất kết dạng đồ hoạ lên lên thiết bị xuất khác máy in, máy vẽ GDI tạo ra điểm vẽ, đường, chữ… Windows dùng GDI để tạo giao diện người dùng cửa sổ, biểu tượng, menu, hộp hội thoại

• GDI cho phép vẽ thiết bị logic hay gọi thiết bị ảo bitmaps metafiles

• GDI tạo khả đồ hoạ độc lập thiết bị, cung cấp thủ tục vẽ cho tất các loại thiết bị thay cho loại thiết bị khác hình, máy in, bitmaps metafile

Các thiết bị GDI

• GDI vẽ thiết bị khác nhờ điều khiển thiết bị

• Bộ điều khiển thiết bị cho GDI biết khả vẽ có sẵn thiết bị (nhờ device capability bits) Có cờ cho biết khả thiết bị: vẽ cung, đường thẳng, đa giác, bitmaps text

• Mỗi điều khiển thiết bị phải cung cấp hai thủ tục, vẽ điểm vẽ đường • Bitmap ln ln có hình chữ nhật, hình ảnh lưu trữ nhớ giống

(24)

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

GDI dùng đối tượng vẽ logic để có khả độ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ữ) logical colors (màu luận lý)

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

• Ngữ cảnh thiết bị tập hợp thuộc tính vẽ có pen để vẽ đường, brush để tô màu font để hiển thị văn

• Nhờ ngữ cảnh thiết bị, hàm vẽ trở nên đơn giản với tham số giảm đến mức tối thiểu thuộc tính vẽ lấy từ ngữ cảnh thiết bị Ta tham chiếu đến ngữ cảnh thiết bị nhờ 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);

(25)(26)

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

• Vai trị thứ ba ngữ cảnh thiết bị ngăn cách xuất liệu nhiều chương trình truy xuất thiết bị lúc (vai trị permission slip) Đối với máy in dùng chế spooling đối với cửa sổ ứng dùng, dùng clipping

• Khi ứng dụng muốn vẽ, phải mượn DC từ Windows Borrowing routines Returning routines Description BeginPaint

GetDC

EndPaint ReleaseDC

(27)

• Thực thao tác vẽ • Trả lại DC cho Windows

Hai hàm mượn trả DC thông dụng BeginPaint EndPaint, BeginPaint cho handle đến ngữ cảnh thiết bị phần xén vùng client

Việc vẽ 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 RealeaseDC để thực thao tác vẽ có biến cố nhập chuột hay bàn phím bấm GetDC trả handle đến toàn vùng client cửa sổ ứng dụng

case WM_CHAR:

hdc = GetDC(hWnd);

// Thao tác vẽ

ReleaseDC(hWnd, hdc); break;

Ta dùng GetWindowDC để vẽ lên phần non-client cửa sổ ứng dụng 2.5 Vẽ điểm

Trong phần ta tìm hiểu chi tiết qui trình vẽ vùng client cửa sổ ứng dụng, và thao tác vẽ cách trực tiếp bật tắt pixel hình Các chương tiếp theo trình bày 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 thực hiển thị chấm đen vùng client của cửa sổ ứng dụng Chương trình pixel gồm tập tin chương trình nguồn sau:

stdafx.h stdafx.cpp pixel.cpp

// Trich doan pixel.cpp

(28)

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

return 0; }

2.5.2 SetPixel vaø GetPixel

Để vẽ điểm, ta dùng hàm SetPixel, để lấy giá trị pixel xác định 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 hàm sau:

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

COLORREF SetPixel(

HDC hdc, // handle of device context

int X, // x-coordinate of pixel

int Y, // y-coordinate of pixel

COLORREF crColor // pixel color

);

Hàm GetPixel trả giá trị đỏ, xanh cây, xanh dương pixel vị trí qui định trước

COLORREF GetPixel(

HDC hdc, // handle of device context

int XPos, // x-coordinate of pixel

int nYPos // y-coordinate of pixel

);

COLORREF số DWORD lưu trữ thành phần R,G,B biểu diễn màu Ta dùng macro RGB để tạo màu mong muốn

(29)

BOOL GetClientRect(

HWND hWnd, // handle of window

LPRECT lpRect // address of structure for client coordinates );

LPRECT trỏ đến cấu trúc RECT

typedef struct _RECT { // rc LONG left;

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

2.6 Taïo Marker

Chương trình Marker sử dụng SetPixel GetPixel để đánh dấu hình chữ thập nhỏ (+) 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.

(30)

#define MAX_MARKERS 100 #define MARKER_SIZE

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:

(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 chương trình

Hàm cửa sổ chương trình xử lý bốn thơng điệp WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_PAINT WM_DESTROY WM_LBUTTONDOWN WM_RBUTTONDOWN thơng điệp mắt chuột, gởi tới cho hàm cửa sổ mắt trái (hoặc phải) chuột bấm Trong Marker, ta xử lý thông điệp WM_LBUTTONDOWN cách thêm điểm vào mảng lưu trữ các điểm 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í trỏ chuột thời điểm bấm (hoặc nhả)

2.7 Vẽ vẽ lại

Cửa sổ Windows thường bị che phần hộp hội thoại, menu, trỏ chuột hay cửa sổ khác Khi khơng cịn bị che, phần cần phục hồi Trong số trường hợp đặc biệt phần bị che tương đối nhỏ trỏ chuột di chuyển hay hộp hội thoại mở ra, Windows tìm cách lưu phần bị che để dễ phục hồi sau

Trong đa số trường hợp lại, chế phục hồi phần cửa sổ bị che thân cửa sổ tự nó vẽ lại phần hư Điều thực cửa sổ nhận thông điệp WM_PAINT Windows gởi thông điệp WM_PAINT đến cửa sổ bị che trường hợp sau:

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

• Cửa sổ thay đổi kích thước (lớp cửa sổ phải đăng ký với cờ CS_HREDRAW CS_VREDRAW bật)

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

(32)

Hàm mượn ngữ cảnh thiết bị BeginPaint, hàm trả EndPaint Hai hàm sử dụng cấu trúc PAINTSTRUCT Khai báo cấu trúc PAINSTRUCT hàm sau

typedef struct tagPAINTSTRUCT { // ps

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

HDC BeginPaint(

HWND hwnd, // handle to window

LPPAINTSTRUCT lpPaint // pointer to structure for paint information

);

BOOL EndPaint(

HWND hWnd, // handle to window

CONST PAINTSTRUCT *lpPaint // pointer to structure for paint data );

BeginPaint mượn Windows ngữ cảnh thiết bị (DC) đưa thông tin vẽ vào cấu trúc PAINSTRUCT BeginPaint sử dụng để hoạt động với thông điệp WM_PAINT BeginPaint cung cấp thông tin vùng xén, vùng không hợp lệ

Hàm EndPaint trả lại DC cho Windows Khi hàm gọi Windows khôi phục lại thuộc tính vẽ sẵn sàng cho chương trình cần vẽ lần Đồng thời EndPaint thông báo vùng không hợp lệ sửa chữa

(33)

);

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

InvalidateRect(hWnd, NULL, TRUE);

Tham số thứ ba cờ xoá, giá trị TRUE cho biết xoá phần trước vẽ Tham số thứ hai 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 vùng client Vẽ lại toàn vùng client không hiệu Thông thường, ta cần vẽ lại vùng nhỏ bị hư hay cần cập nhật, ta gọi vùng vùng không hợp lệ Trong marker, lần thêm dấu thập mới, ta cần vẽ lại vùng đủ chứa marker

Ví dụ sau minh hoạ việc vẽ lại toàn dấu thập vị trí mắt trái chuột 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;

2.10 InvertPixel vaø DrawMarker

Sử dụng SetPixel để vẽ dấu thập khơng có tác dụng (vơ hình) màu trùng với màu dấu thập Để bảo đảm việc đánh dấu nền, ta phủ định giá trị pixel màu nền Điều thực nhờ GetPixel theo sau SetPixel với giá trị phủ định từng bit

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

(34)

}

Để đánh dấu 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 cách phủ định giá trị pixel đang có

2.11 GetDC ReleaseDC

Mỗi mắt chuột bấm, thay gián tiếp lệnh việc vẽ lại thơng qua InvalidateRect để gởi WM_PAINT Ta vẽ trực tiếp dấu thập nhận thông điệp mắt trái chuột được bấm Điều thực GetDC ReleaseDC Hàm GetDC cho mượn ngữ cảnh thiết bị đến toàn vùng client, ReleaseDC trả lại ngữ cảnh Giữa hai thao tác mượn trả này, ta thực thao tác vẽ

// Marker.cpp : Defines the entry point for the application // Version 1.2: Dung GetDC de ve 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

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);

(35)

EndPaint(hWnd, &ps); break;

case WM_DESTROY:

PostQuitMessage(0); break;

default:

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

(36)

3 Vẽ đường

3.1 Đường

• Đường đối tượng hình học có cách nối liên tiếp điểm • Đường loại đối tượng hình học mở Mỗi đường có điểm đầu điểm cuối

• Đường vẽ theo giải thuật inclusive/exclusive, nghĩa điểm đầu thuộc đường, nhưng điểm cuối không thuộc đường

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

GDI cung cấp hàm vẽ đườ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(

HDC hdc, int X, int Y, LPPOINT lpPoint );

BOOL LineTo(

(37)

CONST POINT *lppt, // pointer to array containing endpoints

int cPoints // number of points in the array

);

BOOL PolylineTo(

HDC hdc, // handle to device context

CONST POINT *lppt, // pointer to array of points

DWORD cCount // number of points in array

);

BOOL PolyPolyline(

HDC hdc, // handle of a device context

CONST POINT *lppt, // address of an array of points

CONST DWORD *lpdwPolyPoints, // address of an array of values

DWORD cCount // number of counts in the second array

);

Linedrag: Ví dụ minh hoạ MoveTo LineTo

(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) {

(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 cải tiến dùng Polyline PolyPolyline 3.2.3 Arc

Haøm Arc vẽ cung phần hình Ellipse BOOL Arc(

HDC hdc, // handle to device context

int nLeftRect, // x-coord : UPL

int nTopRect, // y-coord : UPL

int nRightRect, // x-coord : LWR

(40)

BOOL ArcTo(

HDC hdc, // handle to device context

int nLeftRect, // x-coord : UPL

int nTopRect, // y-coord : UPL

int nRightRect, // x-coord : LWR

int nBottomRect, // y-coord : LWR

int nXRadial1, // x-coord of the first radial ending point

int nYRadial1, // y-coord of the first radial ending point

int nXRadial2, // x-coord of the second radial ending point

int nYRadial2 // y-coord of the second radial ending point

);

3.2.5 AngleArc BOOL AngleArc(

HDC hdc, // handle to device context

int X, // x-coordinate of circle's center

int Y, // y-coordinate of circle's center

DWORD dwRadius, // circle's radius

FLOAT eStartAngle, // arc's start angle

FLOAT eSweepAngle // arc's sweep angle );

3.2.6 PolyBezier vaø PolyBezierTo

Hàm PolyBezier vẽ hay nhiều đường cong Bézier BOOL PolyBezier(

HDC hdc, // handle to device context

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

);

(41)(42)

Thuộc tính Ý nghóa Màu (background color)

Chế độ (background mode) Vị trí hành (curent position) Chế độ vẽ (drawing mode) Bút vẽ (pen)

Màu thứ hai bút vẽ PS_SOLID Tắt/mở màu

Điểm hành cho thao tác LineTo, ArcTo… Thao tác vẽ boolean điểm vẽ

Màu, độ rộng kiểu vẽ đường Hàm để thay đổi màu chế độ

COLORREF SetBkColor(HDC hdc, COLORREF crColor); int SetBkMode(HDC hdc, int iBkMode);

iBkMode OPAQUE TRANSPARENT

3.4 Bút vẽ (pen)

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

• Bút vẽ chia sẻ các chương trình thiết bị

• GDI cung cấp sẵn số bút vẽ Người sử dụng tự tạo thêm bút vẽ khác • Bút vẽ dùng độc lập thiết bị thực chất bút vẽ luận lý Khi GDI vẽ,

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

3.4.1 Tạo dùng bút vẽ có sẵn

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

Bút vẽ tham chiếu đến handle Hàm GetStockObject cho phép lấy handle một bút vẽ có sẵn

(43)

hpenOld = SelectObject(hdc, hPenNew);

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

SelectObject(hdc, hPenOld);

3.4.2 Tạo bút vẽ

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

Haøm CreatePen

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ó giá trị định nghĩa sẵn 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

Haøm CreatePenIndirect

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

typedef struct tagLOGPEN { // lgpn UINT lopnStyle;

POINT lopnWidth; COLORREF lopnColor; } LOGPEN;

(44)

3.4.3 Ví dụ minh hoạ 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[] = {

(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; }

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

Chế độ vẽ thao tác Boolean điểu khiển cách GDI vẽ điểm, đường, hình Chế độ vẽ cịn được gọi chế độ trộn trộn logic với giá trị có sẵn Hàm SetRop2 cho phép thay đổi chế độ vẽ Hàm GetRop2 cho biết chế độ vẽ hành

int SetROP2(

HDC hdc, // handle of device context

int fnDrawMode // drawing mode

);

(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

(47)

4 Vẽ miền

4.1 Miền

• Miền hay hình kín đối tượng hình học có hai phần: miền đường biên bao quanh miền Ta cịn gọi hình hình có tơ màu

• GDI dùng hai cách diễn dịch toạ độ tâm điểm (pixel-centered) giao điểm lưới (grid-intersection) để vẽ miền Toạ độ tâm điểm dùng để vẽ đường toạ độ giao điểm lưới dùng để xén (clipping) Một ảnh hưởng toạ độ giao điểm lưới hình vẽ có kích thước nhỏ pixel so với toạ độ tâm điểm

• Toạ độ tâm điểm dùng với hàm vẽ đa giác, toạ độ giao điểm lưới dùng để vẽ hình chữ nhật, ellipse, chord, pie

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

Hàm Toạ độ Ý nghĩa

Polygon tâm điểm đa giác

PolyPolygon tâm điểm nhiều đa giác

Chord giao điểm lưới một phần cung khép kín đườ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 4.2 Các hàm vẽ miền

(48)

4.2.1 Vẽ đa giác – Polygon PolyPolygon

Hàm Polygon vẽ đa giác cách nối điểm dùng bút vẽ sẵn có ngữ cảnh thiết bị Nếu điểm đầu điểm cuối không trùng nhau, đoạn thẳng thêm vào để nối Miền bên tơ chổi vẽ có sẵn ngữ cảnh thiết bị

BOOL Polygon(

HDC hdc, // handle to device context

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

);

Hàm PolyPolygon vẽ nhiều đa giác với lệnh gọi Chương trình vẽ nhiều đa giác dùng hàm chạy nhanh gọi nhiều lần hàm vẽ đa giác

BOOL PolyPolygon(

HDC hdc, // handle to device context

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

);

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

BOOL Rectangle(

HDC hdc, // handle to device context

(49)

Hình 4-1 Rectangle

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

HDC hdc, // handle to device context

(50)

Hình 4-2 RoundRect

Vẽ khung chữ nhật int FrameRect(

HDC hDC, // handle to device context

CONST RECT *lprc, // pointer to rectangle coordinates HBRUSH hbr // handle to brush

);

Tô chữ nhật int FillRect(

HDC hDC, // handle to device context

CONST RECT *lprc, // pointer to structure with rectangle HBRUSH hbr // handle to brush

);

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

HDC hDC, // handle to device context

CONST RECT *lprc // pointer to structure with rectangle );

4.2.3 Ellipse, Pie vaø Chord BOOL Ellipse(

HDC hdc, // handle to device context

(51)

Hình 4-3 Ellipse

BOOL Pie(

HDC hdc, // handle to device context

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

(52)

Hình 4-4 Pie

BOOL Chord(

HDC hdc, // handle to device context

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

int nYRadial1, // y-coord of the first radial's endpoint int nXRadial2, // x-coord of the second radial's endpoint int nYRadial2 // y-coord of the second radial's endpoint );

Hình 4-5 Chord

4.3 Các thuộc tính tô màu

(53)

Thuộc tính vẽ Ý nghóa

Màu (Background Color): Màu thứ hai bút vẽ chổi vẽ đường gạch Chế độ (Background Color): Bật / Tắt màu

Chổi vẽ (Brush): Màu để tô miền hình Gốc chổi vẽ (Brush Origin): Vị trí gốc chổi vẽ đường gạch Chế độ vẽ(Drawing Mode) Thao tác Boolean với

Bút vẽ (Pen) Màu, độ rộng kiểu đườngbiên Chế độ tô đa giác (Polygon Fill Mode) Để tô màu đa giác

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

int SetPolyFillMode(

HDC hdc, // handle of device context

int iPolyFillMode // polygon fill mode

);

Hàm để thay đổi màu SetBkColor COLORREF SetBkColor(

HDC hdc,

COLORREF crColor );

Và hàm để thay đổi chế độ SetBkMode int SetBkMode(

HDC hdc, int iBkMode );

(54)

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

4.4 Chổi vẽ (Brushes)

Chổi vẽ thuộc tính để tơ Chổi vẽ tạo thành phần: Kiểu (Style), màu (color) mẫu (Pattern) Kích thước chỗi vẽ tính pixel x pixel Chổi vẽ chia sẻ giữa chương trình thiết bị Windows cung cấp sẵn số chổi vẽ (stock brushes) Ngoài ra, người sử dụng tự tạo chổi vẽ riêng Ta quản lý chổi vẽ handle đến chổi vẽ gọi HBRUSH

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

(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);

(56)

SelectObject để chọn chổi vẽ vừa tạo Sau 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 chổi vẽ gạch chéo 45 độ có màu xanh dương vẽ hình ellipse bằng chổi vẽ

case WM_PAINT:

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

HBRUSH hbr;

GetClientRect(hWnd, &rt);

hbr = CreateHatchBrush(HS_BDIAGONAL, RGB(0,0,0xff)); SelectObject(hdc, hbr);

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

DeleteObject(hbr);

break;

Chổi vẽ tạo sẵn cửa sổ vừa tạo giải phóng cửa sổ bị huỷ

case WM_CREATE:

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

break; case WM_PAINT:

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

GetClientRect(hWnd, &rt);

SelectObject(hdc, hbr);

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

break; case WM_DESTROY:

DeleteObject(hbr);

(57)

4.4.3 Tạo trực tiếp chổi vẽ Hàm CreateSolidBrush

Hàm CreateSolidBrush tạo chổi vẽ luận lý đặc với màu quy định tham số crColor

HBRUSH CreateSolidBrush(

COLORREF crColor // brush color value );

Haøm CreateHatchBrush

Hàm CreateSolidBrush tạo chổi vẽ luận lý đường gạch quy định bởi tham số fnStyle có màu clrref

HBRUSH CreateHatchBrush( int fnStyle, // hatch style COLORREF clrref // color value );

Xem hàm CreateBrushIndirect bên để biết giá trị hợp lệ cho fnStyle

Haøm CreatePatternBrush

Hàm CreatePatternBrush tạo chổi vẽ từ mẫu xác định bitmap nhớ.

HBRUSH CreatePatternBrush( HBITMAP hbmp // handle to bitmap );

4.4.4 Tạo gián tiếp chổi vẽ Haøm CreateBrushIndirect

Hàm CreateBrushIndirect kết hợp tất hàm tạo chổi vẽ kể trên, cho phép tạo chổi vẽ với kiểu, màu, mẫu từ cấu trúc chổi vẽ luận lý cho trước

(58)

LONG lbHatch; } LOGBRUSH;

lbStyle

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

Giá trị Ý nghóa

BS_DIBPATTERN Mẫu chổi vẽ định nghĩa 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 Choåi vẽ rỗng

BS_NULL Chổi vẽ rỗng, giống BS_HOLLOW

BS_PATTERN Chỗi vẽ có mẫu định nghĩa bitmap nhớ BS_PATTERN8X8 Tương tự BS_PATTERN

BS_SOLID Chổi vẽ đặc lbColor

Màu chổi vẽ Nếu lbStyle BS_HOLLOW hay BS_PATTERN, lbColor không có ý nghóa

lbHatch

Khi lbStyle BS_HATCHED, lbHatch rõ cách thức gạch, quy định hướng đường gạch

Giá trị Ý nghóa

HS_BDIAGONAL Gạch theo góc 45 độ từ lên từ trái sang phải

(59)

4.5 Ví dụ chổi vẽ

Chương trình minh hoạ Brush tạo chổi vẽ người sử dụng định nghĩa Tập tin chương trình nguồn brush.cpp 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) {

(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:

(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); }

(62)

4.6 Miền thuộc tính bút vẽ PS_INSIDEFRAME

Bút vẽ có độ rộng thuộc tính PS_INSIDEFRAME kết hợp với vẽ miền cho biết bút vẽ sẽđược tô đậm vào phần miền Hình vẽ minh hoạ bút vẽ có thuộc tính Đoạn chương trình tương ứng 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;

(63)

Hình 4-10 – PS_INSIDEFRAME style

4.7 Con đường (Paths)

Con đường tập hợp đường thẳng cung GDI lưu trữ nội Con đường được xem mở rộng miền Sau tạo đường vẽ đường viền tơ màu

4.7.1 BeginPath vaø EndPath

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

(64)

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

Các hàm thao tác đường bao gồm StrokePath, FillPath StrokeAndFillPath

Hàm StrokePath vẽ đường viền đường hành bút vẽ sẵn có ngữ cảnh thiết bị

BOOL StrokePath(HDC hdc);

Hàm FillPath đóng tất đường mở bên đường hành tô miền bên đường chổi vẽ hành chế độ tô đa giác hành

BOOL FillPath(HDC hdc);

Hàm StrokeAndFillPath làm công việc hai hàm trên, đóng tất đường mở bên đường hành, vẽ đường viền đường với bút vẽ hành tô miền bên đường chổi vẽ hành chế độ tô đa giác hành

BOOL StrokeAndFillPath(HDC hdc );

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

Chương trình minh hoạ vẽ cánh hoa đường PolyBezier nội vào đường Sau dùng hàm StrokeAndFillPath để vẽ đường viền tô màu cánh hoa

(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

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) {

(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;

(67)

mới với điểm đầu tâm, điểm cuối dùng để tạo điểm điều khiển cho PolyBezierTo

(68)

5 Xuất Văn

5.1 Tổng quan

Văn (text) phương tiện xuất Cơ chế xuất văn GDI Windows hoàn toàn khác với 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 theo định hướng dòng phù hợp với thiết bị xuất tương tự máy đánh chữ

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

• Cách tiếp cận xuất văn định hướng hình Có thể định vị xuất liệu đến vị trí hình theo dịng cột

gotoxy(x,y);

cout << “Hello, world\n”;

• Cách tiếp cận GDI Windows định hướng pixel GDI cho phép định vị văn xuất đến pixel Người sử dụng dễ dàng trộn lẫn văn với xuất liệu hình học khác, dễ dàng trộn lẫn văn có kích thước khác

• Vì giao diện Windows giao diện đồ hoạ, văn xuất đến đơn vị nhỏ pixel, qui trình xuất khơng khác vẽ đối tượng hình học khác nên ta cịn nói xuất văn bản vẽ text

• Font dùng để vẽ văn Một font bao gồm mẫu mơ tả hình dạng kích thước của ký tự (chữ, số, dấu…) Mỗi thiết bị GDI hỗ trợ hay nhiều font

5.2 Các hàm xuất văn

(69)

ExtTextOut

Hàm ExtTextOut vẽ dòng ký tự điểm qui định, dùng font hành, màu hành, màu chữ hành Ngồi cung cấp hình chữ nhật để xén, làm tối (opaque), kết hợp hai

BOOL ExtTextOut(

HDC hdc, // handle to device context

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

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

UINT cbCount, // number of characters in string

CONST INT *lpDx // pointer to array of intercharacter spacing

// values

);

fuOptions lấy kết hơp giá trị

Giá trị Ý nghóa

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

ETO_OPAQUE Màu hành dùng để tơ hình chữ nhật

lpDx tuỳ chọn, trỏ đến mảng giá trị qui định khoảng cách ký tự nhau chuỗi

TabbedTextOut

(70)

);

Hình 5-1 - TabledTextOut

Ví dụ: Sắp theo cột 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

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

(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);

(72)

break; default:

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

return 0; }

DrawText

Hàm DrawText xuất văn có định dạng bên hình chữ nhật Văn định dạng theo tab, xuống dòng, canh ký tự

int DrawText(

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

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

);

uFormat

Qui định cách định dạng Có thể kết hợp cờ sau:

Giá trị Ý nghóa

DT_BOTTOM Dùng với DT_SINGLELINE để canh

DT_CALCRECT Tính kích thước (chiều rộng cao) text không vẽ Trả chiều cao văn định dạng

DT_CENTER Canh

DT_EXPANDTABS Dùng tab, tab stop ký tự

(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)

(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; }

GrayString

Haøm GrayString vẽ văn màu xám BOOL GrayString(

HDC hDC, // handle to the device context

HBRUSH hBrush, // handle to the brush for graying

GRAYSTRINGPROC lpOutputFunc, // pointer to the callback function LPARAM lpData, // pointer to application-defined data

int nCount, // number of characters to output

int X, // horizontal position

int Y, // vertical position

(75)

Hình 5-4 – GrayString

Ví dụ: Đoạn chương trình sau cho xuất liệu 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:

(76)

Khoảng cách ký tự Số pixel thêm vào ký tự để canh lề Canh lề (trái, phải,…) Quan hệ với điểm mốc

Màu chữ Màu thân chữ văn 5.3.1 Màu văn

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

Chế độ OPAQUE (mặc nhiên) vẽ màu lên phần character cell chữ văn Ta dùng hàm SetColor để thay đổi màu chữ văn

COLORREF SetTextColor(

HDC hdc, // handle of device context

COLORREF crColor // text color

);

5.3.2 Canh haøng (text alignment)

Canh hàng văn cho phép thay đổi vị trí tương đối văn so với điểm mốc Hàm SetTextAlign cho phép canh hàng văn

UINT SetTextAlign(

HDC hdc, // handle of device context

UINT fMode // text-alignment flag );

fMode có giá trị

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, ta gọi:

(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++) {

(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); }

(79)

Font VNI-Times nghieâng

Font Courier New Bold

Font Wingdings : 

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

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

Raster font, gọi Bitmap Font, font mà ký tự lưu trữ thành bitmap pixel Một font raster thiết kế với kích thước cố định tỉ lệ ngang dọc định Bitmap font khả phóng to thu nhỏ

Vector Font định nghĩa dãy đoạn thẳng cung nối với Vector Font phóng to thu nhỏ, sử dụng thiết bị có độ phân giải khác nhau, chuyển qua bitmap không bị thông tin Vector khơng tốt kích thước nhỏ lớn TrueType Fonts sử dụng từ Windows 3.1 Trong TrueType Font, ký tự gồm 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 phóng to thu nhỏ (phần chương trình gọi hints) Khả này đặc biệt hữu dụng để sửa sai font sử dụng kích thước nhỏ nhỏ Nhờ TrueType Font phóng to thu nhỏ đến kích thước Kết liệu thuộc dạng bitmap đưa lên hình hay máy in, TrueType font tạo điều kiện dễ dàng để đạt đến WYSIWYG

Windows 95 coù 13 font chuaån: Courier New

Courier New Bold

Courier New Italic

Courier New Bold Italic Times New Roman

Times New Roman Italic

(80)

Point đơn vị đo kích thước cho font, 1/72 inch 5.4.2 Tạo sử dụng font

Có thể tạo font trực tiếp gián tiếp

Để tạo trực tiếp font, ta dùng hàm CreateFont CreateFont

Hàm CreateFont tạo font luận lý với đặc trưng quy định tham số Font sau tạo dùng cho thiết bị

HFONT CreateFont(

int nHeight, // logical height of font

int nWidth, // logical average character width int nEscapement, // angle of escapement int nOrientation, // base-line orientation angle int fnWeight, // font weight

DWORD fdwItalic, // italic attribute flag

DWORD fdwUnderline, // underline attribute flag DWORD fdwStrikeOut, // strikeout attribute flag DWORD fdwCharSet, // character set identifier DWORD fdwOutputPrecision, // output precision DWORD fdwClipPrecision, // clipping precision DWORD fdwQuality, // output quality DWORD fdwPitchAndFamily, // pitch and family

LPCTSTR lpszFace // pointer to typeface name string );

Xem cấu trúc font luận lý LOGFONT để biết ý nghĩa tham số

Để tạo gián tiếp font ta dùng hàm CreateFontIndirect cấu trúc LOGFONT LOGFONT

(81)

BYTE lfCharSet; BYTE lfOutPrecision; BYTE lfClipPrecision; BYTE lfQuality;

BYTE lfPitchAndFamily;

TCHAR lfFaceName[LF_FACESIZE]; } LOGFONT;

lfHeight

Chiều cao tính theo đơn vị luận lý character cell bao gồm internal-leading nhưng khơng tính external leading Khi có giá trị > 0, qui định khoảng cách dịng (line spacing) Khi 0, GDI dùng chiều cao để tìm font khớp Khi giá trị lfHeight > GDI chuyển sang đơn vị toạ độ thiết bị lấy giá trị tuyệt đối của giá trị để chọn font có chiều cao khớp

Trong chế độ MM_TEXT ta 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ý ký tự font Thơng thường lfWidth có giá trị

lfWeight

Cho biết "độ mập" font, có giá trị từ đến 1000 Giá trị 400 font bình thường 700 bold

Giá trị Weight

(82)

Giá trị TRUE qui định gạch lfStrikeOut

Giá trị TRUE qui định gạch ngang lfCharSet

Qui định chữ, 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ó giá trị

DEFAULT_QUALITY DRAFT_QUALITY PROOF_QUALITY

lfPitchAndFamily

Qui định họ font pitch Hai bit thấp qui định picth font

DEFAULT_PITCH FIXED_PITCH VARIABLE_PITCH

Bít đến qui định họ font

FF_DECORATIVE Old English ví dụ FF_DONTCARE

FF_MODERN Pica, Elite, and CourierNew

(83)

);

5.5 Các thông số văn baûn

Windows cung cấp số hàm tiện ích cung cấp thông tin văn Cấu trúc SIZE thường dùng hàm tính toán văn

SIZE

Cấu trúc SIZE định nghĩa chiều rộng chiều cao hình chữ nhật

typedef struct tagSIZE { // siz LONG cx;

LONG cy; } SIZE;

GetTextExtentPoint32

Hàm GetTextExtentPoint32 tính chiều rộng chiều cao chuỗi văn BOOL GetTextExtentPoint32(

HDC hdc, // handle to device context LPCTSTR lpString, // pointer to text string int cbString, // number of characters in string LPSIZE lpSize // pointer to structure for string size );

TEXTMETRIC

Cấu trúc TEXTMETRIC chứa thông tin font vật lý Tất kích thước dùng đơn vị luận lý, nghĩa phụ thuộc vào chế độ toạ độ hành

typedef struct tagTEXTMETRIC { // tm LONG tmHeight;

(84)

BCHAR tmLastChar; BCHAR tmDefaultChar; BCHAR tmBreakChar; BYTE tmItalic;

BYTE tmUnderlined; BYTE tmStruckOut; BYTE tmPitchAndFamily; BYTE tmCharSet; } TEXTMETRIC;

GetTextMetrics

Hàm GetTextMetrics cho biết thông tin metric font hành chọn BOOL GetTextMetrics(

HDC hdc, // handle to device context

(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);

(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 Vieät Nam VNI-Times co 24 nghieâ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));

(87)

5.6 Font vaø Path

Các hàm xuất văn TextOut, DrawText… dùng font hành để xuất chữ Các hàm này định nghĩa điểm bên path Để đường ký tự font tạo nên điểm trong path, ta dùng chế độ TRANSPARENT

Hình 5-7 – Font Path

Sau trích đoạn hàm WndProc chương trình tạo nên xuất liệu

// Font_Path.cpp int nPenWidth = 2;

COLORREF crPen = RGB(0xff,0,0);

(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);

(89)

6 Bộ định thời

6.1 Giới thiệu

Bộ định thời thiết bị xuất đặn “lưu ý” ứng dụng sau khoảng thời gian qui định trước Chương trình định khoảng thời gian Windows gởi thông điệp WM_TIMER để báo hiệu khoảng thời gian trôi qua Bộ định thời cho phép tạo ứng dụng có tính động, chẳng hạn đồng hồ để hiển thị thời gian Bộ định thời cịn Windows có được số tính như:

• Đa nhiệm – Chương trình tự cắt chức để xử lý riêng thông qua định thời, nhờ nhanh chóng trả quyền điều khiển cho Windows

• Duy trì cập nhật thơng báo trạng thái hoạt động máy • Kết thúc chương trình demo tự chạy

• Cài đặt chức autosave

• Cài đặt chương trình đa phương tiện • Các chương trình trò chơi

Bộ định thời bảo đảm cho phép chương trình giành lại quyền điều khiển sau thoát khỏi thủ tục cửa sổ

6.2 Các hàm thao tác định thời

Ta cấp phát định thời cách dùng hàm SetTimer giải phóng định thời

KillTimer

UINT_PTR SetTimer(

(90)

HWND hWnd, // handle to window UINT_PTR uIDEvent // timer identifier

);

6.3 Hoạt động định thời

Khi lập trình mơi trường DOS, ta tạo định thời cách bẫy ngắt cứng (như Int hay Int 13) Các ngắt xảy sau tick (mỗi tick 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 ngắt cứng Chính hệ điều hành Windows xử lý ngắt cứng thay cho ứng dụng Windows trì biến đếm cho định thời giảm biến đếm sau tick Thông điệp WM_TIMER gởi vào hàng đợi thông điệp khi biến đếm giảm đến

Cơ chế hoạt động bảo đảm định thời khơng ngắt ngang q trình xử lý thơng điệp thông điệp định thời WM_TIMER đưa vào hàng đợi trước xử lý

Trong Windows Me, 98, 97, 95 độ phân giải định thời 55 ms (mili giây) Windows NT, Windows 2000, Windows XP, độ phân giải 10 ms Các ứng dụng nhận thông điệp định thời nhanh độ phân giải nêu Và khoảng thời gian để gởi thông điệp không hoàn toàn giá trị quy định hàm SetTimer Ví dụ, gọi SetTimer với khoảng cách định thời 1000 ms Windows Me, 98, 95 sau 989 ms, thông điệp gởi lần (tại sao) Tuy nhiên định thời khơng đồng

6.4 Tính khơng đồng định thời

Thông điệp WM_TIMER đưa vào hàng đợi thông điệp với thông điệp khác theo thứ tự thời điểm phát sinh thơng điệp Vì ta qui định khoảng cách hai thơng điệp là 1000 ms không bảo đảm nhận thông điệp sau 1000 ms, hay 989 ms Nếu ứng dụng cịn bận xử lý thơng điệp khác khoảng thời gian dài giây, nó không nhận thông điệp WM_TIMER khoảng thời gian Cần lưu ý WM_TIMER thơng điệp có độ ưu tiên thấp

(91)

Tham số nIDEvent có ý nghĩa ta cần tạo nhiều định thời ứng dụng Tham số lpTimerFunc trỏ đến thủ tục định thời để xử lý thông điệp Nếu tham số bằng NULL, thông điệp WM_TIMER gởi đến cho thủ tục cửa sổ

Ta sử dụng định thời cách gọi hàm SetTimer với thủ tục định thời có giá trị NULL xử lý thông điệp WM_TIMER thủ tục cửa sổ

Ví dụ 1: Chương trình bounce

Chương trình bounce sau vẽ hình tròn nhỏ di chuyển tự động bên vùng client Ta dùng hàm SetTimer xử lý thông điệp WM_TIMER Hình trịn nhỏ tự động thay đổi màu sau khoảng thời gian qui định trước (1 giây) Tham số TimerProc với giá trị NULL làm phát sinh thông điệp WM_TIMER Cách gọi sau:

SetTimer(hWnd, ID_MOVE_TIMER, 10, NULL);

SetTimer(hWnd, ID_CHANGECOLOR_TIMER, 1000, NULL);

Chương trình gồm tập tin chương trình nguồn: Stdafx.h stdafx.cpp Bounce.cpp

(92)

Hình 6-2 Cửa sổ chương trình

Sau 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};

(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 < && dx < 0) dx = -dx;

if (y + rBall > rClient.bottom && dy > 0) dy = -dy; if (y - rBall < && dy < 0) dy = -dy;

InvalidateRect(hWnd, &r, TRUE); break;

case ID_CHANGECOLOR_TIMER: ++nCrIdx %= nColors; break;

(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

Chương trình hoạt động tương tự chương trình ví dụ ta qui định hàm xử lý timer sau:

SetTimer(hWnd, ID_MOVE_TIMER, 10, TimerProc);

SetTimer(hWnd, ID_CHANGECOLOR_TIMER, 1000, TimerProc);

Sau 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;

(95)

if (y - rBall < && 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:

(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

(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:

(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 < && 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 < && pb->dy < 0) pb->dy = -pb->dy;

InvalidateRect(hWnd, &r, TRUE); break;

case WM_PAINT:

(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

(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);

(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)),

(102)

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

Windows 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 đặc điểm Windows khả WYSIWYG, người sử dụng tương tác với chương trình

Một thao tác tương tác môi trường Windows kéo lê (dragging) Người sử dụng kéo lê để vẽ, 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ạ

7.1 SetCapture, GetCapture vaø ReleaseCapture

Kéo lê môi trường Windows thực nhờ chế bắt giữ chuột SetCapture

Hàm SetCapture bắt giữ trỏ chuột thành sở hữu cửa sổ Khi cửa sổ bắt giữ được chuột nhập liệu từ chuột gởi cho cửa sổ đó, dù vị trí trỏ chuột hay ngồi đường biên cửa sổ Tại thời điểm, có cửa sổ bắt giữ chuột

Nếu trỏ chuột khỏi phạm vi cửa sổ bắt giữ chuột, nhập liệu từ chuột được gởi cho cửa sổ mắt chuột bấm

HWND SetCapture(

HWND hWnd // handle of window to receive mouse capture );

Tham số hWnd cho biết cửa sổ bắt giữ chuột Hàm trả handle đến cửa sổ trước đó bắt giữ chuột, NULL trước chưa có cửa sổ bắt giữ chuột

(103)

BOOL ReleaseCapture(VOID)

GetCapture

Hàm GetCapture cho biết cửa sổ bắt giữ chuột (nếu có) HWND GetCapture(VOID)

Ta dùng hàm GetCapture để xác định người sử dụng có q trình tương tác với chương trình cách kéo lê chuột để thao tác (di chuyển, thay đổi kích thước, quay…) đối tượng hay không

7.2 Kéo lên đối tượng cách xố vẽ lại Ví dụ ứng dụng – Chương trình ObjectDrag V1

Chương trình sau minh hoạ sử dụng hàm kể để thao tác đối tượng đồ hoạ Chương trình ban đầu vẽ hình ellipse, người sử dụng sau thay đổi vị trí hình ellipse cách đưa trỏ chuột vào bên hình, bấm mắt trái kéo lê

Khi người sử dụng bấm mắt trái chuột bên hình ellipse, cursor đổi sang hình dạng khác cho biết người sử dụng trình kéo lê Khi trỏ chuột kéo lê đến đâu, hình ellipse di chuyển theo đến đó, điều thực nhờ xử lý thông điệp WM_MOUSEMOVE, hàm GetCapture gọi xử lý thông điệp để xác định trỏ chuột có bị bắt giữ khơng, nói cách khác, chuột kéo lên khơng Ta di chuyển hình cách cập nhật vị trí, sau xố hình cũ vẽ lại hình vị trí Trước con chuột bị bắt giữ cửa sổ nhờ gọi hàm SetCapture xử lý thông điệp WM_LBUTTONDOWN Việc bắt giữ chuột bảo đảm trình người sử dụng kéo lê, nếu trỏ chuột phạm vi cửa sổ thông điệp chuột 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 cách buông mắt chuột Hàm ReleaseCapture gọi để giải phóng chuột

Chương trình gồm số tập tin tài nguyên

small.ico ObjectDrag.ico

resource.h

Và số tập tin chương trình nguồn

(104)(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);

(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; }

7.3 Kéo lên đối tượng chọn chế độ vẽ R2_NOTXORPEN

Trong cách tiếp cận trên, ta di chuyển hình cách xố cũ vẽ Điều làm cho cửa sổ client bị chớp trình kéo lê Hơn hình phức tạp địi hỏi thời gian để vẽ hoạt động chương trình khơng hiệu Một cách tiếp cận đại vẽ đối tượng tạm, thông thường đường biên đối tượng thực sự, hay đơn giản hình chữ nhật ngoại tiếp đối tượng, sau kéo lê để di chuyển đối tượng tạm Khi người sử dụng buông mắt chuột để kết thúc trình kéo lê, vẽ lại đối tượng thực

(107)

Có 16 chế độ vẽ, ta quan tâm đến hai chế độ vẽ R2_XORPEN R2_NOTXORPEN Cả hai chế độ vẽ có chung đặc điểm hình vẽ hai lần, khơi phục lại trạng thái trước vẽ Với hai chế độ vẽ kể trên, đối tượng tạm được kéo lê Ta vẽ lại lần hình dáng vị trí cũ, thao tác khôi phục lại phần chưa vẽ, sau ta vẽ lại đối tượng hình dáng, vị trí Chế độ R2_XORPEN phù hợp với cửa sổ có đen R2_NOTXORPEN phù hợp với cửa sổ có 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 hàm SetCapture, GetCapture, ReleaseCapture kết hợp với chế độ vẽ R2_NOTXORPEN kể để thao tác đối tượng đồ hoạ Chương trình ban đầu vẽ hình ellipse, người sử dụng sau thay đổi vị trí của hình ellipse cách đưa trỏ chuột vào bên hình, bấm mắt trái kéo lê Khi người sử dụng bấm mắt trái chuột bên hình ellipse, cursor đổi sang hình dạng khác cho biết người sử dụng trình kéo lê Đồng thời hình ellipse tạm với nét vẽ PS_DOT, chổi vẽ rỗng vẽ chồng lên hình ellipse có chế độ vẽ R2_NOTXORPEN Khi trỏ chuột kéo lê đến đâu, hình ellipse tạm di chuyển theo đến đó, điều thực nhờ xử lý thông điệp WM_MOUSEMOVE, hàm GetCapture gọi xử lý thông điệp để xác định trỏ chuột có bị bắt giữ khơng, nói cách khác, chuột kéo lên không Ta di chuyển hình elippse tạm cách vẽ lại để khơi phục phần bị hình vẽ lên, cập nhật vị trí, sau vẽ lại hình ellipse tạm này, 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ê, vẽ lại đối tượng thực vị trí Chương trình gồm số tập tin tài nguyên

small.ico ObjectDrag.ico

resource.h

Và số tập tin chương trình nguồn

ObjectDrag.rc StdAfx.h StdAfx.cpp

(108)(109)(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:

(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);

(112)

Ví dụ ứng dụng – Chương trình ObjectDrag V6

Chương trình ObjectDrag V6 cho phép 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 hình ellipse vẽ trước vẽ hình ellipse mới Nếu vị trí chuột bấm trong hình ellipse vẽ di chuyển hình ellipse theo chiều di chuyển chuột Người sử dụng thay đổi thuộc tính tơ màu tơ hình ellipse vẽ

(113)

Hình 7-8 Cửa sổ chương trình sau di chuyển ba hình đầu vẽ thêm 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) {

(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 //

(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); }

(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;

(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); }

(118)

8 Sử dụng tài nguyên

Window cho phép người sử dụng đưa vào chương trình tài nguyên thuộc loại biểu tượng (icons), trỏ (cursors), bitmap, bảng chuỗi (string tables), bảng phím tắt (Accelerator), hộp hội thoại (Dialog), thực đơn (menu), công cụ (toolbar) siêu văn (văn loại HTML) Một số tài nguyên thuộc loại Windows cung cấp sẵn gọi tài nguyên chuẩn Người sử dụng tự tạo tài nguyên khác thông qua tập tin tài ngun có phần RC (Resource Script)

Tập tin tài nguyên văn viết theo cú pháp ngơn ngữ lập trình để mơ tả tài nguyên cấp cho chương trình

(119)

• Biểu tượng chuẩn (standard icons) biểu tượng Windows cung cấp sẵn, những biểu tượng xác định có phần tiếp đầu ngữ IDI_ Windows 95 cung cấp 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 biểu tượng người sử dụng định nghĩa cách tạo tập tin biểu tượng có phần ICO mô tả tập tin tài nguyên tham chiếu đến tập tin Ví dụ sau trích đoạn 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"

(120)

8.1.2 Các thao tác biểu tượng LoadIcon

Hàm LoadIcon nạp tài nguyên icon từ tập tin thực thi trả kết điểm đến biểu tượng nạp

HICON LoadIcon(

HINSTANCE hInstance, // handle to application instance LPCTSTR lpIconName // icon-name string or icon resource

// identifier

);

Để nạp biểu tượng chuẩn ta dùng LoadIcon với hInstance có giá trị NULL DrawIcon

Hàm DrawIcon vẽ biểu tượng vị trí qui định tham số X,Y BOOL DrawIcon(

HDC hDC, // handle to device context int X, // x-coordinate of upper-left corner int Y, // y-coordinate of upper-left corner HICON hIcon // handle to icon to draw );

Ví dụ:

Để nạp biểu tượng 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 kích thước biểu tượng, ta dùng hàm int GetSystemMetrics(

int nIndex // system metric or configuration setting to retrieve );

Với nIndex lấy giá trị sau tương ứng với biểu tượng

(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,

(122)

8.2 Con trỏ (cursor) 8.2.1 Tổng quan

Một trỏ (cursor) hình ảnh kích thước nhỏ để rõ vị trí hành thiết bị trỏ (pointing device) 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, trỏ chuột di chuyển tương ứng

• Người sử dụng qui định hình dạng trỏ cho ứng dụng Khi người sử dụng di chuyển chuột đến ứng dụng đó, trỏ đổi sang hình dạng tương ứng • Người sử dụng qui định trỏ khác vùng khác ứng

duïng

(123)

• Custom Cursors trỏ người sử dụng định nghĩa cách tạo tập tin con trỏ có phần ICO mô tả tập tin tài nguyên tham chiếu đến tập tin Cách định nghĩa tương tự biểu tượng

8.2.2 Caùc thao taùc trỏ LoadCursor

Hàm LoadCursor nạp tài nguyên trỏ từ tập tin thực thi trả kết điểm đến biểu tượng nạp

HCURSOR LoadCursor(

HINSTANCE hInstance, // handle to application instance

LPCTSTR lpCursorName // name string or cursor resource identifier );

ShowCursor

Hàm ShowCursor hiển thị hay làm biến curosr int ShowCursor(

BOOL bShow // cursor visibility flag );

SetCursor

Hàm SetCursor định trỏ cho ứng dụng HCURSOR SetCursor(

HCURSOR hCursor // handle to cursor );

Ví dụ:

SetCursor(LoadCursor(NULL, IDC_WAIT));

8.3 Phím tắt (Keyboard Accelerators)

Phím tắt phương tiện để người sử dụng điều khiển chương trình bàn phím, phím tắt ánh xạ thao tác bàn phím thành 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 dùng thay cho việc chọn chức menu chuột

(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 phím windows định nghĩa sẵn như: VK_F1, VK_F2,… Để sử dụng phím tắt, dùng LoadAccelerator 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)) {

(125)

8.4 Menu 8.4.1 Toång quan

Menu phương tiện để ánh xạ thao tác chọn xử lý với xử lý thực Một menu là danh sách mục qui định tuỳ chọn nhóm tuỳ chọn (một submenu) của ứng dụng Bấm vào mục menu hay menu kích hoạt ựng dụng thực thi lệnh tương ứng

Windows có ba loại menu system menu – top level menu – popup menu Menu tạo với dấu hiệu nhận diện sau (Visual clues):

• Arrow : nested menu • Seperator : group menu • Check mark

• Ellipsis (…)

(126)

8.4.2 Menu template

Người sử dụng định nghĩa menu 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ó lựa chọn

CHECKED GRAYED INACTIVE

MENUBREAK – toplevel menu MENUBARBREAK – popup menu

8.4.3 Menu Handles:

(127)

1 Thiết kế mẫu menu 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 mục menu

2 Xử lý thơng điệp WM_COMMAND phát sinh kích hoạt mục menu, ta xử lý cho trường hợp wParam có giá trị tương ứng với ID mục menu bấm Chương trình sau minh hoạ sử dụng menu đơn giản

Chương trình bounce sau cho phép tạo hình trịn nhỏ di chuyển bên vùng client bằng cách vào menu File – New Ball loại bớt hình vào menu File – Remove Ball

Sau trích đoạn chương trình nguồn:

struct BALL {

(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;

(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 < && 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 < && 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

(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

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

(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 ; 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);

(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; }

8.5 Bitmap 8.5.1 Toång quan

Một bitmap đối tượng đồ hoạ dùng để tạo, thao tác lưu trữ hình ảnh vào nhớ hay tập tin đĩa Các thao tác bimap phóng to, trượt, quay, vẽ…

Windows sẵn cung cấp số bitmap gọi bitmap chuẩn Người sử dụng tự định nghĩa các bitmap riêng gọi custom bitmap

Bitmap loại đối tượng đồ hoạ chọn cho ngữ cảnh thiết bị (tượng tự bút vẽ, chổi vẽ, font…)

Về mặt lưu trữ nội bộ, cấu trúc bitmap bao gồm:

• Phần đầu (header) mơ tả độ phân giải thiết bị ma trận pixel tạo, kích thước hình chữ nhật, kích thước mảng bít biểu diễn pixel…

• Bảng palette luận lý

• Mảng bit định nghĩa mối quan hệ pixel ảnh bitmapped với phần tử bảng palette

(133)

CreateBitmap Creates a bitmap

CreateBitmapIndirect Creates a bitmap

CreateCompatibleBitmap Creates a bitmap compatible with a device

CreateDIBitmap Creates a device-dependent bitmap (DDB) from a DIB

CreateDIBSection Creates a DIB that applications can write to directly

ExtFloodFill Fills an area of the display surface with the current brush

GetBitmapDimensionEx Gets the dimensions of a bitmap

GetDIBColorTable Retrieves RGB color values from a DIB section bitmap

GetDIBits Copies a bitmap into a buffer

GetPixel Gets the RGB color value of the pixel at a given coordinate

GetStretchBltMode Gets the current stretching mode

GradientFill Fills rectangle and triangle structures

LoadBitmap Loads a bitmap from a module's executable file

MaskBlt Combines the color data in the source and destination bitmaps

PlgBlt Performs a bit-block transfer

SetBitmapDimensionEx Sets the preferred dimensions to a bitmap

SetDIBColorTable Sets RGB values in a DIB

SetDIBits Sets the pixels in a bitmap using color data from a DIB

SetDIBitsToDevice Sets the pixels in a rectangle using color data from a DIB

SetPixel Sets the color for a pixel

SetPixelV Sets a pixel to the best approximation of a color

SetStretchBltMode Sets the bitmap stretching mode

StretchBlt Copies a bitmap and stretches or compresses it

StretchDIBits Copies the color data in a DIB

(134)

Hàm CreateBitmap tạo bitmap với độ rộng, chiều cao, định dạng màu HBITMAP CreateBitmap(

int nWidth, // bitmap width, in pixels

intnHeight, // bitmap height, in pixels

UINTcPlanes, // number of color planes

UINTcBitsPerPel, // number of bits to identify color

CONST VOID*lpvBits // color data array

);

CreateCompatibleBitmap

Hàm CreateCompatibleBitmap tạo bitmap tương thích với thiết bị liên kết với ngữ cảnh thiết bị hdc

HBITMAP CreateCompatibleBitmap( HDC hdc, // handle to DC

int nWidth, // width of bitmap, in pixels int nHeight // height of bitmap, in pixels

);

BitBlt

Hàm BitBlt chuyển dịch khối bit liệu màu hình chữ nhật pixels từ ngữ cảnh thiết bị đến ngữ cảnh thiết bị khác.

BOOL BitBlt(

HDC hdcDest, // handle to destination DC

intnXDest, // x-coord of destination upper-left corner

intnYDest, // y-coord of destination upper-left corner

intnWidth, // width of destination rectangle

intnHeight, // height of destination rectangle

HDChdcSrc, // handle to source DC

intnXSrc, // x-coordinate of source upper-left corner

intnYSrc, // y-coordinate of source upper-left corner

DWORDdwRop // raster operation code

(135)

int nHeightDest, // height of destination rectangle HDC hdcSrc, // handle to source DC

int nXOriginSrc, // x-coord of source upper-left corner int nYOriginSrc, // y-coord of source upper-left corner int nWidthSrc, // width of source rectangle

int nHeightSrc, // height of source rectangle DWORD dwRop // raster operation code

);

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ị ảnh bitmap cửa sổ client Bitmap được nhúng vào tập tin tài nguyên Hàm LoadBitmap nạp bitmap vào nhớ, hàm DrawBitmap sử dụng BitBlt để chuyển bit từ ngữ cảnh thiết bị loại nhớ cửa sổ client

Chương trình gồm tập tin tài nguyên:

Campanula.BMP CampanulaSM.BMP ShowBitmap.ICO SMALL.ICO

Và tập tin chương trình nguồn:

(136)

Hình 8-7 Cửa sổ client chương trình

Bản thân tài ngun bitmap dùng hộp hội thoại

Hình 8-8 Hộp hội thoại chương trình

Sau 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"

///////////////////////////////////////////////////////////////////////////// //

(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); }

//

(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:

(139)

Chương trình gồm tập tin tài nguyên:

CampanulaSM.BMP ShowBitmap.ICO SMALL.ICO

Và 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 chương trình

Sau trích đoạn tập tin ShowBitmap.cpp

(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:

(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; }

(142)

9 Custom Menu

Sử dụng menu mức đơn giản, ta dễ dàng ánh xạ thao tác chọn chức menu với thao tác thực thực Bên cạnh Windows cịn cho phép người sử dụng tạo menu phong phú nhờ hỗ trợ bitmap cho mục menu cung cung cấp khả cho người sử dụng tự vẽ menu

9.1 Các hàm thao tác menu

Windows cung cấp loạt hàm cho phép thao tác menu Bảng liệt kê hàm thông dụng hỗ trợ lập trình menu

Function Description

AppendMenu Thêm mục vào menu hay menu

CheckMenuItem Đánh dấu xoá dấu mục menu

CheckMenuRadioItem Đánh dấu mục menu làm cho trở thành

nút radio

CreateMenu Taïo menu

CreatePopupMenu Taïo popup menu

DeleteMenu Xoá menu Item

DestroyMenu Xoá hẳn menu

(143)

GetMenuItemCount Lấy số mục menu

GetMenuItemID Lấy số nhận diện menu item

GetMenuItemInfo Lấy thông tin menu

GetMenuItemRect Lấy kích thước hình chữ nhật bao menu

GetMenuState Lấy giá trị cờ trạng thái menu item

GetMenuString Lấy nội dung văn menu item

GetSubMenu Lấy handle đến menu drop-down menu

kích hoạt menu item

GetSystemMenu Sao chép thay đổi menu hệ thống

InsertMenu Chèn menu item vào menu

InsertMenuItem Chèn menu item vào menu

IsMenu Xác định handle có phải menu handle không

LoadMenu Nạp tài nguyên resource

ModifyMenu Thay đổi menu item

RemoveMenu Xoá menu item

SetMenu Gắn menu với cửa sổ

SetMenuDefaultItem Định menu item cho menu

SetMenuInfo Định lại thông tin cho moät menu

SetMenuItemBitmaps Liên kết bitmap với menu item

SetMenuItemInfo Thay đổi thông tin menu item

(144)

HMENU LoadMenu(

HINSTANCE hInstance, // handle to application instance

LPCTSTR lpMenuName // menu name string or menu-resource

// identifier

);

SetMenu

Hàm SetMenu định menu cho ứng dụng BOOL SetMenu(

HWND hWnd, // handle to window HMENU hMenu // handle to menu );

DrawMenuBar

Hàm DrawMenuBar vẽ lại menu ứng dụng BOOL DrawMenuBar(

HWND hWnd // handle to window with menu bar to redraw );

AppendMenu

Hàm AppendMenu thêm menu vào cuối menu, menu drop-down menu, menu con, hay menu shortcut Ta dùng hàm để qui định nội dung, dáng vẻ hoạt động mục menu

BOOL AppendMenu(

HMENU hMenu, // handle to menu UINT uFlags, // menu-item options

UINT_PTR uIDNewItem, // identifier, menu, or submenu LPCTSTR lpNewItem // menu-item content

);

lpNewItem

Con trỏ đến nội dung, nội dung có ý nghĩa thay đổi tuỳ theo giá trị uFlags có bao hàm giá trị MF_BITMAP, MF_OWNERDRAW, hay MF_STRING

Value Description

(145)

Value Description

MF_BITMAP Dùng bitmap làm menu item lpNewItem chứa handle đến bitmap

MF_CHECKED Đặt dấu check bên cạnh menu item Nếu ứng dụng cung cấp bitmaps check-mark, cờ làm hiển thị dấu check bitmap bên cạnh menu item

MF_DISABLED Làm menu không hiệu lực, không làm mờ MF_ENABLED Làm menu có hiệu lực

MF_GRAYED Làm menu mờ không hiệu lực

MF_MENUBARBREAK Đối với menu, tương đương cờ MF_MENUBREAK Đối với trường hợp khác, dùng đường gạch đứng để ngăn cách cột

MF_MENUBREAK Mục menu đặt vào hàng (đối với menu bar) or hoặc cột (đối với drop-down menu, submenu, 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ẽ đường ngang để ngăn cách MF_STRING Menu có nội dung chuỗi MF_UNCHECKED

InsertMenuItem

Hàm InsertMenuItem chèn phần tử vào menu BOOL InsertMenuItem(

(146)

9.2 Thay đổi menu chương trình

Ta thay đổi menu chương trình cách nạp menu hàm LoadMenu và chọn menu SetMenu Chương trình sau cho phép thay đổi menu chương trình, người sử dụng chọn menu tiếng Anh tiếng Việt không dấu Việc thực cách nạp menu chương trình thực thi

(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

// Hello.rc

#include "resource.h" #include "windows.h"

///////////////////////////////////////////////////////////////////////////// //

(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

(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;

(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;

(151)

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

Người sử dụng tạo menu cách dùng hàm CreateMenu, sau sử dụng hàm AppendMenu để tạo nên thành phần menu

Để tạo menu tự vẽ tham số uFlag hàm AppendMenu có bao gồm thuộc tính MF_OWNERDRAW

BOOL AppendMenu(

HMENU hMenu, // handle to menu to be changed UINT uFlags, // menu-item flags

UINT uIDNewItem, // menu-item identifier or handle to drop-down menu or

submenu

LPCTSTR lpNewItem // menu-item content );

Trình tự tạo mục menu tự vẽ sau:

1) Gọi hàm AppendMenu với tham số uFlags bao gồm giá trị MF_OWNERDRAW

MF_OWNERDRAW Qui định mục menu người sử dụng tự vẽ Trước menu hiển thị lần đầu tiên, cửa sổ sở hữu menu nhận thông điệp WM_MEASUREITEM để lấy chiều dài chiều rộng của mục menu Thơng điệp WM_DRAWITEM sau gởi cho thủ tục cửa sổ dáng vẻ menu cần 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ố kèm wParam giá trị nhận dạng

(152)

Thông điệp WM_DRAWITEM gởi cho cửa sổ chứa menu mục menu với thuộc tính MF_OWNERDRAW cần cập nhật

WM_DRAWITEM

idCtl = (UINT) wParam; // control identifier lpdis = (LPDRAWITEMSTRUCT) lParam; // item-drawing information

Tham số lParam cho biết thông tin mà cửa sổ chứa menu cần phải nắm để vẽ mục menu Các thơng tin lưu 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ể để vẽ menu vào ngữ cảnh thiết bị, thành phần hDC cấu trúc

(153)(154)(155)

help.bmp hinhve.bmp

file.bmp brush.bmp

small.ico VnMenu.ico

Và 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 //

(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 //

(157)

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, {

{"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},

(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) {

(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;

(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;

(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:

(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,

(163)

}

Ví dụ Chương trình VnMenu V2 tạo menu có tiếng Việt cách tự vẽ mục menu tương tự ví dụ có thêm chức cho phép lựa chọn menu tiếng Việt menu tiếng Anh

(164)

Hình 9-8 VnMenu với lựa chọn tiếng Anh

Ta chuyển qua lại tiếng Anh tiếng Việt nhờ trì hai menu riêng rẽ Menu tiếng Anh định nghĩa từ tập tin tài nguyên, menu tiếng Việt có cách tự vẽ mục menu

Trích đoạn chương trình nguồn sau minh hoạ việc chuyển đổi qua lại hai menu

// VnMenu.cpp : Defines the entry point for the application //

#include "stdafx.h" #include "resource.h"

(165)

};

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 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, {

(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) {

(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 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));

(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;

(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);

(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); }

(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; }

(172)(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, {

(174)

},

"&Chổi vẽ", IDB_BRUSH, 7, {

{"Đặc", ID_BRUSH_SOLID, MF_OWNERDRAW}, {"Chéo ngược", ID_BRUSH_BWDIAGONAL, MF_OWNERDRAW}, {"Chéo xi", ID_BRUSH_FWDIAGONAL, MF_OWNERDRAW}, {"Chéo", ID_BRUSH_DIAGCROSS, MF_OWNERDRAW},

{"Ngang dọc", ID_BRUSH_CROSS, MF_OWNERDRAW}, {"Ngang", ID_BRUSH_HORIZONTAL, MF_OWNERDRAW}, {"Dọc", ID_BRUSH_VERTICAL, MF_OWNERDRAW}, },

"&Tùy Choï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},

(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 khun", "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;

(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;

(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;

(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);

}

(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;

(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;

(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) {

(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; }

9.4 Bimap Menu

Người sử dụng tạo menu cách dùng hàm CreateMenu, sau sử dụng hàm AppendMenu để tạo nên thành phần menu

Để 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

(183)(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

{

(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 chép", ID_EDIT_COPY, MF_BITMAP, IDB_EDIT_COPY}, {"Cắt", ID_EDIT_CUT, MF_BITMAP, IDB_EDIT_CUT},

{"Daù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},

(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);

(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 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);

(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;

(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);

(190)

break; default:

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

return 0; }

(191)

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

Nhu cầu tạo cửa sổ lớn: Cửa sổ log, debug, text…, ứng dụng MDI Qui trình tạo cửa sổ bao gồm đăng ký lớp gọi hàm tạo cửa sổ

10.1 Đăng ký lớp cửa sổ (tư tưởng OOP) 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ụ để 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)

{

//

(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; }

10.2 Tạo cửa sổ

Dùng CreateWindow CreateWindowEx HWND CreateWindowEx(

DWORD dwExStyle, // extended window style

LPCTSTR lpClassName, // pointer to registered class name LPCTSTR lpWindowName, // pointer to window name

DWORD dwStyle, // window style

int x, // horizontal position of window int y, // vertical position of window int nWidth, // window width

int nHeight, // window height

HWND hWndParent, // handle to parent or owner window

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

(193)

WS_VISIBLE Tạo cửa sổ hiển thị từ đầu WS_EX_MDICHILD Tạo cửa sổ MDI

WS_EX_TOPMOST Tạo cửa sổ topmost Cửa sổ 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 thơng số kích thước cửa sổ int GetSystemMetrics(

int nIndex // system metric or configuration setting to retrieve );

10.3 Tạo cửa sổ

Đăng ký tạo cửa sổ con, lớp đăng ký cho cửa sổ phải có hàm xử lý 10.3.1 Chương trình MDI V1

(194)

Hình 10-1 Cửa sổ

// 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);

(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;

(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);

(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; }

(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:

(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)

{

(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;

... Khi người sử dụng di chuyển chuột, trỏ chuột di chuyển tương ứng

• Người sử dụng qui định hình dạng trỏ cho ứng dụng Khi người sử dụng di chuyển chuột đến ứng dụng đó,... Tương tác với người sử dụng

Windows 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 đặc điểm Windows khả WYSIWYG, người sử dụng tương tác với chương... cửa sổ có đen R2_NOTXORPEN phù hợp với cửa sổ có 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

Ngày đăng: 20/04/2021, 01:48

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

w