• Cöûa soå che ñoùng laïi hoaëc di chuyeån. • Chöông trình giaùn tieáp gôûi WM_PAINT thoâng qua haøm InvalidateRect. • Cöûa soå ñöôïc cuoán. • Menu popup môû ra roài ñoùng laïi.. Haø[r]
(1)(2)ĐẠI HỌC TÔN ĐỨC THẮNG
TÓM TẮT BÀI GIẢNG
(3)1 Toång quan
1.1 Windows 95 lập trình mơi trường Windows
1.2 Chương trình Windows
1.3 Thủ tục cửa sổ 11
1.4 Môi trường Visual C++ 13
2 Tổng quan Giao Diện Thiết Bị Đồ Hoạ (GDI) 15
2.1 Giới thiệu 15
2.2 Giao diện người dùng 15
2.3 Ngữ cảnh thiết bị (device context) 16
2.4 Quy trình vẽ 18
2.5 Vẽ điểm 19
2.6 Taïo Marker 21
2.7 Vẽ vẽ lại 23
2.8 BeginPaint vaø EndPaint 23
2.9 Vùng hợp lệ không hợp lệ 24
2.10 InvertPixel vaø DrawMarker 25
2.11 GetDC vaø ReleaseDC 26
3 Vẽ đường 28
3.1 Đường 28
3.2 Các hàm vẽ đường 28
3.3 Thuộc tính vẽ đường 33
3.4 Bút vẽ (pen) 34
(4)5 Xuất Văn 60
5.1 Tổng quan 60
5.2 Các hàm xuất văn 60
TextOut 60
ExtTextOut 61
TabbedTextOut 61
DrawText 64
GrayString 66
5.3 Các thuộc tính xuất văn 67
5.4 Fonts 70
CreateFont 72
LOGFONT 72
CreateFontIndirect 74
5.5 Các thông số văn 75
SIZE 75
GetTextExtentPoint32 75
TEXTMETRIC 75
GetTextMetrics 76
5.6 Font vaø Path 79
6 Bộ định thời 81
6.1 Giới thiệu 81
6.2 Các hàm thao tác định thời 81
(5)ShowCursor 115
SetCursor 115
7.3 Phím tắt (Keyboard Accelerators) 115
7.4 Menu 117
7.5 Bitmap 124
LoadBitmap 125
CreateCompatibleBitmap 126
BitBlt 126
StretchBlt 126
8 Custom Menu 134
8.1 Các hàm thao tác menu 134
LoadMenu 135
SetMenu 136
DrawMenuBar 136
AppendMenu 136
InsertMenuItem 137
8.2 Thay đổi menu chương trình 138
8.3 Menu người sử dụng tự vẽ (Owner Draw Menu) 143
8.4 Bimap Menu 174
9 Cửa Sổ Và Nút Điều Khiển 183
9.1 Đăng ký lớp cửa sổ (tư tưởng OOP) 183
9.2 Tạo cửa sổ 184
(6)10.5 Một số ví dụ sử dụng hộp hội thoại modal 218
11 Tương tác với người sử dụng 94
11.1 SetCapture, GetCapture vaø ReleaseCapture 94
SetCapture 94
ReleaseCapture 94
GetCapture 95
11.2 Kéo lên đối tượng cách xoá vẽ lại 95
11.3 Kéo lên đối tượng chọn chế độ vẽ R2_NOTXORPEN 98
SetROP2 98
12 Biến đổi toạ độ 232
12.1 Các thuộc tính GDI 232
12.2 Các chế độ toạ độ GDI 232
SetMapMode 233
GetMapMode 233
12.3 Tịnh tiến nhân tỉ lệ 234
DPtoLP 234
LPtoDP 234
SetViewportOrgEx 235
GetViewportOrgEx 235
SetWindowOrgEx 235
GetWindowOrgEx 235
12.4 Chế độ thiết bị (Device Mapping Mode) 236
(7)13.4 Các thao tác cửa sổ trượt 252
SetScrollRange 252
SetScrollPos 253
ScrollWindow 253
SCROLLINFO 253
SetScrollInfo 254
GetScrollInfo 254
ShowScrollBar 255
13.5 Các ví dụ sử dụng cửa sổ trượt 255
(8)Lời nói đầu
Hệ điều hành Windows với giao diện theo hướng đồ hoạ trở thành môi trường làm việc chuẩn cho người sử dụng máy vi tính cá nhân Việc xây dựng chương trình hoạt động môi trường Windows trở thành yêu cầu thiết yếu cho người xây dựng phần mềm mơi trường DOS hồn thành nhiệm vụ lịch sử
Để theo kịp với trào lưu giới, khoa Công nghệ thông tin Tốn ứng dụng trường Đại học Tơn Đức Thắng cập nhật bổ sung môn Lập trình Windows vào chương trình đào tạo khoa
Tài liệu biên soạn nhằm phục vụ cho sinh viên chuyên ngành Công nghệ thông Tin trường Nội dung bao gồm kỹ thuật cốt yếu lập trình mơi trường Windows sử dụng ngôn ngữ C/C++
Windows cung cấp ngàn hàm để lập trình viên tạo ứng dụng khác nhau, từ soạn thảo văn bản, vẽ đối tượng đồ hoạ, hiển thị hình ảnh, phim video, tạo âm thanh, truyền liệu… Phạm vi tài liệu giới hạn nội dung bao gồm vẽ hình, xuất văn bản, xây dựng giao diện, tạo hộp hội thoại… Từ kiến thức trên, sinh viên có thể tham khảo thêm thư viện Windows cung cấp để xây dựng ứng dụng lớn theo yêu cầu luận văn tốt nghiệp phần mềm thực
Tài liệu mang tính chất tóm tắt kiến thức khơng thể tránh khỏi sai sót Rất mong nhận sự phê bình, đóng góp ý kiến bạn đồng nghiệp sinh viên
(9)1 Toång quan
1.1 Windows 95 lập trình mơi trường Windows • Hệ điều hành 32 bit
• Giao diện người dùng kiểu đồ hoạ (GUI) − Giao diện trực quan (Visual Interface)
− WYSIWYG, người sử dụng tương tác với chương trình
− Các ứng dụng Windows có giao diện thống với dáng vẻ : Một cửa sổ hình chữ nhật, có tiêu đề, menu, hộp hội thoại, trượt …
• Đa nhiệm
− Mỗi chương trình chiếm phần tài nguyên hệ thống có phần nhớ thường trú
− Nhiều chương trình kích hoạt chạy lúc
− Các phiên Windows 16 bít hoạt động đa nhiệm theo chế nonpreemtive − Windows 32 bít hoạt động theo chế preemtive Mỗi chương trình
tách thành thread thực lúc • •Quản lý nhớ
− Mã chương trình liệu dịch chuyển nhớ vật lý − Khả sử dụng nhớ phụ
(10)− Chương trình viết cho Windows khơng điều khiển trực tiếp thiết bị xuất hình hay máy in mà thơng qua ngơn ngữ lập trình đồ hoạ gọi Giao Diện Thiết Bị Đồ Hoạ (Graphics Device Interface: GDI)
− Các phần mềm MS-DOS cần có tập tin điều khiển thiết bị kèm (màn hình, máy in) Chương trình viết cho Windows khơng cần quan tâm đến vấn đề • Kiến trúc hướng thông điệp (message driven)
− Windows ứng dụng Windows hoạt động theo chế truyền, nhận thông điệp, hoạt động chương trình thay đổi tuỳ theo thơng điệp mà nhận được, thông điệp gởi qua lại ứng dụng Windows, ứng dụng với
− Cửa sổ ứng dụng tự động vẽ lại có thay đổi kích thước hay vùng bị che Điều thực nhờ hệ điều hành gởi thông điệp cho chương trình • Thủ tục cửa sổ
− Hệ điều hành lệnh cho ứng dụng nhờ thủ tục cửa sổ (Window Procedure) Hàm cửa sổ cho biết phản ứng chương trình với tác động bên user input…
− Hệ điều hành Windows gởi thơng điệp cho chương trình ứng dụng cách gọi hàm cửa sổ ứng dụng đó, với tham số thơng điệp
• Tài nguyên
− Window cho phép người sử dụng đưa vào chương trình tài nguyên thuộc loại biểu tượng (icons), trỏ (cursors), bitmap, bảng chuỗi (string tables), bảng phím tắt (Accelerator), hộp hội thoại (Dialog), menu, công cụ (toolbar) siêu văn (văn loại HTML)
(11)Hình 1-1 – Biểu tượng chuẩn Windows
1.2 Chương trình Windows 1.2.1 Các tập tin chương trình nguồn
sdafx.h, stdafx.cpp, hello.cpp stdafx.h
#include <windows.h> // C RunTime Header Files #include <stdlib.h>
#include <malloc.h> #include <memory.h> #include <tchar.h>
stdafx.cpp
(12)LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
MSG msg;
// Initialize global strings MyRegisterClass(hInstance);
// Perform application initialization: if (!InitInstance (hInstance, nCmdShow)) {
return FALSE; }
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg); DispatchMessage(&msg); }
return msg.wParam; }
// FUNCTION: MyRegisterClass()
// PURPOSE: Registers the window class ATOM MyRegisterClass(HINSTANCE hInstance) {
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0; wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
(13)hInst = hInstance; // Store instance handle in our global variable hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!hWnd)
return FALSE;
ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd);
return TRUE; }
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) // WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps; HDC hdc;
LPSTR szHello = "Hello, world"; switch (message)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here RECT rt;
GetClientRect(hWnd, &rt);
DrawText(hdc, szHello, strlen(szHello), &rt, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hWnd, &ps); break;
case WM_DESTROY:
PostQuitMessage(0); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
(14)CW Create Window option
DT DrawText option
Ví dụ có sử dụng chương trình
CS_HREDRAW DT_SINGLELINE WM_CREATE
CS_VREDRAW IDC_ARROW WM_DESTROY
CW_USEDEFAULT DT_VCENTER WM_PAINT
DT_CENTER IDI_APPLICATION WS_OVERLAPPEDWINDOW
1.2.3 Kiểu liệu • Đơn
WORD BYTE DWORD INT
PSTR LPSTR UINT
WPARAM LPARAM LRESULT
APIENTRY CALLBACK ( stdcall) • Cấu trúc
MSG Cấu trúc thông điệp WNDCLASSEX Cấu trúc lớp cửa sổ PAINSTRUCT Cấu trúc paint
RECT Cấu trúc hình chữ nhật • Handle (chỉ điểm)
HINSTANCE Handle đến thể chương trình HWND Handle đến cửa sổ
HDC Handle đến ngữ cảnh thiết bị (device context) HICON Handle đến biểu tượng
(15)hdc handle to device context hwnd handle to window
i index
l LONG (long int) lp long pointer
n int pt POINT
by BYTE
i int b f : BOOL
w WORD
sz chuỗi kết thúc ‘\0’ 1.2.5 Hoạt động chương trình
• Chương trình có hai hàm WinMain, WndProc hai hàm phục vụ MyRegisterClass InitInstance
• Trình tự thực chương trình Windows − Đăng ký lớp cửa sổ,
− Tạo cửa sổ hiển thị cửa sổ
− Xử lý thông điệp nhờ vòng lặp message thủ tục cửa sổ
− WinMain không trực tiếp gọi hàm WndProc, hàm gọi Windows • Các hàm Windows
− Windows cung cấp ngàn hàm thư viện − Các hàm sử dụng chương trình bao gồm
(16)TranslateMesage – Chuyển đổi thơng điệp bàn phím DispatchMesage – Gởi thông điệp đến thủ tục cửa sổ BeginPaint, EndPaint – Khởi động kt việc vẽ cửa sổ GetClientRect – Lấy kích thước vùng client cửa sổ DrawText – Hiển thị chuỗi ký tự
PostQuitMessage – Đưa thơng điệp “quit : khỏi chương trình” vào hàng đợi thơng điệp
DefWindowProc – Thực xử lý thông điệp khơng chương trình xử lý.
1.2.6 Hàm (WinMain)
Gồm phần đăng ký lớp, tạo cửa sổ, hiển thị cửa sổ, xử lý thông điệp int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) hInstance : Handle đến chương trình hành
hPrevInstance : Handle đến chương trình trước (Windows 95 : ln ln NULL) Trong Windows 32 bit, hPrevInstance luôn NULL, để có tối đa phiên bản, ta dùng hàm FindWindow
hwndPrev = FindWindow(“HelloAppClass”, 0); if (hwndPrev)
{
// switch to prev ver }
lpCmdLine : Tham số dòng lệnh
nCmdShow : tuỳ chọn cách hiển thị cửa sổ
(17)int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor;
HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; HICON hIconSm;
} WNDCLASSEX;
Đăng ký
Ta đăng ký lớp cửa sổ cách đưa thông tin vào cấu trúc cửa sổ gọi hàm RegisterClassEx
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0; wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, IDI_APPLICATION); RegisterClassEx(&wcex);
1.2.8 Tạo cửa sổ
(18)HMENU hMenu, // handle to menu or child-window identifier HANDLE hInstance, // handle to application instance
LPVOID lpParam // pointer to window-creation data );
HWND CreateWindowEx(
DWORD dwExStyle, // extended window style
LPCTSTR lpClassName, // pointer to registered class name LPCTSTR lpWindowName, // pointer to window name
DWORD dwStyle, // window style
int x, // horizontal position of window int y, // vertical position of window int nWidth, // window width
int nHeight, // window height
HWND hWndParent, // handle to parent or owner window
HMENU hMenu, // handle to menu, or child-window identifier HINSTANCE hInstance, // handle to application instance
LPVOID lpParam // pointer to window-creation data );
Ví dụ
hWnd = CreateWindow(
szWindowClass, // Window class name “Hello program”, // Window name
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // horizontal position of window 0, // vertical position of window CW_USEDEFAULT, // window width
0, // window height
NULL, // parent window handle
NULL, // handle to menu, or child-window identifier hInstance, // handle to application instance
NULL); // pointer to window-creation data
(19)while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg); DispatchMessage(&msg); }
msg biến có kiểu MSG typedef struct tagMSG {
HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; }MSG;
1.3 Thủ tục cửa sổ 1.3.1 Xử lý thông điệp
Thủ tục cửa sổ hàm xử lý thông điệp tương ứng lớp cửa sổ Thủ tục cửa sổ rỏ phản ứng của chương trình với biến cố nhập, qui định cách vẽ lại phần cửa sổ trước bị che khuất
switch (message) {
case WM_CREATE:
// Xu ly thong diep WM_CREATE break;
(20)WM_PAINT : Thông điệp gởi cần vẽ lại cửa sổ WM_CHAR : Thông điệp ký tự bấm
WM_TIMER : Thông điệp định thời
WM_LBUTTONDOWN : Thông điệp gởi mắt trái chuột bấm
WM_DESTROY: Thông điệp rõ Windows huỷ cửa sổ sau nhận lệnh người dùng Các ứng dụng xử lý thông điệp theo cách chuẩn gọi PostQuitMessage(0);
1.3.2 Thông điệp WM_DESTROY cách kết thúc chương trình
• Chương trình kết thúc bình thường phải bảo đảm dọn dẹp giải phóng lại nhớ cũng tài nguyên khác mà chương trình dùng Chương trình kết thúc vịng lặp thơng điệp nhận thơng điệp WM_QUIT
• Chương trình kết thúc nhiều cách: Bằng [System menu] Close, bấm Alt-F4 biểu tượng X góc bên phải Mỗi cách dẫn đến loạt thông điệp gởi cho hàm cửa sổ Hàm cửa sổ xử lý thơng điệp gởi cho hàm xử lý cửa sổ
• Một số thơng điệp hàm cửa sổ quan tâm xử lý WM_DESTROY Xử lý
chuẩn cho thông điệp gọi PostQuitMessage(0) Hàm chèn WM_QUIT vào hàng đợi thông điệp GetMessage trả nhận WM_QUIT kết thúc vịng lặp thơng điệp
• Dãy thơng điệp sau gởi cho hàm cửa sổ người sử dụng bấm tổ hợp phím tắt Alt-F4
WM_SYSKEYDOWN [Alt] bấm WM_SYSKEYDOWN [F4] bấm
(21)1.3.3 Xử lý thông điệp
− Các ứng dụng Windows có giao diện chung giống Điều thực nhờ hàm xử lý thông điệp
− Thông điệp không WndProc quan tâm xử lý gởi cho DefWindowProc xử lý DefWindowProc hàm xử lý thơng điệp, hoạt động giống nhau cho tất ứng dụng Windows
− WndProc gởi thơng điệp cho DefWindowProc ngược lại Ví dụ, người sử dụng nhấn tổ hợp phím Alt-F4 để tắt ứng dụng, loạt thông điệp gởi cho WinProc
− Thông điệp gởi cho hàm cửa sổ trước Hàm cửa sổ gọi DefWindowProc để xử lý thơng điệp khơng quan tâm
− Người sử dụng thay đổi vài hoạt động cách tự xử lý số thơng điệp
1.4 Môi trường Visual C++
(22)Hình 1-2 – Tạo project VC++ 6.0
(23)2 Tổng quan Giao Diện Thiết Bị Đồ Hoạ (GDI)
Giao diện đồ hoạ đặc tính bật hệ điều hành Windows Trong chương này ta trình bày tổng quan vấn đề liên quan đến thao tác tạo tạo xuất liệu đồ hoạ, các thao tác vẽ dùng GDI Windows
2.1 Giới thiệu
• GDI thư viện đồ hoạ Windows, xử lý thao tác xuất kết dạng đồ hoạ lên lên thiết bị xuất khác máy in, máy vẽ GDI tạo ra điểm vẽ, đường, chữ… Windows dùng GDI để tạo giao diện người dùng cửa sổ, biểu tượng, menu, hộp hội thoại
• GDI cho phép vẽ thiết bị logic hay gọi thiết bị ảo bitmaps metafiles
• GDI tạo khả đồ hoạ độc lập thiết bị, cung cấp thủ tục vẽ cho tất các loại thiết bị thay cho loại thiết bị khác hình, máy in, bitmaps metafile
Các thiết bị GDI
• GDI vẽ thiết bị khác nhờ điều khiển thiết bị
• Bộ điều khiển thiết bị cho GDI biết khả vẽ có sẵn thiết bị (nhờ device capability bits) Có cờ cho biết khả thiết bị: vẽ cung, đường thẳng, đa giác, bitmaps text
• Mỗi điều khiển thiết bị phải cung cấp hai thủ tục, vẽ điểm vẽ đường • Bitmap ln ln có hình chữ nhật, hình ảnh lưu trữ nhớ giống
(24)2.2.2 Các đối tượng vẽ logic
GDI dùng đối tượng vẽ logic để có khả độc lập thiết bị Đối tượng vẽ logic mô tả xuất liệu mức cao, độc lập với thiết bị Các đối tượng vẽ bao gồm pens (bút), brushes (chổi vẽ), fonts (kiểu chữ) logical colors (màu luận lý)
2.3 Ngữ cảnh thiết bị (device context)
• Ngữ cảnh thiết bị tập hợp thuộc tính vẽ có pen để vẽ đường, brush để tô màu font để hiển thị văn
• Nhờ ngữ cảnh thiết bị, hàm vẽ trở nên đơn giản với tham số giảm đến mức tối thiểu thuộc tính vẽ lấy từ ngữ cảnh thiết bị Ta tham chiếu đến ngữ cảnh thiết bị nhờ handle đến ngữ cảnh thiết bị (hdc)
Ví dụ :
hdc = BeginPaint(hWnd, &ps); RECT rt;
GetClientRect(hWnd, &rt);
DrawText(hdc, szHello, strlen(szHello), &rt,
DT_CENTER);
EndPaint(hWnd, &ps);
(25)(26)Hình 2-2 – Các vùng xén tạo nên hàng rào không cho phép ứng dụng vẽ lên cửa sổ ứng dụng khác
• Vai trị thứ ba ngữ cảnh thiết bị ngăn cách xuất liệu nhiều chương trình truy xuất thiết bị lúc (vai trị permission slip) Đối với máy in dùng chế spooling đối với cửa sổ ứng dùng, dùng clipping
• Khi ứng dụng muốn vẽ, phải mượn DC từ Windows Borrowing routines Returning routines Description BeginPaint
GetDC
EndPaint ReleaseDC
(27)• Thực thao tác vẽ • Trả lại DC cho Windows
Hai hàm mượn trả DC thông dụng BeginPaint EndPaint, BeginPaint cho handle đến ngữ cảnh thiết bị phần xén vùng client
Việc vẽ Windows thường theo khuôn mẫu sau: case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); // Các thao tác vẽ
EndPaint(hWnd, &ps); break;
Có thể dùng GetDC RealeaseDC để thực thao tác vẽ có biến cố nhập chuột hay bàn phím bấm GetDC trả handle đến toàn vùng client cửa sổ ứng dụng
case WM_CHAR:
hdc = GetDC(hWnd);
// Thao tác vẽ
ReleaseDC(hWnd, hdc); break;
Ta dùng GetWindowDC để vẽ lên phần non-client cửa sổ ứng dụng 2.5 Vẽ điểm
Trong phần ta tìm hiểu chi tiết qui trình vẽ vùng client cửa sổ ứng dụng, và thao tác vẽ cách trực tiếp bật tắt pixel hình Các chương tiếp theo trình bày thao tác vẽ đường, hình có tơ màu, vẽ chữ…
2.5.1 Chương trình Pixel
Chương trình pixel sau thực hiển thị chấm đen vùng client của cửa sổ ứng dụng Chương trình pixel gồm tập tin chương trình nguồn sau:
stdafx.h stdafx.cpp pixel.cpp
// Trich doan pixel.cpp
(28)return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
2.5.2 SetPixel vaø GetPixel
Để vẽ điểm, ta dùng hàm SetPixel, để lấy giá trị pixel xác định cửa sổ ứng dụng, ta dùng hàm GetPixel Trong chương trình trên, ta cịn dùng hàm GetClientRect Khai báo hàm sau:
Hàm SetPixel đặt giá trị đỏ, xanh cây, xanh dương (RGB) cho pixel toạ độ qui định trước
COLORREF SetPixel(
HDC hdc, // handle of device context
int X, // x-coordinate of pixel
int Y, // y-coordinate of pixel
COLORREF crColor // pixel color
);
Hàm GetPixel trả giá trị đỏ, xanh cây, xanh dương pixel vị trí qui định trước
COLORREF GetPixel(
HDC hdc, // handle of device context
int XPos, // x-coordinate of pixel
int nYPos // y-coordinate of pixel
);
COLORREF số DWORD lưu trữ thành phần R,G,B biểu diễn màu Ta dùng macro RGB để tạo màu mong muốn
(29)BOOL GetClientRect(
HWND hWnd, // handle of window
LPRECT lpRect // address of structure for client coordinates );
LPRECT trỏ đến cấu trúc RECT
typedef struct _RECT { // rc LONG left;
LONG top; LONG right; LONG bottom; } RECT;
2.6 Taïo Marker
Chương trình Marker sử dụng SetPixel GetPixel để đánh dấu hình chữ thập nhỏ (+) khi người sử dụng bấm mắt chuột trái Các tập tin chương trình nguồn gồm.
(30)#define MAX_MARKERS 100 #define MARKER_SIZE
void DrawMarker(HDC hdc, POINT pt, int size);
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps; HDC hdc;
int i;
static POINT apt[MAX_MARKERS]; static int nMarkers = 0;
switch (message) {
case WM_LBUTTONDOWN:
if (nMarkers <= MAX_MARKERS) {
apt[nMarkers].x = LOWORD(lParam); apt[nMarkers].y = HIWORD(lParam); nMarkers++;
InvalidateRect(hWnd, NULL, TRUE); }
break;
case WM_RBUTTONDOWN: nMarkers = 0;
InvalidateRect(hWnd, NULL, TRUE); break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); for (i = 0; i < nMarkers; i++)
DrawMarker(hdc, apt[i], MARKER_SIZE); EndPaint(hWnd, &ps);
break; case WM_DESTROY:
PostQuitMessage(0); break;
default:
(31)for (i = 1; i <= size; i++) {
InvertPixel(hdc, x-i, y); InvertPixel(hdc, x+i, y); InvertPixel(hdc, x, y-i); InvertPixel(hdc, x, y+i); }
}
2.6.2 Hoạt động chương trình
Hàm cửa sổ chương trình xử lý bốn thơng điệp WM_LBUTTONDOWN, WM_RBUTTONDOWN, WM_PAINT WM_DESTROY WM_LBUTTONDOWN WM_RBUTTONDOWN thơng điệp mắt chuột, gởi tới cho hàm cửa sổ mắt trái (hoặc phải) chuột bấm Trong Marker, ta xử lý thông điệp WM_LBUTTONDOWN cách thêm điểm vào mảng lưu trữ các điểm gởi thông báo cần vẽ lại nhờ hàm InvalidateRect
Tham số lParam gởi kèm theo thơng điệp cho biết vị trí trỏ chuột thời điểm bấm (hoặc nhả)
2.7 Vẽ vẽ lại
Cửa sổ Windows thường bị che phần hộp hội thoại, menu, trỏ chuột hay cửa sổ khác Khi khơng cịn bị che, phần cần phục hồi Trong số trường hợp đặc biệt phần bị che tương đối nhỏ trỏ chuột di chuyển hay hộp hội thoại mở ra, Windows tìm cách lưu phần bị che để dễ phục hồi sau
Trong đa số trường hợp lại, chế phục hồi phần cửa sổ bị che thân cửa sổ tự nó vẽ lại phần hư Điều thực cửa sổ nhận thông điệp WM_PAINT Windows gởi thông điệp WM_PAINT đến cửa sổ bị che trường hợp sau:
• Cửa sổ che đóng lại di chuyển
• Cửa sổ thay đổi kích thước (lớp cửa sổ phải đăng ký với cờ CS_HREDRAW CS_VREDRAW bật)
• Chương trình gián tiếp gởi WM_PAINT thơng qua hàm InvalidateRect • Cửa sổ
(32)Hàm mượn ngữ cảnh thiết bị BeginPaint, hàm trả EndPaint Hai hàm sử dụng cấu trúc PAINTSTRUCT Khai báo cấu trúc PAINSTRUCT hàm sau
typedef struct tagPAINTSTRUCT { // ps
HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; BYTE rgbReserved[32]; } PAINTSTRUCT;
HDC BeginPaint(
HWND hwnd, // handle to window
LPPAINTSTRUCT lpPaint // pointer to structure for paint information
);
BOOL EndPaint(
HWND hWnd, // handle to window
CONST PAINTSTRUCT *lpPaint // pointer to structure for paint data );
BeginPaint mượn Windows ngữ cảnh thiết bị (DC) đưa thông tin vẽ vào cấu trúc PAINSTRUCT BeginPaint sử dụng để hoạt động với thông điệp WM_PAINT BeginPaint cung cấp thông tin vùng xén, vùng không hợp lệ
Hàm EndPaint trả lại DC cho Windows Khi hàm gọi Windows khôi phục lại thuộc tính vẽ sẵn sàng cho chương trình cần vẽ lần Đồng thời EndPaint thông báo vùng không hợp lệ sửa chữa
(33));
Trong marker ta gọi với tham số
InvalidateRect(hWnd, NULL, TRUE);
Tham số thứ ba cờ xoá, giá trị TRUE cho biết xoá phần trước vẽ Tham số thứ hai vùng cửa sổ cần vẽ lại Giá trị NULL cho biết cần phải vẽ lại toàn vùng client Vẽ lại toàn vùng client không hiệu Thông thường, ta cần vẽ lại vùng nhỏ bị hư hay cần cập nhật, ta gọi vùng vùng không hợp lệ Trong marker, lần thêm dấu thập mới, ta cần vẽ lại vùng đủ chứa marker
Ví dụ sau minh hoạ việc vẽ lại toàn dấu thập vị trí mắt trái chuột bấm
// Marker ver 1.1 : Chi ve lai phan hinh chu nhat vua du chua marker case WM_LBUTTONDOWN:
if (nMarkers <= MAX_MARKERS) {
apt[nMarkers].x = LOWORD(lParam); apt[nMarkers].y = HIWORD(lParam); nMarkers++;
r.bottom = HIWORD(lParam) + MARKER_SIZE + 1; r.top = HIWORD(lParam) - MARKER_SIZE - 1; r.left = LOWORD(lParam) - MARKER_SIZE - 1; r.right = LOWORD(lParam) + MARKER_SIZE + 1; InvalidateRect(hWnd, &r, TRUE);
} break;
2.10 InvertPixel vaø DrawMarker
Sử dụng SetPixel để vẽ dấu thập khơng có tác dụng (vơ hình) màu trùng với màu dấu thập Để bảo đảm việc đánh dấu nền, ta phủ định giá trị pixel màu nền Điều thực nhờ GetPixel theo sau SetPixel với giá trị phủ định từng bit
void InvertPixel(HDC hdc, int x, int y) {
(34)}
Để đánh dấu có màu khác nhau, DrawMarker phải bảo đảm vẽ dấu chữ thập bằng gọi InvertPixel, InvertPixel đảo ngược giá trị pixel cách phủ định giá trị pixel đang có
2.11 GetDC ReleaseDC
Mỗi mắt chuột bấm, thay gián tiếp lệnh việc vẽ lại thơng qua InvalidateRect để gởi WM_PAINT Ta vẽ trực tiếp dấu thập nhận thông điệp mắt trái chuột được bấm Điều thực GetDC ReleaseDC Hàm GetDC cho mượn ngữ cảnh thiết bị đến toàn vùng client, ReleaseDC trả lại ngữ cảnh Giữa hai thao tác mượn trả này, ta thực thao tác vẽ
// Marker.cpp : Defines the entry point for the application // Version 1.2: Dung GetDC de ve bam mat trai chuot // Trich doan phan WndProc
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
#define MAX_MARKERS 100 #define MARKER_SIZE
HDC hdc;
TCHAR szHello[MAX_LOADSTRING]; int i;
static POINT apt[MAX_MARKERS]; static int nMarkers = 0;
switch (message) {
case WM_LBUTTONDOWN:
if (nMarkers <= MAX_MARKERS) {
apt[nMarkers].x = LOWORD(lParam); apt[nMarkers].y = HIWORD(lParam); nMarkers++;
hdc = GetDC(hWnd);
(35)EndPaint(hWnd, &ps); break;
case WM_DESTROY:
PostQuitMessage(0); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
(36)3 Vẽ đường
3.1 Đường
• Đường đối tượng hình học có cách nối liên tiếp điểm • Đường loại đối tượng hình học mở Mỗi đường có điểm đầu điểm cuối
• Đường vẽ theo giải thuật inclusive/exclusive, nghĩa điểm đầu thuộc đường, nhưng điểm cuối không thuộc đường
3.2 Các hàm vẽ đường
GDI cung cấp hàm vẽ đường thẳng, cung, đường cong bezier, polyline Một số hàm vẽ đường sử dụng cấu trúc POINT
typedef struct tagPOINT {
LONG x; LONG y; } POINT;
3.2.1 MoveToEx vaø LineTo
BOOL MoveToEx(
HDC hdc, int X, int Y, LPPOINT lpPoint );
BOOL LineTo(
(37)CONST POINT *lppt, // pointer to array containing endpoints
int cPoints // number of points in the array
);
BOOL PolylineTo(
HDC hdc, // handle to device context
CONST POINT *lppt, // pointer to array of points
DWORD cCount // number of points in array
);
BOOL PolyPolyline(
HDC hdc, // handle of a device context
CONST POINT *lppt, // address of an array of points
CONST DWORD *lpdwPolyPoints, // address of an array of values
DWORD cCount // number of counts in the second array
);
Linedrag: Ví dụ minh hoạ MoveTo LineTo
(38)#include "stdafx.h" #include "resource.h" #define MAX_SEGS 100 #define MAX_POINTS 10000
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc;
TCHAR szPrompt[MAX_LOADSTRING];
LoadString(hInst, IDS_PROMPT, szPrompt, MAX_LOADSTRING); int i,j;
static int nSegs, nPoints; static int anCounts[MAX_SEGS+1]; static BOOL bMouseDown;
static POINT oldPoint, apt[MAX_POINTS]; switch (message)
{
case WM_LBUTTONDOWN:
if (nSegs < MAX_SEGS && nPoints < MAX_POINTS) {
bMouseDown = TRUE; SetCapture(hWnd);
anCounts[nSegs] = nPoints;
apt[nPoints].x = LOWORD(lParam); apt[nPoints++].y = HIWORD(lParam); nSegs++;
} break;
case WM_MOUSEMOVE: if (bMouseDown) {
if (nPoints < MAX_POINTS) {
(39)case WM_RBUTTONDOWN: nSegs = nPoints = 0;
InvalidateRect(hWnd, NULL, TRUE); break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); RECT rt;
GetClientRect(hWnd, &rt);
DrawText(hdc, szPrompt, strlen(szPrompt), &rt, DT_CENTER); for (i = 0; i < nSegs; i++)
{
j = anCounts[i];
MoveToEx(ps.hdc, apt[j].x, apt[j].y, &oldPoint); for (; j < anCounts[i+1]; j++)
LineTo(ps.hdc, apt[j].x, apt[j].y); }
EndPaint(hWnd, &ps); break;
case WM_DESTROY:
PostQuitMessage(0); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
Chương trình cải tiến dùng Polyline PolyPolyline 3.2.3 Arc
Haøm Arc vẽ cung phần hình Ellipse BOOL Arc(
HDC hdc, // handle to device context
int nLeftRect, // x-coord : UPL
int nTopRect, // y-coord : UPL
int nRightRect, // x-coord : LWR
(40)BOOL ArcTo(
HDC hdc, // handle to device context
int nLeftRect, // x-coord : UPL
int nTopRect, // y-coord : UPL
int nRightRect, // x-coord : LWR
int nBottomRect, // y-coord : LWR
int nXRadial1, // x-coord of the first radial ending point
int nYRadial1, // y-coord of the first radial ending point
int nXRadial2, // x-coord of the second radial ending point
int nYRadial2 // y-coord of the second radial ending point
);
3.2.5 AngleArc BOOL AngleArc(
HDC hdc, // handle to device context
int X, // x-coordinate of circle's center
int Y, // y-coordinate of circle's center
DWORD dwRadius, // circle's radius
FLOAT eStartAngle, // arc's start angle
FLOAT eSweepAngle // arc's sweep angle );
3.2.6 PolyBezier vaø PolyBezierTo
Hàm PolyBezier vẽ hay nhiều đường cong Bézier BOOL PolyBezier(
HDC hdc, // handle to device context
CONST POINT *lppt, // pointer to endpoints and control points DWORD cPoints // count of endpoints and control points
);
(41)(42)Thuộc tính Ý nghóa Màu (background color)
Chế độ (background mode) Vị trí hành (curent position) Chế độ vẽ (drawing mode) Bút vẽ (pen)
Màu thứ hai bút vẽ PS_SOLID Tắt/mở màu
Điểm hành cho thao tác LineTo, ArcTo… Thao tác vẽ boolean điểm vẽ
Màu, độ rộng kiểu vẽ đường Hàm để thay đổi màu chế độ
COLORREF SetBkColor(HDC hdc, COLORREF crColor); int SetBkMode(HDC hdc, int iBkMode);
iBkMode OPAQUE TRANSPARENT
3.4 Bút vẽ (pen)
• Bút vẽ thuộc tính rõ cách vẽ đường, bút vẽ có ba thành phần màu, độ rộng kiểu
• Bút vẽ chia sẻ các chương trình thiết bị
• GDI cung cấp sẵn số bút vẽ Người sử dụng tự tạo thêm bút vẽ khác • Bút vẽ dùng độc lập thiết bị thực chất bút vẽ luận lý Khi GDI vẽ,
yêu cầu thiết bị nhận biết bút vẽ điều khiển thiết bị vẽ đường theo chất lượng yêu cầu Nhờ bút vẽ chia sẻ thiết bị khác
3.4.1 Tạo dùng bút vẽ có sẵn
Windows cung cấp sẵn tập bút vẽ (stock pens) Đó bút vẽ đen, trắng rỗng Hàm GetStockObject
Bút vẽ tham chiếu đến handle Hàm GetStockObject cho phép lấy handle một bút vẽ có sẵn
(43)hpenOld = SelectObject(hdc, hPenNew);
…// Các thao tác vẽ đường
SelectObject(hdc, hPenOld);
3.4.2 Tạo bút vẽ
Người sử dụng tự tạo bút vẽ khác bút vẽ Windows cung cấp, có hai cách để tạo bút vẽ
Haøm CreatePen
HPEN hpen;
hpen = CreatePen (nPenStyle, nWidth, crColor);
Để tạo bút vẽ đặc, độ rộng 4, màu đỏ, dùng
hpen = CreatePen (PS_SOLID, R, RGB(255,0,0));
nPenStype có giá trị định nghĩa sẵn sau:
PS_SOLID Pen is solid PS_DASH Pen is dashed PS_DOT Pen is dotted
PS_DASHDOT Pen has alternating dashes and dots PS_DASHDOTDOT Pen has dashes and double dots PS_NULL Pen is invisible
PS_INSIDEFRAME Pen is solid, to be drawn inside a filled figure
Haøm CreatePenIndirect
Dùng cấu trúc LOGPEN để tạo bút vẽ
typedef struct tagLOGPEN { // lgpn UINT lopnStyle;
POINT lopnWidth; COLORREF lopnColor; } LOGPEN;
(44)3.4.3 Ví dụ minh hoạ thuộc tính bút vẽ
Hình 3-3 – Pen
// Pen.cpp : Defines the entry point for the application // Trich doan ham WndProc
#define dim(a) sizeof(a)/sizeof(a[0]) struct PenData
{
char *Name;
int nWidth, nStyle; COLORREF crColor; };
PenData aPenTab[] = {
(45)int i; HPEN hpen
switch (message) {
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
for (i = 0; i < dim(aPenTab); i++) {
TextOut(hdc, 10, i*30, aPenTab[i].Name, lstrlen(aPenTab[i].Name));
hpen = CreatePen(aPenTab[i].nStyle, aPenTab[i].nWidth,
aPenTab[i].crColor);
SelectObject(hdc, hpen);
MoveToEx(hdc, 150, i*30+10, NULL); LineTo(hdc, 500, i*30+10);
DeleteObject(hpen); }
EndPaint(hWnd, &ps); break;
case WM_DESTROY:
PostQuitMessage(0); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
3.5 Các chế độ vẽ (Drawing Modes) ảnh hưởng đến vẽ đường
Chế độ vẽ thao tác Boolean điểu khiển cách GDI vẽ điểm, đường, hình Chế độ vẽ cịn được gọi chế độ trộn trộn logic với giá trị có sẵn Hàm SetRop2 cho phép thay đổi chế độ vẽ Hàm GetRop2 cho biết chế độ vẽ hành
int SetROP2(
HDC hdc, // handle of device context
int fnDrawMode // drawing mode
);
(46)R2_MASKPEN Pixel is a combination of the colors common to both the pen and the screen R2_MASKPENNOT Pixel is a combination of the colors common to both the pen and the inverse of
the screen
R2_MERGENOTPEN Pixel is a combination of the screen color and the inverse of the pen color R2_MERGEPEN Pixel is a combination of the pen color and the screen color
R2_MERGEPENNOT Pixel is a combination of the pen color and the inverse of the screen color R2_NOP Pixel remains unchanged
R2_NOT Pixel is the inverse of the screen color R2_NOTCOPYPEN Pixel is the inverse of the pen color
R2_NOTMASKPEN Pixel is the inverse of the R2_MASKPEN color R2_NOTMERGEPEN Pixel is the inverse of the R2_MERGEPEN color R2_NOTXORPEN Pixel is the inverse of the R2_XORPEN color R2_WHITE Pixel is always
(47)4 Vẽ miền
4.1 Miền
• Miền hay hình kín đối tượng hình học có hai phần: miền đường biên bao quanh miền Ta cịn gọi hình hình có tơ màu
• GDI dùng hai cách diễn dịch toạ độ tâm điểm (pixel-centered) giao điểm lưới (grid-intersection) để vẽ miền Toạ độ tâm điểm dùng để vẽ đường toạ độ giao điểm lưới dùng để xén (clipping) Một ảnh hưởng toạ độ giao điểm lưới hình vẽ có kích thước nhỏ pixel so với toạ độ tâm điểm
• Toạ độ tâm điểm dùng với hàm vẽ đa giác, toạ độ giao điểm lưới dùng để vẽ hình chữ nhật, ellipse, chord, pie
• GDI cung cấp hàm vẽ hình với cách diễn dịch toạ độ tương ứng sau:
Hàm Toạ độ Ý nghĩa
Polygon tâm điểm đa giác
PolyPolygon tâm điểm nhiều đa giác
Chord giao điểm lưới một phần cung khép kín đường thẳng Ellipse giao điểm lưới ellipse
Pie giao điểm lưới hình cánh quạt Rectangle giao điểm lưới hình chữ nhật
RoundRect giao điểm lưới hình chữ nhật có góc trịn 4.2 Các hàm vẽ miền
(48)4.2.1 Vẽ đa giác – Polygon PolyPolygon
Hàm Polygon vẽ đa giác cách nối điểm dùng bút vẽ sẵn có ngữ cảnh thiết bị Nếu điểm đầu điểm cuối không trùng nhau, đoạn thẳng thêm vào để nối Miền bên tơ chổi vẽ có sẵn ngữ cảnh thiết bị
BOOL Polygon(
HDC hdc, // handle to device context
CONST POINT *lpPoints, // pointer to polygon's vertices int nCount // count of polygon's vertices
);
Hàm PolyPolygon vẽ nhiều đa giác với lệnh gọi Chương trình vẽ nhiều đa giác dùng hàm chạy nhanh gọi nhiều lần hàm vẽ đa giác
BOOL PolyPolygon(
HDC hdc, // handle to device context
CONST POINT *lpPoints, // pointer to array of vertices for polygons CONST INT *lpPolyCounts, // pointer to array with count of vertices int nCount // count of polygons
);
4.2.2 Vẽ hình chữ nhật – Rectangle, RoundRect, FrameRect InvertRect Vẽ hình chữ nhật
BOOL Rectangle(
HDC hdc, // handle to device context
(49)Hình 4-1 Rectangle
Vẽ hình chữ nhật có góc trịn BOOL RoundRect(
HDC hdc, // handle to device context
(50)Hình 4-2 RoundRect
Vẽ khung chữ nhật int FrameRect(
HDC hDC, // handle to device context
CONST RECT *lprc, // pointer to rectangle coordinates HBRUSH hbr // handle to brush
);
Tô chữ nhật int FillRect(
HDC hDC, // handle to device context
CONST RECT *lprc, // pointer to structure with rectangle HBRUSH hbr // handle to brush
);
Đảo màu hình chữ nhật BOOL InvertRect(
HDC hDC, // handle to device context
CONST RECT *lprc // pointer to structure with rectangle );
4.2.3 Ellipse, Pie vaø Chord BOOL Ellipse(
HDC hdc, // handle to device context
(51)Hình 4-3 Ellipse
BOOL Pie(
HDC hdc, // handle to device context
int nLeftRect, // x-coord of bounding rectangle's upper-left corner int nTopRect, // y-coord of bounding rectangle's upper-left corner int nRightRect, // x-coord of bounding rectangle's lower-right corner int nBottomRect, // y-coord of bounding rectangle's lower-right corner int nXRadial1, // x-coord of first radial's endpoint
(52)Hình 4-4 Pie
BOOL Chord(
HDC hdc, // handle to device context
int nLeftRect, // x-coord of the upper-left corner of bounding rectangle int nTopRect, // y-coord of the upper-left corner of bounding rectangle int nRightRect, // x-coord of the lower-right corner of bounding rectangle int nBottomRect, // y-coord of the lower-right corner of bounding rectangle int nXRadial1, // x-coord of the first radial's endpoint
int nYRadial1, // y-coord of the first radial's endpoint int nXRadial2, // x-coord of the second radial's endpoint int nYRadial2 // y-coord of the second radial's endpoint );
Hình 4-5 Chord
4.3 Các thuộc tính tô màu
(53)Thuộc tính vẽ Ý nghóa
Màu (Background Color): Màu thứ hai bút vẽ chổi vẽ đường gạch Chế độ (Background Color): Bật / Tắt màu
Chổi vẽ (Brush): Màu để tô miền hình Gốc chổi vẽ (Brush Origin): Vị trí gốc chổi vẽ đường gạch Chế độ vẽ(Drawing Mode) Thao tác Boolean với
Bút vẽ (Pen) Màu, độ rộng kiểu đườngbiên Chế độ tô đa giác (Polygon Fill Mode) Để tô màu đa giác
Chế độ tơ màu ALTERNATE WINDING Để thay đổi chế độ tô màu, ta dùng hàm PolyFillMode
int SetPolyFillMode(
HDC hdc, // handle of device context
int iPolyFillMode // polygon fill mode
);
Hàm để thay đổi màu SetBkColor COLORREF SetBkColor(
HDC hdc,
COLORREF crColor );
Và hàm để thay đổi chế độ SetBkMode int SetBkMode(
HDC hdc, int iBkMode );
(54)Hình 4-6 – Chế độ tô màu ALTERNATE WINDING
4.4 Chổi vẽ (Brushes)
Chổi vẽ thuộc tính để tơ Chổi vẽ tạo thành phần: Kiểu (Style), màu (color) mẫu (Pattern) Kích thước chỗi vẽ tính pixel x pixel Chổi vẽ chia sẻ giữa chương trình thiết bị Windows cung cấp sẵn số chổi vẽ (stock brushes) Ngoài ra, người sử dụng tự tạo chổi vẽ riêng Ta quản lý chổi vẽ handle đến chổi vẽ gọi HBRUSH
4.4.1 Tạo sử dụng chổi vẽ có sẵn
(55)GRAY_BRUSH HOLLOW_BRUSH LTGRAY_BRUSH NULL_BRUSH WHITE_BRUSH
Hollow brush (equivalent to NULL_BRUSH) Light gray brush
Null brush (equivalent to HOLLOW_BRUSH) White brush
Ví dụ
HBRUSH hbr;
hbr = GetStockObject(GRAY_BRUSH); SelectObject(hdc, hbr);
(56)SelectObject để chọn chổi vẽ vừa tạo Sau dùng xong dùng DeleteObject để giải phóng tài nguyên Windows cấp cho chổi vẽ
Đoạn chương trình sau tạo chổi vẽ gạch chéo 45 độ có màu xanh dương vẽ hình ellipse bằng chổi vẽ
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); RECT rt;
HBRUSH hbr;
GetClientRect(hWnd, &rt);
hbr = CreateHatchBrush(HS_BDIAGONAL, RGB(0,0,0xff)); SelectObject(hdc, hbr);
Ellipse(hdc, rt.right/8, rt.bottom/8, 7*rt.right/8, 7*rt.bottom/8); EndPaint(hWnd, &ps);
DeleteObject(hbr);
break;
Chổi vẽ tạo sẵn cửa sổ vừa tạo giải phóng cửa sổ bị huỷ
case WM_CREATE:
hbr = CreateHatchBrush(HS_BDIAGONAL, RGB(0,0,0xff));
break; case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); RECT rt;
GetClientRect(hWnd, &rt);
SelectObject(hdc, hbr);
Ellipse(hdc, rt.right/8, rt.bottom/8, 7*rt.right/8, 7*rt.bottom/8); EndPaint(hWnd, &ps);
break; case WM_DESTROY:
DeleteObject(hbr);
(57)4.4.3 Tạo trực tiếp chổi vẽ Hàm CreateSolidBrush
Hàm CreateSolidBrush tạo chổi vẽ luận lý đặc với màu quy định tham số crColor
HBRUSH CreateSolidBrush(
COLORREF crColor // brush color value );
Haøm CreateHatchBrush
Hàm CreateSolidBrush tạo chổi vẽ luận lý đường gạch quy định bởi tham số fnStyle có màu clrref
HBRUSH CreateHatchBrush( int fnStyle, // hatch style COLORREF clrref // color value );
Xem hàm CreateBrushIndirect bên để biết giá trị hợp lệ cho fnStyle
Haøm CreatePatternBrush
Hàm CreatePatternBrush tạo chổi vẽ từ mẫu xác định bitmap nhớ.
HBRUSH CreatePatternBrush( HBITMAP hbmp // handle to bitmap );
4.4.4 Tạo gián tiếp chổi vẽ Haøm CreateBrushIndirect
Hàm CreateBrushIndirect kết hợp tất hàm tạo chổi vẽ kể trên, cho phép tạo chổi vẽ với kiểu, màu, mẫu từ cấu trúc chổi vẽ luận lý cho trước
(58)LONG lbHatch; } LOGBRUSH;
lbStyle
Kiểu brush Thành phần lbStyle có giá trị sau:
Giá trị Ý nghóa
BS_DIBPATTERN Mẫu chổi vẽ định nghĩa bitmap có kích thước 8x8 pixels thuộc loại DIB
BS_DIBPATTERN8X8 Tương tự BS_DIBPATTERN BS_HATCHED Chổi vẽ Hatched
BS_HOLLOW Choåi vẽ rỗng
BS_NULL Chổi vẽ rỗng, giống BS_HOLLOW
BS_PATTERN Chỗi vẽ có mẫu định nghĩa bitmap nhớ BS_PATTERN8X8 Tương tự BS_PATTERN
BS_SOLID Chổi vẽ đặc lbColor
Màu chổi vẽ Nếu lbStyle BS_HOLLOW hay BS_PATTERN, lbColor không có ý nghóa
lbHatch
Khi lbStyle BS_HATCHED, lbHatch rõ cách thức gạch, quy định hướng đường gạch
Giá trị Ý nghóa
HS_BDIAGONAL Gạch theo góc 45 độ từ lên từ trái sang phải
(59)4.5 Ví dụ chổi vẽ
Chương trình minh hoạ Brush tạo chổi vẽ người sử dụng định nghĩa Tập tin chương trình nguồn brush.cpp sau:
#define MAX_BRUSHES 30
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc;
int i;
static nBrushes, nCurBrush; static HBRUSH ahbr[MAX_BRUSHES]; static TCHAR *aszName[MAX_BRUSHES]; static BYTE acPattern1[] = {
0xff, 0, 0xe7, 0, 0xc3, 0, 0x99, 0, 0x3c, 0, 0x7e, 0, 0xff, 0, 0xff, 0, };
static BYTE acPattern2[] = { 0x81, 0,
0x82, 0, 0x84, 0, 0xf8, 0, 0x0, 0, 0x0, 0, 0x0, 0, 0x0, 0, };
switch (message) {
(60)aszName[nBrushes++] = "Hatch HS_FDIAGONAL";
ahbr[nBrushes] = CreateHatchBrush(HS_HORIZONTAL, RGB(0,0,0xff)); aszName[nBrushes++] = "Hatch HS_HORIZONTAL";
ahbr[nBrushes] = CreateHatchBrush(HS_VERTICAL, RGB(0,0,0xff)); aszName[nBrushes++] = "Hatch HS_VERTICAL";
hBitmap = CreateBitmap(8,8,1,1, acPattern1); ahbr[nBrushes] = CreatePatternBrush(hBitmap); aszName[nBrushes++] = "Pattern Brush 1"; DeleteObject(hBitmap);
hBitmap = CreateBitmap(8,8,1,1, acPattern2); ahbr[nBrushes] = CreatePatternBrush(hBitmap); aszName[nBrushes++] = "Pattern Brush 2"; DeleteObject(hBitmap);
hBitmap = LoadBitmap(hInst, (LPCTSTR)IDB_BITMAP1); ahbr[nBrushes] = CreatePatternBrush(hBitmap); aszName[nBrushes++] = "RC Pattern Brush 1"; DeleteObject(hBitmap);
hBitmap = LoadBitmap(hInst, (LPCTSTR)IDB_BITMAP2); ahbr[nBrushes] = CreatePatternBrush(hBitmap); aszName[nBrushes++] = "RC Pattern Brush 2"; DeleteObject(hBitmap);
break;
case WM_COMMAND:
wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // Parse the menu selections: switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break;
case IDM_EXIT:
DestroyWindow(hWnd); break;
default:
(61)Rectangle(hdc, 180, i*35+10, 600, i*35+39); }
EndPaint(hWnd, &ps); break;
case WM_DESTROY:
for (i = 0; i < nBrushes; i++) DeleteObject(ahbr[i]); PostQuitMessage(0);
break; default:
return DefWindowProc(hWnd, message, wParam, lParam); }
(62)4.6 Miền thuộc tính bút vẽ PS_INSIDEFRAME
Bút vẽ có độ rộng thuộc tính PS_INSIDEFRAME kết hợp với vẽ miền cho biết bút vẽ sẽđược tô đậm vào phần miền Hình vẽ minh hoạ bút vẽ có thuộc tính Đoạn chương trình tương ứng sau:
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rt);
hpen = CreatePen(PS_SOLID, 16, RGB(0,255,0)); SelectObject(hdc, hpen);
Ellipse(hdc, 0, 0, 4*rt.right/7, 4*rt.bottom/7); DeleteObject(hpen);
hpen = CreatePen(PS_INSIDEFRAME, 16, RGB(0,255,0)); SelectObject(hdc, hpen);
Ellipse(hdc, 3*rt.right/7, 3*rt.bottom/7, rt.right, rt.bottom); DeleteObject(hpen);
SelectObject(hdc, GetStockObject(HOLLOW_BRUSH)); hpen = CreatePen(PS_SOLID, 1, RGB(255,0,0)); SelectObject(hdc, hpen);
Ellipse(hdc, 0, 0, 4*rt.right/7, 4*rt.bottom/7);
Ellipse(hdc, 3*rt.right/7, 3*rt.bottom/7, rt.right, rt.bottom); DeleteObject(hpen);
rt.left = 3*rt.right/7; rt.top = 3*rt.bottom/7;
DrawText(hdc, szPrompt, strlen(szPrompt), &rt, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hWnd, &ps); break;
(63)Hình 4-10 – PS_INSIDEFRAME style
4.7 Con đường (Paths)
Con đường tập hợp đường thẳng cung GDI lưu trữ nội Con đường được xem mở rộng miền Sau tạo đường vẽ đường viền tơ màu
4.7.1 BeginPath vaø EndPath
Để định nghĩa đường ta theo trình tự: Mở đường, thực thao tác vẽ, đóng đường
(64)4.7.2 Các hàm thao tác đường
Các hàm thao tác đường bao gồm StrokePath, FillPath StrokeAndFillPath
Hàm StrokePath vẽ đường viền đường hành bút vẽ sẵn có ngữ cảnh thiết bị
BOOL StrokePath(HDC hdc);
Hàm FillPath đóng tất đường mở bên đường hành tô miền bên đường chổi vẽ hành chế độ tô đa giác hành
BOOL FillPath(HDC hdc);
Hàm StrokeAndFillPath làm công việc hai hàm trên, đóng tất đường mở bên đường hành, vẽ đường viền đường với bút vẽ hành tô miền bên đường chổi vẽ hành chế độ tô đa giác hành
BOOL StrokeAndFillPath(HDC hdc );
4.7.3 Ví dụ sử dụng đường – Vẽ tơ hoa PolyBezier
Chương trình minh hoạ vẽ cánh hoa đường PolyBezier nội vào đường Sau dùng hàm StrokeAndFillPath để vẽ đường viền tô màu cánh hoa
(65)Hình 4-11 – Minh hoạ Path
// Bezier_Flower.cpp : Defines the entry point for the application // Ve va to dung ham PolyBezier, Path
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) //
void DrawMarker(HDC hdc, POINT pt, int size);
void CreateFlowerPoints(POINT ptCenter, POINT pt, POINT aptFlowers[]); #define MAX_FLOWERS 100
#define MAX_MARKERS (2*MAX_FLOWERS) #define MARKER_SIZE
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc;
TCHAR szPrompt[] = TEXT("Bezier Flowers"); static POINT apt[MAX_MARKERS];
static int nMarkers; int i;
POINT aptFlowers[12]; static HPEN hpen; static HBRUSH hbrush; static int nPenWidth = 2;
static COLORREF crPen = RGB(0xff,0,0); switch (message)
{
case WM_CREATE:
hbrush = CreateSolidBrush(RGB(0, 0xff, 0)); // Green brush hpen = CreatePen(PS_SOLID, nPenWidth, crPen); // Red pen break;
case WM_LBUTTONDOWN:
if (nMarkers < MAX_MARKERS) {
(66)GetClientRect(hWnd, &rt);
DrawText(hdc, szPrompt, strlen(szPrompt), &rt, DT_CENTER); SelectObject(hdc, hbrush);
SelectObject(hdc, hpen);
for (i = 0; i < nMarkers; i++) {
DrawMarker(hdc, apt[i], MARKER_SIZE); if (i%2 == 1)
{
aptFlowers[0] = apt[i-1];
CreateFlowerPoints(apt[i-1], apt[i], aptFlowers); BeginPath(hdc);
MoveToEx(hdc, apt[i-1].x, apt[i-1].y, NULL); PolyBezierTo(hdc, aptFlowers, 12);
CloseFigure(hdc); EndPath(hdc); StrokeAndFillPath(hdc); } } EndPaint(hWnd, &ps); break; case WM_DESTROY: DeleteObject(hpen); DeleteObject(hbrush); PostQuitMessage(0); break; default:
return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
void CreateFlowerPoints(POINT ptCenter, POINT pt, POINT aptFlowers[]) {
aptFlowers[0] = aptFlowers[1] = pt; aptFlowers[1].x = 2*ptCenter.x - pt.x; aptFlowers[2] = ptCenter;
aptFlowers[3].x = aptFlowers[0].x; aptFlowers[4].x = aptFlowers[1].x;
(67)mới với điểm đầu tâm, điểm cuối dùng để tạo điểm điều khiển cho PolyBezierTo
(68)5 Xuất Văn
5.1 Tổng quan
Văn (text) phương tiện xuất Cơ chế xuất văn GDI Windows hoàn toàn khác với cách tiếp cận cổ điển
• Ở giai đoạn đầu lập trình, chương trình dùng cách tiếp cận xuất văn theo định hướng dòng phù hợp với thiết bị xuất tương tự máy đánh chữ
cout << “Nhap vao hai so nguyen: ”; cin >> i >> j;
• Cách tiếp cận xuất văn định hướng hình Có thể định vị xuất liệu đến vị trí hình theo dịng cột
gotoxy(x,y);
cout << “Hello, world\n”;
• Cách tiếp cận GDI Windows định hướng pixel GDI cho phép định vị văn xuất đến pixel Người sử dụng dễ dàng trộn lẫn văn với xuất liệu hình học khác, dễ dàng trộn lẫn văn có kích thước khác
• Vì giao diện Windows giao diện đồ hoạ, văn xuất đến đơn vị nhỏ pixel, qui trình xuất khơng khác vẽ đối tượng hình học khác nên ta cịn nói xuất văn bản vẽ text
• Font dùng để vẽ văn Một font bao gồm mẫu mơ tả hình dạng kích thước của ký tự (chữ, số, dấu…) Mỗi thiết bị GDI hỗ trợ hay nhiều font
5.2 Các hàm xuất văn
(69)ExtTextOut
Hàm ExtTextOut vẽ dòng ký tự điểm qui định, dùng font hành, màu hành, màu chữ hành Ngồi cung cấp hình chữ nhật để xén, làm tối (opaque), kết hợp hai
BOOL ExtTextOut(
HDC hdc, // handle to device context
int X, // x-coordinate of reference point int Y, // y-coordinate of reference point UINT fuOptions, // text-output options
CONST RECT *lprc, // optional clipping and/or opaquing rectangle LPCTSTR lpString, // points to string
UINT cbCount, // number of characters in string
CONST INT *lpDx // pointer to array of intercharacter spacing
// values
);
fuOptions lấy kết hơp giá trị
Giá trị Ý nghóa
ETO_CLIPPED Văn bị xén vào hình chữ nhật
ETO_OPAQUE Màu hành dùng để tơ hình chữ nhật
lpDx tuỳ chọn, trỏ đến mảng giá trị qui định khoảng cách ký tự nhau chuỗi
TabbedTextOut
(70));
Hình 5-1 - TabledTextOut
Ví dụ: Sắp theo cột TabledTextOut
// TabbedText.cpp : Defines the entry point for the application // FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
#define MAX_FONTS 50
#define dim(a) sizeof(a)/sizeof(a[0]) #define N
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
(71)int nItems = dim(aTextData); int i;
SIZE textSize; int aTab[N]; LOGFONT lf;
static char *aFaceName[] = {
"Arial",
"Times New Roman", "Bookman Old Style", "BankGothic Lt BT" };
const int nFaces = sizeof(aFaceName)/sizeof(aFaceName[0]); static HFONT ahf[MAX_FONTS];
static int nFonts, nCurFont;
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING); switch (message)
{
case WM_CREATE:
memset(&lf, 0, sizeof(lf)); for (i = 0; i < nFaces; i++) {
lf.lfHeight = 32; lf.lfWidth = 12;
lstrcpy(lf.lfFaceName, aFaceName[i]); ahf[nFonts++] = CreateFontIndirect(&lf); }
break;
case WM_RBUTTONDOWN:
nCurFont = ++nCurFont % nFonts; InvalidateRect(hWnd, NULL, TRUE); break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); SelectObject(hdc, ahf[nCurFont]); RECT rt;
GetClientRect(hWnd, &rt);
(72)break; default:
return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
DrawText
Hàm DrawText xuất văn có định dạng bên hình chữ nhật Văn định dạng theo tab, xuống dòng, canh ký tự
int DrawText(
HDC hDC, // handle to device context LPCTSTR lpString, // pointer to string to draw int nCount, // string length, in characters
LPRECT lpRect, // pointer to struct with formatting dimensions UINT uFormat // text-drawing flags
);
uFormat
Qui định cách định dạng Có thể kết hợp cờ sau:
Giá trị Ý nghóa
DT_BOTTOM Dùng với DT_SINGLELINE để canh
DT_CALCRECT Tính kích thước (chiều rộng cao) text không vẽ Trả chiều cao văn định dạng
DT_CENTER Canh
DT_EXPANDTABS Dùng tab, tab stop ký tự
(73)DT_WORDBREAK Ngắt dòng
Hình 5-2 - Minh hoạ DrawText với DT_CENTER
Hình 5-3 - Minh hoạ DrawText với DT_CENTER | DT_WORDBREAK
Ví dụ minh hoạ – Chương trình WordBreak
// Text_WordBreak.cpp :
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
(74){
case WM_LBUTTONDOWN:
bWordBreak = !bWordBreak;
InvalidateRect(hWnd, NULL, TRUE); break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); RECT rt;
GetClientRect(hWnd, &rt); if (bWordBreak)
DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER | DT_WORDBREAK);
else
DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
EndPaint(hWnd, &ps); break;
case WM_DESTROY:
PostQuitMessage(0); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
GrayString
Haøm GrayString vẽ văn màu xám BOOL GrayString(
HDC hDC, // handle to the device context
HBRUSH hBrush, // handle to the brush for graying
GRAYSTRINGPROC lpOutputFunc, // pointer to the callback function LPARAM lpData, // pointer to application-defined data
int nCount, // number of characters to output
int X, // horizontal position
int Y, // vertical position
(75)Hình 5-4 – GrayString
Ví dụ: Đoạn chương trình sau cho xuất liệu graystring
// GrayString.cpp :
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) //
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc;
TCHAR szHello[] = "Hello This is a string created by a call to GrayString";
switch (message) {
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
GrayString(hdc, (HBRUSH)GetStockObject(BLACK_BRUSH), NULL, (LPARAM)szHello, strlen(szHello), 10, 50, 0, 0); EndPaint(hWnd, &ps);
break; case WM_DESTROY:
PostQuitMessage(0); break;
default:
(76)Khoảng cách ký tự Số pixel thêm vào ký tự để canh lề Canh lề (trái, phải,…) Quan hệ với điểm mốc
Màu chữ Màu thân chữ văn 5.3.1 Màu văn
Ba thuộc tính ngữ cảnh liên quan đến màu văn màu nền, chế độ màu thân chữ văn
Chế độ OPAQUE (mặc nhiên) vẽ màu lên phần character cell chữ văn Ta dùng hàm SetColor để thay đổi màu chữ văn
COLORREF SetTextColor(
HDC hdc, // handle of device context
COLORREF crColor // text color
);
5.3.2 Canh haøng (text alignment)
Canh hàng văn cho phép thay đổi vị trí tương đối văn so với điểm mốc Hàm SetTextAlign cho phép canh hàng văn
UINT SetTextAlign(
HDC hdc, // handle of device context
UINT fMode // text-alignment flag );
fMode có giá trị
Canh theo chiều ngang: TA_LEFT, TA_CENTER, TA_RIGHT Canh theo chiều dọc: TA_TOP, TA_BASELINE, TA_BOTTOM Ví dụ, để canh trên, ta gọi:
(77)HDC hdc;
TCHAR szHello[MAX_LOADSTRING];
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING); int i;
static TextAttrData aTextAttrTab[] = {
"Top Left Black", TA_LEFT | TA_TOP, RGB(0,0,0), "Top Right Red", TA_RIGHT | TA_TOP, RGB(0xff,0,0), "Top Center Blue", TA_CENTER | TA_TOP, RGB(0,0,0xff), "Bottom Left Gray", TA_LEFT | TA_BOTTOM, RGB(0,0,0),
"Bottom Right Green", TA_RIGHT | TA_BOTTOM, RGB(0,0xff,0), "Bottom Center Blue", TA_CENTER | TA_BOTTOM, RGB(0,0,0xff), "Baseline Left Black", TA_LEFT | TA_BASELINE, RGB(0,0,0),
"Baseline Center Yellow", TA_CENTER | TA_BASELINE, RGB(0xff,0xff,0), "Baseline Right Blue", TA_RIGHT | TA_BASELINE, RGB(0xff,0,0),
};
static HBRUSH hbr; switch (message) {
case WM_CREATE:
hbr = CreateHatchBrush(HS_BDIAGONAL, RGB(0,0,0xff)); break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); POINT pt;
SelectObject(hdc, hbr);
Ellipse(hdc, 50,10, 200, 400); Ellipse(hdc, 250,10, 400, 400); Ellipse(hdc, 450,10, 600, 400);
for (i = 0; i < dim(aTextAttrTab); i++) {
SetTextAlign(hdc, aTextAttrTab[i].nAlign); SetTextColor(hdc, aTextAttrTab[i].crColor); pt.x = 100, pt.y = i*40+20;
TextOut(hdc, 100, i*40+20, aTextAttrTab[i].Name, lstrlen(aTextAttrTab[i].Name));
DrawMarker(hdc, pt, 10); }
SetBkColor(hdc, RGB(0xc0,0xc0,0xc0)); for (i = 0; i < dim(aTextAttrTab); i++) {
(78)TextOut(hdc, 500, i*40+20, aTextAttrTab[i].Name, lstrlen(aTextAttrTab[i].Name)); DrawMarker(hdc, pt, 10);
}
EndPaint(hWnd, &ps); break;
case WM_DESTROY:
PostQuitMessage(0); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
(79)Font VNI-Times nghieâng
Font Courier New Bold
Font Wingdings :
Font Symbol : αβχδεφγηιϕκλµνοπθρστυϖωξψζ←↑→↓°±≥ΣΩΦ 5.4.1 Các loại font
GDI cung cấp loại font ứng với ba kỹ thuật lưu trữ khác raster font, vector font truetype font
Raster font, gọi Bitmap Font, font mà ký tự lưu trữ thành bitmap pixel Một font raster thiết kế với kích thước cố định tỉ lệ ngang dọc định Bitmap font khả phóng to thu nhỏ
Vector Font định nghĩa dãy đoạn thẳng cung nối với Vector Font phóng to thu nhỏ, sử dụng thiết bị có độ phân giải khác nhau, chuyển qua bitmap không bị thông tin Vector khơng tốt kích thước nhỏ lớn TrueType Fonts sử dụng từ Windows 3.1 Trong TrueType Font, ký tự gồm liệu là đường nét thuộc loại thẳng hay cung, thêm vào phần chương trình điều khiển để bảo đảm chất lượng font phóng to thu nhỏ (phần chương trình gọi hints) Khả này đặc biệt hữu dụng để sửa sai font sử dụng kích thước nhỏ nhỏ Nhờ TrueType Font phóng to thu nhỏ đến kích thước Kết liệu thuộc dạng bitmap đưa lên hình hay máy in, TrueType font tạo điều kiện dễ dàng để đạt đến WYSIWYG
Windows 95 coù 13 font chuaån: Courier New
Courier New Bold
Courier New Italic
Courier New Bold Italic Times New Roman
Times New Roman Italic
(80)Point đơn vị đo kích thước cho font, 1/72 inch 5.4.2 Tạo sử dụng font
Có thể tạo font trực tiếp gián tiếp
Để tạo trực tiếp font, ta dùng hàm CreateFont CreateFont
Hàm CreateFont tạo font luận lý với đặc trưng quy định tham số Font sau tạo dùng cho thiết bị
HFONT CreateFont(
int nHeight, // logical height of font
int nWidth, // logical average character width int nEscapement, // angle of escapement int nOrientation, // base-line orientation angle int fnWeight, // font weight
DWORD fdwItalic, // italic attribute flag
DWORD fdwUnderline, // underline attribute flag DWORD fdwStrikeOut, // strikeout attribute flag DWORD fdwCharSet, // character set identifier DWORD fdwOutputPrecision, // output precision DWORD fdwClipPrecision, // clipping precision DWORD fdwQuality, // output quality DWORD fdwPitchAndFamily, // pitch and family
LPCTSTR lpszFace // pointer to typeface name string );
Xem cấu trúc font luận lý LOGFONT để biết ý nghĩa tham số
Để tạo gián tiếp font ta dùng hàm CreateFontIndirect cấu trúc LOGFONT LOGFONT
(81)BYTE lfCharSet; BYTE lfOutPrecision; BYTE lfClipPrecision; BYTE lfQuality;
BYTE lfPitchAndFamily;
TCHAR lfFaceName[LF_FACESIZE]; } LOGFONT;
lfHeight
Chiều cao tính theo đơn vị luận lý character cell bao gồm internal-leading nhưng khơng tính external leading Khi có giá trị > 0, qui định khoảng cách dịng (line spacing) Khi 0, GDI dùng chiều cao để tìm font khớp Khi giá trị lfHeight > GDI chuyển sang đơn vị toạ độ thiết bị lấy giá trị tuyệt đối của giá trị để chọn font có chiều cao khớp
Trong chế độ MM_TEXT ta tính chiều cao theo point size:
lfHeight = -MulDiv(PointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72);
lfWidth
Chiều rộng tính theo đơn vị luận lý ký tự font Thơng thường lfWidth có giá trị
lfWeight
Cho biết "độ mập" font, có giá trị từ đến 1000 Giá trị 400 font bình thường 700 bold
Giá trị Weight
(82)Giá trị TRUE qui định gạch lfStrikeOut
Giá trị TRUE qui định gạch ngang lfCharSet
Qui định chữ, có giá trị sau:
ANSI_CHARSET BALTIC_CHARSET CHINESEBIG5_CHARSET DEFAULT_CHARSET EASTEUROPE_CHARSET GB2312_CHARSET GREEK_CHARSET HANGUL_CHARSET MAC_CHARSET OEM_CHARSET RUSSIAN_CHARSET SHIFTJIS_CHARSET SYMBOL_CHARSET TURKISH_CHARSET
lfQuality
Qui định chất lượng xuất Có thể có giá trị
DEFAULT_QUALITY DRAFT_QUALITY PROOF_QUALITY
lfPitchAndFamily
Qui định họ font pitch Hai bit thấp qui định picth font
DEFAULT_PITCH FIXED_PITCH VARIABLE_PITCH
Bít đến qui định họ font
FF_DECORATIVE Old English ví dụ FF_DONTCARE
FF_MODERN Pica, Elite, and CourierNew
(83));
5.5 Các thông số văn baûn
Windows cung cấp số hàm tiện ích cung cấp thông tin văn Cấu trúc SIZE thường dùng hàm tính toán văn
SIZE
Cấu trúc SIZE định nghĩa chiều rộng chiều cao hình chữ nhật
typedef struct tagSIZE { // siz LONG cx;
LONG cy; } SIZE;
GetTextExtentPoint32
Hàm GetTextExtentPoint32 tính chiều rộng chiều cao chuỗi văn BOOL GetTextExtentPoint32(
HDC hdc, // handle to device context LPCTSTR lpString, // pointer to text string int cbString, // number of characters in string LPSIZE lpSize // pointer to structure for string size );
TEXTMETRIC
Cấu trúc TEXTMETRIC chứa thông tin font vật lý Tất kích thước dùng đơn vị luận lý, nghĩa phụ thuộc vào chế độ toạ độ hành
typedef struct tagTEXTMETRIC { // tm LONG tmHeight;
(84)BCHAR tmLastChar; BCHAR tmDefaultChar; BCHAR tmBreakChar; BYTE tmItalic;
BYTE tmUnderlined; BYTE tmStruckOut; BYTE tmPitchAndFamily; BYTE tmCharSet; } TEXTMETRIC;
GetTextMetrics
Hàm GetTextMetrics cho biết thông tin metric font hành chọn BOOL GetTextMetrics(
HDC hdc, // handle to device context
(85)// Font_Create.cpp : Defines the entry point for the application //
HFONT MyCreateFont (HDC hdc, char * szFaceName, int nPointSize, BOOL bBold = FALSE, BOOL bItalic = FALSE) {
HFONT hFont ; LOGFONT lf ;
memset(&lf, 0, sizeof(lf));
lf.lfHeight = -MulDiv(nPointSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
(86)int nPointSize;
BOOL bBold, bItalics; };
#define MAX_FONTS 20
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; LOGFONT lf; int i;
static FONTDATA aFontData[] = {
"Arial size 20", "Arial", 20, FALSE, FALSE,
"Times New Roman Bold size 30", "Times New Roman", 30, TRUE, FALSE, "Font Vieät Nam VNI-Times co 24 nghieâng", "VNI-Times", 24, FALSE, TRUE, "VNI-Bodon-Poster co 24 maäp", "VNI-Bodon-Poster", 24, TRUE, FALSE, "VNI-Hobo co 40 maäp", "VNI-Hobo", 40, TRUE, FALSE
};
const int nFonts = sizeof(aFontData)/sizeof(aFontData[0]); static HFONT ahf[MAX_FONTS];
switch (message) {
case WM_CREATE:
memset(&lf, 0, sizeof(lf)); hdc = GetDC(hWnd);
for (i = 0; i < nFonts; i++)
ahf[i] = MyCreateFont(hdc, aFontData[i].szFaceName, aFontData[i].nPointSize, aFontData[i].bBold, aFontData[i].bItalics);
ReleaseDC(hWnd, hdc); break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); for (i = 0; i < nFonts; i++) {
SelectObject(hdc, ahf[i]);
TextOut(hdc, 30, 50*(i+1), aFontData[i].szPrompt, lstrlen(aFontData[i].szPrompt));
(87)5.6 Font vaø Path
Các hàm xuất văn TextOut, DrawText… dùng font hành để xuất chữ Các hàm này định nghĩa điểm bên path Để đường ký tự font tạo nên điểm trong path, ta dùng chế độ TRANSPARENT
Hình 5-7 – Font Path
Sau trích đoạn hàm WndProc chương trình tạo nên xuất liệu
// Font_Path.cpp int nPenWidth = 2;
COLORREF crPen = RGB(0xff,0,0);
(88)switch (message) {
case WM_CREATE:
memset(&lf, 0, sizeof(lf)); lf.lfHeight = 120;
lf.lfWidth = 0;
lstrcpy(lf.lfFaceName, "Times New Roman"); hfont = CreateFontIndirect(&lf);
hbrush = CreateSolidBrush(RGB(0, 0xff, 0)); // Green brush hpen = CreatePen(PS_SOLID, nPenWidth, crPen); // Red pen crText = RGB(0xc0, 0xc0, 0xc0); // Gray text
break; case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); RECT rt;
GetClientRect(hWnd, &rt); rt.top += 10;
SelectObject(hdc, hfont); SelectObject(hdc, hbrush); SelectObject(hdc, hpen); BeginPath(hdc);
DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER); EndPath(hdc);
StrokeAndFillPath(hdc); SetBkMode(hdc, TRANSPARENT); BeginPath(hdc);
DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
EndPath(hdc);
StrokeAndFillPath(hdc); SetTextColor(hdc, crText);
(89)6 Bộ định thời
6.1 Giới thiệu
Bộ định thời thiết bị xuất đặn “lưu ý” ứng dụng sau khoảng thời gian qui định trước Chương trình định khoảng thời gian Windows gởi thông điệp WM_TIMER để báo hiệu khoảng thời gian trôi qua Bộ định thời cho phép tạo ứng dụng có tính động, chẳng hạn đồng hồ để hiển thị thời gian Bộ định thời cịn Windows có được số tính như:
• Đa nhiệm – Chương trình tự cắt chức để xử lý riêng thông qua định thời, nhờ nhanh chóng trả quyền điều khiển cho Windows
• Duy trì cập nhật thơng báo trạng thái hoạt động máy • Kết thúc chương trình demo tự chạy
• Cài đặt chức autosave
• Cài đặt chương trình đa phương tiện • Các chương trình trò chơi
Bộ định thời bảo đảm cho phép chương trình giành lại quyền điều khiển sau thoát khỏi thủ tục cửa sổ
6.2 Các hàm thao tác định thời
Ta cấp phát định thời cách dùng hàm SetTimer giải phóng định thời
KillTimer
UINT_PTR SetTimer(
(90)HWND hWnd, // handle to window UINT_PTR uIDEvent // timer identifier
);
6.3 Hoạt động định thời
Khi lập trình mơi trường DOS, ta tạo định thời cách bẫy ngắt cứng (như Int hay Int 13) Các ngắt xảy sau tick (mỗi tick 3600/65536 tức khoảng 1/18,2 giây hay 55 mili giây)
Các ứng dụng Windows khơng bẫy ngắt cứng Chính hệ điều hành Windows xử lý ngắt cứng thay cho ứng dụng Windows trì biến đếm cho định thời giảm biến đếm sau tick Thông điệp WM_TIMER gởi vào hàng đợi thông điệp khi biến đếm giảm đến
Cơ chế hoạt động bảo đảm định thời khơng ngắt ngang q trình xử lý thơng điệp thông điệp định thời WM_TIMER đưa vào hàng đợi trước xử lý
Trong Windows Me, 98, 97, 95 độ phân giải định thời 55 ms (mili giây) Windows NT, Windows 2000, Windows XP, độ phân giải 10 ms Các ứng dụng nhận thông điệp định thời nhanh độ phân giải nêu Và khoảng thời gian để gởi thông điệp không hoàn toàn giá trị quy định hàm SetTimer Ví dụ, gọi SetTimer với khoảng cách định thời 1000 ms Windows Me, 98, 95 sau 989 ms, thông điệp gởi lần (tại sao) Tuy nhiên định thời khơng đồng
6.4 Tính khơng đồng định thời
Thông điệp WM_TIMER đưa vào hàng đợi thông điệp với thông điệp khác theo thứ tự thời điểm phát sinh thơng điệp Vì ta qui định khoảng cách hai thơng điệp là 1000 ms không bảo đảm nhận thông điệp sau 1000 ms, hay 989 ms Nếu ứng dụng cịn bận xử lý thơng điệp khác khoảng thời gian dài giây, nó không nhận thông điệp WM_TIMER khoảng thời gian Cần lưu ý WM_TIMER thơng điệp có độ ưu tiên thấp
(91)Tham số nIDEvent có ý nghĩa ta cần tạo nhiều định thời ứng dụng Tham số lpTimerFunc trỏ đến thủ tục định thời để xử lý thông điệp Nếu tham số bằng NULL, thông điệp WM_TIMER gởi đến cho thủ tục cửa sổ
Ta sử dụng định thời cách gọi hàm SetTimer với thủ tục định thời có giá trị NULL xử lý thông điệp WM_TIMER thủ tục cửa sổ
Ví dụ 1: Chương trình bounce
Chương trình bounce sau vẽ hình tròn nhỏ di chuyển tự động bên vùng client Ta dùng hàm SetTimer xử lý thông điệp WM_TIMER Hình trịn nhỏ tự động thay đổi màu sau khoảng thời gian qui định trước (1 giây) Tham số TimerProc với giá trị NULL làm phát sinh thông điệp WM_TIMER Cách gọi sau:
SetTimer(hWnd, ID_MOVE_TIMER, 10, NULL);
SetTimer(hWnd, ID_CHANGECOLOR_TIMER, 1000, NULL);
Chương trình gồm tập tin chương trình nguồn: Stdafx.h stdafx.cpp Bounce.cpp
(92)Hình 6-2 Cửa sổ chương trình
Sau trích đoạn chương trình nguồn tập tin bounce.cpp
// bounce.cpp : Defines the entry point for the application //
//
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) //
// PURPOSE: Processes messages for the main window //
// WM_COMMAND - process the application menu // WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return //
//
enum {ID_MOVE_TIMER = 1, ID_CHANGECOLOR_TIMER = 2};
(93)SetTimer(hWnd, ID_MOVE_TIMER, 10, NULL);
SetTimer(hWnd, ID_CHANGECOLOR_TIMER, 1000, NULL); break;
case WM_COMMAND:
wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break; case IDM_EXIT: DestroyWindow(hWnd); break; default:
return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_TIMER: switch(wParam) { case ID_MOVE_TIMER: RECT rClient, r;
GetClientRect(hWnd, &rClient); r.left = x - rBall - abs(dx); r.right = x + rBall + abs(dx); r.top = y - rBall - abs(dy); r.bottom = y + rBall + abs(dy); x += dx;
y += dy;
if (x + rBall > rClient.right && dx > 0) dx = -dx; if (x - rBall < && dx < 0) dx = -dx;
if (y + rBall > rClient.bottom && dy > 0) dy = -dy; if (y - rBall < && dy < 0) dy = -dy;
InvalidateRect(hWnd, &r, TRUE); break;
case ID_CHANGECOLOR_TIMER: ++nCrIdx %= nColors; break;
(94)KillTimer(hWnd, ID_CHANGECOLOR_TIMER); PostQuitMessage(0);
break; default:
return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
Ví dụ 2: Chương trình bounce
Chương trình hoạt động tương tự chương trình ví dụ ta qui định hàm xử lý timer sau:
SetTimer(hWnd, ID_MOVE_TIMER, 10, TimerProc);
SetTimer(hWnd, ID_CHANGECOLOR_TIMER, 1000, TimerProc);
Sau trích đoạn chương trình nguồn tập tin bounce.cpp
// bounce.cpp : Defines the entry point for the application //
enum {ID_MOVE_TIMER = 1, ID_CHANGECOLOR_TIMER = 2};
VOID CALLBACK TimerProc (HWND hWnd, UINT message, UINT wParam, DWORD lParam) {
static int x = 40,y = 50,rBall=10,dx=5,dy=5, nCrIdx;
static COLORREF aColorTab [] = {RGB(0xff,0,0), RGB(0,0xff,0), RGB(0,0,0xff), RGB(0,0,0), RGB(0xff,0xff,0xff),RGB(0xc0,0xc0,0xc0)};
static int nColors = sizeof(aColorTab)/sizeof(aColorTab[0]); switch(wParam)
{
case ID_MOVE_TIMER: RECT rClient, r;
(95)if (y - rBall < && dy < 0) dy = -dy; SelectObject(hdc, hbr);
SelectObject(hdc, GetStockObject(BLACK_PEN));
Ellipse(hdc, x - rBall, y - rBall, x + rBall, y + rBall); DeleteObject(hbr);
ReleaseDC(hWnd, hdc); break;
case ID_CHANGECOLOR_TIMER: ++nCrIdx %= nColors; break;
} } //
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) //
// PURPOSE: Processes messages for the main window //
// WM_COMMAND - process the application menu // WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return //
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc;
TCHAR szHello[MAX_LOADSTRING];
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING); switch (message)
{
case WM_CREATE:
SetTimer(hWnd, ID_MOVE_TIMER, 10, TimerProc);
SetTimer(hWnd, ID_CHANGECOLOR_TIMER, 1000, TimerProc); break;
case WM_COMMAND:
(96)hdc = BeginPaint(hWnd, &ps); EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
KillTimer(hWnd, ID_MOVE_TIMER);
KillTimer(hWnd, ID_CHANGECOLOR_TIMER); PostQuitMessage(0);
break; default:
return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
Ví dụ 3: Chương trình bounce
(97)// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) // // struct BALL { int x,y,r,dx,dy; COLORREF crColor; }; BALL CreateNewBall() { BALL b;
b.x = 0, b.y = 0, b.r = 10, b.dx = b.dy = 5; return b;
};
BALL CreateNewBall(int x, int y, int r, int dx, int dy, COLORREF color) {
BALL b;
b.x = x, b.y = y, b.r = r, b.dx = dx; b.dy = dy; b.crColor = color;
return b; };
const int MAX_BALLS = 100;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc;
TCHAR szHello[MAX_LOADSTRING]; static BALL aBalls[MAX_BALLS]; static int nBalls;
static COLORREF aColorTab [] = {RGB(0xff,0,0), RGB(0,0xff,0), RGB(0,0,0xff), RGB(0,0,0), RGB(0xff,0xff,0xff),RGB(0xc0,0xc0,0xc0)};
static int nColors = sizeof(aColorTab)/sizeof(aColorTab[0]); LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
RECT rt;
switch (message) {
case WM_CREATE:
(98)break; default:
return DefWindowProc(hWnd, message, wParam, lParam); } break; case WM_CHAR: switch(toupper(TCHAR(wParam))) { case 'N': GetClientRect(hWnd, &rt);
aBalls[nBalls] = CreateNewBall(rand()%rt.right, rand()%rt.bottom, 10, 5, 5, aColorTab[nBalls%nColors]);
SetTimer(hWnd, nBalls++, 10, NULL); break;
case 'R':
if (nBalls > 0)
KillTimer(hWnd, nBalls); InvalidateRect(hWnd, NULL, TRUE); break;
} break; case WM_TIMER:
RECT rClient, r; BALL *pb;
pb = &aBalls[wParam];
GetClientRect(hWnd, &rClient);
r.left = pb->x - pb->r - abs(pb->dx); r.right = pb->x + pb->r + abs(pb->dx); r.top = pb->y - pb->r - abs(pb->dy); r.bottom = pb->y + pb->r + abs(pb->dy); pb->x += pb->dx;
pb->y += pb->dy;
if (pb->x + pb->r > rClient.right && pb->dx > 0) pb->dx = -pb->dx; if (pb->x - pb->r < && pb->dx < 0) pb->dx = -pb->dx;
if (pb->y + pb->r > rClient.bottom && pb->dy > 0) pb->dy = -pb->dy; if (pb->y - pb->r < && pb->dy < 0) pb->dy = -pb->dy;
InvalidateRect(hWnd, &r, TRUE); break;
case WM_PAINT:
(99)break;
case WM_DESTROY:
PostQuitMessage(0); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
Ví dụ 4: Chương trình QuaDat
(100)VOID VeVongTron(HDC hdc, int tx, int ty, int bk, HBRUSH hbr) {
SelectObject(hdc, hbr);
Ellipse(hdc, tx-bk, ty-bk, tx+bk, ty+bk); }
//
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) //
// PURPOSE: Processes messages for the main window //
// WM_COMMAND - process the application menu // WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return //
//
const float PI = 3.1415926F;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc;
TCHAR szHello[MAX_LOADSTRING];
static int nBkQuiDao = 160, nBkMatTroi = 20, nBkQuaDat = 8;
static COLORREF crMatTroi = RGB(255,0,0), crQuaDat = RGB(0,255,0); static HBRUSH hbrMatTroi, hbrQuaDat;
// static int txQuaDat, tyQuaDat; static HPEN hpenQuiDao;
static float fGoc = -PI/4, fStep = 0.1f;
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
(101){
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break;
case IDM_EXIT:
DestroyWindow(hWnd); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
break; case WM_TIMER:
hdc = GetDC(hWnd);
SetROP2(hdc, R2_NOTXORPEN); GetClientRect(hWnd, &rt);
VeVongTron(hdc, rt.right/2 + int(nBkQuiDao*cos(fGoc)),
rt.bottom/2 + int(nBkQuiDao*sin(fGoc)), nBkQuaDat, hbrQuaDat); fGoc -= fStep;
VeVongTron(hdc, rt.right/2 + int(nBkQuiDao*cos(fGoc)),
rt.bottom/2 + int(nBkQuiDao*sin(fGoc)), nBkQuaDat, hbrQuaDat); ReleaseDC(hWnd, hdc);
break; case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here GetClientRect(hWnd, &rt);
DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
VeDuongTron(hdc, rt.right/2, rt.bottom/2, nBkQuiDao, hpenQuiDao); SelectObject(hdc, GetStockObject(BLACK_PEN));
VeVongTron(hdc, rt.right/2, rt.bottom/2, nBkMatTroi, hbrMatTroi); SetROP2(hdc, R2_NOTXORPEN);
VeVongTron(hdc, rt.right/2 + int(nBkQuiDao*cos(fGoc)),
(102)7 Tương tác với người sử dụng
Windows hệ điều hành có giao diện trực quan thân thiện với người sử dụng Một đặc điểm Windows khả WYSIWYG, người sử dụng tương tác với chương trình
Một thao tác tương tác môi trường Windows kéo lê (dragging) Người sử dụng kéo lê để vẽ, kéo lê để thay đổi kích thước cửa sổ, kéo lê để thay đổi vị trí, kích thước hay quay đối tượng đồ hoạ
7.1 SetCapture, GetCapture vaø ReleaseCapture
Kéo lê môi trường Windows thực nhờ chế bắt giữ chuột SetCapture
Hàm SetCapture bắt giữ trỏ chuột thành sở hữu cửa sổ Khi cửa sổ bắt giữ được chuột nhập liệu từ chuột gởi cho cửa sổ đó, dù vị trí trỏ chuột hay ngồi đường biên cửa sổ Tại thời điểm, có cửa sổ bắt giữ chuột
Nếu trỏ chuột khỏi phạm vi cửa sổ bắt giữ chuột, nhập liệu từ chuột được gởi cho cửa sổ mắt chuột bấm
HWND SetCapture(
HWND hWnd // handle of window to receive mouse capture );
Tham số hWnd cho biết cửa sổ bắt giữ chuột Hàm trả handle đến cửa sổ trước đó bắt giữ chuột, NULL trước chưa có cửa sổ bắt giữ chuột
(103)BOOL ReleaseCapture(VOID)
GetCapture
Hàm GetCapture cho biết cửa sổ bắt giữ chuột (nếu có) HWND GetCapture(VOID)
Ta dùng hàm GetCapture để xác định người sử dụng có q trình tương tác với chương trình cách kéo lê chuột để thao tác (di chuyển, thay đổi kích thước, quay…) đối tượng hay không
7.2 Kéo lên đối tượng cách xố vẽ lại Ví dụ ứng dụng – Chương trình ObjectDrag V1
Chương trình sau minh hoạ sử dụng hàm kể để thao tác đối tượng đồ hoạ Chương trình ban đầu vẽ hình ellipse, người sử dụng sau thay đổi vị trí hình ellipse cách đưa trỏ chuột vào bên hình, bấm mắt trái kéo lê
Khi người sử dụng bấm mắt trái chuột bên hình ellipse, cursor đổi sang hình dạng khác cho biết người sử dụng trình kéo lê Khi trỏ chuột kéo lê đến đâu, hình ellipse di chuyển theo đến đó, điều thực nhờ xử lý thông điệp WM_MOUSEMOVE, hàm GetCapture gọi xử lý thông điệp để xác định trỏ chuột có bị bắt giữ khơng, nói cách khác, chuột kéo lên khơng Ta di chuyển hình cách cập nhật vị trí, sau xố hình cũ vẽ lại hình vị trí Trước con chuột bị bắt giữ cửa sổ nhờ gọi hàm SetCapture xử lý thông điệp WM_LBUTTONDOWN Việc bắt giữ chuột bảo đảm trình người sử dụng kéo lê, nếu trỏ chuột phạm vi cửa sổ thông điệp chuột chuyển cho cửa sổ bắt giữ chuột Người sử dụng kết thúc việc di chuyển hình cách buông mắt chuột Hàm ReleaseCapture gọi để giải phóng chuột
Chương trình gồm số tập tin tài nguyên
small.ico ObjectDrag.ico
resource.h
Và số tập tin chương trình nguồn
(104)(105)// ObjectDrag.cpp : Defines the entry point for the application //
bool IsInsideEllipse(int x1, int y1, int x2, int y2, int x, int y) {
double a = (double(x2)-x1)/2, b = (double(y2)-y1)/2; if (!a || !b) return false;
double xx = x - (double(x1)+x2)/2, yy = y - (double(y1)+y2)/2; double xr = xx/a, yr = yy/b;
return xr*xr+yr*yr <= 1; }
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc;
TCHAR szHello[MAX_LOADSTRING]; static HCURSOR hCursorMove; static x1,y1,x2,y2, px, py; static HBRUSH hbr;
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING); switch (message)
{
case WM_CREATE: RECT rt;
GetClientRect(hWnd, &rt);
x1 = rt.right/4; x2 = 3*rt.right/4; y1 = rt.bottom/4; y2 = 3*rt.bottom/4; HBITMAP hBitmap;
hBitmap = LoadBitmap(hInst, (LPCTSTR)IDB_HEART); hbr = CreatePatternBrush(hBitmap);
DeleteObject(hBitmap);
hCursorMove = LoadCursor(NULL, IDC_SIZEALL); break;
case WM_LBUTTONDOWN: int x, y;
px = x = LOWORD(lParam); py = y = HIWORD(lParam);
(106){
SetCursor(hCursorMove); if (GetCapture() == hWnd) {
int dx = x-px, dy = y-py; x1 += dx; y1 += dy;
x2 += dx; y2 += dy; px = x;
py = y;
InvalidateRect(hWnd, NULL, TRUE); }
} break; case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER); SelectObject(hdc, hbr);
Ellipse(hdc,x1,y1,x2,y2); EndPaint(hWnd, &ps); break;
case WM_DESTROY:
PostQuitMessage(0); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
7.3 Kéo lên đối tượng chọn chế độ vẽ R2_NOTXORPEN
Trong cách tiếp cận trên, ta di chuyển hình cách xố cũ vẽ Điều làm cho cửa sổ client bị chớp trình kéo lê Hơn hình phức tạp địi hỏi thời gian để vẽ hoạt động chương trình khơng hiệu Một cách tiếp cận đại vẽ đối tượng tạm, thông thường đường biên đối tượng thực sự, hay đơn giản hình chữ nhật ngoại tiếp đối tượng, sau kéo lê để di chuyển đối tượng tạm Khi người sử dụng buông mắt chuột để kết thúc trình kéo lê, vẽ lại đối tượng thực
(107)Có 16 chế độ vẽ, ta quan tâm đến hai chế độ vẽ R2_XORPEN R2_NOTXORPEN Cả hai chế độ vẽ có chung đặc điểm hình vẽ hai lần, khơi phục lại trạng thái trước vẽ Với hai chế độ vẽ kể trên, đối tượng tạm được kéo lê Ta vẽ lại lần hình dáng vị trí cũ, thao tác khôi phục lại phần chưa vẽ, sau ta vẽ lại đối tượng hình dáng, vị trí Chế độ R2_XORPEN phù hợp với cửa sổ có đen R2_NOTXORPEN phù hợp với cửa sổ có trắng
Ví dụ ứng dụng – Chương trình ObjectDrag V2
Chương trình ObjectDrag V2 sau minh hoạ sử dụng hàm SetCapture, GetCapture, ReleaseCapture kết hợp với chế độ vẽ R2_NOTXORPEN kể để thao tác đối tượng đồ hoạ Chương trình ban đầu vẽ hình ellipse, người sử dụng sau thay đổi vị trí của hình ellipse cách đưa trỏ chuột vào bên hình, bấm mắt trái kéo lê Khi người sử dụng bấm mắt trái chuột bên hình ellipse, cursor đổi sang hình dạng khác cho biết người sử dụng trình kéo lê Đồng thời hình ellipse tạm với nét vẽ PS_DOT, chổi vẽ rỗng vẽ chồng lên hình ellipse có chế độ vẽ R2_NOTXORPEN Khi trỏ chuột kéo lê đến đâu, hình ellipse tạm di chuyển theo đến đó, điều thực nhờ xử lý thông điệp WM_MOUSEMOVE, hàm GetCapture gọi xử lý thông điệp để xác định trỏ chuột có bị bắt giữ khơng, nói cách khác, chuột kéo lên không Ta di chuyển hình elippse tạm cách vẽ lại để khơi phục phần bị hình vẽ lên, cập nhật vị trí, sau vẽ lại hình ellipse tạm này, với chế độ vẽ R2_NOTXORPEN để chuẩn bị cho lần xử lý sau Khi người sử dụng bng mắt chuột để kết thúc q trình kéo lê, vẽ lại đối tượng thực vị trí Chương trình gồm số tập tin tài nguyên
small.ico ObjectDrag.ico
resource.h
Và số tập tin chương trình nguồn
ObjectDrag.rc StdAfx.h StdAfx.cpp
(108)(109)(110)////////////////////////////////////////////////////////////////// bool IsInsideEllipse(int x1, int y1, int x2, int y2, int x, int y) {
double a = (double(x2)-x1)/2, b = (double(y2)-y1)/2; if (!a || !b) return false;
double xx = x - (double(x1)+x2)/2, yy = y - (double(y1)+y2)/2; double xr = xx/a, yr = yy/b;
return xr*xr+yr*yr <= 1; }
////////////////////////////////////////////////////////////////////////// // FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc;
TCHAR szHello[MAX_LOADSTRING]; static int x1,y1,x2,y2,xc,yc,px,py; static HBRUSH hbr;
static HCURSOR hCursorMove; static HPEN hPenDot;
int dx, dy; RECT rt;
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING); switch (message)
{
case WM_CREATE:
GetClientRect(hWnd, &rt);
x1 = rt.right/4; x2 = 3*rt.right/4; y1 = rt.bottom/4; y2 = 3*rt.bottom/4;
hbr = CreateHatchBrush(HS_DIAGCROSS, RGB(255,0,0)); hCursorMove = LoadCursor(NULL, IDC_SIZEALL);
hPenDot = CreatePen(PS_DOT, 1, RGB(0,0,255)); break;
case WM_COMMAND:
(111)case WM_LBUTTONDOWN:
xc = LOWORD(lParam); yc = HIWORD(lParam); if (IsInsideEllipse(x1,y1,x2,y2,xc,yc)) {
hdc = GetDC(hWnd);
SelectObject(hdc,hPenDot); SetROP2(hdc, R2_NOTXORPEN); Ellipse(hdc,x1,y1,x2,y2); SetCursor(hCursorMove); SetCapture(hWnd);
} break;
case WM_MOUSEMOVE:
if (GetCapture() == hWnd) {
hdc = GetDC(hWnd);
SelectObject(hdc,hPenDot); SetROP2(hdc, R2_NOTXORPEN); Ellipse(hdc,x1,y1,x2,y2); px = xc; py = yc;
xc = LOWORD(lParam); yc = HIWORD(lParam); dx = xc-px, dy = yc - py;
x1 += dx, x2 += dx, y1 += dy, y2 += dy;
Ellipse(hdc,x1,y1,x2,y2); }
break;
case WM_LBUTTONUP:
if (GetCapture() == hWnd) {
ReleaseCapture();
InvalidateRect(hWnd, NULL, TRUE); }
break; case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rt);
(112)Ví dụ ứng dụng – Chương trình ObjectDrag V6
Chương trình ObjectDrag V6 cho phép vẽ di chuyển hình ellipse Người sử dụng bấm chuột và kéo lê Nếu điểm bấm khơng thuộc hình ellipse vẽ trước vẽ hình ellipse mới Nếu vị trí chuột bấm trong hình ellipse vẽ di chuyển hình ellipse theo chiều di chuyển chuột Người sử dụng thay đổi thuộc tính tơ màu tơ hình ellipse vẽ
(113)Hình 7-8 Cửa sổ chương trình sau di chuyển ba hình đầu vẽ thêm hình khác
// ObjectDrag.cpp : Defines the entry point for the application //
class CEllipse {
int x1,y1,x2,y2; COLORREF crColor; int hatch;
public:
CEllipse(){Set(0,0,0,0); SetColor(RGB(0,0,255)); SetHatch(HS_DIAGCROSS);} CEllipse(int _x1, int _y1, int _x2, int _y2) {
(114)};
void CEllipse::Draw(HDC hdc) {
HBRUSH hbr;
hbr = CreateHatchBrush(hatch, crColor); SelectObject(hdc, hbr);
Ellipse(hdc,x1,y1,x2,y2); DeleteObject(hbr);
}
bool CEllipse::IsInside(int x, int y) {
double a = (double(x2)-x1)/2, b = (double(y2)-y1)/2; if (!a || !b) return false;
double xx = x - (double(x1)+x2)/2, yy = y - (double(y1)+y2)/2; double xr = xx/a, yr = yy/b;
return xr*xr+yr*yr <= 1; }
void CEllipse::Translate(int dx, int dy) {
x1 += dx; y1 += dy; x2 += dx; y2 += dy; }
int FindEllipse(CEllipse ae[], int n, int x, int y) {
for (int i = n-1; i >= 0; i ) if (ae[i].IsInside(x,y))
return i; return -1; }
////////////////////////////////////////////////////////// // FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
// PURPOSE: Processes messages for the main window //
(115)static COLORREF aCrTab[] = {
RGB(0,255,0), RGB(0, 255,255),RGB(0,0,0), RGB(255,0,0), RGB(0,0,255), RGB(255,255,0),
};
static int aHatch[] = { HS_HORIZONTAL, HS_VERTICAL, HS_FDIAGONAL, HS_BDIAGONAL, HS_CROSS, HS_DIAGCROSS };
int dx, dy, i;
static int curColor, curHatch = 5;
static INT flag = DRAW, nEllipse,nCur = -1; RECT rt;
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING); switch (message)
{
case WM_CREATE:
GetClientRect(hWnd, &rt);
hCursorMove = LoadCursor(NULL, IDC_SIZEALL); hCursorCross = LoadCursor(NULL, IDC_CROSS); hPenDot = CreatePen(PS_DOT, 1, RGB(0,0,255)); break;
case WM_COMMAND:
wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break;
case IDM_EXIT:
DestroyWindow(hWnd); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
(116)break;
case WM_LBUTTONDOWN:
xc = LOWORD(lParam); yc = HIWORD(lParam);
if ((nCur = FindEllipse(ae, nEllipse, xc, yc)) >= 0) {
flag = MOVE;
hdc = GetDC(hWnd);
SelectObject(hdc,hPenDot); SetROP2(hdc, R2_NOTXORPEN); ae[nCur].Draw(hdc); SetCursor(hCursorMove); SetCapture(hWnd); }
else if (nEllipse < MAX_ELLIPSE)// DRAW {
flag = DRAW;
ae[nEllipse].Set(xc,yc,xc,yc,aCrTab[curColor],aHatch[curHatch]); hdc = GetDC(hWnd);
SetROP2(hdc, R2_NOTXORPEN); ae[nEllipse].Draw(hdc); SetCursor(hCursorCross); SetCapture(hWnd); nEllipse++; } else
flag = -1; break;
case WM_MOUSEMOVE:
if (GetCapture() == hWnd) {
if (flag == MOVE) {
hdc = GetDC(hWnd);
SelectObject(hdc,hPenDot); SetROP2(hdc, R2_NOTXORPEN); ae[nCur].Draw(hdc);
px = xc; py = yc;
xc = LOWORD(lParam); yc = HIWORD(lParam); dx = xc-px, dy = yc - py;
(117){
ReleaseCapture();
InvalidateRect(hWnd, NULL, TRUE); }
break; case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); GetClientRect(hWnd, &rt); for (i = 0; i < nEllipse; i++)
ae[i].Draw(hdc); EndPaint(hWnd, &ps); char buf[128];
wsprintf(buf, "Number of Ellipses: %d", nEllipse); DrawText(hdc, buf, lstrlen(buf), &rt, DT_CENTER); break;
case WM_DESTROY:
PostQuitMessage(0); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
(118)8 Sử dụng tài nguyên
Window cho phép người sử dụng đưa vào chương trình tài nguyên thuộc loại biểu tượng (icons), trỏ (cursors), bitmap, bảng chuỗi (string tables), bảng phím tắt (Accelerator), hộp hội thoại (Dialog), thực đơn (menu), công cụ (toolbar) siêu văn (văn loại HTML) Một số tài nguyên thuộc loại Windows cung cấp sẵn gọi tài nguyên chuẩn Người sử dụng tự tạo tài nguyên khác thông qua tập tin tài ngun có phần RC (Resource Script)
Tập tin tài nguyên văn viết theo cú pháp ngơn ngữ lập trình để mơ tả tài nguyên cấp cho chương trình
(119)• Biểu tượng chuẩn (standard icons) biểu tượng Windows cung cấp sẵn, những biểu tượng xác định có phần tiếp đầu ngữ IDI_ Windows 95 cung cấp biểu tượng chuẩn sau:
IDI_APPLICATION IDI_HAND
IDI_QUESTION IDI_EXCLAMATION IDI_ASTERISK IDI_WINLOGO IDI_WARNING IDI_EXCLAMATION IDI_ERROR IDI_INFORMATION IDI_ASTERISK
• Custom Icons biểu tượng người sử dụng định nghĩa cách tạo tập tin biểu tượng có phần ICO mô tả tập tin tài nguyên tham chiếu đến tập tin Ví dụ sau trích đoạn tập tin RC phần định nghĩa biểu tượng
IDI_SCROLL ICON DISCARDABLE "Scroll.ICO" IDI_SMALL ICON DISCARDABLE "SMALL.ICO"
(120)8.1.2 Các thao tác biểu tượng LoadIcon
Hàm LoadIcon nạp tài nguyên icon từ tập tin thực thi trả kết điểm đến biểu tượng nạp
HICON LoadIcon(
HINSTANCE hInstance, // handle to application instance LPCTSTR lpIconName // icon-name string or icon resource
// identifier
);
Để nạp biểu tượng chuẩn ta dùng LoadIcon với hInstance có giá trị NULL DrawIcon
Hàm DrawIcon vẽ biểu tượng vị trí qui định tham số X,Y BOOL DrawIcon(
HDC hDC, // handle to device context int X, // x-coordinate of upper-left corner int Y, // y-coordinate of upper-left corner HICON hIcon // handle to icon to draw );
Ví dụ:
Để nạp biểu tượng người sử dụng định nghĩa
wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_SCROLL);
Để nạp biểu tượng chuẩn
HICON hIcon2 = LoadIcon(NULL, IDI_APPLICATION);
Để lấy thơng tin kích thước biểu tượng, ta dùng hàm int GetSystemMetrics(
int nIndex // system metric or configuration setting to retrieve );
Với nIndex lấy giá trị sau tương ứng với biểu tượng
(121)LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc;
TCHAR szHello[MAX_LOADSTRING];
TCHAR szPrompt[] = "Standard Icons"; static ICONDATA aIconTab[] =
{ "IDI_APPLICATION", IDI_APPLICATION, "IDI_ASTERISK", IDI_ASTERISK, "IDI_ERROR", IDI_ERROR, "IDI_EXCLAMATION", IDI_EXCLAMATION, "IDI_HAND", IDI_HAND, "IDI_INFORMATION", IDI_INFORMATION, "IDI_QUESTION", IDI_QUESTION, "IDI_WARNING", IDI_WARNING, "IDI_WINLOGO", IDI_WINLOGO };
int x, y, i; switch (message) {
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); RECT rt;
int cxIcon, cyIcon;
GetClientRect(hWnd, &rt);
DrawText(hdc, szPrompt, strlen(szPrompt), &rt, DT_CENTER); HICON hIcon;
hIcon = LoadIcon(NULL, (LPCTSTR)IDI_APPLICATION); cxIcon = GetSystemMetrics(SM_CXICON);
cyIcon = GetSystemMetrics(SM_CYICON); y = 20;
SetTextAlign(hdc, TA_CENTER);
for (i = 0, x = 70; i < dim(aIconTab); i++, x += XSTEP) {
if (x > rt.right - 50) x = 70, y += YSTEP;
DrawIcon(hdc, x, y, LoadIcon(NULL, aIconTab[i].iconId)); TextOut(hdc, x+cxIcon/2, y+40, aIconTab[i].szName,
(122)8.2 Con trỏ (cursor) 8.2.1 Tổng quan
Một trỏ (cursor) hình ảnh kích thước nhỏ để rõ vị trí hành thiết bị trỏ (pointing device) chuột, bút vẽ hay track ball
Hình 8-4 Con trỏ chuẩn
• Khi người sử dụng di chuyển chuột, trỏ chuột di chuyển tương ứng
• Người sử dụng qui định hình dạng trỏ cho ứng dụng Khi người sử dụng di chuyển chuột đến ứng dụng đó, trỏ đổi sang hình dạng tương ứng • Người sử dụng qui định trỏ khác vùng khác ứng
duïng
(123)• Custom Cursors trỏ người sử dụng định nghĩa cách tạo tập tin con trỏ có phần ICO mô tả tập tin tài nguyên tham chiếu đến tập tin Cách định nghĩa tương tự biểu tượng
8.2.2 Caùc thao taùc trỏ LoadCursor
Hàm LoadCursor nạp tài nguyên trỏ từ tập tin thực thi trả kết điểm đến biểu tượng nạp
HCURSOR LoadCursor(
HINSTANCE hInstance, // handle to application instance
LPCTSTR lpCursorName // name string or cursor resource identifier );
ShowCursor
Hàm ShowCursor hiển thị hay làm biến curosr int ShowCursor(
BOOL bShow // cursor visibility flag );
SetCursor
Hàm SetCursor định trỏ cho ứng dụng HCURSOR SetCursor(
HCURSOR hCursor // handle to cursor );
Ví dụ:
SetCursor(LoadCursor(NULL, IDC_WAIT));
8.3 Phím tắt (Keyboard Accelerators)
Phím tắt phương tiện để người sử dụng điều khiển chương trình bàn phím, phím tắt ánh xạ thao tác bàn phím thành thơng điệp định nghĩa sẵn, WM_COMMAND với thông số kèm theo cho biết chi tiết thơng điệp, phím tắt dùng thay cho việc chọn chức menu chuột
(124)IDC_HELLO ACCELERATORS MOVEABLE PURE BEGIN
"/", IDM_ABOUT, ASCII, ALT, NOINVERT "?", IDM_ABOUT, ASCII, ALT, NOINVERT "A", ID_LUACHON_TIENGANH, VIRTKEY, ALT, NOINVERT "C", ID_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT "N", ID_FILE_NEW, VIRTKEY, CONTROL, NOINVERT "O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT "V", ID_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT "V", ID_OPTION_VIETNAMESE, VIRTKEY, ALT, NOINVERT "X", ID_EDIT_CUT, VIRTKEY, CONTROL, NOINVERT END
Có thể sử dụng phím windows định nghĩa sẵn như: VK_F1, VK_F2,… Để sử dụng phím tắt, dùng LoadAccelerator TranslateAccelerator
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
// TODO: Place code here MSG msg;
HACCEL hAccelTable;
// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadString(hInstance, IDC_HELLO, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance);
// Perform application initialization: if (!InitInstance (hInstance, nCmdShow)) {
return FALSE; }
hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_HELLO); // Main message loop:
while (GetMessage(&msg, NULL, 0, 0)) {
(125)8.4 Menu 8.4.1 Toång quan
Menu phương tiện để ánh xạ thao tác chọn xử lý với xử lý thực Một menu là danh sách mục qui định tuỳ chọn nhóm tuỳ chọn (một submenu) của ứng dụng Bấm vào mục menu hay menu kích hoạt ựng dụng thực thi lệnh tương ứng
Windows có ba loại menu system menu – top level menu – popup menu Menu tạo với dấu hiệu nhận diện sau (Visual clues):
• Arrow : nested menu • Seperator : group menu • Check mark
• Ellipsis (…)
(126)8.4.2 Menu template
Người sử dụng định nghĩa menu tập tin tài nguyên (resource)
IDC_MENU MENU DISCARDABLE BEGIN
POPUP "&File" BEGIN
MENUITEM "&Open \tCtrl+O", ID_FILE_OPEN MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE MENUITEM "Save &As ", ID_FILE_SAVEAS MENUITEM "E&xit", IDM_EXIT
END
POPUP "&Help" BEGIN
MENUITEM "&About ", IDM_ABOUT END
MENUITEM "Quit !", ID_QUIT
MENUITEM "BreakCol", 65535, MENUBREAK MENUITEM "Break ?", 65535
END
Dạng tổng quát
POPUP text [, optionlist] BEGIN
MENUITEM text, result-code [optionlist] END
POPUP … BEGIN
MENUITEM "&About ", IDM_ABOUT END
Có lựa chọn
CHECKED GRAYED INACTIVE
MENUBREAK – toplevel menu MENUBARBREAK – popup menu
8.4.3 Menu Handles:
(127)1 Thiết kế mẫu menu tập tin tài ngun Có thể qui định phím tắt tương ứng cho mỗi mục menu Định nghĩa ID nhận diện cho mục menu
2 Xử lý thơng điệp WM_COMMAND phát sinh kích hoạt mục menu, ta xử lý cho trường hợp wParam có giá trị tương ứng với ID mục menu bấm Chương trình sau minh hoạ sử dụng menu đơn giản
Chương trình bounce sau cho phép tạo hình trịn nhỏ di chuyển bên vùng client bằng cách vào menu File – New Ball loại bớt hình vào menu File – Remove Ball
Sau trích đoạn chương trình nguồn:
struct BALL {
(128)b.crColor = color; return b;
};
const int MAX_BALLS = 100;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc;
TCHAR szHello[MAX_LOADSTRING]; static BALL aBalls[MAX_BALLS]; static int nBalls;
static COLORREF aColorTab [] = {RGB(0xff,0,0), RGB(0,0xff,0), RGB(0,0,0xff), RGB(0,0,0), RGB(0xff,0xff,0xff),RGB(0xc0,0xc0,0xc0)};
static int nColors = sizeof(aColorTab)/sizeof(aColorTab[0]); LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
RECT rt;
switch (message) {
case WM_COMMAND:
wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); switch (wmId)
{
case ID_FILE_NEWBALL:
GetClientRect(hWnd, &rt);
aBalls[nBalls] = CreateNewBall(rand()%rt.right, rand()%rt.bottom, 10, 5, 5, aColorTab[nBalls%nColors]);
SetTimer(hWnd, nBalls++, 10, NULL); break;
case ID_FILE_REMOVEBALL: if (nBalls > 0)
KillTimer(hWnd, nBalls); InvalidateRect(hWnd, NULL, TRUE); break;
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break;
(129)r.bottom = pb->y + pb->r + abs(pb->dy); pb->x += pb->dx;
pb->y += pb->dy;
if (pb->x + pb->r > rClient.right && pb->dx > 0) pb->dx = -pb->dx; if (pb->x - pb->r < && pb->dx < 0) pb->dx = -pb->dx;
if (pb->y + pb->r > rClient.bottom && pb->dy > 0) pb->dy = -pb->dy; if (pb->y - pb->r < && pb->dy < 0) pb->dy = -pb->dy;
InvalidateRect(hWnd, &r, TRUE); break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); RECT rt;
int i;
GetClientRect(hWnd, &rt);
DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER); HBRUSH hbr;
for (i = 0; i < nBalls; i++) {
hbr = CreateSolidBrush(aBalls[i].crColor); SelectObject(hdc, hbr);
Ellipse(hdc, aBalls[i].x - aBalls[i].r, aBalls[i].y - aBalls[i].r, aBalls[i].x + aBalls[i].r, aBalls[i].y + aBalls[i].r);
DeleteObject(hbr); } EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default:
return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
8.4.5 Sử dụng checkmark
(130)Hình 8-6 Menu với checkmark // Trich doan resource.h
#define ID_BRUSHCOLOR_WHITE 32771 #define ID_BRUSHCOLOR_RED 32772 #define ID_BRUSHCOLOR_BLUE 32773 #define ID_BRUSHCOLOR_GREEN 32774 #define ID_BRUSHCOLOR_LTGRAY 32775 #define ID_BRUSHCOLOR_GRAY 32776 #define ID_BRUSHCOLOR_BLACK 32777
// Ellipse.cpp : Defines the entry point for the application #define MAX_MARKERS 100
#define MARKER_SIZE
void DrawMarker(HDC hdc, POINT pt, int size);
(131)switch (message) {
case WM_COMMAND:
hMenu = GetMenu(hWnd); wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); switch (wmId) { case ID_BRUSHCOLOR_WHITE: case ID_BRUSHCOLOR_RED: case ID_BRUSHCOLOR_BLUE: case ID_BRUSHCOLOR_GREEN: case ID_BRUSHCOLOR_LTGRAY: case ID_BRUSHCOLOR_GRAY: case ID_BRUSHCOLOR_BLACK:
CheckMenuItem (hMenu, nBrushColor, MF_UNCHECKED); nBrushColor = LOWORD (wParam) ;
CheckMenuItem (hMenu, nBrushColor, MF_CHECKED) ; InvalidateRect (hWnd, NULL, TRUE) ;
return ; default:
return DefWindowProc(hWnd, message, wParam, lParam); }
break;
case WM_LBUTTONDOWN:
if (nMarkers <= MAX_MARKERS) {
apt[nMarkers].x = LOWORD(lParam); apt[nMarkers].y = HIWORD(lParam); nMarkers++;
InvalidateRect(hWnd, NULL, TRUE); }
break;
case WM_RBUTTONDOWN: nMarkers = 0;
InvalidateRect(hWnd, NULL, TRUE); break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
hbr = CreateHatchBrush(HS_BDIAGONAL,
aColorTab[nBrushColor - ID_BRUSHCOLOR_WHITE]); RECT rt;
GetClientRect(hWnd, &rt);
(132)SelectObject(hdc, hbr);
Ellipse(hdc, apt[i-1].x, apt[i-1].y, apt[i].x, apt[i].y); }
}
DeleteObject(hpen); DeleteObject(hbr); EndPaint(hWnd, &ps); break;
case WM_DESTROY:
PostQuitMessage(0); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
8.5 Bitmap 8.5.1 Toång quan
Một bitmap đối tượng đồ hoạ dùng để tạo, thao tác lưu trữ hình ảnh vào nhớ hay tập tin đĩa Các thao tác bimap phóng to, trượt, quay, vẽ…
Windows sẵn cung cấp số bitmap gọi bitmap chuẩn Người sử dụng tự định nghĩa các bitmap riêng gọi custom bitmap
Bitmap loại đối tượng đồ hoạ chọn cho ngữ cảnh thiết bị (tượng tự bút vẽ, chổi vẽ, font…)
Về mặt lưu trữ nội bộ, cấu trúc bitmap bao gồm:
• Phần đầu (header) mơ tả độ phân giải thiết bị ma trận pixel tạo, kích thước hình chữ nhật, kích thước mảng bít biểu diễn pixel…
• Bảng palette luận lý
• Mảng bit định nghĩa mối quan hệ pixel ảnh bitmapped với phần tử bảng palette
(133)CreateBitmap Creates a bitmap
CreateBitmapIndirect Creates a bitmap
CreateCompatibleBitmap Creates a bitmap compatible with a device
CreateDIBitmap Creates a device-dependent bitmap (DDB) from a DIB
CreateDIBSection Creates a DIB that applications can write to directly
ExtFloodFill Fills an area of the display surface with the current brush
GetBitmapDimensionEx Gets the dimensions of a bitmap
GetDIBColorTable Retrieves RGB color values from a DIB section bitmap
GetDIBits Copies a bitmap into a buffer
GetPixel Gets the RGB color value of the pixel at a given coordinate
GetStretchBltMode Gets the current stretching mode
GradientFill Fills rectangle and triangle structures
LoadBitmap Loads a bitmap from a module's executable file
MaskBlt Combines the color data in the source and destination bitmaps
PlgBlt Performs a bit-block transfer
SetBitmapDimensionEx Sets the preferred dimensions to a bitmap
SetDIBColorTable Sets RGB values in a DIB
SetDIBits Sets the pixels in a bitmap using color data from a DIB
SetDIBitsToDevice Sets the pixels in a rectangle using color data from a DIB
SetPixel Sets the color for a pixel
SetPixelV Sets a pixel to the best approximation of a color
SetStretchBltMode Sets the bitmap stretching mode
StretchBlt Copies a bitmap and stretches or compresses it
StretchDIBits Copies the color data in a DIB
(134)Hàm CreateBitmap tạo bitmap với độ rộng, chiều cao, định dạng màu HBITMAP CreateBitmap(
int nWidth, // bitmap width, in pixels
intnHeight, // bitmap height, in pixels
UINTcPlanes, // number of color planes
UINTcBitsPerPel, // number of bits to identify color
CONST VOID*lpvBits // color data array
);
CreateCompatibleBitmap
Hàm CreateCompatibleBitmap tạo bitmap tương thích với thiết bị liên kết với ngữ cảnh thiết bị hdc
HBITMAP CreateCompatibleBitmap( HDC hdc, // handle to DC
int nWidth, // width of bitmap, in pixels int nHeight // height of bitmap, in pixels
);
BitBlt
Hàm BitBlt chuyển dịch khối bit liệu màu hình chữ nhật pixels từ ngữ cảnh thiết bị đến ngữ cảnh thiết bị khác.
BOOL BitBlt(
HDC hdcDest, // handle to destination DC
intnXDest, // x-coord of destination upper-left corner
intnYDest, // y-coord of destination upper-left corner
intnWidth, // width of destination rectangle
intnHeight, // height of destination rectangle
HDChdcSrc, // handle to source DC
intnXSrc, // x-coordinate of source upper-left corner
intnYSrc, // y-coordinate of source upper-left corner
DWORDdwRop // raster operation code
(135)int nHeightDest, // height of destination rectangle HDC hdcSrc, // handle to source DC
int nXOriginSrc, // x-coord of source upper-left corner int nYOriginSrc, // y-coord of source upper-left corner int nWidthSrc, // width of source rectangle
int nHeightSrc, // height of source rectangle DWORD dwRop // raster operation code
);
8.5.3 Một số ví dụ sử dụng bitmap Ví dụ 1: Chương trình ShowBitmap
Chương trình ShowBitmap cho phép hiển thị ảnh bitmap cửa sổ client Bitmap được nhúng vào tập tin tài nguyên Hàm LoadBitmap nạp bitmap vào nhớ, hàm DrawBitmap sử dụng BitBlt để chuyển bit từ ngữ cảnh thiết bị loại nhớ cửa sổ client
Chương trình gồm tập tin tài nguyên:
Campanula.BMP CampanulaSM.BMP ShowBitmap.ICO SMALL.ICO
Và tập tin chương trình nguồn:
(136)Hình 8-7 Cửa sổ client chương trình
Bản thân tài ngun bitmap dùng hộp hội thoại
Hình 8-8 Hộp hội thoại chương trình
Sau trích đoạn chương trình nguồn Trích đoạn Resource.rc
#include "resource.h"
///////////////////////////////////////////////////////////////////////////// //
// Icon //
// Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems
IDI_SHOWBITMAP ICON DISCARDABLE "ShowBitmap.ICO" IDI_SMALL ICON DISCARDABLE "SMALL.ICO"
///////////////////////////////////////////////////////////////////////////// //
(137)///////////////////////////////////////////////////////////////////////////// //
// Bitmap //
IDB_FLOWER BITMAP DISCARDABLE "Campanula.BMP" IDB_FLOWER_SM BITMAP DISCARDABLE "CampanulaSM.BMP"
Trích đoạn ShowBitmap.cpp
// ShowBitmap.cpp : Defines the entry point for the application //
#include "stdafx.h" #include "resource.h" //
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); //
// DrawBitmap
void DrawBitmap(HDC hdc, HBITMAP hBitmap, int xStart, int yStart) {
BITMAP bmp; HDC hdcMem;
hdcMem = CreateCompatibleDC(hdc); SelectObject(hdcMem, hBitmap);
GetObject(hBitmap, sizeof(bmp), (LPVOID) &bmp);
BitBlt(hdc, xStart, yStart, bmp.bmWidth, bmp.bmHeight, hdcMem, 0, 0, SRCCOPY);
DeleteDC(hdcMem); }
//
(138){
case WM_CREATE:
hBitmap = LoadBitmap(hInst, (LPCTSTR)IDB_FLOWER); BITMAP bmp;
GetObject(hBitmap, sizeof(bmp), (LPVOID) &bmp); sizeDoc.cx = bmp.bmWidth;
sizeDoc.cy = bmp.bmHeight; RECT rtWnd, rtClient;
GetWindowRect(hWnd, &rtWnd); GetClientRect(hWnd, &rtClient);
MoveWindow(hWnd, 0, 0, sizeDoc.cx+rtWnd.right-rtWnd.left-rtClient.right,
sizeDoc.cy+rtWnd.bottom-rtWnd.top-rtClient.bottom, TRUE); break;
case WM_COMMAND:
wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); // Parse the menu selections: switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break;
case IDM_EXIT:
DestroyWindow(hWnd); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
break; case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); DrawBitmap(hdc, hBitmap, 0, 0); EndPaint(hWnd, &ps); break; case WM_DESTROY: PostQuitMessage(0); break; default:
(139)Chương trình gồm tập tin tài nguyên:
CampanulaSM.BMP ShowBitmap.ICO SMALL.ICO
Và tập tin chương trình nguồn:
StdAfx.h StdAfx.cpp resource.h ShowBitmap.rc ShowBitmap.h ShowBitmap.cpp
Hình 8-9 Cửa sổ client chương trình
Sau trích đoạn tập tin ShowBitmap.cpp
(140)HDC hdcMem;
hdcMem = CreateCompatibleDC(hdc); SelectObject(hdcMem, hBitmap);
GetObject(hBitmap, sizeof(bmp), (LPVOID) &bmp);
BitBlt(hdc, xStart, yStart, bmp.bmWidth, bmp.bmHeight, hdcMem, 0, 0, SRCCOPY);
DeleteDC(hdcMem); }
//
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) //
// PURPOSE: Processes messages for the main window //
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc;
TCHAR szHello[MAX_LOADSTRING]; static SIZE sizeBmp;
static HBITMAP hBitmap; static int nGap = 1;
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING); switch (message)
{
case WM_CREATE:
hBitmap = LoadBitmap(hInst, (LPCTSTR)IDB_FLOWER_SM); BITMAP bmp;
GetObject(hBitmap, sizeof(bmp), (LPVOID) &bmp); sizeBmp.cx = bmp.bmWidth;
sizeBmp.cy = bmp.bmHeight; break;
case WM_SIZE:
(141)break; default:
return DefWindowProc(hWnd, message, wParam, lParam); }
break; case WM_PAINT:
RECT rt;
int i, nx, ny; SIZE size;
hdc = BeginPaint(hWnd, &ps); DrawBitmap(hdc, hBitmap, 0, 0); GetClientRect(hWnd, &rt);
size = sizeBmp; size.cx += nGap; size.cy += nGap;
nx = (rt.right-1)/size.cx+1; ny = (rt.bottom-1)/size.cy+1; for (i = 1; i < nx; i++)
BitBlt(hdc, size.cx*i, 0, sizeBmp.cx, sizeBmp.cy, hdc, 0, 0, SRCCOPY);
for (i = 1; i < ny; i++)
BitBlt(hdc, 0, size.cy*i, rt.right, sizeBmp.cy, hdc, 0, 0, SRCCOPY);
EndPaint(hWnd, &ps); break;
case WM_DESTROY:
PostQuitMessage(0); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
(142)9 Custom Menu
Sử dụng menu mức đơn giản, ta dễ dàng ánh xạ thao tác chọn chức menu với thao tác thực thực Bên cạnh Windows cịn cho phép người sử dụng tạo menu phong phú nhờ hỗ trợ bitmap cho mục menu cung cung cấp khả cho người sử dụng tự vẽ menu
9.1 Các hàm thao tác menu
Windows cung cấp loạt hàm cho phép thao tác menu Bảng liệt kê hàm thông dụng hỗ trợ lập trình menu
Function Description
AppendMenu Thêm mục vào menu hay menu
CheckMenuItem Đánh dấu xoá dấu mục menu
CheckMenuRadioItem Đánh dấu mục menu làm cho trở thành
nút radio
CreateMenu Taïo menu
CreatePopupMenu Taïo popup menu
DeleteMenu Xoá menu Item
DestroyMenu Xoá hẳn menu
(143)GetMenuItemCount Lấy số mục menu
GetMenuItemID Lấy số nhận diện menu item
GetMenuItemInfo Lấy thông tin menu
GetMenuItemRect Lấy kích thước hình chữ nhật bao menu
GetMenuState Lấy giá trị cờ trạng thái menu item
GetMenuString Lấy nội dung văn menu item
GetSubMenu Lấy handle đến menu drop-down menu
kích hoạt menu item
GetSystemMenu Sao chép thay đổi menu hệ thống
InsertMenu Chèn menu item vào menu
InsertMenuItem Chèn menu item vào menu
IsMenu Xác định handle có phải menu handle không
LoadMenu Nạp tài nguyên resource
ModifyMenu Thay đổi menu item
RemoveMenu Xoá menu item
SetMenu Gắn menu với cửa sổ
SetMenuDefaultItem Định menu item cho menu
SetMenuInfo Định lại thông tin cho moät menu
SetMenuItemBitmaps Liên kết bitmap với menu item
SetMenuItemInfo Thay đổi thông tin menu item
(144)HMENU LoadMenu(
HINSTANCE hInstance, // handle to application instance
LPCTSTR lpMenuName // menu name string or menu-resource
// identifier
);
SetMenu
Hàm SetMenu định menu cho ứng dụng BOOL SetMenu(
HWND hWnd, // handle to window HMENU hMenu // handle to menu );
DrawMenuBar
Hàm DrawMenuBar vẽ lại menu ứng dụng BOOL DrawMenuBar(
HWND hWnd // handle to window with menu bar to redraw );
AppendMenu
Hàm AppendMenu thêm menu vào cuối menu, menu drop-down menu, menu con, hay menu shortcut Ta dùng hàm để qui định nội dung, dáng vẻ hoạt động mục menu
BOOL AppendMenu(
HMENU hMenu, // handle to menu UINT uFlags, // menu-item options
UINT_PTR uIDNewItem, // identifier, menu, or submenu LPCTSTR lpNewItem // menu-item content
);
lpNewItem
Con trỏ đến nội dung, nội dung có ý nghĩa thay đổi tuỳ theo giá trị uFlags có bao hàm giá trị MF_BITMAP, MF_OWNERDRAW, hay MF_STRING
Value Description
(145)Value Description
MF_BITMAP Dùng bitmap làm menu item lpNewItem chứa handle đến bitmap
MF_CHECKED Đặt dấu check bên cạnh menu item Nếu ứng dụng cung cấp bitmaps check-mark, cờ làm hiển thị dấu check bitmap bên cạnh menu item
MF_DISABLED Làm menu không hiệu lực, không làm mờ MF_ENABLED Làm menu có hiệu lực
MF_GRAYED Làm menu mờ không hiệu lực
MF_MENUBARBREAK Đối với menu, tương đương cờ MF_MENUBREAK Đối với trường hợp khác, dùng đường gạch đứng để ngăn cách cột
MF_MENUBREAK Mục menu đặt vào hàng (đối với menu bar) or hoặc cột (đối với drop-down menu, submenu, shortcut menu), khơng có đường gạch đứng ngăn cách
MF_OWNERDRAW Người sử dụng tự vẽ menu MF_POPUP Menu thuộc loại popup
MF_SEPARATOR Vẽ đường ngang để ngăn cách MF_STRING Menu có nội dung chuỗi MF_UNCHECKED
InsertMenuItem
Hàm InsertMenuItem chèn phần tử vào menu BOOL InsertMenuItem(
(146)9.2 Thay đổi menu chương trình
Ta thay đổi menu chương trình cách nạp menu hàm LoadMenu và chọn menu SetMenu Chương trình sau cho phép thay đổi menu chương trình, người sử dụng chọn menu tiếng Anh tiếng Việt không dấu Việc thực cách nạp menu chương trình thực thi
(147)#define IDD_ABOUTBOX 103 #define IDS_APP_TITLE 103 #define IDM_ABOUT 104 #define IDM_EXIT 105 #define IDS_HELLO 106 #define IDI_HELLO 107 #define IDS_HELLOVN 107 #define IDI_SMALL 108 #define IDC_HELLO 109 #define IDC_HELLOVN 110 #define IDR_MAINFRAME 128 #define ID_FILE_NEW 32771 #define ID_FILE_OPEN 32772 #define ID_FILE_SAVE 32773 #define ID_FILE_PRINT 32774 #define ID_EDIT_COPY 32775 #define ID_EDIT_CUT 32776 #define ID_EDIT_PASTE 32777 #define ID_OPTION_VIETNAMESE 32778 #define ID_LUACHON_TIENGANH 32779 #define IDC_STATIC -1 // Next default values for new objects //
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 132 #define _APS_NEXT_COMMAND_VALUE 32780 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 110 #endif
#endif
// Hello.rc
#include "resource.h" #include "windows.h"
///////////////////////////////////////////////////////////////////////////// //
(148)POPUP "&File" BEGIN
MENUITEM "&New\tCtrl+N", ID_FILE_NEW MENUITEM "&Open\tCtrl+O", ID_FILE_OPEN MENUITEM "&Save\tCtrl+S", ID_FILE_SAVE MENUITEM SEPARATOR
MENUITEM "&Print", ID_FILE_PRINT MENUITEM "E&xit", IDM_EXIT END
POPUP "&Edit" BEGIN
MENUITEM "&Copy\tCtrl+C", ID_EDIT_COPY MENUITEM "Cut\tCtrl+X", ID_EDIT_CUT MENUITEM "&Paste\tCtrl+V", ID_EDIT_PASTE END
POPUP "&Option" BEGIN
MENUITEM "&Vietnamese\tAlt+V", ID_OPTION_VIETNAMESE END
POPUP "&Help" BEGIN
MENUITEM "&About ", IDM_ABOUT END
END
IDC_HELLOVN MENU DISCARDABLE BEGIN
POPUP "&Ho So" BEGIN
MENUITEM "Ho so &Moi\tCtrl+N", ID_FILE_NEW MENUITEM "Mo &Ho so\tCtrol+O", ID_FILE_OPEN MENUITEM "&Luu Ho so\tCtrl+S", ID_FILE_SAVE MENUITEM SEPARATOR
MENUITEM "&In Ho so", ID_FILE_PRINT MENUITEM "&Thoat", IDM_EXIT END
POPUP "&Soan Thao" BEGIN
(149)//
IDC_HELLO ACCELERATORS MOVEABLE PURE BEGIN
"/", IDM_ABOUT, ASCII, ALT, NOINVERT "?", IDM_ABOUT, ASCII, ALT, NOINVERT "A", ID_LUACHON_TIENGANH, VIRTKEY, ALT, NOINVERT "C", ID_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT "N", ID_FILE_NEW, VIRTKEY, CONTROL, NOINVERT "O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT "S", ID_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT "V", ID_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT "V", ID_OPTION_VIETNAMESE, VIRTKEY, ALT, NOINVERT "X", ID_EDIT_CUT, VIRTKEY, CONTROL, NOINVERT END
///////////////////////////////////////////////////////////////////////////// //
// String Table //
STRINGTABLE DISCARDABLE BEGIN
IDS_APP_TITLE "Hello"
IDS_HELLO "Hello World! Press Alt + V to change to Vietnamese" IDS_HELLOVN "Xin chao! Bam Alt + A de chuyen sang tieng Anh" IDC_HELLO "HELLO"
IDC_HELLOVN "HELLOVN" END
// Hello.cpp : Defines the entry point for the application // FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc;
TCHAR szHello[MAX_LOADSTRING]; HMENU hMenu;
(150)hMenu = LoadMenu(hInst, (LPCSTR)IDC_HELLOVN); SetMenu(hWnd, hMenu);
DrawMenuBar(hWnd); bVietNamese = TRUE;
InvalidateRect(hWnd, NULL, TRUE); break;
case ID_LUACHON_TIENGANH:
hMenu = LoadMenu(hInst, (LPCSTR)IDC_HELLO); SetMenu(hWnd, hMenu);
DrawMenuBar(hWnd); bVietNamese = FALSE;
InvalidateRect(hWnd, NULL, TRUE); break;
case ID_FILE_NEW:
MessageBox(hWnd, "File New", "Message", MB_OK); break;
case ID_FILE_OPEN:
MessageBox(hWnd, "File Open", "Message", MB_OK); break;
case ID_FILE_SAVE:
MessageBox(hWnd, "File Save", "Message", MB_OK); break;
case ID_EDIT_COPY:
MessageBox(hWnd, "Edit Copy", "Message", MB_OK); break;
case ID_EDIT_CUT:
MessageBox(hWnd, "Edit Cut", "Message", MB_OK); break;
case ID_EDIT_PASTE:
MessageBox(hWnd, "Edit Paste", "Message", MB_OK); break;
case IDM_EXIT:
DestroyWindow(hWnd); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
break; case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); RECT rt;
(151)9.3 Menu người sử dụng tự vẽ (Owner Draw Menu)
Người sử dụng tạo menu cách dùng hàm CreateMenu, sau sử dụng hàm AppendMenu để tạo nên thành phần menu
Để tạo menu tự vẽ tham số uFlag hàm AppendMenu có bao gồm thuộc tính MF_OWNERDRAW
BOOL AppendMenu(
HMENU hMenu, // handle to menu to be changed UINT uFlags, // menu-item flags
UINT uIDNewItem, // menu-item identifier or handle to drop-down menu or
submenu
LPCTSTR lpNewItem // menu-item content );
Trình tự tạo mục menu tự vẽ sau:
1) Gọi hàm AppendMenu với tham số uFlags bao gồm giá trị MF_OWNERDRAW
MF_OWNERDRAW Qui định mục menu người sử dụng tự vẽ Trước menu hiển thị lần đầu tiên, cửa sổ sở hữu menu nhận thông điệp WM_MEASUREITEM để lấy chiều dài chiều rộng của mục menu Thơng điệp WM_DRAWITEM sau gởi cho thủ tục cửa sổ dáng vẻ menu cần cập nhật
2) Xử lý thông điệp WM_MEASUREITEM
Thông điệp WM_MEASUREITEM sử dụng cấu trúc MEASUREITEMSTRUCT typedef struct tagMEASUREITEMSTRUCT { // mis
UINT CtlType; // type of control
UINT CtlID; // combo box, list box, or button identifier UINT itemID; // menu item, variable-height list box, // or combo box identifier
UINT itemWidth; // width of menu item, in pixels
UINT itemHeight; // height of single item in list box menu, // in pixels
DWORD itemData; // application-defined 32-bit value } MEASUREITEMSTRUCT;
Thông điệp WM_MEASUREITEM có hai tham số kèm wParam giá trị nhận dạng
(152)Thông điệp WM_DRAWITEM gởi cho cửa sổ chứa menu mục menu với thuộc tính MF_OWNERDRAW cần cập nhật
WM_DRAWITEM
idCtl = (UINT) wParam; // control identifier lpdis = (LPDRAWITEMSTRUCT) lParam; // item-drawing information
Tham số lParam cho biết thông tin mà cửa sổ chứa menu cần phải nắm để vẽ mục menu Các thơng tin lưu cấu trúc DRAWITEMSTRUCT typedef struct tagDRAWITEMSTRUCT { // dis
UINT CtlType; UINT CtlID; UINT itemID; UINT itemAction; UINT itemState; HWND hwndItem; HDC hDC; RECT rcItem; DWORD itemData; } DRAWITEMSTRUCT;
Người sử dụng xử lý thông điệp kể để vẽ menu vào ngữ cảnh thiết bị, thành phần hDC cấu trúc
(153)(154)(155)help.bmp hinhve.bmp
file.bmp brush.bmp
small.ico VnMenu.ico
Và tập tin chương trình nguồn
resource.h StdAfx.h
VnMenu.h StdAfx.cpp
VnMenu.rc VnMenu.cpp
Trích đoạn chương trình nguồn resource.h
// resource.h
// Used by VnMenu.rc //
(156)#define ID_BRUSH_CROSS 32792 #define ID_BRUSH_HORIZONTAL 32793 #define ID_BRUSH_VERTICAL 32794 #define ID_FIGURE_ELLIPSE 32795 #define ID_FIGURE_RECTANGLE 32796 #define ID_FIGURE_ROUNDRECT 32797 #define ID_FIGURE_CHORD 32798 #define ID_FIGURE_PIE 32799
Trích đoạn chương trình nguồn vnmenu.cpp
// VnMenu.cpp : Defines the entry point for the application //
#include "stdafx.h" #include "resource.h"
HFONT MyCreateFont (HDC hdc, char * szFaceName, int nPointSize, BOOL bBold = FALSE, BOOL bItalic = FALSE) {
HFONT hFont ; LOGFONT lf ;
memset(&lf, 0, sizeof(lf));
lf.lfHeight = -MulDiv(nPointSize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
lf.lfWeight = bBold ? FW_BOLD : FW_NORMAL; lf.lfItalic = bItalic;
strcpy (lf.lfFaceName, szFaceName) ; hFont = CreateFontIndirect (&lf) ; return hFont ;
} //
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) //
// PURPOSE: Processes messages for the main window //
(157)struct M_POPUP {
char *name; UINT bitmapId; int nItems;
M_ITEM aItems[MAX_ITEMS]; };
static M_POPUP aVnMenu[] = {
"&Hồ sơ", IDB_FILE, 5, {
{"Hồ sơ mới", ID_FILE_NEW, MF_OWNERDRAW}, {"Mở hồ sơ", ID_FILE_OPEN, MF_OWNERDRAW}, {"Lưu hồ sơ", ID_FILE_SAVE, MF_OWNERDRAW}, {"", 0, MF_SEPARATOR},
{"In", ID_FILE_PRINT, MF_OWNERDRAW}, {"Thoát", IDM_EXIT, MF_OWNERDRAW}, },
"&Soạn Thảo", IDB_EDIT, 3, {
{"Sao chép", ID_EDIT_COPY, MF_OWNERDRAW}, {"Cắt", ID_EDIT_CUT, MF_OWNERDRAW},
{"Dán", ID_EDIT_PASTE, MF_OWNERDRAW}, },
"Đổi Menu Font", IDB_FONT, 4, {
{"VNI-Times", ID_FONT_VNI_TIMES, MF_OWNERDRAW}, {"VNI-Courier", ID_FONT_VNI_COURIER, MF_OWNERDRAW}, {"VNI-Aptima", ID_FONT_VNI_APTIMA, MF_OWNERDRAW}, {"VNI-Cooper", ID_FONT_VNI_COOPER, MF_OWNERDRAW} },
"&Hình", IDB_FIGURE, 5, {
{"Hình Bầu dục", ID_FIGURE_ELLIPSE, MF_OWNERDRAW}, {"Hình Chữ Nhật", ID_FIGURE_RECTANGLE, MF_OWNERDRAW}, {"Hình chữ nhật trịn", ID_FIGURE_ROUNDRECT, MF_OWNERDRAW}, {"Hình Vành khun", ID_FIGURE_CHORD, MF_OWNERDRAW},
(158){
{"Thoâng tin", IDM_ABOUT, MF_OWNERDRAW}, }
};
struct FONTMENUDATA {
UINT id;
TCHAR szFaceName[32]; int nPointSize;
BOOL bBold, bItalics; };
static FONTMENUDATA aFontData[] = {
{ID_FONT_VNI_TIMES, "VNI-Times", 12},
{ID_FONT_VNI_COURIER, "VNI-Couri", 12, TRUE}, {ID_FONT_VNI_APTIMA, "VNI-Aptima", 12},
{ID_FONT_VNI_COOPER, "VNI-Cooper", 12, TRUE, TRUE}, };
#define dim(a) (sizeof(a)/sizeof(a[0])) const int nPopups = dim(aVnMenu);
const int nFonts = dim(aFontData);
HMENU AddMenuPopup(HMENU hMenu, M_POPUP mp) {
HMENU hMenuPopup = CreatePopupMenu();
AppendMenu (hMenu, MF_POPUP|MF_BITMAP, (UINT)hMenuPopup, (LPSTR)LoadBitmap (hInst, (LPSTR)mp.bitmapId));
for (int i = 0; i < mp.nItems; i++)
AppendMenu (hMenuPopup, mp.aItems[i].attr, mp.aItems[i].id, mp.aItems[i].name);
return hMenuPopup; }
M_ITEM *SearchItem(int n, M_POPUP *a, UINT id) {
(159)LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc;
TCHAR szName[][MAX_LOADSTRING] = {"Hình Ellipse", "Hình Chữ Nhật", "Hình Chữ nhật trịn", "Chord", "Pie"};
static HMENU hMenuVn; static HFONT ahf[nFonts];
static int cxCheck, cyCheck, nBrush;
static WORD nBrushIdx, nFigIdx, nCurFont = 1; static COLORREF crHighlight, crHighlightText; static HBRUSH ahbr[7];
M_ITEM *pItem; switch (message) {
case WM_CREATE: int i;
LOGFONT lf;
crHighlight = GetSysColor(COLOR_HIGHLIGHT);
crHighlightText = GetSysColor(COLOR_HIGHLIGHTTEXT); cxCheck = GetSystemMetrics(SM_CXMENUCHECK);
cyCheck = GetSystemMetrics(SM_CYMENUCHECK); ZeroMemory(&lf, sizeof(lf));
hdc = GetDC(hWnd);
for (i = 0; i < nFonts; i++)
ahf[i] = MyCreateFont(hdc, aFontData[i].szFaceName, aFontData[i].nPointSize, aFontData[i].bBold, aFontData[i].bItalics);
ahbr[0] = CreateSolidBrush(RGB(0,0, 255));
ahbr[1] = CreateHatchBrush(HS_BDIAGONAL, RGB(0,0,255)); ahbr[2] = CreateHatchBrush(HS_FDIAGONAL, RGB(0,0,255)); ahbr[3] = CreateHatchBrush(HS_DIAGCROSS, RGB(0,0,255)); ahbr[4] = CreateHatchBrush(HS_CROSS, RGB(0,0,255)); ahbr[5] = CreateHatchBrush(HS_HORIZONTAL, RGB(0,0,255)); ahbr[6] = CreateHatchBrush(HS_VERTICAL, RGB(0,0,255)); nBrush = 7;
(160)lpmi = (LPMEASUREITEMSTRUCT)lParam; HFONT hfont, hfontOld;
// switch(lpmi->itemID)
pItem = SearchItem(nPopups, aVnMenu, lpmi->itemID); if (pItem)
{
hdc = GetDC (hWnd);
for (i = 0; i < dim(aFontData); i++) if (lpmi->itemID == aFontData[i].id)
break;
if (i < dim(aFontData)) hfont = ahf[i]; else
hfont = ahf[0];
SelectObject (hdc, hfont);
GetTextExtentPoint32(hdc, pItem->name, lstrlen(pItem->name), &sizeText);
ReleaseDC (hWnd, hdc);
lpmi->itemWidth = sizeText.cx + cxCheck; lpmi->itemHeight = max(sizeText.cy, cyCheck); }
break;
case WM_DRAWITEM:
LPDRAWITEMSTRUCT lpdi;
lpdi = (LPDRAWITEMSTRUCT)lParam; COLORREF crTextOld, crBackOld; BOOL bSwap;
int xText, yText; char *szText; HBRUSH hbrOld;
pItem = SearchItem(nPopups, aVnMenu, lpdi->itemID); if (pItem)
{
for (i = 0; i < dim(aFontData); i++) if (lpdi->itemID == aFontData[i].id)
break;
(161)cxCheck = GetSystemMetrics(SM_CXMENUCHECK); xText = lpdi -> rcItem.left + cxCheck; yText = lpdi -> rcItem.top;
ExtTextOut(lpdi->hDC, xText, yText, ETO_OPAQUE, &lpdi->rcItem, szText, strlen(szText), NULL);
if (lpdi->itemID - ID_FONT_VNI_TIMES == nCurFont || lpdi->itemID - ID_FIGURE_ELLIPSE == nFigIdx || lpdi->itemID - ID_BRUSH_SOLID == nBrushIdx) {
hbrOld = (HBRUSH)SelectObject(lpdi->hDC, ahbr[0]); Circle>hDC, lpdi->rcItem.left+cxCheck/2,
(lpdi->rcItem.top + lpdi->rcItem.bottom)/2, 5); SelectObject(lpdi->hDC, hbrOld);
}
SelectObject (lpdi->hDC, hfontOld); if (bSwap) { SetTextColor(lpdi->hDC, crTextOld); SetBkColor(lpdi->hDC, crBackOld); } } break; case WM_COMMAND:
wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); switch (wmId) { case ID_FONT_VNI_TIMES: case ID_FONT_VNI_COURIER: case ID_FONT_VNI_APTIMA: case ID_FONT_VNI_COOPER:
(162)break;
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break;
case IDM_EXIT:
DestroyWindow(hWnd); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
break; case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); RECT rt;
GetClientRect(hWnd, &rt);
SelectObject (hdc, ahf[nCurFont]);
DrawText(hdc, szName[nFigIdx], strlen(szName[nFigIdx]), &rt, DT_CENTER);
SelectObject(hdc, ahbr[nBrushIdx]); switch(ID_FIGURE_ELLIPSE + nFigIdx) {
case ID_FIGURE_ELLIPSE:
Ellipse(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4, 3*rt.bottom/4);
break;
case ID_FIGURE_RECTANGLE:
Rectangle(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4, 3*rt.bottom/4);
break;
case ID_FIGURE_ROUNDRECT:
RoundRect(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4, 3*rt.bottom/4, 30, 30);
break;
case ID_FIGURE_PIE:
Pie(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4, 3*rt.bottom/4, rt.right, rt.bottom/2, 0, 0);
break;
case ID_FIGURE_CHORD:
Chord(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4,
(163)}
Ví dụ Chương trình VnMenu V2 tạo menu có tiếng Việt cách tự vẽ mục menu tương tự ví dụ có thêm chức cho phép lựa chọn menu tiếng Việt menu tiếng Anh
(164)Hình 9-8 VnMenu với lựa chọn tiếng Anh
Ta chuyển qua lại tiếng Anh tiếng Việt nhờ trì hai menu riêng rẽ Menu tiếng Anh định nghĩa từ tập tin tài nguyên, menu tiếng Việt có cách tự vẽ mục menu
Trích đoạn chương trình nguồn sau minh hoạ việc chuyển đổi qua lại hai menu
// VnMenu.cpp : Defines the entry point for the application //
#include "stdafx.h" #include "resource.h"
(165)};
static M_POPUP aVnMenu[] = {
"&Hoà sô", IDB_FILE, 5, {
{"Hồ sơ mới", ID_FILE_NEW, MF_OWNERDRAW}, {"Mở hồ sơ", ID_FILE_OPEN, MF_OWNERDRAW}, {"Lưu hồ sơ", ID_FILE_SAVE, MF_OWNERDRAW}, {"", 0, MF_SEPARATOR},
{"In", ID_FILE_PRINT, MF_OWNERDRAW}, {"Thoát", IDM_EXIT, MF_OWNERDRAW}, },
"&Soạn Thảo", IDB_EDIT, 3, {
{"Sao cheùp", ID_EDIT_COPY, MF_OWNERDRAW}, {"Cắt", ID_EDIT_CUT, MF_OWNERDRAW},
{"Dán", ID_EDIT_PASTE, MF_OWNERDRAW}, },
"Đổi Menu Font", IDB_FONT, 4, {
{"VNI-Times", ID_FONT_VNI_TIMES, MF_OWNERDRAW}, {"VNI-Courier", ID_FONT_VNI_COURIER, MF_OWNERDRAW}, {"VNI-Aptima", ID_FONT_VNI_APTIMA, MF_OWNERDRAW}, {"VNI-Cooper", ID_FONT_VNI_COOPER, MF_OWNERDRAW} },
"&Hình", IDB_FIGURE, 5, {
{"Hình Chữ Nhật", ID_FIGURE_RECTANGLE, MF_OWNERDRAW}, {"Hình Bầu dục", ID_FIGURE_ELLIPSE, MF_OWNERDRAW},
{"Hình chữ nhật trịn", ID_FIGURE_ROUNDRECT, MF_OWNERDRAW}, {"Hình Vành khun", ID_FIGURE_CHORD, MF_OWNERDRAW},
{"Hình bánh", ID_FIGURE_PIE, MF_OWNERDRAW}, },
"&Chổi vẽ", IDB_BRUSH, 7, {
(166){"Thoâng tin", IDM_ABOUT, MF_OWNERDRAW}, }
};
struct FONTMENUDATA {
UINT id;
TCHAR szFaceName[32]; int nPointSize;
BOOL bBold, bItalics; };
static FONTMENUDATA aFontData[] = {
{ID_FONT_VNI_TIMES, "VNI-Times", 12},
{ID_FONT_VNI_COURIER, "VNI-Couri", 12, TRUE}, {ID_FONT_VNI_APTIMA, "VNI-Aptima", 12},
{ID_FONT_VNI_COOPER, "VNI-Cooper", 12, TRUE, TRUE}, };
#define dim(a) (sizeof(a)/sizeof(a[0])) const int nPopups = dim(aVnMenu);
const int nFonts = dim(aFontData);
HMENU AddMenuPopup(HMENU hMenu, M_POPUP mp) {
HMENU hMenuPopup = CreatePopupMenu();
AppendMenu (hMenu, MF_POPUP|MF_BITMAP, (UINT)hMenuPopup, (LPSTR)LoadBitmap (hInst, (LPSTR)mp.bitmapId));
for (int i = 0; i < mp.nItems; i++)
AppendMenu (hMenuPopup, mp.aItems[i].attr, mp.aItems[i].id, mp.aItems[i].name);
return hMenuPopup; }
M_ITEM *SearchItem(int n, M_POPUP *a, UINT id) {
(167)LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc;
TCHAR* szName[][5] = {
{"Ellipse", "Rectangle", "RoundRect", "Chord", "Pie"},
{"Hình Bầu dục", "Hình Chữ Nhật", "Hình Chữ nhật trịn", "Hình vành khun", "Hình bánh"}
};
static HMENU hMenu, hMenuVn; static HFONT ahf[nFonts];
static int cxCheck, cyCheck, nBrush;
static WORD nBrushIdx, nFigIdx, nCurFont = 0; static COLORREF crHighlight, crHighlightText; static HBRUSH ahbr[7];
static bool bVietNamese = true; M_ITEM *pItem; switch (message) { case WM_CREATE: int i; LOGFONT lf;
crHighlight = GetSysColor(COLOR_HIGHLIGHT);
crHighlightText = GetSysColor(COLOR_HIGHLIGHTTEXT); cxCheck = GetSystemMetrics(SM_CXMENUCHECK);
cyCheck = GetSystemMetrics(SM_CYMENUCHECK); ZeroMemory(&lf, sizeof(lf));
hdc = GetDC(hWnd);
for (i = 0; i < nFonts; i++)
ahf[i] = MyCreateFont(hdc, aFontData[i].szFaceName, aFontData[i].nPointSize, aFontData[i].bBold, aFontData[i].bItalics);
ahbr[0] = CreateSolidBrush(RGB(0,0, 255));
(168)hMenuPopup = CreatePopupMenu(); SetMenu(hWnd, hMenuVn);
break;
case WM_MEASUREITEM: SIZE sizeText;
LPMEASUREITEMSTRUCT lpmi;
lpmi = (LPMEASUREITEMSTRUCT)lParam; HFONT hfont, hfontOld;
pItem = SearchItem(nPopups, aVnMenu, lpmi->itemID); if (pItem)
{
hdc = GetDC (hWnd);
for (i = 0; i < dim(aFontData); i++) if (lpmi->itemID == aFontData[i].id)
break;
if (i < dim(aFontData)) hfont = ahf[i]; else
hfont = ahf[0];
SelectObject (hdc, hfont);
GetTextExtentPoint32(hdc, pItem->name, lstrlen(pItem->name), &sizeText);
ReleaseDC (hWnd, hdc);
lpmi->itemWidth = sizeText.cx + cxCheck; lpmi->itemHeight = max(sizeText.cy, cyCheck); }
break;
case WM_DRAWITEM:
LPDRAWITEMSTRUCT lpdi;
lpdi = (LPDRAWITEMSTRUCT)lParam; COLORREF crTextOld, crBackOld; BOOL bSwap;
int xText, yText; char *szText; HBRUSH hbrOld;
(169)crBackOld = SetBkColor (lpdi->hDC, crHighlight); bSwap = TRUE;
} else
bSwap = FALSE;
cxCheck = GetSystemMetrics(SM_CXMENUCHECK); xText = lpdi -> rcItem.left + cxCheck; yText = lpdi -> rcItem.top;
ExtTextOut(lpdi->hDC, xText, yText, ETO_OPAQUE, &lpdi->rcItem, szText, strlen(szText), NULL);
if (lpdi->itemID - ID_FONT_VNI_TIMES == nCurFont || lpdi->itemID - ID_FIGURE_ELLIPSE == nFigIdx || lpdi->itemID - ID_BRUSH_SOLID == nBrushIdx) {
hbrOld = (HBRUSH)SelectObject(lpdi->hDC, ahbr[0]); Circle>hDC, lpdi->rcItem.left+cxCheck/2,
(lpdi->rcItem.top + lpdi->rcItem.bottom)/2, 5); SelectObject(lpdi->hDC, hbrOld);
}
SelectObject (lpdi->hDC, hfontOld); if (bSwap)
{
SetTextColor(lpdi->hDC, crTextOld); SetBkColor(lpdi->hDC, crBackOld); }
} break;
case WM_COMMAND:
wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); switch (wmId)
{
case ID_FONT_VNI_TIMES: case ID_FONT_VNI_COURIER: case ID_FONT_VNI_APTIMA: case ID_FONT_VNI_COOPER:
CheckMenuItem(hMenu, ID_FONT_VNI_TIMES + nCurFont, MF_UNCHECKED);
(170)CheckMenuItem(hMenu, ID_BRUSH_SOLID + nBrushIdx, MF_UNCHECKED);
nBrushIdx = wmId - ID_BRUSH_SOLID; CheckMenuItem(hMenu, wmId, MF_CHECKED); InvalidateRect(hWnd, NULL, TRUE);
break;
case ID_FIGURE_ELLIPSE: case ID_FIGURE_RECTANGLE: case ID_FIGURE_ROUNDRECT: case ID_FIGURE_CHORD: case ID_FIGURE_PIE:
CheckMenuItem(hMenu, ID_FIGURE_ELLIPSE + nFigIdx, MF_UNCHECKED);
nFigIdx = wmId - ID_FIGURE_ELLIPSE; CheckMenuItem(hMenu, wmId, MF_CHECKED); InvalidateRect(hWnd, NULL, TRUE);
break;
case ID_OPTION_ENGLISH: bVietNamese = false; SetMenu(hWnd, hMenu); break;
case ID_OPTION_TIENGVIET: SetMenu(hWnd, hMenuVn); bVietNamese = true; break;
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break;
case IDM_EXIT:
DestroyWindow(hWnd); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
(171)case ID_FIGURE_RECTANGLE:
Rectangle(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4, 3*rt.bottom/4);
break;
case ID_FIGURE_ROUNDRECT:
RoundRect(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4, 3*rt.bottom/4, 30, 30);
break;
case ID_FIGURE_PIE:
Pie(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4, 3*rt.bottom/4, rt.right, rt.bottom/2, 0, 0);
break;
case ID_FIGURE_CHORD:
Chord(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4,
3*rt.bottom/4, rt.right, rt.bottom/3, 0, rt.bottom); break;
}
EndPaint(hWnd, &ps); break;
case WM_DESTROY:
for (i = 0; i < nBrush; i++) DeleteObject(ahbr[i]); PostQuitMessage(0);
break; default:
return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
(172)(173)// VnMenu.cpp : Defines the entry point for the application //
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) //
#define MAX_ITEMS 10 struct M_ITEM
{
char *name; UINT id; UINT attr; };
struct M_POPUP {
char *name; UINT bitmapId; int nItems;
M_ITEM aItems[MAX_ITEMS]; };
static M_POPUP aVnMenu[] = {
"&Hồ sơ", IDB_FILE, 5, {
{"Hồ sơ mới", ID_FILE_NEW, MF_OWNERDRAW}, {"Mở hồ sơ", ID_FILE_OPEN, MF_OWNERDRAW}, {"Lưu hồ sơ", ID_FILE_SAVE, MF_OWNERDRAW}, {"", 0, MF_SEPARATOR},
{"In", ID_FILE_PRINT, MF_OWNERDRAW}, {"Thoát", IDM_EXIT, MF_OWNERDRAW}, },
"&Soạn Thảo", IDB_EDIT, 3, {
{"Sao chép", ID_EDIT_COPY, MF_OWNERDRAW}, {"Cắt", ID_EDIT_CUT, MF_OWNERDRAW},
{"Dán", ID_EDIT_PASTE, MF_OWNERDRAW}, },
"Đổi Menu Font", IDB_FONT, 4, {
(174)},
"&Chổi vẽ", IDB_BRUSH, 7, {
{"Đặc", ID_BRUSH_SOLID, MF_OWNERDRAW}, {"Chéo ngược", ID_BRUSH_BWDIAGONAL, MF_OWNERDRAW}, {"Chéo xi", ID_BRUSH_FWDIAGONAL, MF_OWNERDRAW}, {"Chéo", ID_BRUSH_DIAGCROSS, MF_OWNERDRAW},
{"Ngang dọc", ID_BRUSH_CROSS, MF_OWNERDRAW}, {"Ngang", ID_BRUSH_HORIZONTAL, MF_OWNERDRAW}, {"Dọc", ID_BRUSH_VERTICAL, MF_OWNERDRAW}, },
"&Tùy Choïn", IDB_OPTION, 1, {
{"English", ID_OPTION_ENGLISH, MF_OWNERDRAW}, },
"&Hướng dẫn", IDB_HELP, 1, {
{"Thoâng tin", IDM_ABOUT, MF_OWNERDRAW}, }
};
struct FONTMENUDATA {
UINT id;
TCHAR szFaceName[32]; int nPointSize;
BOOL bBold, bItalics; };
static FONTMENUDATA aFontData[] = {
{ID_FONT_VNI_TIMES, "VNI-Times", 12},
{ID_FONT_VNI_COURIER, "VNI-Couri", 12, TRUE}, {ID_FONT_VNI_APTIMA, "VNI-Aptima", 12},
(175)}
M_ITEM *SearchItem(int n, M_POPUP *a, UINT id) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < a[i].nItems; j++) if (a[i].aItems[j].id == id)
return &a[i].aItems[j]; }
return NULL; }
BOOL Circle(HDC hdc, int x, int y, int r) {
return Ellipse(hdc, x-r, y-r, x+r, y+r); }
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc;
TCHAR* szName[][5] = {
{"Ellipse", "Rectangle", "RoundRect", "Chord", "Pie"},
{"Hình Bầu dục", "Hình Chữ Nhật", "Hình Chữ nhật trịn", "Hình vành khun", "Hình cánh quạt"}
};
static HMENU hMenu, hMenuVn; static HFONT ahf[nFonts];
static int cxCheck, cyCheck, nBrush;
static WORD nBrushIdx, nFigIdx, nCurFont = 0; static COLORREF crHighlight, crHighlightText; static HBRUSH ahbr[7];
static bool bVietNamese = true; M_ITEM *pItem;
(176)ahf[i] = MyCreateFont(hdc, aFontData[i].szFaceName, aFontData[i].nPointSize, aFontData[i].bBold,
aFontData[i].bItalics);
ahbr[0] = CreateSolidBrush(RGB(0,0, 255));
ahbr[1] = CreateHatchBrush(HS_BDIAGONAL, RGB(0,0,255)); ahbr[2] = CreateHatchBrush(HS_FDIAGONAL, RGB(0,0,255)); ahbr[3] = CreateHatchBrush(HS_DIAGCROSS, RGB(0,0,255)); ahbr[4] = CreateHatchBrush(HS_CROSS, RGB(0,0,255)); ahbr[5] = CreateHatchBrush(HS_HORIZONTAL, RGB(0,0,255)); ahbr[6] = CreateHatchBrush(HS_VERTICAL, RGB(0,0,255)); nBrush = 7;
ReleaseDC(hWnd, hdc); hMenu = GetMenu(hWnd); hMenuVn = CreateMenu();
for (i = 0; i < nPopups; i++)
AddMenuPopup(hMenuVn, aVnMenu[i]); HMENU hMenuPopup;
hMenuPopup = CreatePopupMenu(); SetMenu(hWnd, hMenuVn);
break;
case WM_MEASUREITEM: SIZE sizeText;
LPMEASUREITEMSTRUCT lpmi;
lpmi = (LPMEASUREITEMSTRUCT)lParam; HFONT hfont, hfontOld;
switch(lpmi->itemID) {
case ID_FIGURE_ELLIPSE: case ID_FIGURE_RECTANGLE: case ID_FIGURE_ROUNDRECT: case ID_FIGURE_CHORD: case ID_FIGURE_PIE:
lpmi->itemWidth = 80; lpmi->itemHeight = 30; break;
(177)for (i = 0; i < dim(aFontData); i++) if (lpmi->itemID == aFontData[i].id)
break;
if (i < dim(aFontData)) hfont = ahf[i]; else
hfont = ahf[nCurFont]; SelectObject (hdc, hfont);
GetTextExtentPoint32(hdc, pItem->name, lstrlen(pItem->name), &sizeText);
ReleaseDC (hWnd, hdc);
lpmi->itemWidth = sizeText.cx + cxCheck; lpmi->itemHeight = max(sizeText.cy, cyCheck); } break; } break; case WM_DRAWITEM: LPDRAWITEMSTRUCT lpdi;
lpdi = (LPDRAWITEMSTRUCT)lParam; COLORREF crTextOld, crBackOld; BOOL bSwap;
int xText, yText; char *szText; switch(lpdi->itemID) { case ID_FIGURE_ELLIPSE: case ID_FIGURE_RECTANGLE: case ID_FIGURE_ROUNDRECT: case ID_FIGURE_CHORD: case ID_FIGURE_PIE: HBRUSH hbrOld; int orgRop; bSwap = false;
hbrOld = (HBRUSH)SelectObject(lpdi->hDC, ahbr[nBrushIdx]); if (lpdi->itemState & ODS_SELECTED)
{
xText = lpdi -> rcItem.left; yText = lpdi -> rcItem.top;
(178)lpdi->rcItem.right, lpdi->rcItem.bottom-2); break;
case ID_FIGURE_RECTANGLE:
Rectangle(>hDC, >rcItem.left+cxCheck, lpdi->rcItem.top + 2,
lpdi->rcItem.right, lpdi->rcItem.bottom-2); break;
case ID_FIGURE_ROUNDRECT:
RoundRect(>hDC, >rcItem.left+cxCheck, lpdi->rcItem.top + 2,
lpdi->rcItem.right, lpdi->rcItem.bottom-2, 5, 5); break;
case ID_FIGURE_CHORD:
Chord(>hDC, >rcItem.left+cxCheck,
lpdi->rcItem.top + 2, lpdi->rcItem.right, lpdi->rcItem.bottom-2, >rcItem.right, >rcItem.bottom-10,
lpdi->rcItem.left, (lpdi->rcItem.top+lpdi->rcItem.bottom)/2); break;
case ID_FIGURE_PIE:
Pie(lpdi->hDC, lpdi->rcItem.left+cxCheck, lpdi->rcItem.top + 2, lpdi->rcItem.right, lpdi->rcItem.bottom+20,
lpdi->rcItem.right, (lpdi->rcItem.top+lpdi->rcItem.bottom)/2,
lpdi->rcItem.left, lpdi->rcItem.top); break;
}
if (lpdi->itemID - ID_FIGURE_ELLIPSE == nFigIdx) {
SelectObject(lpdi->hDC, ahbr[0]);
Circle>hDC, lpdi->rcItem.left+cxCheck/2, (lpdi->rcItem.top + lpdi->rcItem.bottom)/2, 5);
}
if (bSwap) {
SetBkColor(lpdi->hDC, crBackOld); SetROP2(lpdi->hDC, orgRop);
}
(179)xText = lpdi -> rcItem.left; yText = lpdi -> rcItem.top;
crBackOld = SetBkColor (lpdi->hDC, crHighlight); orgRop = SetROP2(lpdi->hDC, R2_XORPEN);
bSwap = true; }
cxCheck = GetSystemMetrics(SM_CXMENUCHECK);
ExtTextOut(lpdi->hDC, xText, yText, ETO_OPAQUE, &lpdi->rcItem, "", 0, NULL);
Rectangle(lpdi->hDC, lpdi->rcItem.left+cxCheck, lpdi->rcItem.top + 2,
lpdi->rcItem.right, lpdi->rcItem.bottom-2); if (lpdi->itemID - ID_BRUSH_SOLID == nBrushIdx)
{
SelectObject(lpdi->hDC, ahbr[0]);
Circle>hDC, lpdi->rcItem.left+cxCheck/2, (lpdi->rcItem.top + lpdi->rcItem.bottom)/2, 5);
}
if (bSwap) {
SetBkColor(lpdi->hDC, crBackOld); SetROP2(lpdi->hDC, orgRop);
}
SelectObject(lpdi->hDC, hbrOld); break;
default:
pItem = SearchItem(nPopups, aVnMenu, lpdi->itemID); if (pItem)
{
for (i = 0; i < dim(aFontData); i++) if (lpdi->itemID == aFontData[i].id)
break;
if (i < dim(aFontData)) hfont = ahf[i]; else
hfont = ahf[nCurFont]; szText = pItem->name;
(180)ExtTextOut(lpdi->hDC, xText, yText, ETO_OPAQUE, &lpdi->rcItem, szText, strlen(szText), NULL);
if (lpdi->itemID - ID_FONT_VNI_TIMES == nCurFont || lpdi->itemID - ID_FIGURE_ELLIPSE == nFigIdx || lpdi->itemID - ID_BRUSH_SOLID == nBrushIdx) {
hbrOld = (HBRUSH)SelectObject(lpdi->hDC, ahbr[0]); Circle>hDC, lpdi->rcItem.left+cxCheck/2, (lpdi->rcItem.top + lpdi->rcItem.bottom)/2, 5);
SelectObject(lpdi->hDC, hbrOld); }
SelectObject (lpdi->hDC, hfontOld); if (bSwap)
{
SetTextColor(lpdi->hDC, crTextOld); SetBkColor(lpdi->hDC, crBackOld); }
} break; }
break;
case WM_COMMAND:
wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); switch (wmId)
{
case ID_FONT_VNI_TIMES: case ID_FONT_VNI_COURIER: case ID_FONT_VNI_APTIMA: case ID_FONT_VNI_COOPER:
CheckMenuItem(hMenu, ID_FONT_VNI_TIMES + nCurFont, MF_UNCHECKED);
nCurFont = wmId - ID_FONT_VNI_TIMES; CheckMenuItem(hMenu, wmId, MF_CHECKED); InvalidateRect(hWnd, NULL, TRUE);
break;
(181)case ID_FIGURE_ROUNDRECT: case ID_FIGURE_CHORD: case ID_FIGURE_PIE:
CheckMenuItem(hMenu, ID_FIGURE_ELLIPSE + nFigIdx, MF_UNCHECKED);
nFigIdx = wmId - ID_FIGURE_ELLIPSE; CheckMenuItem(hMenu, wmId, MF_CHECKED); InvalidateRect(hWnd, NULL, TRUE);
break;
case ID_OPTION_ENGLISH: bVietNamese = false; SetMenu(hWnd, hMenu); break;
case ID_OPTION_TIENGVIET: SetMenu(hWnd, hMenuVn); bVietNamese = true; break;
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break;
case IDM_EXIT:
DestroyWindow(hWnd); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
break; case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); RECT rt;
GetClientRect(hWnd, &rt);
SelectObject (hdc, ahf[nCurFont]);
DrawText(hdc, szName[bVietNamese][nFigIdx],
strlen(szName[bVietNamese][nFigIdx]), &rt, DT_CENTER); SelectObject(hdc, ahbr[nBrushIdx]);
switch(ID_FIGURE_ELLIPSE + nFigIdx) {
(182)Pie(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4, 3*rt.bottom/4, rt.right, rt.bottom/2, 0, 0);
break;
case ID_FIGURE_CHORD:
Chord(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4,
3*rt.bottom/4, rt.right, rt.bottom/3, 0, rt.bottom); break;
}
EndPaint(hWnd, &ps); break;
case WM_DESTROY:
for (i = 0; i < nBrush; i++) DeleteObject(ahbr[i]); PostQuitMessage(0);
break; default:
return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
9.4 Bimap Menu
Người sử dụng tạo menu cách dùng hàm CreateMenu, sau sử dụng hàm AppendMenu để tạo nên thành phần menu
Để có bitmap menu, ta gọi hàm AppendMenu với uFlag có bao gồm thuộc tính MF_BITMAP
MF_BITMAP Dùng bitmap để vẽ menu item Tham số lpNewItem chứa handle đến bitmap
(183)(184)Hình 9-13 Các menu item làbitmap
Trích đoạn chương trình nguồn vnmenu.cpp
// VnMenu.cpp : Defines the entry point for the application //
#include "stdafx.h" #include "resource.h" //
//
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG) //
#define MAX_ITEMS 10 struct M_ITEM
{
(185){
"&Hồ sơ", IDB_FILE, 6, {
{"Hồ sơ mới", ID_FILE_NEW, MF_BITMAP, IDB_FILE_NEW}, {"Mở hồ sơ", ID_FILE_OPEN, MF_BITMAP, IDB_FILE_OPEN}, {"Lưu hồ sơ", ID_FILE_SAVE, MF_BITMAP, IDB_FILE_SAVE}, {"", 0, MF_SEPARATOR},
{"In", ID_FILE_PRINT, MF_BITMAP, IDB_FILE_PRINT}, {"Thoát", IDM_EXIT, MF_BITMAP, IDB_FILE_EXIT}, },
"&Soạn Thảo", IDB_EDIT, 3, {
{"Sao chép", ID_EDIT_COPY, MF_BITMAP, IDB_EDIT_COPY}, {"Cắt", ID_EDIT_CUT, MF_BITMAP, IDB_EDIT_CUT},
{"Daùn", ID_EDIT_PASTE, MF_BITMAP, IDB_EDIT_PASTE}, },
"Đổi Menu Font", IDB_FONT, 4, {
{"VNI-Times", ID_FONT_VNI_TIMES, MF_BITMAP, IDB_FONT_TIMES},
{"VNI-Courier", ID_FONT_VNI_COURIER, MF_BITMAP, IDB_FONT_COURIER}, {"VNI-Aptima", ID_FONT_VNI_APTIMA, MF_BITMAP, IDB_FONT_APTIMA}, {"VNI-Cooper", ID_FONT_VNI_COOPER, MF_BITMAP, IDB_FONT_COOPER} },
"&Hình", IDB_FIGURE, 5, {
{"Hình Chữ Nhật", ID_FIGURE_ELLIPSE, MF_BITMAP, IDB_FIGURE_ELLIPSE},
{"Hình Bầu dục", ID_FIGURE_RECTANGLE, MF_BITMAP, IDB_FIGURE_RECTANGLE},
{"Hình chữ nhật trịn", ID_FIGURE_ROUNDRECT, MF_BITMAP, IDB_FIGURE_ROUNDRECT},
{"Hình Vành khuyên", ID_FIGURE_CHORD, MF_BITMAP, IDB_FIGURE_CHORD}, {"Hình bánh", ID_FIGURE_PIE, MF_BITMAP, IDB_FIGURE_PIE},
},
"&Chổi vẽ", IDB_BRUSH, 8, {
{"Đặc", ID_BRUSH_SOLID, MF_BITMAP, IDB_BRUSH_SOLID}, {" ", ID_BRUSH_SOLID, MF_SEPARATOR},
(186){
{"Thoâng tin", IDM_ABOUT, MF_BITMAP, IDB_HELP_THONGTIN}, }
};
struct FONTMENUDATA {
UINT id;
TCHAR szFaceName[32]; int nPointSize;
BOOL bBold, bItalics; };
static FONTMENUDATA aFontData[] = {
{ID_FONT_VNI_TIMES, "VNI-Times", 12},
{ID_FONT_VNI_COURIER, "VNI-Couri", 12, TRUE}, {ID_FONT_VNI_APTIMA, "VNI-Aptima", 12},
{ID_FONT_VNI_COOPER, "VNI-Cooper", 12, TRUE, TRUE}, };
#define dim(a) (sizeof(a)/sizeof(a[0])) const int nPopups = dim(aVnMenu);
const int nFonts = dim(aFontData);
HMENU AddMenuPopup(HMENU hMenu, M_POPUP mp) {
HMENU hMenuPopup = CreatePopupMenu();
AppendMenu (hMenu, MF_POPUP|MF_BITMAP, (UINT)hMenuPopup,
(LPSTR)LoadBitmap (hInst, (LPSTR)mp.bitmapId)); for (int i = 0; i < mp.nItems; i++)
if (mp.aItems[i].attr & MF_BITMAP)
AppendMenu (hMenuPopup, mp.aItems[i].attr, mp.aItems[i].id, (LPSTR)LoadBitmap(hInst, (LPSTR)mp.aItems[i].bitmapId)); else
AppendMenu (hMenuPopup, mp.aItems[i].attr, mp.aItems[i].id, mp.aItems[i].name);
(187)BOOL Circle(HDC hdc, int x, int y, int r) {
return Ellipse(hdc, x-r, y-r, x+r, y+r); }
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent; PAINTSTRUCT ps; HDC hdc;
TCHAR* szName[][5] = {
{"Ellipse", "Rectangle", "RoundRect", "Chord", "Pie"},
{"Hình Bầu dục", "Hình Chữ Nhật", "Hình Chữ nhật trịn", "Hình vành khun", "Hình bánh"}
};
static HMENU hMenu, hMenuVn; static HFONT ahf[nFonts];
static int cxCheck, cyCheck, nBrush;
static WORD nBrushIdx, nFigIdx, nCurFont = 0; static COLORREF crHighlight, crHighlightText; static HBRUSH ahbr[7];
static bool bVietNamese = true; switch (message)
{
case WM_CREATE: int i;
LOGFONT lf;
crHighlight = GetSysColor(COLOR_HIGHLIGHT);
crHighlightText = GetSysColor(COLOR_HIGHLIGHTTEXT); cxCheck = GetSystemMetrics(SM_CXMENUCHECK);
cyCheck = GetSystemMetrics(SM_CYMENUCHECK); ZeroMemory(&lf, sizeof(lf));
hdc = GetDC(hWnd);
for (i = 0; i < nFonts; i++)
ahf[i] = MyCreateFont(hdc, aFontData[i].szFaceName, aFontData[i].nPointSize, aFontData[i].bBold,
aFontData[i].bItalics);
(188)for (i = 0; i < nPopups; i++)
AddMenuPopup(hMenuVn, aVnMenu[i]); HMENU hMenuPopup;
hMenuPopup = CreatePopupMenu(); SetMenu(hWnd, hMenuVn);
break;
case WM_COMMAND:
wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); switch (wmId)
{
case ID_FONT_VNI_TIMES: case ID_FONT_VNI_COURIER: case ID_FONT_VNI_APTIMA: case ID_FONT_VNI_COOPER:
CheckMenuItem(hMenu, ID_FONT_VNI_TIMES + nCurFont, MF_UNCHECKED);
nCurFont = wmId - ID_FONT_VNI_TIMES; CheckMenuItem(hMenu, wmId, MF_CHECKED); InvalidateRect(hWnd, NULL, TRUE);
break;
case ID_BRUSH_SOLID: case ID_BRUSH_BWDIAGONAL: case ID_BRUSH_FWDIAGONAL: case ID_BRUSH_DIAGCROSS: case ID_BRUSH_CROSS: case ID_BRUSH_HORIZONTAL: case ID_BRUSH_VERTICAL:
CheckMenuItem(hMenu, ID_BRUSH_SOLID + nBrushIdx, MF_UNCHECKED);
nBrushIdx = wmId - ID_BRUSH_SOLID; CheckMenuItem(hMenu, wmId, MF_CHECKED); InvalidateRect(hWnd, NULL, TRUE);
break;
(189)case ID_OPTION_TIENGVIET: SetMenu(hWnd, hMenuVn); bVietNamese = true; break;
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About); break;
case IDM_EXIT:
DestroyWindow(hWnd); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
break; case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); RECT rt;
GetClientRect(hWnd, &rt);
SelectObject (hdc, ahf[nCurFont]);
DrawText(hdc, szName[bVietNamese][nFigIdx],
strlen(szName[bVietNamese][nFigIdx]), &rt, DT_CENTER); SelectObject(hdc, ahbr[nBrushIdx]);
switch(ID_FIGURE_ELLIPSE + nFigIdx) {
case ID_FIGURE_ELLIPSE:
Ellipse(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4, 3*rt.bottom/4);
break;
case ID_FIGURE_RECTANGLE:
Rectangle(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4, 3*rt.bottom/4);
break;
case ID_FIGURE_ROUNDRECT:
RoundRect(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4, 3*rt.bottom/4, 30, 30);
break;
case ID_FIGURE_PIE:
Pie(hdc, rt.right/4, rt.bottom/4, 3*rt.right/4, 3*rt.bottom/4, rt.right, rt.bottom/2, 0, 0);
(190)break; default:
return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
(191)10 Cửa Sổ Và Nút Điều Khiển
Nhu cầu tạo cửa sổ lớn: Cửa sổ log, debug, text…, ứng dụng MDI Qui trình tạo cửa sổ bao gồm đăng ký lớp gọi hàm tạo cửa sổ
10.1 Đăng ký lớp cửa sổ (tư tưởng OOP) Dùng hàm RegisterClass
typedef struct _WNDCLASS { UINT style;
WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor;
HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; } WNDCLASS;
Ví dụ để nhận thơng điệp bấm mắt chuột hai lần liên tiếp, dùng CS_DBLCLKS Để tạo
private DC, duøng CS_OWNDC
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//
(192)break; case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); // …
EndPaint(hWnd, &ps); break;
case WM_DESTROY:
PostQuitMessage(0); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
10.2 Tạo cửa sổ
Dùng CreateWindow CreateWindowEx HWND CreateWindowEx(
DWORD dwExStyle, // extended window style
LPCTSTR lpClassName, // pointer to registered class name LPCTSTR lpWindowName, // pointer to window name
DWORD dwStyle, // window style
int x, // horizontal position of window int y, // vertical position of window int nWidth, // window width
int nHeight, // window height
HWND hWndParent, // handle to parent or owner window
HMENU hMenu, // handle to menu, or child-window identifier HINSTANCE hInstance, // handle to application instance
(193)WS_VISIBLE Tạo cửa sổ hiển thị từ đầu WS_EX_MDICHILD Tạo cửa sổ MDI
WS_EX_TOPMOST Tạo cửa sổ topmost Cửa sổ có trạng thái ban đầu sau:
WS_VISIBLE, WS_MAXIMIZED, WS_MINIMIZED, WS_DISABLE WS_OVERLAPPED, WS_OVERLAPPEDWINDOW
Hàm GetSystemMetrics cho biết thơng số kích thước cửa sổ int GetSystemMetrics(
int nIndex // system metric or configuration setting to retrieve );
10.3 Tạo cửa sổ
Đăng ký tạo cửa sổ con, lớp đăng ký cho cửa sổ phải có hàm xử lý 10.3.1 Chương trình MDI V1
(194)Hình 10-1 Cửa sổ
// Mdi.cpp : Defines the entry point for the application //
#include "stdafx.h" #include "resource.h"
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance TCHAR szTitle[MAX_LOADSTRING]; // The title bar text TCHAR szWindowClass[MAX_LOADSTRING];// The title bar text TCHAR szTextWndClass[MAX_LOADSTRING];// The title bar text TCHAR szBallWndClass[MAX_LOADSTRING];// The title bar text
// Foward declarations of functions included in this code module: ATOM MyRegisterClass(HINSTANCE hInstance);
ATOM TextWndRegisterClass(HINSTANCE hInstance); ATOM BallWndRegisterClass(HINSTANCE hInstance); BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK TextWndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK BallWndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
(195)BallWndRegisterClass(hInstance);
// Perform application initialization: if (!InitInstance (hInstance, nCmdShow))
return FALSE;
hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_MDI);
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0)) {
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg); DispatchMessage(&msg); }
}
return msg.wParam; }
// FUNCTION: MyRegisterClass()
ATOM MyRegisterClass(HINSTANCE hInstance) {
WNDCLASSEX wcex;
(196)wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);
return RegisterClassEx(&wcex); }
ATOM TextWndRegisterClass(HINSTANCE hInstance) {
WNDCLASSEX wcex;
LoadString(hInstance, IDS_TEXTCLASS, szTextWndClass, MAX_LOADSTRING);
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)TextWndProc; wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL;
wcex.lpszClassName = szTextWndClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);
(197)wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL;
wcex.lpszClassName = szBallWndClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL); return RegisterClassEx(&wcex);
}
// FUNCTION: InitInstance(HANDLE, int)
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) {
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd) {
return FALSE; }
(198)PAINTSTRUCT ps; HDC hdc;
TCHAR szParent[MAX_LOADSTRING]; HWND hWndChild;
LoadString(hInst, IDS_PARENT, szParent, MAX_LOADSTRING); static int xx, yy;
switch (message) {
case WM_COMMAND:
wmId = LOWORD(wParam); wmEvent = HIWORD(wParam); switch (wmId)
{
case ID_FILE_NEWTEXT:
hWndChild = CreateWindow(szTextWndClass, szTitle, WS_OVERLAPPEDWINDOW | WS_CHILD | WS_CLIPSIBLINGS, xx, yy, 300, 150, hWnd, NULL, hInst, NULL);
if (!hWndChild) return TRUE; xx += 20; yy += 20;
if (xx > 70) xx = yy = 0;
ShowWindow(hWndChild, SW_SHOW); UpdateWindow(hWndChild);
break;
case ID_FILE_NEWBALL:
(199)DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
break; case IDM_EXIT:
DestroyWindow(hWnd); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
break; case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); RECT rt;
GetClientRect(hWnd, &rt);
DrawText(hdc, szParent, strlen(szParent), &rt, DT_CENTER); EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0); break;
default:
return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
LRESULT CALLBACK TextWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
(200)break; case WM_PAINT:
hdc = BeginPaint(hWnd, &ps); RECT rt;
GetClientRect(hWnd, &rt);
DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER); EndPaint(hWnd, &ps);
break; default:
return DefWindowProc(hWnd, message, wParam, lParam); }
return 0; }
LRESULT CALLBACK BallWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps; HDC hdc;
static int xPos, yPos, dx = 5, dy = 5; static int r = 20;
RECT rt;
switch (message) {
case WM_CREATE:
SetTimer(hWnd, 0, 8, NULL); GetClientRect(hWnd, &rt); xPos = rt.right/2;
... Khi người sử dụng di chuyển chuột, trỏ chuột di chuyển tương ứng• Người sử dụng qui định hình dạng trỏ cho ứng dụng Khi người sử dụng di chuyển chuột đến ứng dụng đó,... Tương tác với người sử dụng
Windows hệ điều hành có giao diện trực quan thân thiện với người sử dụng Một đặc điểm Windows khả WYSIWYG, người sử dụng tương tác với chương... cửa sổ có đen R2_NOTXORPEN phù hợp với cửa sổ có trắng
Ví dụ ứng dụng – Chương trình ObjectDrag V2
Chương trình ObjectDrag V2 sau minh hoạ sử dụng