Bài giảng Lập trình Windows - Bài 2: Giao diện đồ họa người dùng

doc 42 trang phuongnguyen 6190
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Lập trình Windows - Bài 2: Giao diện đồ họa người dùng", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên

Tài liệu đính kèm:

  • docbai_giang_lap_trinh_windows_bai_2_giao_dien_do_hoa_nguoi_dun.doc

Nội dung text: Bài giảng Lập trình Windows - Bài 2: Giao diện đồ họa người dùng

  1. Bài 2 GIAO DIỆN ĐỒ HỌA NGƯỜI DÙNG Như đã giới thiệu trong bài học đầu tiên, giao diện đồ họa người dùng (GUI – Graphical User Interface) chính là đặc điểm làm cho các ứng dụng trên MS Windows trở nên thân thiện, dễ sử dụng Và cũng chính nhờ đặc điểm này mà các chương trình trên máy tính trở nên gần gũi và thực tế hơn rất nhiều so với các ứng dụng trước đây trên MS-DOS. Bài học này sẽ giúp các bạn tạo lập cũng như lập trình xử lý cho các đối tượng giao diện quen thuộc trên Windows như thanh trình đơn (menu), các đối tượng điều khiển (control) như button, edit, combo box, trên môi trường công cụ MS Visual C++ 6.0. 2.1. CƠ CHẾ LẬP TRÌNH SỬ DỤNG TÀI NGUYÊN Khi sử dụng các ứng dụng trên Windows, chắc chắn các bạn dễ dàng nhận ra chúng có cùng những đặc trưng giao diện cũng như cách xử lý các đối tượng giao diện này. Ví dụ, khi muốn thực hiện một công việc nào đó, người ta thường chọn theo từng nhóm các thao tác đã được định trước trên một thanh menu; khi thao tác các phím nóng người ta có thể dùng tổ hợp Alt + ký tự gạch dưới của một đối tượng menu item, button, static, nào đó. Việc thể hiện giao diện giống nhau cũng như có cùng cơ chế xử lý của các ứng dụng trên Windows là do hệ điều hành quyết định. Chúng được định nghĩa trong các tập tin liên kết của hệ
  2. Lập trình Windows thống (USER.EXE - 16 bits và USER32.DLL - 32 bits). Dựa trên đặc điểm này, các công cụ soạn thảo và biên dịch được xây dựng sao cho các lập trình viên khi lập trình chỉ cần thực hiện các thao tác khá đơn giản là đưa thông tin các đối tượng cần tạo lập theo các mẫu (template) đã được định nghĩa trước. Cơ chế này gọi là lập trình sử dụng tài nguyên (resource-based programming). 2.1.1. Cửa sổ workspace ResourceView Trong các project ứng dụng trên Windows viết bằng C, tài nguyên được định nghĩa trong các tập tin RC (resource). Với sự hỗ trợ trực quan của MS Visual C++ 6.0, ta dễ dàng xem và thao tác các resource này trong workspace ResourceView. Hình 2.1. Tạo lập resource trên môi trường Visual C++ 6.0 Bằng cách chọn tập tin BaiTap.rc trong workspace FileView hoặc click chọn vào ResourceView của project 2
  3. Bài 2. Giao diện Đồ họa Người dùng BaiTap trong bài 1, các tài nguyên của project này sẽ thể hiện như trong hình 2.1. Để thao tác resource nào ta chỉ việc click chọn vào đối tượng ấy trên ResourceView. Khi đó mặc định Visual C++ 6.0 sẽ tự động mở cửa sổ resource editor trực quan tương ứng để ta thêm, bớt, hoặc chỉnh sửa. 2.1.2. Cấu trúc resource dạng văn bản và editor trực quan Thật ra, tài nguyên được định nghĩa theo các định dạng văn bản (text) cho sẵn trong tập tin RC. Và khi ta mở các tập tin này, Visual C++ 6.0 mới nạp dữ liệu text này và thể hiện giao diện trực quan cho ta dễ thao tác. Hình 2.2. Chọn mở xem tập tin resource dưới dạng text 3
  4. Lập trình Windows Do đó, trong bài học, chúng ta chủ yếu tìm hiểu cách thao tác trực quan. Bên cạnh đó, chúng ta cũng cần hiểu cơ bản về cách tổ chức văn bản của các resource để có thể chỉnh sửa khi dữ liệu không chính xác và Visual C++ 6.0 không thể nạp được. Khi chọn xem tập tin RC dưới dạng text (Open as Text)- xem hình 2.2, Visual C++ 6.0 sẽ thể hiện toàn bộ nội dung văn bản của tập tin này ở cửa sổ soạn thảo (text editor). Với project BaiTap ta có nội dung tập tin BaiTap.rc như sau: 1 //Microsoft Visual C++ generated resource script. 2 // 3 #include "resource.h" 4 #define APSTUDIO_READONLY_SYMBOLS 5 ///////////////////////////////////////////////////////////////////////////// 6 // 7 // Generated from the TEXTINCLUDE 2 resource. 8 // 9 #define APSTUDIO_HIDDEN_SYMBOLS 10 #include "windows.h" 11 #undef APSTUDIO_HIDDEN_SYMBOLS 12 ///////////////////////////////////////////////////////////////////////////// 13 #undef APSTUDIO_READONLY_SYMBOLS 14 #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 15 #ifdef _WIN32 16 LANGUAGE 9, 1 17 #pragma code_page(1252) 18 #endif //_WIN32 19 ///////////////////////////////////////////////////////////////////////////// 20 // 21 // Icon 22 // 23 // Icon with lowest ID value placed first to ensure application icon 24 // remains consistent on all systems. 25 IDI_BAITAP ICON DISCARDABLE "BaiTap.ICO" 26 IDI_SMALL ICON DISCARDABLE "SMALL.ICO" 27 ///////////////////////////////////////////////////////////////////////////// 28 // 29 // Menu 30 // 4
  5. Bài 2. Giao diện Đồ họa Người dùng 31 IDC_BAITAP MENU DISCARDABLE 32 BEGIN 33 POPUP "&File" 34 BEGIN 35 MENUITEM "E&xit", IDM_EXIT 36 END 37 POPUP "&Help" 38 BEGIN 39 MENUITEM "&About ", IDM_ABOUT 40 END 41 END 42 ///////////////////////////////////////////////////////////////////////////// 43 // 44 // Accelerator 45 // 46 IDC_BAITAP ACCELERATORS MOVEABLE PURE 47 BEGIN 48 "?", IDM_ABOUT, ASCII, ALT 49 "/", IDM_ABOUT, ASCII, ALT 50 END 51 ///////////////////////////////////////////////////////////////////////////// 52 // 53 // Dialog 54 // 55 IDD_ABOUTBOX DIALOG DISCARDABLE 22, 17, 230, 75 56 STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU 57 CAPTION "About" 58 FONT 8, "System" 59 BEGIN 60 ICON IDI_BAITAP,IDC_MYICON,14,9,16,16 61 LTEXT "BaiTap Version 1.0",IDC_STATIC,49,10,119,8,SS_NOPREFIX 62 LTEXT "Copyright (C) 2003",IDC_STATIC,49,20,119,8 63 DEFPUSHBUTTON "OK",IDOK,195,6,30,11,WS_GROUP 64 END 65 #ifdef APSTUDIO_INVOKED 66 ///////////////////////////////////////////////////////////////////////////// 67 // 68 // TEXTINCLUDE 69 // 70 2 TEXTINCLUDE DISCARDABLE 71 BEGIN 72 "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" 73 "#include ""windows.h""\r\n" 74 "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" 75 "#include ""resource.h""\r\n" 5
  6. Lập trình Windows 76 "\0" 77 END 78 3 TEXTINCLUDE DISCARDABLE 79 BEGIN 80 "\r\n" 81 "\0" 82 END 83 #endif // APSTUDIO_INVOKED 84 ///////////////////////////////////////////////////////////////////////////// 85 // 86 // String Table 87 // 88 STRINGTABLE DISCARDABLE 89 BEGIN 90 IDC_BAITAP "BAITAP" 91 IDS_APP_TITLE "BaiTap" 92 IDS_HELLO "Hello World!" 93 END 94 #endif 95 ///////////////////////////////////////////////////////////////////////////// 96 #ifndef APSTUDIO_INVOKED 97 ///////////////////////////////////////////////////////////////////////////// 98 // 99 // Generated from the TEXTINCLUDE 3 resource. 100 // 101 ///////////////////////////////////////////////////////////////////////////// 102 #endif // not APSTUDIO_INVOKED 103 } Như đã nói, chúng ta thao tác các đối tượng resource bằng công cụ trực quan. Khi đó, Visual C++ 6.0 tự động phát sinh các dữ liệu text tương ứng. Đối với project bài tập này, dữ liệu của Accelerator IDC_BAITAP (xem hình 2.1) tương ứng đoạn text từ dòng 40 đến 50 ở trên, Dialog IDD_ABOUTBOX tương ứng đoạn text từ dòng 55 đến 64, v.v Ngoài các macro định nghĩa các dạng mẫu resource - ví dụ DIALOG là macro cho biết resource là hộp thoại, LTEXT là macro của control static text canh lề trái trên hộp thoại, - trong nội dung tập tin RC ở trên còn có các giá trị được định 6
  7. Bài 2. Giao diện Đồ họa Người dùng nghĩa xác định định danh (identifier) của các đối tượng tài nguyên. Đây là điểm cần chú ý khi tạo lập và chỉnh sửa các resource. Trong phần tìm hiểu các đối tượng resource cụ thể như menu, control, ở sau, chúng ta sẽ nói lại về vấn đề này. Chúng được định nghĩa (define) thay cho các số nguyên để người dùng dễ lập trình. Khi viết code, ví dụ trong phần xử lý menu trong thông điệp WM_COMMAND của project bài tập, lập trình viên xử lý menu item thông qua giá trị đã định nghĩa này (IDM_EXIT, IDM_ABOUT) mà không quan tâm tới giá trị số nguyên cụ thể của nó; trong khi để quản lý các menu item thì hệ thống quản lý thông qua giá trị số của chúng. Hình 2.3. Chọn xem thông tin của tài nguyên Trong project Win32 Application mà Visual C++ 6.0 tạo ra thì các giá trị này được phát sinh đồng thời trong tập tin resource.h khi ta thao tác các resource tương ứng. Nội dung tập tin resource.h của project BaiTap như sau: 7
  8. Lập trình Windows Hình 2.4. Xem thông tin về tập tin resource.h 1 //{{NO_DEPENDENCIES}} 2 // Microsoft Visual C++ generated include file. 3 // Used by BAITAP.RC 4 // 5 #define IDR_MAINFRAME 128 6 #define IDD_BAITAP_DIALOG 102 7 #define IDD_ABOUTBOX 103 8 #define IDS_APP_TITLE 103 9 #define IDM_ABOUT 104 10 #define IDM_EXIT 105 11 #define IDS_HELLO 106 12 #define IDI_BAITAP 107 13 #define IDI_SMALL 108 14 #define IDC_BAITAP 109 15 #define IDC_MYICON 2 16 #define IDC_STATIC -1 17 // Next default values for new objects 18 // 19 #ifdef APSTUDIO_INVOKED 20 #ifndef APSTUDIO_READONLY_SYMBOLS 21 #define _APS_NEXT_RESOURCE_VALUE 129 22 #define _APS_NEXT_COMMAND_VALUE 32771 8
  9. Bài 2. Giao diện Đồ họa Người dùng 23 #define _APS_NEXT_CONTROL_VALUE 1000 24 #define _APS_NEXT_SYMED_VALUE 110 25 #endif 26 #endif Các giá trị này được định nghĩa thay thế cho các macro được phát sinh khác nhau cho các project khác nhau. Hơn nữa, chúng luôn là các giá trị khác nhau cho các macro khác nhau. Và vì thế nếu chúng ta soạn thảo chúng dưới dạng text, cần phải chú ý để tránh trường hợp trùng lắp giá trị, bởi trong các trường hợp này khi cài đặt code cho thao tác các đối tượng liên quan có thể dẫn đến các xử lý tương ứng không chính xác. Hình 2.5. Macro và giá trị của tài nguyên đã định nghĩa Ngoài ra, để xem các macro được định nghĩa của các tài nguyên trong tập tin BaiTap.rc ta có thể xem trực quan qua sự hỗ trợ của MS Visual C++ 6.0. Bằng cách click chuột phải (mặc 9
  10. Lập trình Windows định) lên “BaiTap resources” trên ResourceView – hình 2.3. – ta chọn tiếp “Resouce Includes ” để xem các thông tin về tập tin resource.h – hình 2.4, hoặc chọn “Resource Symbols ” để xem giá trị các macro đã được định nghĩa – hình 2.5. 2.2. TRÌNH ĐƠN Trong hầu hết các ứng dụng, thao tác của người dùng thường thể hiện theo từng nhóm công việc. Để tiện việc chọn lựa công việc, chúng ta có thể lần theo một cây phân cấp gọi là menu (trình đơn). Nhờ có menu, giao tiếp giữa người dùng với ứng dụng trở nên dễ dàng, đơn giản thông qua lựa chọn trực quan các mục chọn gọi là menu item. MS Windows hỗ trợ một số dạng thể hiện menu dựa trên các yêu cầu thao tác khác nhau của người dùng như shortcut menu (thường thể hiện khi người dùng rightclick lên cửa sổ ứng dụng), popup menu (thanh menu thông thường dưới thanh tiêu đề ở các ứng dụng), system menu (menu hệ thống của cửa sổ ứng dụng, gằn liền các thao tác như Maximize, Minimize, Close, ). Trong phạm vi môn học, chúng ta chỉ tìm hiểu cơ bản về menu với chức năng đơn giản nhất là nhận thao tác của người dùng (mà không xử lý thể hiện giao diện phức tạp với font chữ, icon ảnh, ) thông qua thanh menu thông thường – xem hình 2.6. Các item của thanh menu hiển thị ngay dưới thanh tiêu đề của ứng dụng tạo thành thanh menu chính (top-level menu). Trên menu chính, chúng ta có một hay nhiều item liệt kê – đại diện cho nhóm chức năng - gọi là drop-down menu (thả xuống) 10
  11. Bài 2. Giao diện Đồ họa Người dùng hay popup menu (bung lên). Ứng với mỗi item liệt kê như vậy, chúng ta lại có thể cấu trúc như ở menu chính, nghĩa là có thể tạo thêm các popup cấp 2 (rồi tương tự sau đó là cấp 3, 4, ) và các item con khác. Chú ý là chúng ta chỉ nhận và xử lý thao tác của các menu item mà thôi. Hình 2.6. Thanh menu của MS Visual C++ 6.0 2.2.1. Tạo mẫu tài nguyên menu trên công cụ trực quan Có hai cách chính để tạo lập một tài nguyên menu trong tập tin resource (.RC). 11
  12. Lập trình Windows  Cách 1: Mở tập tin .RC dưới dạng text và sau đó tiến hành tạo menu bằng cách gõ các dòng lệnh theo cấu trúc của menu trong tập tin RC. Thông thường, cách này ít dùng vì khá phức tạp, khó nhớ và khó quản lý các giá trị chỉ danh của các item mà ta phải định nghĩa. Chúng ta sẽ không tìm hiểu về cách thao tác này trong môn học, tuy nhiên, các bạn cần tham khảo cấu trúc có sẵn cúa chúng sau khi tạo lập trực quan (ở cách 2) để có thể xử lý khi có lỗi về phần code tương ứng.  Cách 2: Sử dụng công cụ tạo lập và thao tác tài nguyên trực quan của MS Visual C++ 6.0. Bằng cách chọn tài nguyên của project qua workspace ResourceView như ở hình 2.1, rồi chọn mục Menu để thao tác. Ta có thể chọn một menu đã có sẵn để sửa lại, hoặc Insert Menu để thêm menu mới (Visual C++ 6.0 sẽ tạo một menu với ID của menu tự phát sinh. Ta có thể click chuột phải trên menu mới tạo, chọn Properties để thay đổi các thông số cần thiết – xem hình 2.7). Hình 2.7. Properties của tài nguyên menu IDC_NOTEPAD 12
  13. Bài 2. Giao diện Đồ họa Người dùng Một ứng dụng thường có nhiều menu item, và mỗi menu item được hệ thống quản lý thông qua một số nguyên xác định như là định danh của nó. Khi lập trình, để dễ quản lý các định danh, ta định nghĩa (define) chúng thông qua các tên khác nhau Một item được định nghĩa với nội dung hiển thị (Caption), định danh ID và kiểu thể hiện (styles). Chúng ta có thể thao tác trực quan khi click chọn properties của chúng trên công cụ trực quan – xem hình 2.8. Hình 2.8. Properties của menu item IDM_NEW Caption chính là nội dung hiển thị trên menu. Để thể hiện chức năng hỗ trợ phím nóng , ta dùng ký tự ‘&’ trước ký tự tương ứng. Để đặt tab (theo cột cho các item của một popup), ta dùng ký tự tab ‘\t’ trước phần text của cột mới, và dùng ký tự canh lề align ‘\a’ trong text để canh hàng phải cho các ký tự đi sau nó. ID là giá trị định danh của mỗi item. Như đã nói ở trên, đây là các giá trị số nguyên, tuy nhiên để dễ lập trình, ta sẽ định nghĩa chúng dưới dạng các tên gợi nhớ. Khi thao tác, chúng ta chỉ cần ghi tên gợi nhớ, mặc định với các project của chúng ta thì Visual C++ 6.0 phát sinh phần định nghĩa 13
  14. Lập trình Windows trong tập tin resource.h (tập tin này được include trong tập tin RC tương ứng). Các check box còn lại cho phép chúng ta chọn các kiểu (style) ban đầu cho item. Các kiểu chính của một item được liệt kê trong bảng 2.1. Kiểu Tác dụng Separator Tạo đường phân cách giữa các item. Check Thêm dấu check bên trái item. Pop-up Tạo item dạng popup. Vô hiệu hoá một item. Text hiển thị của item Grayed tương ứng có màu xám. Vô hiệu hóa item nhưng vẫn hiển thị text bình Inactive thường. Bảng 2.1. Các kiểu chọn menu item Để thấy rõ chi tiết cấu trúc tương ứng ở dạng text, ta mở tập tin .RC ở dạng text để quan sát. Khi đó, ta cũng có thể chỉnh sửa menu bằng cách thêm hay hay xoá các dòng lệnh cần thiết. 2.2.2. Nạp tài nguyên menu vào ứng dụng Để nạp tài nguyên menu vào ứng dụng khi thực thi chương trình, ta có những cách thông dụng sau:  Khi định nghĩa lớp cửa sổ chính trong hàm MyRegisterClass, ta thiết lập thuộc tính lpszMenuName của cấu trúc lớp WNDCLASSEX (hoặc WNDCLASS) bằng định danh của menu trong tập tin tài nguyên. 14
  15. Bài 2. Giao diện Đồ họa Người dùng Ví dụ: Nếu khai báo biến WNDCLASSEX wcex trong hàm MyRegisterClass(HINSTANCE hInstance) thì ta nạp menu IDR_MENU1 bằng cách gán: wcex.lpszMenuName = (LPCSTR)IDR_MENU1;  Dùng hàm LoadMenu với tham số thứ 2 là ID của menu và nhận về handle của menu. HMENU hMenu; hMenu = LoadMenu(hInstance, (LPCSTR)IDR_MENU1); Khi có handle của menu rồi ta nạp menu vào ứng dụng bằng một trong hai cách sau: Cách 1: Khi gọi hàm tạo cửa sổ chính CreateWindow (hoặc CreateWindowEx) trong hàm InitInstance, ta chỉ định tham số hMenu là handle nhận được ở trên. hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, hMenu, hInstance, NULL); Trong trường hợp này menu chỉ định trong hàm CreateWindow sẽ thay thế lên menu trước đó (nếu có) của lớp cửa sổ szWindowClass. Cách 2: Nếu trong hàm CreateWindow tham số hMenu bằng NULL, ta gọi hàm SetMenu với tham số thứ 2 là định danh của menu đã tạo lập. SetMenu(hWnd, hMenu); Cách này cho phép ta thay đổi động menu của cửa sổ. 15
  16. Lập trình Windows 2.2.3. Xử lý thông điệp menu Windows phát sinh một số thông điệp và gởi cho cửa sổ cha của của menu khi người dùng tác động lên chúng. Đối với cửa sổ chính của các ứng dụng thông thường mà chúng ta đang thực hành, ta có thể chặn các thông điệp thường gặp sau đây và viết code xử lý trong hàm WndProc.  WM_INITMENU: Thông điệp này được phát sinh khi các menu item nhận tác động, với hai thông số wParam, và lParam như sau. wParam: Handle của menu. lParam: Không sử dụng. Thông thường, ta chặn thông điệp này để xử lý các thao tác thay đổi trạng thái các item trước khi chúng nhận tác động của người dùng.  WM_INITMENUPOPUP: Thông điệp được phát sinh khi một drop-down menu hoặc submenu có khả năng được kích hoạt. wParam: handle của menu. lParam: Giá trị word thấp (LOWORD) xác định thứ tự (từ 0) của các item mở drop-down hay submenu; và word cao (HIWORD) cho biết drop-down menu có phải là system menu không, bằng TRUE nếu phải và ngược lại. Tương tự thông điệp WM_INITMENU, thông điệp này được dùng để hiệu lực hoá hay vô hiệu hoá một item trong popup menu trước khi nó được hiển thị. Ví 16
  17. Bài 2. Giao diện Đồ họa Người dùng dụ như trong chương trình soạn thảo văn bản Notepad, trước khi hiển thị item Paste, chương trình kiểm tra dữ liệu trong bộ nhớ đệm clipboard, nếu có thì item Paste mới có hiệu lực còn ngược lại item Paste không có hiệu lực. Để hiệu lực hoá hay vô hiệu hoá một item uIDItem trong menu hMenu ta dùng hàm EnableMenuItem với thông số cờ uEnable là MF_ENABLED, MF_DISABLED hoặc MF_GRAYED kết hợp cách chọn MF_BYCOMMAND, hoặc MF_BYPOSITION. BOOL EnableMenuItem(HMENU hMenu, UINT uIDItem, UINT uEnable);  WM_MENUSELECT: Thông điệp này được phát sinh khi người dùng bàn phím hay thiết bị chuột chuyển thao tác chọn (select) các item của menu. LOWORD(wParam): Menu ID hoặc chỉ mục của popup menu. HIWORD(wParam): Các cờ chọn lựa. lParam: Handle của menu. Các cờ cửa thông điệp này có thể là kết hợp của các giá trị MF_GRAYED, MF_DISABLED, MF_CHECKED, MF_BITMAP,MF_POPUP, MF_HELP, MF_SYSMENU, và MF_MOUSESELECT. Chúng ta thường sử dụng thông điệp WM_MENUSELECT khi muốn thay đổi một số thể hiện trên vùng làm việc của cửa sổ khi di chuyển chọn các item. Mặc định ta để Windows xử lý khi gọi hàm DefWindowProc. 17
  18. Lập trình Windows  WM_COMMAND: Thông điệp được phát sinh khi người dùng chọn một item trên menu. Đây là thông điệp được sử dụng nhiều nhất khi ta thao tác menu ứng dụng. WM_COMMAND cũng là thông điệp gởi cho cửa sổ cha của các control (phần 2.4) khi chúng nhận thao tác, hoặc là tác động menu và control dưới dạng phím nóng (phần 2.3). Các thông số gởi kèm thông điệp này như sau. LOWORD(wParam): Menu ID hoặc control ID. HIWORD(wParam): Mã thông điệp thông báo (notification code) của control, bằng 1 cho phím nóng và bằng 0 nếu thao tác menu. lParam: Handle của control gởi thông điệp thông báo. Các trường hợp khác giá trị này là NULL.  Giống như thông điệp WM_COMMAND, thông điệp WM_SYSCOMMAND được phát sinh khi người dùng chọn một item trong menu hệ thống. Menu hệ thống là menu do Windows cấp với định danh ID của các item được định nghĩa sẵn. Các ID này được xác định thông qua tham số wParam với các giá trị như SC_CLOSE, SC_MAXIMIZE, SC_MINIMIZE, SC_MOVE, . Thông số lParam xác định vị trí tọa độ màn hình nếu system menu nhận tác động bằng thiết bị chuột. Để hiểu rõ ta tiến hành tạo project ứng dụng Win32 Application dạng A typical “Hello World!” application có tên là MyNotepad - tương tự chương trình soạn thảo văn bản Notepad của Windows. Tạo sẽ tạo lập các menu item với Caption như ở bảng 2.2 (phần phím tắt sẽ tìm hiểu ở phần 2.3). 18
  19. Bài 2. Giao diện Đồ họa Người dùng Caption ID Phím tắt Popup &File &New\tCtrl+N IDM_NEW Ctrl+N &Open \tCtrl+O IDM_OPEN Ctrl+O &Save\tCtrl+S IDM_SAVE Ctrl+S Save &As IDM_SAVE_AS Page Set&up IDM_PAGE_SETUP &Print \tCtrl+P IDM_PRINT Ctrl+P E&xit IDM_EXIT Popup &Edit &Undo\tCtrl+Z IDM_UNDO Ctrl+Z Cu&t\tCtrl+X IDM_CUT Ctrl+X &Copy\tCtrl+C IDM_COPY Ctrl+C &Paste\tCtrl+V IDM_PASTE Ctrl+V De&lete\tDel IDM_DELETE Delete &Find \tCtrl+F IDM_FIND Ctrl+F Find &Next\tF3 IDM_FIND_NEXT F3 &Replace \tCtrl+H IDM_REPLACE Ctrl+H &Go To \tCtrl+G IDM_GOTO Ctrl+G Select &All\tCtrl+A IDM_SELECT_ALL Ctrl+A Time/&Date\tF5 IDM_TIME_DATE F5 Popup F&ormat &Word Wrap IDM_WORD_WRAP &Font IDM_FONT Popup &Help &Help Topics IDM_HELP_TOPICS &About MyNotepad IDM_ABOUT Bảng 2.2. Danh danh menu item của ứng dụng MyNotepad 19
  20. Lập trình Windows Sau khi tạo lập bằng công cụ trực quan, ta có thể xem lại nội dung của tài nguyên menu IDC_MYNOTEPAD dưới dạng text như sau: 1 IDC_MYNOTEPAD MENU DISCARDABLE 2 BEGIN 3 POPUP "&File" 4 BEGIN 5 MENUITEM "&New\tCtrl+N", IDM_NEW 6 MENUITEM "&Open \tCtrl+O", IDM_OPEN 7 MENUITEM "&Save\tCtrl+S", IDM_SAVE 8 MENUITEM "Save &As ", IDM_SAVE_AS 9 MENUITEM SEPARATOR 10 MENUITEM "Page Set&up ", IDM_PAGE_SETUP 11 MENUITEM "&Print \tCtrl+P", IDM_PRINT 12 MENUITEM SEPARATOR 13 MENUITEM "E&xit", IDM_EXIT 14 END 15 POPUP "&Edit" 16 BEGIN 17 MENUITEM "&Undo\tCtrl+Z", IDM_UNDO 18 MENUITEM SEPARATOR 19 MENUITEM "Cu&t\tCtrl+X", IDM_CUT 20 MENUITEM "&Copy\tCtrl+C", IDM_COPY 21 MENUITEM "&Paste\tCtrl+V", IDM_PASTE 22 MENUITEM "De&lete\tDel", IDM_DELETE 23 MENUITEM SEPARATOR 24 MENUITEM "&Find \tCtrl+F", IDM_FIND 25 MENUITEM "Find &Next\tF3", IDM_FIND_NEXT 26 MENUITEM "&Replace \tCtrl+H", IDM_REPLACE 27 MENUITEM "&Go To \tCtrl+G", IDM_GOTO 28 MENUITEM SEPARATOR 29 MENUITEM "Select &All\tCtrl+A", IDM_SELECT_ALL 30 MENUITEM "Time/&Date\tF5", IDM_TIME_DATE 31 END 32 POPUP "F&ormat" 33 BEGIN 34 MENUITEM "&Word Wrap", IDM_WORD_WRAP 35 MENUITEM "&Font ", IDM_FONT 36 END 37 POPUP "&Help" 38 BEGIN 39 MENUITEM "&Help Topics", IDM_HELP_TOPICS 40 MENUITEM "&About MyNotepad", IDM_ABOUT 41 END 42 END 20
  21. Bài 2. Giao diện Đồ họa Người dùng 2.3. PHÍM TẮT Để chọn một item trên menu ta dùng thiết bị chuột chuột hoặc thao tác bàn phím để di chuyển đến item cần chọn rồi kích hoạt item đó hoặc chúng ta chọn các item bằng cách kết hợp một tổ hợp phím nào đó, gọi là phím tắt (acceleraror) – Chẳng hạn như tổ hợp phím Ctrl + O ứng với thao tác kích hoạt menu File Open thường thấy ở các ứng dụng. 2.3.1. Tạo lập tài nguyên Accelerator Tương tự như các dạng tài nguyên khác, ta có thể tạo lập tài nguyên phím tắt dưới dạng văn bản theo cấu trúc được định nghĩa sẵn. Tuy nhiên, ta cũng không làm theo cách đó mà chỉ thao tác với công cụ trực quan của MS Visual C++. Hình 2.9. Thao tác bảng phím tắt trên Visual C++ 6.0 21
  22. Lập trình Windows Từ cửa sổ workspace ResourceView của project MyNotepad, ta chọn mục Accelerator, rồi chọn đối tượng IDC_MYNOTEPAD. Visual C++ sẽ liệt kê cho chúng ta các phím tắt nào đã được định nghĩa, và ta chỉ việc thêm hay thay đổi các giá trị của chúng. Ta có thể thêm một phím tắt bằng cách click vào một mục trống trên bảng phím tắt, lúc này Visual C++ mở hộp thoại Accel Properties để ta cập nhật các thông tin cần thiết theo mục đích của project ứng dụng. Bảng 2.3 giải thích các thông số trên hộp thoại Accel Properties. ID Định danh ID của Item muốn tạo phím tắt. Key Phím kết hợp trong tổ hợp phím tắt. Ctrl Kết hợp thêm phím Ctrl. Alt Kết hợp thêm phím Alt. Shift Kết hợp thêm phím Shift. Type Chọn mã phím ảo (virtual) hay mã ASCII. Bảng 2.3. Các thông số thuộc tính Accelerator Để ý là trong mục Key, bên cạnh các phím thể hiện ký tự cụ thể, các phím hệ thống được định nghĩa với tên là VK_XXX, như VK_F1, VK_ADD, . Khi muốn kết hợp các phím này thì ta chọn các tên mã có sẵn. Với liệt kê ở bảng 2.2, các bạn hãy tạo bảng phím tắt cho ứng dụng MyNotepad. 2.3.2. Nạp và xử lý bảng Accelerators trong ứng dụng Để sử dụng tài nguyên Accelerator, ta cần khai báo một biến handle của nó, kiểu là HACCEL. Rồi dùng hàm 22
  23. Bài 2. Giao diện Đồ họa Người dùng LoadAccelerators để nạp tài nguyên lpTableName vào cho tiến trình ứng dụng hInstance. Hàm này trả về handle của bảng Accelerators. HACCEL LoadAccelerators(HINSTANCE hInstance, LPCTSTR lpTableName); Sau khi có handle của bảng Accelerators, ta dùng hàm TranslateAccelerator để chuyển các thao tác phím có liên quan thành thông điệp WM_COMMAND để gởi cho cửa sổ tương ứng trong vòng lặp xử lý thông điệp của hàm WinMain. Các bạn xem lại đoạn code xử lý tương ứng được MS Visual C++ 6.0 phát sinh trong các project đã tìm hiểu. int TranslateAccelerator(HWND hWnd, HACCEL hAccTable, LPMSG lpMsg); Đoạn code dưới đây của hàm WndProc trong tập tin MyNotepad.cpp thể hiện việc xuất lên màn hình thông báo việc người dùng chọn các item (hoặc trực tiếp hoặc bằng tổ hợp phím tắt) của ứng dụng MyNotepad, sử dụng hàm MessageBox. int MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType); Tham số hWnd xác định cửa sổ cha quản lý hộp thoại thông báo, lpCaption là tiêu đề hộp thoại và lpText là nội dung thông báo. Ta có thể kết hợp các giá trị cờ xác định dạng thông báo như MB_OK, MB_OKCANCEL, MB_YESNO, với MB_ICONWARNING, MB_ICONQUESTION, . Cho tham số uType. Hàm trả về giá trị xác định cách chọn thao tác của người dùng như IDOK, IDCANCEL, IDYES, . 23
  24. Lập trình Windows 1 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, 2 LPARAM lParam) 3 { 4 int wmId, wmEvent; 5 PAINTSTRUCT ps; 6 HDC hdc; 7 TCHAR szHello[MAX_LOADSTRING]; 8 LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING); 9 10 switch (message) 11 { 12 case WM_COMMAND: 13 wmId = LOWORD(wParam); 14 wmEvent = HIWORD(wParam); 15 // Parse the menu selections: 16 switch (wmId) 17 { 18 // Popup File 19 case IDM_NEW: 20 MessageBox(NULL, "Ban vua chon muc File New", 21 "Thong bao", MB_OK); 22 break; 23 case IDM_OPEN: 24 MessageBox(NULL, "Ban vua chon muc File Open", 25 "Thong bao", MB_OK); 26 break; 27 case IDM_SAVE: 28 MessageBox(NULL, "Ban vua chon muc File Save", 29 "Thong bao", MB_OK); 30 break; 31 case IDM_SAVE_AS: 32 MessageBox(NULL, "Ban vua chon muc File Save As", 33 "Thong bao", MB_OK); 34 break; 35 case IDM_PAGE_SETUP: 36 MessageBox(NULL, "Ban vua chon muc File Page 37 Setup", "Thong bao", MB_OK); 38 break; 39 case IDM_PRINT: 40 MessageBox(NULL, "Ban vua chon muc File Print", 41 "Thong bao", MB_OK); 42 break; 43 case IDM_EXIT: 44 DestroyWindow(hWnd); 45 break; 46 // Popup Edit 47 case IDM_UNDO: 48 MessageBox(NULL, "Ban vua chon muc Edit Undo", 49 "Thong bao", MB_OK); 24
  25. Bài 2. Giao diện Đồ họa Người dùng 50 break; 51 case IDM_CUT: 52 MessageBox(NULL, "Ban vua chon muc Edit Cut", 53 "Thong bao", MB_OK); 54 break; 55 case IDM_COPY: 56 MessageBox(NULL, "Ban vua chon muc Edit Copy", 57 "Thong bao", MB_OK); 58 break; 59 case IDM_PASTE: 60 MessageBox(NULL, "Ban vua chon muc Edit Paste", 61 "Thong bao", MB_OK); 62 break; 63 case IDM_DELETE: 64 MessageBox(NULL, "Ban vua chon muc Edit Delete", 65 "Thong bao", MB_OK); 66 break; 67 case IDM_FIND: 68 MessageBox(NULL, "Ban vua chon muc Edit Find", 69 "Thong bao", MB_OK); 70 break; 71 case IDM_FIND_NEXT: 72 MessageBox(NULL, "Ban vua chon muc Edit Find 73 Next", "Thong bao", MB_OK); 74 break; 75 case IDM_REPLACE: 76 MessageBox(NULL, "Ban vua chon muc Edit Replace", 77 "Thong bao", MB_OK); 78 break; 79 case IDM_GOTO: 80 MessageBox(NULL, "Ban vua chon muc Edit Goto", 81 "Thong bao", MB_OK); 82 break; 83 case IDM_SELECT_ALL: 84 MessageBox(NULL, "Ban vua chon muc Edit Select 85 All", "Thong bao", MB_OK); 86 break; 87 case IDM_TIME_DATE: 88 MessageBox(NULL, "Ban vua chon muc Edit Time 89 Date", "Thong bao", MB_OK); 90 break; 91 // Popup Format 92 case IDM_WORD_WRAP: 93 MessageBox(NULL, "Ban vua chon muc Format Word 94 Wrap", "Thong bao", MB_OK); 95 break; 96 case IDM_FONT: 97 MessageBox(NULL, "Ban vua chon muc Format Font", 98 "Thong bao", MB_OK); 25
  26. Lập trình Windows 99 break; 100 // Popup Help 101 case IDM_HELP_TOPICS: 102 MessageBox(NULL, "Ban vua chon muc Help Topics", 103 "Thong bao", MB_OK); 104 break; 105 case IDM_ABOUT: 106 DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, 107 (DLGPROC)About); 108 break; 109 default: 110 return DefWindowProc(hWnd, message, wParam, 111 lParam); 112 } 113 break; 114 case WM_PAINT: 115 hdc = BeginPaint(hWnd, &ps); 116 // TODO: Add any drawing code here 117 RECT rt; 118 GetClientRect(hWnd, &rt); 119 DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER); 120 EndPaint(hWnd, &ps); 121 break; 122 case WM_DESTROY: 123 PostQuitMessage(0); 124 break; 125 default: 126 return DefWindowProc(hWnd, message, wParam, lParam); 127 } 128 return 0; 129 } 2.4. CÁC ĐỐI TƯỢNG ĐIỂU KHIỂN Trên môi trường trực quan của Windows, bên cạnh cửa sổ chính dùng để thể hiện dữ liệu ứng dụng, hay menu dùng để thể hiện các nhóm thao tác chọn lựa của người dùng, chúng ta còn thường thấy một số dạng cửa sổ con có giao diện và cách xử lý đặc trưng, gọi là đối tượng điều khiển (control). Về bản chất, đối tượng control được cấu trúc như một cửa sổ, nhưng có những xử lý khác hơn. Chúng thường được sử dụng như các công cụ hữu hiệu nhất để xuất (thể hiện) và nhận thông tin cơ 26
  27. Bài 2. Giao diện Đồ họa Người dùng bản từ người dùng theo các định dạng cho trước. Các control thường được sử dụng trên các hộp thoại (xem bài 3), tuy nhiên chúng ta cũng có thể thao tác chúng ngay trên cửa sổ chính. Khi lập trình bằng Win32 Application, chúng ta sử dụng các đối tượng control đã được Windows định nghĩa trước, hoặc xây dựng dạng control riêng cho ứng dụng. Các đối tượng đã được định nghĩa sẵn bao gồm: Button: Đối tượng nút nhấn hoặc chọn, gồm push button (nút nhấn), check box (ô chọn), radio button (ô chọn theo nhóm), group box (khung nhóm), bitmap button (nút nhấn dưới dạng ảnh), . Edit: Đối tượng cho phép người dùng thao tác văn bản như gõ văn bản, copy, xóa, . Rich edit: Tương tự như edit text, nhưng đối tượng này còn hỗ trợ thêm việc định dạng văn bản text và có khả năng kết hợp được các đối tượng nhúng COM. Hiện tại Microsoft cung cấp cho chúng ta hai dạng Rich edit qua hai phiên bản 1.0 và 2.0. List box: Đối tượng cho phép liệt kê và chọn lựa một hay nhiều mục chọn trong một danh sách được xây dựng. Combo box: Đối tượng có dạng kết hợp của một edit và một list box, cho phép người dùng thao tác edit hay chọn lựa các mục chọn trong một danh sách. Scroll bar: Đối tượng thanh trượt, thường được sử dụng để xác định hướng và kích thước hỗ trợ cho việc cuộn xem các thông tin của một cửa sổ nào đó. 27
  28. Lập trình Windows Static: Đối tượng control đơn giản nhất, đóng vai trò như phần nhãn (label) text, hình ảnh, hỗ trợ cho các control khác. Đối tượng static, theo như cách gọi tên của nó, là tĩnh – thường không nhận các tác động của người dùng. Trong phạm vi môn học, chúng ta chỉ tìm hiểu về các control thông qua đối tượng edit, với những vấn đề chính nhất là tạo lập như một cửa sổ, phân tích các kiểu của từng loại đối tượng, và xử lý các dạng thông điệp liên quan, nhất là thông điệp thông báo. 2.4.1. Thao tác control với các hàm cửa sổ Như đã nói ở trên, control cũng là đối tượng cửa sổ, vì thế chúng cũng được tạo lập giống như cửa sổ chính. Ta có thể sử dụng hàm CreateWindow hoặc CreateWindowEx để tạo lập một đối tượng control. HWND CreateWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam); Hàm CreateWindow tạo lập và trả về một đối tượng control HWND xác định bởi lớp control lpClassName được định nghĩa trước. Ứng với các đối tượng liệt kê ở trên, ta dùng các tên lớp là BUTTON, EDIT, RICHEDIT, LISTBOX, COMBOBOX, SCROLLBAR và STATIC. Với mỗi lớp đối tượng, Windows căn cứ vào tham số dwStyle để tạo lập kiểu cụ thể. Như đã nói, đối tượng button có thể là push button, radio button, check box, và được xác định thông qua giá trị của dwStyle là BS_PUSHBUTTON, BS_RADIOBUTTON, BS_CHECKBOX, . Ngoài ra, chúng 28
  29. Bài 2. Giao diện Đồ họa Người dùng ta cũng cần kết hợp một số cờ khác của kiểu cửa sổ theo yêu cầu của ứng như control là cửa sổ con WS_CHILD, được hiển thị khi gọi tạo lập WS_VISIBLE, . Các thông số còn lại tương tự như đã tìm hiểu khi tạo lập cửa sổ chính của ứng dụng. Tuy nhiên, lưu ý là nếu ta tạo control như một cửa sổ con (kiểu là WS_CHILD) thì vị trí x, y của nó dựa vào tọa độ của cửa sổ cha hWndParent. Nếu tạo lập thành công, ứng dụng trả về handle của control, và ta cũng sử dụng các hàm chung về đối tượng cửa sổ để thao tác như ShowWindow, EnableWindow, MoveWindow, . 2.4.2. Xử lý thông điệp cho cửa sổ control Với các lớp cửa sổ control hỗ trợ xử lý thông điệp, ta có thể thao tác chúng bằng cách gởi các thông điệp đến giống như đối với một cửa sổ thông thường. Để gởi một thông điệp đến một cửa sổ, ta có thể dùng hàm SendMessage hoặc PostMessage. LRESULT SendMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); LRESULT PostMessage(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); Hàm SendMessage kết thúc khi thủ tục xử lý của cửa sổ hWnd đã xử lý xong thông điệp Msg. Còn hàm PostMessage chỉ gởi thông điệp mà không cần đợi xử lý xong. Vấn đề của người lập trình chúng ta là phải biết được ứng với mỗi đối tượng control thì ta có thể gởi những thông điệp nào, với các tham số wParam và lParam đi kèm ra sao để xử lý được các công việc theo yêu cầu đặt ra của ứng dụng. 29
  30. Lập trình Windows Bên cạnh các thông điệp thông thường của một cửa sổ mà lớp control có hỗ trợ như vừa đề cập, mỗi dạng control còn định nghĩa thêm một số thông điệp khác gắn liền với thao tác xử lý của nó. Dạng thông điệp này được gởi cho cửa sổ cha quản lý đối tượng control thông qua thông điệp WM_COMMAND, và được biết dưới tên là thông điệp thông báo (notification message) khi control này nhận tác động khác nhau từ người dùng. Đây chính là giá trị xác định bởi thông số HIWORD(lParam) đã được tìm hiểu ở trên trong phần 2.2.3. Chẳng hạn, đối với đối tượng button, khi người dùng click chuột, double click hoặc chuyển focus (tìm hiểu ở bài 5) thì control này gởi cho cửa sổ cha giá trị thông điệp thông báo thông qua các tên thông điệp đã được Windows định nghĩa trước là BN_CLICKED, BN_DBLCLK, BN_SETFOCUS, và BN_KILLFOCUS. 2.4.3. Minh họa với ứng dụng MyNotepad Với phạm vi môn học, chúng ta không thể liệt kê đầy đủ các kiểu và các dạng thông điệp liên quan đến các loại control. Vì thế, khi xây dựng các ứng dụng cụ thể, chúng ta cần tham khảo trong MSDN. Ở đây, tôi sẽ cùng các bạn minh họa việc tạo lập và thực hiện một số thao tác cơ bản về văn bản trên edit text gắn với cửa sổ chính của ứng dụng MyNotepad. Đầu tiên, ta cần tạo lập thêm cửa sổ thao tác văn bản cho ứng dụng. Chúng ta có thể tạo lập cửa sổ này dưới dạng là một đối tượng edit hoặc là rich edit. Ở đây tôi chọn đối tượng là rich edit version 1.0, với handle là biến tĩnh hwndEdit. Chúng 30
  31. Lập trình Windows ta kết hợp tạo lập tại thông điệp WM_CREATE của cửa sổ chính – xin xem code minh họa ở cuối phần này. Để ý là để sử dụng đối tượng rich edit, chúng ta cần include thư viện header richedit.h, và nạp thư viện liên kết động lpFileName là riched32.dll vào project bằng hàm LoadLibrary. HMODULE LoadLibrary(LPCTSTR lpFileName); Chúng ta lại thấy là edit soạn thảo luôn có kích thước cùng với kích thước vùng làm việc của cửa sổ chính. Vì thế, để giải quyết vấn đề này, ta chặn thông điệp WM_SIZE là thông điệp gởi cho cửa sổ chính mỗi khi ứng dụng thay đổi kích thước và cập nhật lại kích thước edit bằng hàm MoveWindow với chiều rộng và chiều cao mới của edit chính là giá trị LOWORD và HIWORD của thông số lParam. Một vấn đề cần quan tâm của ứng dụng MyNotepad nữa là vấn đề focus. Chúng ta sẽ tìm hiểu rõ hơn về vấn đề này ở bài 5 khi nói về thao tác bàn phím. Tuy nhiên, để giải quyết cho ứng dụng này, các bạn có thể hiểu như vậy: Trên Windows, tại một thời điểm chỉ có một cửa sổ có thể nhận tác động của bàn phím, và cửa sổ đó được xem là ở trạng thái giữ focus. Và chúng ta có những hàm và thông điệp liên quan đến vấn đề thông điệp này. Đối với ứng dụng MyNotepad, chúng ta có ít nhất hai cửa sổ là cửa sổ chính và cửa sổ edit, tuy nhiên tại mọi thời điểm, ta đều muốn rằng thao tác phím được gõ sẽ gởi cho edit. Và để làm điều này, mỗi khi cửa sổ chính nhận được focus với thông điệp WM_SETFOCUS, ta đều chuyển cho edit bằng cách sử dụng hàm SetFocus. HWND SetFocus(HWND hWnd); 31
  32. Bài 2. Giao diện Đồ họa Người dùng Hàm SetFocus thiết lập focus cho cửa sổ hWnd và trả về handle của cửa sổ nhận focus trước đó. Đến lúc này, khi các bạn thực thi ứng dụng thì chương trình đã tương đối giống ứng dụng Notepad. Tất cả các thao tác của chúng ta tác động lên edit (mà chủ yếu là thao tác bàn phím) đều được đối tượng control này xử lý theo cách mặc định mà Windows đã cài đặt. Tuy nhiên, chúng ta không thể kết hợp các thao tác của menu (tương ứng là phím tắt) vào cho edit. Trước khi thao tác menu và phím tắt, chúng ta cần chỉnh sửa một ít cho phần code tự phát sinh của ứng dụng dạng A typical “Hello World!” application. Chúng ta cần thay đổi thông số cửa sổ có nhiệm vụ dịch thông điệp phím tắt thành thông điệp COMMAND, và loại bỏ những gì liên quan đến thông điệp WM_PAINT của cửa sổ chính. Vì ứng dụng không vẽ trên vùng làm việc của cửa sổ chính (mà sử dụng edit), nên chúng ta sẽ xoá tất cả những dòng lệnh về khai báo biến, cài đặt, liên quan đến thông điệp này. Vì trong hàm WinMain, ở vòng lặp xử lý thông điệp, tham số thứ nhất của hàm TranslateAccelerator là msg.hwnd, tức là cửa sổ gắn liền với thông điệp. Nếu ta để như vậy thì khi ta gõ phím lên edit (luôn giữ focus), chúng sẽ không chuyển cho cửa sổ chính, và vì thế không giải quyết xem có phải là phím nóng cho menu của cửa sổ chính hay không. Do đó, ta sẽ sửa lại thành biến handle của của cửa sổ chính, tức ta có dòng code thay thế: TranslateAccelerator(hWnd, hAccelTable, &msg) 32
  33. Lập trình Windows Khi đó cần định nghĩa hWnd là biến toàn cục xác định handle của cửa sổ chính thay vì là biến toàn cục trong hàm InitInstance. Bây giờ chúng ta sẽ tìm hiểu các thông điệp liên quan tới edit control để lập trình thao tác cho các menu item Edit như Copy, Paste, Select All, của ứng dụng MyNotepad. Để thực hiện các thao tác Undo, Cut, Copy, Paste và Delete, ta chỉ đơn giản gởi các thông điệp WM_UNDO, WM_CUT, WM_COPY, WM_PASTE, và WM_CLEAR với các thông số wParam và lParam đều bằng 0 cho cửa sổ edit hwndEdit. Các thông điệp này được Windows định nghĩa trước cho đối tượng edit và một số dạng cửa sổ khác. Để thực hiện thao tác Select All, ta sử dụng thông điệp EM_SETSEL được định nghĩa cho đối tượng edit. Thông điệp này chọn (select) một đoạn văn bản trên một edit xác định bởi vị trí đầu và vị trí cuối gởi qua hai tham số wParam và lParam. Nếu hai tham số này tương ứng là 0 và -1 thì Windows chọn toàn bộ văn bản. Các thông điệp trên được gởi bằng hàm SendMessage khi ta chặn các ID ứng với các menu item trong thông điệp WM_COMMAND. Ngoài ra, như đã trình bày ở phần 2.2.3, ta có thể kiểm tra các giá trị của ứng dụng để cho phép hiệu lực hoặc vô hiệu lực các item của một popup tại thông điệp WM_INITMENUPOPUP. Ta sử dụng thông điệp EM_CANUNDO để kiểm tra xem có cho phép thực hiện thao tác Undo trên edit hay không, và EM_GETSEL để nhận xem có người dùng có đang chọn một đoạn text trong edit không để 33
  34. Bài 2. Giao diện Đồ họa Người dùng cho phép Cut, Copy hoặc Delete. Đồng thời sử dụng thêm hàm IsClipboardFormatAvailable để kiểm tra có dữ liệu text trong clipboard của hệ thống hay không hầu cho phép có thể thực hiện thao tác Paste. Ta có nội dung tập tin MyNotepad.cpp sau khi cập nhật thêm một số thao tác như sau: 1 // MyNotepad.cpp : Defines the entry point for the application. 2 // 3 4 #include "stdafx.h" 5 #include "resource.h" 6 #include "richedit.h" 7 8 #define MAX_LOADSTRING 100 9 10 // Global Variables: 11 HINSTANCE hInst; // current instance 12 TCHAR szTitle[MAX_LOADSTRING]; // The title bar text 13 TCHAR szWindowClass[MAX_LOADSTRING]; // The title bar text 14 HWND hWnd; // Chuyển biến cửa sổ hWnd thành toàn cục 15 16 // Foward declarations of functions included in this code module: 17 ATOM MyRegisterClass(HINSTANCE hInstance); 18 BOOL InitInstance(HINSTANCE, int); 19 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); 20 LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM); 21 22 int APIENTRY WinMain(HINSTANCE hInstance, 23 HINSTANCE hPrevInstance, 24 LPSTR lpCmdLine, 25 int nCmdShow) 26 { 27 // TODO: Place code here. 28 MSG msg; 29 HACCEL hAccelTable; 30 31 // Initialize global strings 32 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); 33 LoadString(hInstance, IDC_MYNOTEPAD, szWindowClass, 34 MAX_LOADSTRING); 35 MyRegisterClass(hInstance); 36 37 // Perform application initialization: 34
  35. Lập trình Windows 38 if (!InitInstance (hInstance, nCmdShow)) 39 { 40 return FALSE; 41 } 42 43 hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_MYNOTEPAD); 44 45 // Main message loop: 46 while (GetMessage(&msg, NULL, 0, 0)) 47 { 48 if (!TranslateAccelerator(hWnd, hAccelTable, &msg)) 49 { 50 TranslateMessage(&msg); 51 DispatchMessage(&msg); 52 } 53 } 54 55 return msg.wParam; 56 } 57 58 // 59 // FUNCTION: MyRegisterClass() 60 // 61 // PURPOSE: Registers the window class. 62 // 63 // COMMENTS: 64 // 65 // This function and its usage is only necessary if you want this code 66 // to be compatible with Win32 systems prior to the 'RegisterClassEx' 67 // function that was added to Windows 95. It is important to call this function 68 // so that the application will get 'well formed' small icons associated 69 // with it. 70 // 71 ATOM MyRegisterClass(HINSTANCE hInstance) 72 { 73 WNDCLASSEX wcex; 74 75 wcex.cbSize = sizeof(WNDCLASSEX); 76 77 wcex.style = CS_HREDRAW | CS_VREDRAW; 78 wcex.lpfnWndProc = (WNDPROC)WndProc; 79 wcex.cbClsExtra = 0; 80 wcex.cbWndExtra = 0; 81 wcex.hInstance = hInstance; 82 wcex.hIcon = LoadIcon(hInstance, 83 (LPCTSTR)IDI_MYNOTEPAD); 84 wcex.hCursor = LoadCursor(NULL, IDC_ARROW); 85 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); 86 wcex.lpszMenuName = (LPCSTR)IDC_MYNOTEPAD; 35
  36. Bài 2. Giao diện Đồ họa Người dùng 87 wcex.lpszClassName = szWindowClass; 88 wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL); 89 return RegisterClassEx(&wcex); 90 } 91 92 // 93 // FUNCTION: InitInstance(HANDLE, int) 94 // 95 // PURPOSE: Saves instance handle and creates main window 96 // 97 // COMMENTS: 98 // 99 // In this function, we save the instance handle in a global variable and 100 // create and display the main program window. 101 // 102 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) 103 { 104 hInst = hInstance; // Store instance handle in our global variable 105 106 hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, 107 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, 108 NULL, hInstance, NULL); 109 110 if (!hWnd) 111 { 112 return FALSE; 113 } 114 115 ShowWindow(hWnd, nCmdShow); 116 UpdateWindow(hWnd); 117 118 return TRUE; 119 } 120 121 // 122 // FUNCTION: WndProc(HWND, unsigned, WORD, LONG) 123 // 124 // PURPOSE: Processes messages for the main window. 125 // 126 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, 127 LPARAM lParam) 128 { 129 int wmId, wmEvent; 130 static HWND hwndEdit; 131 int iSelBeg, iSelEnd, iEnable ; 132 133 switch (message) 134 { 135 case WM_CREATE: 36
  37. Lập trình Windows 136 LoadLibrary("riched32.dll"); 137 hwndEdit = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, 138 "RICHEDIT", NULL, WS_CHILD|WS_VISIBLE| 139 WS_HSCROLL|WS_VSCROLL|ES_AUTOHSCROLL| 140 ES_AUTOVSCROLL|ES_NOHIDESEL| 141 ES_DISABLENOSCROLL|ES_MULTILINE, 142 0, 0, 0, 0, hWnd, NULL, hInst, NULL); 143 return TRUE; 144 case WM_SIZE: 145 MoveWindow(hwndEdit, 0, 0, LOWORD(lParam), 146 HIWORD(lParam), TRUE); 147 break; 148 case WM_SETFOCUS: 149 SetFocus(hwndEdit); 150 break; 151 case WM_INITMENUPOPUP: 152 switch (lParam) 153 { 154 case 1: // Edit menu 155 // Enable Undo if edit control can do it 156 EnableMenuItem ((HMENU) wParam, IDM_UNDO, 157 SendMessage (hwndEdit, EM_CANUNDO, 0, 0L) ? 158 MF_ENABLED : MF_GRAYED) ; 159 // Enable Paste if text is in the clipboard 160 EnableMenuItem ((HMENU) wParam, IDM_PASTE, 161 IsClipboardFormatAvailable (CF_TEXT) ? 162 MF_ENABLED : MF_GRAYED) ; 163 // Enable Cut, Copy, and Del if text is selected 164 SendMessage (hwndEdit, EM_GETSEL, (WPARAM) 165 &iSelBeg, (LPARAM) &iSelEnd) ; 166 iEnable = iSelBeg != iSelEnd ? MF_ENABLED : 167 MF_GRAYED ; 168 EnableMenuItem ((HMENU) wParam, IDM_CUT, 169 iEnable) ; 170 EnableMenuItem ((HMENU) wParam, IDM_COPY, 171 iEnable) ; 172 EnableMenuItem ((HMENU) wParam, IDM_DELETE, 173 iEnable) ; 174 break ; 175 } 176 177 case WM_COMMAND: 178 wmId = LOWORD(wParam); 179 wmEvent = HIWORD(wParam); 180 // Parse the menu selections: 181 switch (wmId) 182 { 183 // Popup File 184 case IDM_NEW: 37
  38. Lập trình Windows 185 MessageBox(NULL, "Ban vua chon muc File New", 186 "Thong bao", MB_OK); 187 break; 188 case IDM_OPEN: 189 MessageBox(NULL, "Ban vua chon muc File Open", 190 "Thong bao", MB_OK); 191 break; 192 case IDM_SAVE: 193 MessageBox(NULL, "Ban vua chon muc File Save", 194 "Thong bao", MB_OK); 195 break; 196 case IDM_SAVE_AS: 197 MessageBox(NULL, "Ban vua chon muc File Save As", 198 "Thong bao", MB_OK); 199 break; 200 case IDM_PAGE_SETUP: 201 MessageBox(NULL, "Ban vua chon muc File Page 202 Setup", "Thong bao", MB_OK); 203 break; 204 case IDM_PRINT: 205 MessageBox(NULL, "Ban vua chon muc File Print", 206 "Thong bao", MB_OK); 207 break; 208 case IDM_EXIT: 209 DestroyWindow(hWnd); 210 break; 211 // Popup Edit 212 case IDM_UNDO: 213 SendMessage (hwndEdit, WM_UNDO, 0, 0) ; 214 break; 215 case IDM_CUT: 216 SendMessage (hwndEdit, WM_CUT, 0, 0) ; 217 break; 218 case IDM_COPY: 219 SendMessage (hwndEdit, WM_COPY, 0, 0) ; 220 break; 221 case IDM_PASTE: 222 SendMessage (hwndEdit, WM_PASTE, 0, 0) ; 223 break; 224 case IDM_DELETE: 225 SendMessage (hwndEdit, WM_CLEAR, 0, 0) ; 226 break; 227 case IDM_FIND: 228 MessageBox(NULL, "Ban vua chon muc Edit Find", 229 "Thong bao", MB_OK); 230 break; 231 case IDM_FIND_NEXT: 232 MessageBox(NULL, "Ban vua chon muc Edit Find 233 Next", "Thong bao", MB_OK); 38
  39. Bài 2. Giao diện Đồ họa Người dùng 234 break; 235 case IDM_REPLACE: 236 MessageBox(NULL, "Ban vua chon muc Edit Replace", 237 "Thong bao", MB_OK); 238 break; 239 case IDM_GOTO: 240 MessageBox(NULL, "Ban vua chon muc Edit Goto", 241 "Thong bao", MB_OK); 242 break; 243 case IDM_SELECT_ALL: 244 SendMessage (hwndEdit, EM_SETSEL, 0, -1) ; 245 break; 246 case IDM_TIME_DATE: 247 MessageBox(NULL, "Ban vua chon muc Edit Time 248 Date", "Thong bao", MB_OK); 249 break; 250 // Popup Format 251 case IDM_WORD_WRAP: 252 MessageBox(NULL, "Ban vua chon muc Format Word 253 Wrap", "Thong bao", MB_OK); 254 break; 255 case IDM_FONT: 256 MessageBox(NULL, "Ban vua chon muc Format Font", 257 "Thong bao", MB_OK); 258 break; 259 // Popup Help 260 case IDM_HELP_TOPICS: 261 MessageBox(NULL, "Ban vua chon muc Help Topics", 262 "Thong bao", MB_OK); 263 break; 264 case IDM_ABOUT: 265 DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, 266 (DLGPROC)About); 267 break; 268 default: 269 return DefWindowProc(hWnd, message, wParam, 270 lParam); 271 } 272 break; 273 case WM_DESTROY: 274 PostQuitMessage(0); 275 break; 276 default: 277 return DefWindowProc(hWnd, message, wParam, lParam); 278 } 279 return 0; 280 } 281 282 // Mesage handler for about box. 39
  40. Lập trình Windows 283 LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, 284 LPARAM lParam) 285 { 286 switch (message) 287 { 288 case WM_INITDIALOG: 289 return TRUE; 290 291 case WM_COMMAND: 292 if (LOWORD(wParam) == IDOK || LOWORD(wParam) == 293 IDCANCEL) 294 { 295 EndDialog(hDlg, LOWORD(wParam)); 296 return TRUE; 297 } 298 break; 299 } 300 return FALSE; 301 } 2.5. TÓM TẮT Qua việc tạo lập tài nguyên menu và accelerator, chúng ta đã nắm được cơ chế lập trình hỗ trợ giao diện sử dụng tài nguyên của MS Visual C++ 6.0 trên MS Windows – phần 2.1. Với các đối tượng tài nguyên có sẵn, chúng ta chỉ việc gọi các hàm API để nạp chúng vào ứng dụng, sau đó sử dụng các hàm cũng như dạng thông điệp hỗ trợ cho từng loại để lập trình xử lý. Trong phần 2.2, chúng ta đã tìm hiểu các thông điệp của menu, trong đó đáng chú ý nhất là thông điệp WM_COMMAND gởi cho ứng dụng khi người dùng chọn một item nào đó. Thông qua hàm TranslateAccelerators, chúng ta cũng đã giái quyết được vấn đề phím tắt chuyển thành COMMAND cho ứng dụng – phần 2.3. Cuối cùng, phần 2.4 giới thiệu cơ bản cho chúng ta về các đối tượng control thao tác trực tiếp bằng các hàm API (không 40
  41. Bài 2. Giao diện Đồ họa Người dùng sử dụng tài nguyên). Với một phần ví dụ ứng dụng MyNotepad, chúng ta đã kết hợp được các thao tác hỗ trợ vấn đề Giao tiếp Người dùng Đồ họa ở trên (menu, accelerator, control, ), đồng thời làm quen dần cơ chế thông điệp – đặc điểm quan trọng nhất khi lập trình Windows. 2.6. CÂU HỎI ÔN TẬP – BÀI TẬP 2.6.1. Với ứng dụng Win32 Application dạng A typical “Hello World!” application được MS Visual C++ 6.0 phát sinh, xem lại các đối tượng tài nguyên được tạo lập trực quan trên workspace ResourceView, đồng thời tìm hiểu cấu trúc của chúng dưới dạng văn bản (text). 2.6.2. Thử chèn thêm và chỉnh sửa các đối tượng tài nguyên để làm quen với các công cụ trực quan mà Visual C++ 6.0 hỗ trợ như icon, bitmap, . 2.6.3. Tìm hiểu việc thao tác các chuỗi ký tự trong bảng String Table của tài nguyên ứng dụng trên, và xem việc nạp sử dụng chúng trong ứng dụng. 2.6.4. Với ứng dụng MyNotepad, viết code thực hiện menu item Date/Time chèn ngày giờ vào văn bản ứng dụng. Hướng dẫn: Tìm hiểu và sử dụng hàm GetLocalTime để lấy thời gian hiện tại trên máy tính (vào biến cấu trúc SYSTEMTIME). Sau đó dùng thông điệp EM_REPLACESEL để chèn chuỗi ký tự theo format của bạn vào edit hwndEdit của ứng dụng. Chúng ta có thể dùng hàm wsprintf để định format chuỗi cho biến cấu trúc SYSTEMTIME ở trên. 41
  42. Lập trình Windows 2.6.5*. Tìm hiểu chi tiết về các đối tượng control trong MSDN. Phân tích về button, edit, các kiểu, cấu trúc, thông điệp và hàm hỗ trợ. 2.6.6. Viết ứng dụng đơn giản minh họa việc tạo lập và thao tác đơn giản các nút nhấn trên cửa sổ chính ứng dụng Win32 Application. Ứng dụng tạo lập 4 nút nhấn Left Top, Left Bottom, Right Top và Right Bottom trên màn hình, và xuất thông báo khi người dùng vừa click chọn một nút. Yêu cầu: Các nút nhấn luôn cách biên 10 pt và cách nhau 10 pt. Kích thước thay đổi khi người dùng co giản màn hình. Ta sử dụng thông điệp WM_SIZE để lấy kích thước và dùng hàm MoveWindow để thay đổi chúng. 42