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

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ử

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

int

nHeight

,

// bitmap height, in pixels

UINT

cPlanes

,

// number of color planes

UINT

cBitsPerPel

,

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

int

nXDest

,

// x-coord of destination upper-left corner

int

nYDest

,

// y-coord of destination upper-left corner

int

nWidth

,

// width of destination rectangle

int

nHeight

,

// height of destination rectangle

HDC

hdcSrc

,

// handle to source DC

int

nXSrc

,

// x-coordinate of source upper-left corner

int

nYSrc

,

// y-coordinate of source upper-left corner

DWORD

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