Giáo trình Nhập môn hệ quản trị Cơ sở dữ liệu (Phần 2) - Đặng Thị Thu Hiền

Tài liệu Giáo trình Nhập môn hệ quản trị Cơ sở dữ liệu (Phần 2) - Đặng Thị Thu Hiền: 131 CHƢƠNG 7: LẬP TRÌNH CƠ SỞ DỮ LIỆU TRONG MS ACCESS 1. Giới thiệu lập trình Visual Basic Application MS Access không chỉ đơn thuần là một hệ quản trị cơ sở dữ liệu (CSDL) quan hệ mà nó còn cung cấp một môi trƣờng lập trình với các công cụ khá đầy đủ, dễ sử dụng để phát triển các ứng dụng quản lý vừa và nhỏ. Ngôn ngữ lập trình đƣợc phát triển trong MS Access là Access Basic. Tuy nhiên từ phiên bản MS Access for Windows 95, Access Basic đƣợc thay thế bởi Visual Basic (VB). Hai ngôn ngữ này khá giống nhau và đều đƣợc phát triển từ một thành phần thiết kế chung. Nhƣng ngày nay, VB trở thành ngôn ngữ lập trình chung của chƣơng trình ứng dụng MS Office bao gồm: Access, Excel, Word, PowerPoint và đƣợc gọi là VBA (Visual Basic for Applications). Việc có đƣợc một ngôn ngữ lập trình chung xuyên suốt mọi chƣơng trình ứng mang lại một số lợi điểm quan trọng là:  Ngƣời lập trình chỉ cần biết một ngôn ngữ lập trình để tùy biến, phát triển ứng dụng.  Dễ dàng hợp nhất...

pdf112 trang | Chia sẻ: quangot475 | Lượt xem: 884 | Lượt tải: 1download
Bạn đang xem trước 20 trang mẫu tài liệu Giáo trình Nhập môn hệ quản trị Cơ sở dữ liệu (Phần 2) - Đặng Thị Thu Hiền, để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
131 CHƢƠNG 7: LẬP TRÌNH CƠ SỞ DỮ LIỆU TRONG MS ACCESS 1. Giới thiệu lập trình Visual Basic Application MS Access không chỉ đơn thuần là một hệ quản trị cơ sở dữ liệu (CSDL) quan hệ mà nó còn cung cấp một môi trƣờng lập trình với các công cụ khá đầy đủ, dễ sử dụng để phát triển các ứng dụng quản lý vừa và nhỏ. Ngôn ngữ lập trình đƣợc phát triển trong MS Access là Access Basic. Tuy nhiên từ phiên bản MS Access for Windows 95, Access Basic đƣợc thay thế bởi Visual Basic (VB). Hai ngôn ngữ này khá giống nhau và đều đƣợc phát triển từ một thành phần thiết kế chung. Nhƣng ngày nay, VB trở thành ngôn ngữ lập trình chung của chƣơng trình ứng dụng MS Office bao gồm: Access, Excel, Word, PowerPoint và đƣợc gọi là VBA (Visual Basic for Applications). Việc có đƣợc một ngôn ngữ lập trình chung xuyên suốt mọi chƣơng trình ứng mang lại một số lợi điểm quan trọng là:  Ngƣời lập trình chỉ cần biết một ngôn ngữ lập trình để tùy biến, phát triển ứng dụng.  Dễ dàng hợp nhất các đối tƣợng trong các chƣơng trình ứng dụng. VBA là ngôn ngữ có một số đặc điểm:  Không phân biệt chữ hoa, thƣờng  Hƣớng sự kiện và hƣớng đối tƣợng Việc tổ chức chƣơng trình theo mô hình hƣớng đối tƣợng và hƣớng sự kiện khiến cho các mã lệnh của chƣơng trình, suy cho đến cùng nhất, chỉ đƣợc gọi khi có sự kiện (event) nào đó xảy ra trên các đối tƣợng (object) cụ thể. Sự kiện của các đối tƣợng đƣợc sinh ra có thể do ngƣời dùng tác động chuột/bàn phím vào điều khiển. Ví dụ sự kiện OnClick() của điều khiển Button trên form. Sự kiện cũng có thể đƣợc sinh ra trong quá trình biên dịch. Ví dụ sự kiện Load() của một form. Tuy nhiên, không phải bất kỳ đối tƣợng nào cũng có các sự kiện. Các đối tƣợng là các điều khiển (control) đƣơng nhiên có các sự kiện. Ví dụ TextBox, CommandButton, Form, đều có các sự kiện; trong khi đó đối tƣợng DBEngine lại không thể có sự kiện nào. Nhƣ vậy, toàn bộ mã lệnh của chƣơng trình ứng dụng Access đƣợc tổ chức là các hàm/thủ tục (function/sub) độc lập, bình đẳng (chúng ta sẽ phân biệt hàm và thủ tục trong phần sau). Không có hàm/thủ tục nào là cha, chứa các hàm/thủ tục khác. Không có “điểm vào” của chƣơng trình. Nghĩa là, không có hàm/thủ nào đƣợc chƣơng trình gọi trƣớc nhất để từ đó gọi đến các hàm/thủ tục khác. Tất cả các hàm/thủ tục chỉ đƣợc gọi để đáp ứng các sự kiện tƣơng ứng hoặc đƣợc gọi tƣờng minh trong hàm/thủ tục khác. 132 Chƣơng này sẽ trình bày các nội dung sau:  Module và Access Class Object  Các kiểu dữ liệu, hằng và biến  Các cấu trúc điều khiển  Hàm và thủ tục  Các mô hình truy cập CSDL 2. Module Module là một đối tƣợng nguyên thủy của môi trƣờng lập trình VBA. Toàn bộ mã lệnh VBA trong CSDL đƣợc lƣu trong module dƣới dạng các thủ tục (gồm hàm và thủ tục con). Các thủ tục này có thể độc lập hoặc liên quan đến form/report. Nói cách khác, module là một phƣơng tiện của MS Access để giúp ngƣời lập trình tổ chức mã nguồn của họ sao cho “gọn gàng”, dễ kiểm soát. Ví dụ, ngƣời lập trình nên gom các đoạn mã (hàm/thủ tục) làm việc với CSDL vào một module đặt tên là DataAccessModule, gom các đoạn mã là việc với form vào một module đặt tên là FormModule hay nên viết ra một lớp Student (Class Module) để làm việc với các bản ghi thuộc bảng Student trong CSDL, MS Access 2013 cung cấp 03 loại module: module chuẩn (Standard Module), module lớp (Class Module) và module gắn với form/report (Form/Report Module) Standard module chủ yếu bao gồm tập các hàm/thủ tục. Mỗi hàm/thủ tục này đƣợc gọi từ các hàm/thủ tục khác hoặc từ sự kiện của đối tƣợng hay điều khiển. Khi đó, toàn bộ mã lệnh của chƣơng trình đƣợc tổ chức thành các đơn vị hàm/thủ tục. Các đơn vị hàm/thủ tục này đƣợc gom lại trong một hoặc một số Standard Module để giúp lập trình viên dễ quản lý mã lệnh của mình hơn. Class module thực chất là một lớp do ngƣời dùng định nghĩa. Mỗi Class Module là một lớp của ngƣời dùng có tên chính là tên của Class Module. Lớp của ngƣời dùng định nghĩa cũng đƣợc đối xử bình đẳng nhƣ các lớp đã đƣợc định nghĩa bởi hệ thống (built-in language class). Điều quan trọng ở đây là bạn phải biết khi nào dùng Standard Module và khi nào dùng Class Module hay cả hai. Nó phụ thuộc vào cách thiết kế ứng dụng của bạn. Nếu ứng dụng của bạn đƣợc tổ chức theo kiểu “hƣớng chức năng” (phần mềm là một tập các chức năng có quan hệ với nhau) thì bạn sẽ có xu hƣớng sử dụng Standard Module nhiều hơn. Nếu ứng dụng của bạn đƣợc thiết kế theo mô hình lập trình “phân lớp” (03 lớp chẳng hạn: giao diện, logic, truy cập dữ liệu) thì bạn sẽ đƣơng nhiên sẽ sử dụng Class Module nhiều hơn. Sau đây là chi tiết về các loại module. 133 2.1 Module chuẩn (Standard Module) Module này chứa các biến, thủ tục con có thể đƣợc gọi từ query, form, report, macro, biểu thức, thủ tục khác hoặc từ bất cứ đâu trong chƣơng trình ứng dụng. Nhƣ vậy, ta có thể viết trong Standard Module các nội dung sau đây:  Các khai báo tùy chọn dùng chung cho tất cả các hàm/thủ tục trong Standard Module. Ví dụ: Option Explicit là một khai báo tùy chọn yêu cầu tất cả các biến sau này dùng trong các hàm/thủ tục phải đƣợc khai báo tƣờng minh trƣớc khi dùng  Các khai báo hằng, biến toàn cục  Các hàm/thủ tục Các hàm/thủ tục trong Standard Module với phạm vi truy xuất public (mặc định) có thể đƣợc gọi từ bất kỳ đâu trong CSDL bao gồm các lời gọi từ:  Các hàm/thủ tục khác trong cùng Standard Module với nó  Các hàm/thủ tục trong các Class Module khác  Các thủ tục gắn với các form/report trong MS Access Class Objects Các hàm/thủ tục có phạm vi truy xuất private chỉ đƣợc gọi trong các hàm/thủ tục khác thuộc cùng module với nó. Để tạo Standard Module, trong cửa sổ thiết kế CSDL, chọn lệnh CREATE trên thanh menu, sau đó chọn nút lệnh Module (vùng khoanh đỏ) nhƣ trong hình 7.1 Hình 7.1: Tạo Standard Module từ cửa sổ thiết kế CSDL 134 Kết quả bạn nhận đƣợc là cửa sổ để viết code trong Standard Module nhƣ trong hình 7.2 Hình 7.2 : Cửa sổ code của Standard Module Trong hình 7.2, cửa sổ màn hình đƣợc chia làm 02 panel bao gồm : panel bên trái là Project Explorer Panel để hiển thị các đối tƣợng module. Trong panel này, bạn có thể thêm/bớt hoặc sửa tên (F4) Standard Module, panel bên phải là Code Panel, đây là cửa sổ để bạn viết mã cho mỗi Standard Module đƣợc chọn bên panel trái. Chú ý : bạn có thể bật/tắt các panel này theo ý muốn để vùng quan sát của bạn đƣợc rộng hơn. Ví dụ khi code bạn muốn cửa sổ code (Code Panel) đƣợc rộng bạn nên tắt panel bên trái bằng cách click chuột vào biểu tƣợng dấu X ở góc trên bên phải nhất của panel đó. Khi cần bạn có thể mở lại bằng cách chọn lệnh VIEW/Project Explore trên thanh menu. Khi muốn ghi lại code, bạn cần chọn lệnh File/Save hoặc chọn biểu tƣợng save (chiếc đĩa mềm) trên thanh menu. Lần đầu tiên lƣu, bạn sẽ đƣợc hỏi đặt tên cho Standard Module, những lần sau, MS Access sẽ tự ghi vào tên bạn đã đặt từ lần đầu. Hình 7.3 minh họa cửa sổ lƣu Module3 đƣợc đặt tên là commonFunction. Code panel Project Explorer Panel 135 Hình 7.3 Đặt tên cho Standard Module Khi muốn sửa tên module đã đặt, bạn chọn vào tên module đó trong Project Explorer Panel rồi ấn phím F4. Ví dụ hình 7.4 minh họa cửa sổ đổi tên cho module1 thành tên mới là checkValidFunction Hình 7.4 Đổi tên cho Standard Module đã có Chúng ta quan tâm nhiều đến Code Panel. Sau đây, chúng ta sẽ phân tích Panel này. Phần trên cùng của Panel là hai hộp danh sách thả xuống. Hộp danh sách bên trái luôn có một mục là (General), hộp danh sách bên phải là danh sách các hàm, thủ tục trong Nút lệnh save Bạn nhập tên mới cho module ở thuộc tính (Name) 136 Standard Module (trong hình 7.2 vì chƣa có hàm, thủ tục nào đƣợc viết trong Standard Module nên chỉ có một mục (Declarations) đƣợc hiển thị). Cửa sổ soạn thảo mã lệnh gồm 03 phần, phần khai báo các tùy chọn, khai báo các hằng, biến dùng chung cho các hàm, thủ tục trong module và phần định nghĩa các hàm, thủ tục trong Standard Module. 2.1.1 Khai báo các tùy chọn Các tùy chọn nếu đƣợc khai báo có thể khai báo sau hằng, biến toàn cục nhƣng phải trƣớc phần định nghĩa các hàm/thủ tục. Phần khai báo các tùy chọn ở đây có thể có các tùy chọn sau đƣợc khai báo:  Option Base Statement Khai báo chỉ số thấp nhất cho mảng trong toàn module, mặc định là 0. Cú pháp khai báo: Option Base {0 | 1} Ví dụ: Khi định nghĩa một mảng theo cú pháp Dim a(100) as Integer Mặc định ta sẽ đƣợc một mảng tên là a, các chỉ số chạy từ 0 đến 99. Nếu có tùy chọn Option Base 1 thì mảng a sẽ có 100 phần tử, chỉ số chạy từ 1 đến 100. Tùy chọn này (nếu có) phải đƣợc khai báo trƣớc bất kỳ hàm, thủ tục nào và nó chỉ có tác dụng trong module chứa nó. Option Base chỉ đƣợc khai báo một lần trong một module và phải trƣớc các khai báo mảng.  Option Compare Statement Khai báo phƣơng thức so sánh cho các biểu thức thuộc kiểu chuỗi (String). Cú pháp khai báo: Option Compare {Binary | Text | Database} Option Compare Binary: so sánh chuỗi theo kiểu nhị phân, nghĩa là theo thứ tự sắp xếp của các ký tự trong bảng mã ASCII. Đây là kiểu mặc định Ví dụ: khi có khai báo Option Compare Binary thì ta sẽ có “A” < “B” < ”C” < “a” < “b” < “c” vì mã ASCII của “A” và “a” tƣơng ứng là 65 và 97 (hệ thập phân) Option Compare Text: so sánh theo kiểu trật tự của các ký tự không phân biệt chữ hoa, thƣờng. 137 Ví dụ: Khi khai báo Option Compare Text thì "A" = "a", "B" = "b", , "À" = "à", "Ê" = "ê", Option Compare Database: so sánh xâu dựa trên trật tự đƣợc xác định cục bộ trong Database chứa module đó.  Option Explicit Statement Khai báo để yêu cầu các biến phải đƣợc khai báo tƣờng minh trƣớc khi sử dụng. Tùy chọn này (nếu có) phải đƣợc đặt trƣớc mọi khai báo biến và định nghĩa các hàm, thủ tục con. Cú pháp khai báo: Option Explicit Ví dụ 1: [1] Option Explicit [2] Dim a [3] a = 100 Trong đoạn mã này: + dòng [1]: yêu cầu các biến phải đƣợc khai báo tƣờng minh trƣớc khi sử dụng + dòng [2] khai báo một biến a + dòng [3] gán cho a giá trị 100 Nhƣ vậy, cuối cùng biến a đƣợc nhận giá tri là 100 Ví dụ 2: [1] Option Explicit [2] a = 100 Trong đoạn mã này: + dòng [1]: yêu cầu các biến phải đƣợc khai báo tƣờng minh trƣớc khi sử dụng + dòng [2]: gán cho biến a giá trị 100 mà không có khai báo trƣớc Đoạn mã này khi dịch trình biên dịch sẽ thông báo lỗi “Variable not defined”. Và do đó cần khai báo biến a trƣớc khi gán giá trị cho nó (Dim a) hoặc ta bỏ khai báo Option Explicit đi.  Option Private Statement 138 Khai báo tùy chọn để cấm các truy xuất từ bên ngoài (các ứng dụng, dự án khác) vào các thành phần của module. Cú pháp khai báo: Option Private Module Chú ý: Tùy chọn này chỉ cấm các truy cập từ các dự án (có thể trong cùng ứng dụng), ứng dụng khác tới các thành phần (hằng, biến, hàm, thủ tục, kiểu ngƣời dùng định nghĩa) của module. Mọi truy xuất từ các module, query, form, khác trong cùng cơ sở dữ liệu là đƣợc. 2.1.2 Khai báo hằng, biến toàn cục Hằng, biến toàn cục có phạm vi hoạt động trong toàn bộ module mà nó đƣợc khai báo hoặc có thể rộng hơn (từ các module khác) tùy thuộc vào việc bạn quy định phạm vi truy xuất cho nó là private hay public. Hằng, biến toàn cục có thể đƣợc khai báo trƣớc hoặc sau các khai báo tùy chọn nhƣng bắt buộc phải khai báo trƣớc các hàm/thủ tục. Mặc định hằng, biến toàn cục ở đây có phạm vi truy xuất là private. Tức là, bạn chỉ có thể truy xuất đƣợc chúng từ các hàm, thủ tục trong cùng module với chúng. Bạn không thể truy xuất đƣợc các hằng, biến này từ các module khác. Tuy nhiên, bạn sõ thể thiết lập phạm vi truy xuất public cho chúng với khai báo từ khóa public trƣớc khai báo tên hằng, biến Chú ý: nên hạn chế việc sử dụng hằng/biến toàn cục Cú pháp khai báo hằng, biến sẽ đƣợc trình bày chi tiết trong phần sau. 2.1.3. Hàm, thủ tục (function/sub) Sau các khai báo tùy chọn và hằng, biến là phần định nghĩa các hàm/thủ tục của module. Các hàm/thủ tục đƣợc ra nhƣ là một thƣ viện, việc gọi thi hành chúng phải là tƣờng minh. Mặc định các hàm/thủ tục trong Standard Module có phạm vi truy xuất là public. Chi tiết về hàm và thủ tục sẽ đƣợc trình bày trong phần sau. 2.1.4 Ví dụ Sau đây chúng ta phân tích một Standard Module có tên là commonFunction với dụng ý là module để lƣu các hàm cơ bản, dùng chung. Trong commonFunction chúng ta sẽ định nghĩa một số hàm làm việc với mảng các số double. Mục dích của ví dụ là minh họa các thành phần trong Standard Module Chú ý: để viết các chú thích (comment) trong vùng viết code của MS Access ta sử dụng dấu „ (dấu phẩy) trong dòng chú thích. Ví dụ: ‘ This is a comment. Khi gặp các dòng bắt đầu bằng dấu „, trình biên dịch sẽ bỏ qua tất cả những gì sau dấu „ cho đến khi gặp dòng tiếp theo. ‘Khai báo tùy chọn chỉ số bắt đầu của mảng từ 1 mặc định 139 ‘là 0 và tùy chọn phải khai báo biến tường minh trước ‘khi dùng Option Base 1 Option Explicit ‘Khai báo một biến mảng toàn cục dùng chung a ‘Khai báo một hằng n dụng ý là số phần tử của mảng ‘Phạm vi truy xuất mặc định của a, n là private Dim a(100) As Double Const n As Integer = 10 ‘Định nghĩa các hàm/thủ tục ‘Thủ tục khởi tạo ngẫu nhiên các giá trị mảng a gồm ‘10 phần tử, các phần tử có giá trị <= 100 Sub InitArray() Dim i As Integer ‘Khởi tạo bộ sinh số ngẫu nhiên từ 0 đến 1 ‘sử dụng cho hàm rnd sau này Randomize For i = 1 To n a(i) = Rnd * 100 Next End Sub ‘Thủ tục in các giá trị của mảng a ra màn hình hộp thoại, ‘mỗi giá trị xuất hiện trong một lần hộp thoại xuất hiện, ‘click vào nút lệnh OK để hiển thị phần tử kế tiếp Sub showArray() 140 Dim i For i = 1 To n MsgBox ("a[" & Str(i) & "] = " & Str(a(i))) Next End Sub ‘Thủ tục xếp các phần tử của mảng a theo thứ tự tăng dần Sub sortArrayASC() Dim i, j As Integer For i = 1 To n - 1 For j = i + 1 To n If (a(i) > a(j)) Then Dim tg As Double tg = a(i) a(i) = a(j) a(j) = tg End If Next Next End Sub ‘Hàm trả về giá trị True/False tương ứng khi ‘x thuộc mảng hoặc không Function containInArray(x As Double) Dim i As Integer Dim kt As Boolean kt = False For i = 1 To n If x = a(i) Then kt = True 141 Exit For Next containInArray = kt End Function Bạn nên đọc kỹ các chú ý sau đây. Trong đoạn mã trên: + Biến mảng a và hằng n không khai báo phạm vi truy xuất là public hay private, thì mặc định là private. Điều này có nghĩa là, bạn không thể truy xuất đến a hay n từ bên ngoài module commonFunction. Ví dụ sau đây minh họa với bạn điều đó. Bạn có thể truy xuất a, n từ khắp nơi trong phạm vi module commonFunction nhƣ trong đoạn mã trên nhƣng sang module checkValidFunction bạn không thể truy xuất chúng nữa. Hình 7.5 Không nhìn thấy được a và n từ ngoài module commonFunction Bạn có thể sửa cho a, n thành phạm vi public bằng cách khai báo nhƣ sau: Public a(100) As Double Public Const n As Integer = 10 Nhƣ hình 7.6 dƣới đây, bạn đang ở module checkvalidFunction, bạn hoàn toàn có thể nhìn thấy (truy xuất) a và n trong module commonFunction vì bạn đã có khai báo phạm vi truy xuất Public cho chúng theo cú pháp trên 142 Hình 7.6 Truy xuất a, n từ ngoài module chứa chúng + Các hàm, thủ tục có phạm vi truy xuất mặc định là Public. Do vậy, bạn có thể gọi các hàm/thủ tục containInArray(), InitArray(), showArray(), sortArray() từ khắp nơi. Bạn quan sát lại các hình 7.5 và 7.6. Tuy nhiên, bạn cũng có thể hạn chế phạm vi truy xuất này chỉ trong nội module commonFunction bằng khai báo từ khóa Private trƣớc tên hàm mà bạn muốn. Ví dụ: Bạn khai báo cho 02 thủ tục InitArray() và showArray() có phạm vi truy xuất là private nhƣ sau: Private Sub InitArray() Dim i As Integer Randomize For i = 1 To n a(i) = Rnd * 100 Next End Sub Private Sub showArray() Dim i 143 For i = 1 To n MsgBox ("a[" & Str(i) & "] = " & Str(a(i))) Next End Sub Khi đó, bạn không thể truy xuất đến thủ tục InitArray() và showArray() từ module checkValidFunction nhƣ trong hình 7.7 dƣới đây. Tất nhiên, trong module commonFunction bạn vẫn gọi đƣợc 2 thủ tục trên một cách bình thƣờng. Hình 7.7: Không nhìn thấy InitArray() và showArray() từ module checkValidFunction + Để chạy thử các thủ tục trong đoạn mã trên, bạn có một vài cách, đơn giản nhất là bạn hãy chọn (bôi đen) thủ tục đó và ấn phím F5 nhƣ trong hình 7.8 dƣới đây. Khi đó, các mã lệnh trong phần đƣợc chọn sẽ đƣợc dịch và chạy. Bạn hãy quan sát kết quả chạy chƣơng trình của đoạn mã vừa chọn. 144 Hình 7.8 Chạy thử thủ tục InitArray() và showArray() + Đối với hàm (function) thì bạn có cách khác: gọi từ các thủ tục hoặc bạn chạy thử từ cửa sổ Immediate nhƣ trong hình 7.9 dƣới đây. Để hiển thị cửa sổ Immediate bạn chọn lệnh View/Immediate Window từ trên thanh menu hoặc ấn tổ hợp phím Ctrl + G. Trong cửa sổ Immediate bạn có thể quan sát đƣợc các giá trị của các biến (với điều kiện phạm vi truy xuất public) hoặc giá trị của hàm bằng cú pháp: ? tên biến / tên hàm. Trong hình 7.9, trƣớc tiên, thủ tục InitArray() đƣợc cho chạy và biến mảng a() có phạm vi truy xuất public. Sau đó, ở cửa sổ Immediate, để xem giá trị của a(1) đƣợc khởi tạo là bao nhiêu bạn cần viết: ?a(1) => kết quả a(1) = 22.3095118999481, để xem giá trị của hàm containInArray() bạn cần viết: ?containInArray(22.3095118999481) => kết quả trả về của hàm là True. Nếu gọi ?containInArray(1) => kết quả trả về của hàm là False 145 Hình 7.9 Chạy thử hàm trong cửa sổ Immediate Nhƣ đã trình bày, trong nhiều trƣờng hợp bạn không nên sử dụng biến, hằng toàn cục. Do đó, ví dụ trên sẽ đƣợc viết lại không sử dụng biến toàn cục nhƣ sau: Option Compare Database Option Base 1 Option Explicit Sub initArray(ByRef a() As Double, ByRef n As Integer) Dim i As Integer n = 10 Randomize For i = 1 To n a(i) = Rnd * 100 Next End Sub Sub showArray(a() As Double, n As Integer) 146 Dim i For i = 1 To n MsgBox ("a[" & Str(i) & "] = " & Str(a(i))) Next End Sub Sub sortArrayASC(ByRef a() As Double, n As Integer) Dim i, j As Integer For i = 1 To n - 1 For j = i + 1 To n If (a(i) > a(j)) Then Dim tg As Double tg = a(i) a(i) = a(j) a(j) = tg End If Next Next End Sub Function containInArray(x As Double, a() As Double, n As Integer) Dim i As Integer Dim kt As Boolean kt = False For i = 1 To n If x = a(i) Then kt = True Exit For End If Next containInArray = kt 147 End Function Sub callSub() Dim a(100) As Double Dim n As Integer InitArray a, n showArray a, n End Sub Trong đoạn mã trên: + Mục tiêu của các hàm/thủ tục không thay đổi + Thay đổi tham số đầu vào của các hàm/thủ tục. Bạn chú ý, cách truyền tham biến cho hàm/thủ tục bằng từ khóa ByRef để sau khi kết thúc hàm/thủ tục các giá trị của các biến truyền vào sau từ khóa ByRef giữ lại đƣợc các giá trị đã thiết lập trong nội dung của hàm/thủ tục + Thêm một thủ tục callSub() đƣợc viết để gọi các thủ tục/hàm đã định nghĩa. + Để chạy thử các hàm/thủ tục, bạn cần chọn thủ tục callSub() và ấn F5 nhƣ hình 7.10 dƣới đây. Hình 7.10 Truyền tham biến trong lời gọi thủ tục 148 Kết quả nhận đƣợc khi chạy callSub() là mảng a() các số double đƣợc khởi tạo trong thủ tục InitArray() đƣợc giữ nguyên giá trị khi ra khỏi thủ tục đó (hình 7.11) .. Hình 7.11 Kết quả khởi tạo mảng a() 2.2 Module lớp (Class Module) Có thể xem Class Module là loại module để định nghĩa lớp của ngƣời dùng. Lớp của ngƣời dùng cũng đƣợc đối xử tƣơng tự nhƣ các lớp sẵn có. Mỗi Class Module có hai hàm mặc định là Class_Initialize() và Class_Terminate(). Khi tạo đối tƣợng thuộc Class Module, các hàm này sẽ đƣợc tự động gọi tƣơng ứng khi đối tƣợng đƣợc thiết lập (bằng lệnh set) và hủy bỏ (tƣờng minh hoặc không tƣờng minh). 2.2.1 Tạo Class Module Để tạo Class Module, từ menu trong cửa sổ Database, chọn lệnh CREATE/Class Module (hình 7.12). Hình 7.12 Trong mục CREATE, chọn mục Class Module để tạo mới 1 Module lớp Hoặc cũng có thể tạo Class Module từ cửa sổ code nhƣ hình 7.13 dƣới đây bằng cách click chuột phải vào mục Class Modules rồi chọn lệnh Insert/Class Module 149 Hình 7.13 Tạo mới Class Module từ cửa sổ code Kết quả nhận đƣợc là một cửa sổ để viết mã lệnh cho Class Module nhƣ hình 7.14 dƣới đây Hình 7.14 Cửa sổ để viết mã lệnh cho Class Module Code Panel Project Explorer Panel 150 Trong phần code panel, trên cùng là 02 hộp danh sách. Hộp danh sách bên trái có 02 mục là General và Class. Hộp danh sách bên phải là tên các phƣơng thức của lớp và mục các khai báo (Declarations). Phần bên dƣới là cửa sổ để viết mã lệnh gồm khai báo các tùy chọn, định nghĩa các thuộc tính và định nghĩa các phương thức của lớp. 2.2.2 Khai báo các tùy chọn Phần khai báo gồm các khai báo nhƣ trong Standard Module. 2.2.3 Định nghĩa thuộc tính của lớp Khai báo các thuộc tính cho lớp sau phần khai báo các tùy chọn. Các thuộc tính này thuộc lớp đƣợc khai báo với cú pháp sau đây: Public|Private|Dim Tên_Thuộc_Tính as Kiểu_Dữ_Liệu Trong đó: Tên_Thuộc_Tính: đƣợc đặt theo quy tắc đặt tên: không có dấu cách, không có các ký tự đặc biệt, As: từ khóa Kiểu_dữ_liệu: là các kiểu dữ liệu của hệ thống hoặc các lớp đã đƣợc định nghĩa Public: chỉ định thuộc tính có phạm vi truy xuất toàn cục (khắp nơi trong CSDL). Tuy nhiên, trong lập trình hƣớng đối tƣợng không nên khai báo phạm vi public cho các thuộc tính Private: chỉ định thuộc tính có phạm vi truy xuất chỉ trong nội bộ lớp. Phạm vi này nên đƣợc khai báo cho các thuộc tính của lớp Dim: từ khóa này bản chất dùng để khai báo biến, trong Class Module (lớp) có thể sử dụng Dim trong trƣờng hợp này Dim và Private là nhƣ nhau. Tuy nhiên, không nên sử dụng Dim mà nên sử dụng Public và Private để cho mã lệnh đƣợc rõ ràng theo đúng phong cách lập trình hƣớng đối tƣợng và chƣơng trình biên dịch cũng dễ dàng thi hành hơn. Ví dụ: Ta khai báo một lớp doubleArray gồm các thuộc tính sau đây: Private a(100) As Double Public n As Integer Trong khai báo trên: 151 + Mảng a(100) để chứa các số double, có phạm vi truy xuất là private. Nghĩa là bạn chỉ có thể truy xuất đƣợc thuộc tính a() của lớp khi ở trong lớp đó. Ra khỏi lớp đó, bạn không còn nhìn thấy thuộc tính a() nữa. + n là một số nguyên, chỉ định số phần tử của mảng. Thuộc tính n có phạm vi truy xuất public. Nghĩa là sau này, từ bất kỳ đâu trong ứng dụng bạn có thể tạo một thể hiện của lớp doubleArray. Bạn đều có thể truy xuất vào thuộc tính n từ thể hiện đó. Đoạn mã sau đây minh họa các phạm vi public và private của ví dụ trên: Bên ngoài lớp doubleArray, đối với thể hiện a1 của lớp doubleArray, bạn chỉ thấy a1.n (vì n đƣợc định nghĩa phạm vi truy xuất public) trong khi bạn không thể thấy a1.a() (vì a() đƣợc định nghĩa phạm vi truy xuất private) Nhƣ vậy, có một vấn đề đặt ra ở đây là bạn luôn muốn định nghĩa các thuộc tính của lớp là private để cho an toàn, nhƣng một số trƣờng hợp bạn vẫn muốn cho phép truy xuất vào các thuộc tính private này từ bên ngoài lớp. Giải pháp là bạn định nghĩa các Property Get và Property Let. Property Get là để cho phép đọc đƣợc giá trị của thuộc tính, Property Let là để cho phép ghi giá trị vào thuộc tính. Tất nhiên bạn có thể dùng cả hai Property Get và Property Let để cho phép một thuộc tính vừa có thể đọc đƣợc vừa có thể ghi đƣợc. Trong ví dụ sau, chúng ta sẽ cho phép đọc và ghi vào thuộc tính n của lớp doubleArray từ bên ngoài lớp thông qua Property Get và Property Let của count trong khi đó n vẫn có phạm vi truy xuất là private. 152 Quan sát ví dụ trên, bạn thấy cần thêm đoạn mã: Public Property Get count() As Integer count = n End Property Là để cho phép đọc (lấy giá trị) đƣợc thuộc tính n của lớp doubleArray từ ngoài lớp. Đoạn mã: Public Property Let count(ByVal value As Integer) n = value End Property Là để cho phép ghi (thiết lập) giá trị cho thuộc tính n của lớp doubleArray từ ngoài lớp. Chú ý: bạn có thể đặt trùng tên “count” ở đây đƣợc chấp nhận. Tất nhiên, bạn có thể đặt 2 tên khác nhau nhƣng không nên làm thế sẽ rất lộn xộn và khó theo dõi code cũng nhƣ sử dụng thuộc tính của lớp sau này. Sau khi thêm vào phần định nghĩa thuộc tính của lớp 2 đoạn mã trên, bây giờ bạn có thể đọc/ghi vào thuộc tính n của lớp doubleArray nhƣ sau: 153 Trong đoạn mã trên, bạn thấy bạn thiết lập giá trị 2 cho thuộc tính n thông qua a1.count = 2. Bạn thử in ra giá trị của a1.n thông qua lệnh Debug.Print (a1.count). Kết quả bạn thấy số 2 đƣợc hiển thị trong cửa sổ Immediate 2.2.4 Định nghĩa các phƣơng thức của lớp Phƣơng thức là các hàm của đối tƣợng. Nếu so sánh với các thuộc tính thì bạn có thể thấy: thuộc tính là dữ liệu (Data) còn phƣơng thức là các hoạt động của đối tƣợng. Tất nhiên các hoạt động cần thiết phải có dữ liệu. Định nghĩa phƣơng thức nhƣ sau: Private|Public Sub|Function Tên_Phương_Thức [(Danh_sách_tham_số)] ‘các lệnh trong phương thức End Sub|Function Trong đó: Private | Public: là các khai báo phạm vi truy xuất của phƣơng thức. Ý nghĩa của chúng tƣơng tự nhƣ trong phần định nghĩa các thuộc tính ở trên. Đối với phƣơng thức, bạn nên để phạm vi truy xuất là public hơn là private. Bạn cũng có thể viết các phƣơng thức để đọc, ghi các giá trị cho các thuộc tính thay cho Property Get và Property Let. Tuy nhiên, nhƣ thế không hay và không đúng phong cách hƣớng đối tƣợng. Sub|Function: là các từ khóa quy định phƣơng thức không trả về giá trị (Sub) và có trả về giá trị (Function) End Sub|Function: từ khóa quy định kết thúc phần định nghĩa của phƣơng thức. Tên_Phương_Thức: đƣợc đặt theo quy tắc đặt tên: không có dấu cách, không có các ký tự đặc biệt. 154 Danh_sách_tham_số: là tùy chọn. Mỗi tham số đƣợc khai báo bao theo quy tắc: [ByVal|ByRef] Tên_Tham_Số As Kiểu_Dữ_Liệu ByVal | ByRef là kiểu tham chiếu của tham số. ByVal là tham chiếu theo kiểu tham trị. ByRef là tham chiếu theo kiểu tham biến. Mặc định là ByVal. Tên_Tham_Số: đƣợc đặt theo quy tắc đặt tên. As: từ khóa Kiểu_Dữ_Liệu: là các kiểu dữ liệu đã đƣợc định nghĩa hoặc các lớp, kiểu dữ liệu ngƣời dùng định nghĩa. Các tham số đƣợc phân tách nhau bởi dấu phẩy (,). Ví dụ sau đây sẽ định nghĩa phƣơng thức sortArrayASC() của lớp doubleArray. Phƣơng thức này không có tham số và sẽ thực hiện sắp xếp các phần tử trong thuộc tính mảng a theo thứ tự từ nhỏ đến lớn. sortArrayASC() không trả về giá trị nào. Sub sortArrayASC() Dim i, j As Integer For i = 1 To n - 1 For j = i + 1 To n If (a(i) > a(j)) Then Dim tg As Double tg = a(i) a(i) = a(j) a(j) = tg End If Next Next End Sub Phƣơng thức containInArray(ByVal x as Double) sau đây sẽ kiểm tra xem giá trị x có bằng với một phần tử nào của mảng a trong lớp doubleArray không? Nếu bằng, hàm sẽ trả về giá trị TRUE, ngƣợc lại hàm trả về giá trị FALSE. Public Function containInArray(ByVal x As Double) Dim i As Integer Dim kt As Boolean kt = False For i = 1 To n 155 If x = a(i) Then kt = True Exit For End If Next containInArray = kt End Function 2.2.5 Ví dụ Ta viết lại Standard Module trong ví dụ trƣớc theo cách tiếp cận của Class Module và đặt tên là doubleArray, gồm các mã lệnh sau đây: ‘Khai báo các tùy chọn Option Compare Database Option Base 1 Option Explicit ‘Định nghĩa các thuộc tính của lớp. Thuộc tính a() là một mảng ‘ các số double, thuộc tính n là số lượng các phần tử của mảng a() Private a(100) As Double Private n As Integer ‘Định nghĩa các phương thức của lớp ‘Phương thức để khởi tạo các giá trị cho các thuộc tính a() và n ‘num được truyền vào để khởi tạo cho n còn a() sẽ lấy các giá trị ngẫu nhiên Sub initArray(num As Integer) Dim i As Integer n = num Randomize For i = 1 To n a(i) = Rnd * 100 Next End Sub 156 ‘Phương thức hiển thị các phần tử trong a() của lớp doubleArray Sub showArray() Dim i For i = 1 To n MsgBox ("a[" & Str(i) & "] = " & Str(a(i))) Next End Sub ‘Phương thức sắp xếp các phần tử trong a() của lớp doubleArray Sub sortArrayASC() Dim i, j As Integer For i = 1 To n - 1 For j = i + 1 To n If (a(i) > a(j)) Then Dim tg As Double tg = a(i) a(i) = a(j) a(j) = tg End If Next Next End Sub ‘Hàm kiểm tra xem một giá trị double có thuộc về mảng a() của ‘lớp doubleArray không? Function containInArray(x As Double) Dim i As Integer Dim kt As Boolean kt = False For i = 1 To n If x = a(i) Then kt = True 157 Exit For End If Next containInArray = kt End Function Để dùng đƣợc lớp doubleArray trên bạn cần viết một đoạn mã khác tạo ra các đối tƣợng thuộc vào lớp doubleArray. Giả sử bạn tạo một Standard Module có tên là useArray có mã nhƣ hình 7.15. Sau khi viết xong các mã lệnh, bạn hãy chọn khối mã đó và chạy thử bằng cách ấn phím F5. Bạn hãy quan sát các kết quả. Hình 7.15 Viết mã thử nghiệm lớp doubleArray Chú ý: + Cần thiết phải dùng lệnh Set để thiết lập vùng nhớ cho các đối tƣợng trƣớc khi bạn sử dụng chúng + Để có thể gọi đƣợc các phƣơng thức hay truy xuất đến các thuộc tính của lớp từ một đối tƣợng bên ngoài thì các phƣơng thức hay thuộc tính đó phải có phạm vi truy xuất là public. Trong mã lệnh của hàm test() ở trên, sở dĩ bạn gọi a1.initArray(), a1.showArray(), a1.sortArray() đƣợc là vì phạm vi truy xuất của chúng là public (mặc định nếu bạn không chỉ ra khi định nghĩa), nhƣng bạn hoàn toàn không thể truy xuất a1.n hay a1.a(1) vì n và a() là các thuộc tính của lớp đƣợc khai báo phạm vi truy xuất private + Kết quả chạy test() là: Tạo ra một đối tƣợng (thể hiện) của lớp doubleArray. Gọi phƣơng thức InitArray() để khởi tạo giá trị cho thuộc tính mảng a() và n của a1. Gọi phƣơng thức showArray() để hiển thị các phẩn tử của a1.a(), lần lƣợt mỗi phần tử đƣợc 158 hiển thị trong một hộp msgBox. Gọi phƣơng thức sortArrayASC() để sắp xếp các phần tử của a1.a() theo trật tự tăng dần. 2.3 Module của form/report (Form/report Module) Module này thuộc phần Microsoft Access Class Object (MSCO) trong cửa sổ code VBA. Mặc định tất cả các form/report đều có module ẩn sau nó (thuộc tính HasModule đƣợc thiết lập mặc định là True). Có thể dùng từ khóa Me khi tham chiếu đến các module ẩn sau form/report. Trong Form/Report Module chúng ta có thể tạo các thủ tục theo sự kiện (thủ tục đƣợc tự động gọi khi có sự kiện tƣơng ứng với nó xảy ra) hoặc các thủ tục độc lập có thể đƣợc gọi từ các thủ tục khác trong cùng hoặc khác module. Tuy nhiên, trong phần này chúng ta chỉ nên viết các thủ tục hành xử theo các sự kiện của các điều khiển trên form/report. Để tạo đƣợc các MSCO này, bạn cần tạo form/report trƣớc. Ứng với mỗi form/report, trong trƣờng hợp bạn cần viết code cho các điều khiển trên form/report, hệ thống sẽ tự động tạo ra một module tƣơng ứng gắn với form/report đó. Tên của module đó sẽ đƣợc đặt tự động là Form_Tên_Form/Report_Tên_Report. Hình ảnh trên cho thấy, chúng ta có 02 form với tên lần lƣợt là: frmArrayCalculate và frmSearch, do vậy hệ thống sẽ tự sinh cho ta 02 module MACO là Form_frmArrayCalculate và Form_frmSearch. Tƣơng tự nhƣ vậy với 02 Report rptArrayCalculate và rptSearch. 159 Nhƣ vậy, module MACO sẽ dùng để viết (chứa) các hàm hành xử dựa trên các sự kiện của form/report và các sự kiện của các điều khiển nằm trên form/report. Mỗi form/report sẽ có một module MACO tƣơng ứng. Các thành phần của module MSCO cũng tƣơng tự nhƣ 02 loại module trên, bao gồm: Các khai báo tùy chọn, các khai báo biến toàn cục và định nghĩa các hàm hành xử cho các sự kiện của form/report và các sự kiện của các điều khiển trên form/report Phần khai báo các tùy chọn và các biến toàn cục hoàn toàn tƣơng tự nhƣ Standard Module. Ở đây, chúng ta chỉ quan tâm đến phần định nghĩa các hàm hành xử tƣơng ứng cho các sự kiện của form/report và các sự kiện của các điều khiển trên form/report. 2.3.1 Tạo MSCO module Trƣớc hết bạn cần tạo form/report trƣớc. Sau đó hãy tạo một vài các điều khiển nhƣ TextBox, Button trên form/report. Tiếp theo, để tạo MSCO module, từ thực đơn CREATE của MS Access, bạn cần chọn lệnh Visual Basic nhƣ trong hình dƣới đây. Kết quả bạn nhận đƣợc cửa sổ có dạng nhƣ hình sau. Tại cửa sổ này, bạn có thể khai báo các tùy chọn, khai báo các biến dùng chung cho các hàm phía sau và định nghĩa các hàm hành xử cho các sự kiện của form/report và các sự kiện của các điều khiển trên form/report 160 Hình ảnh trên cho thấy, cửa sổ bên phải là nơi bạn khai báo, định nghĩa các nội dung của MSCO module. Chú ý: + Bạn có thể viết các hàm tự do để xử lý các tình huống riêng ở đây. Các hàm tự do bạn phải gọi chúng một cách tƣờng minh thì chúng mới đƣợc thi hành. Ví dụ: bạn viết hàm sum(a as double, b as double) với dụng ý là hàm sum(a,b) sẽ trả về kết quả của a + b. Tuy nhiên, bạn không nên viết hàm sum(a as double, b as double) ở đây, bạn nên định nghĩa hàm này (và các hàm tƣơng tự) trong một Standard Module. Ở đây bạn vẫn sẽ gọi đƣợc các hàm đó bình thƣờng, cách tiếp cận nhƣ vậy sẽ giúp chƣơng trình của bạn sáng hơn và dễ kiểm soát hơn. + Các hàm hành xử theo sự kiện ở đây có danh sách tham số và tên phải đƣợc khớp với các quy tắc về định nghĩa hàm hành xử cho sự kiện tƣơng ứng của MS Access. 2.3.2 Định nghĩa các hàm hành xử cho các sự kiện Xuất phát từ quan điểm lập trình của VBA ở đây là chƣơng trình hƣớng đối tƣợng và hƣớng sự kiện. Theo đó, toàn bộ chƣơng trình sẽ vận hành theo các sự kiện của các điều khiển nằm trên form/report và các sự kiện của form/report khi có tác động của ngƣời dùng hoặc khi biên dịch. Để định nghĩa các hàm hành xử cho các sự kiện, bạn cần thiết kế form/report và các điều khiển trên form/report trƣớc. Bạn nhớ đặt tên cho các điều khiển sau này sẽ có các sự kiện trên nó. Ví dụ một nút lệnh trên form là bạn muốn sau này ngƣời dùng click vào nó (sự kiện OnClick), chƣơng trình sẽ thực hiện một đoạn mã nào đó đáp ứng sự kiện này. Nhƣ vậy, bạn nên đặt tên rõ ràng cho nút lệnh này. Sau khi thiết kế điều khiển trên form, bạn mở của sổ property của nó (kích chọn vào điều khiển, sau đó ấn phím F4 ) và lựa chọn mục Event là danh sách các sự kiện có thể có dành cho điều khiển đó. Trong phần danh sách các sự kiện của điều khiển, bạn 161 muốn viết hàm hành xử cho sự kiện nào thì chọn vào sự kiện đó, rồi click vào nút bên phải. Kết quả, bạn nhận đƣợc giao diện hình sau: Hình ảnh trên là các sự kiện của một TextBox tên là txtNum, sự kiện OnClick đƣợc chọn để viết hàm hành xử. Trong cửa sổ Choose Builder, bạn cần chọn mục Code Builder. Kết quả bạn nhận đƣợc giao diện nhƣ hình dƣới đây: Hình trên là cửa sổ bạn viết code cho sự kiện OnClick của TextBox txtNum. Chú ý: + Tên của hàm hành xử cho sự kiện đƣợc đặt theo quy tắc: TênĐiềuKhiển_TênSựKiện. Nếu là sự kiện của Form/Report thì sẽ là Form/Report_TênSựKiện (ví dụ Form_Load()). Làm trái quy tắc này hàm của bạn sẽ không đƣợc tự động gọi khi có sự kiện xảy ra với điều khiển. + Các tham số của hàm có thể có hoặc không tùy theo điểu khiển và sự kiện. Bạn cũng không thể thay đổi các kiểu dữ liệu của các tham số này. Tất nhiên, bạn có thể thay tên của chúng. Ví dụ: 162 Hàm hành xử cho sự kiện OnClick của nút lệnh không có tham số: Private Sub cmdSortArrayASC_Click() End Sub Hàm hành xử cho sự kiện KeyDown của TextBox có 2 tham số Private Sub txtNum_KeyDown(KeyCode As Integer, Shift As Integer) End Sub 2.3.3 Nhận xét MSCO module có các đặc điểm sau đây: + Toàn bộ mã cần để tự động hóa form/report thƣờng trú ngay trong form/report đó, không cần phải nhớ tên của đối tƣợng module riêng biệt liên quan đến form/report. + Các thủ tục hƣớng sự kiện trong module đƣợc tự động thi hành dựa trên các sự kiện xảy ra với form/report và các điều khiển trên form/report, không cần phải xác định lời gọi tƣờng minh cho chúng. + MS Access tải Standard Module (đƣợc tham chiếu bởi đối tƣợng module) vào bộ nhớ khi chỉ cần có một tham chiếu đến thủ tục hoặc biến trong module và cho phép nó thƣờng trú trong bộ nhớ chừng nào CSDL còn mở. Trong khi đó, Form/Report Module chỉ đƣợc MS Access tải vào bộ nhớ khi form/report đƣợc mở. Khi form/report bị đóng lại thì Form/Report Module tƣơng ứng với nó cũng bị giải phóng khỏi bộ nhớ. + Khi xuất đi một form/report, toàn bộ mã lệnh trong Form/Report Module tƣơng ứng với nó cũng đƣợc kết xuất theo. + Form/report module có module lớn đi kèm sẽ đƣợc mở ra, đóng lại chậm hơn so với form/report có module nhỏ hoặc không có module đi kèm. 2.3.4 Ví dụ Mục tiêu: Ví dụ sau đây sẽ minh họa một chƣơng trình hoàn thiện tƣơng tác với ngƣời dùng cuối thông qua form giao diện. Nội dung: Chƣơng trình gồm hai chức năng: + Khởi tạo một mảng ngẫu nhiên các số thực với số lƣợng các phần tử do ngƣời dùng nhập + Sắp xếp các phần tử của mảng theo trật tự tăng dần Các bƣớc thực hiện: Gồm 03 bƣớc sau 163 + Thiết kế giao diện + Cài đặt 1 lớp doubleArray hỗ trợ xử lý các thao tác cần thiết với mảng số thực + Cài đặt các hàm hành xử cho các sự kiện ứng với giao diện trên form, sử dụng lớp doubleArray. Chạy thử chƣơng trình Nhƣ vậy, ở đây chúng ta cần quan tâm đến các bƣơc thực hiện, các phần khác đã rõ ràng. Thiết kế giao diện Đề nghị bạn hãy thiết kế một form đặt tên là frmArrayCalculate có giao diện dạng nhƣ sau: Giao diện trên bao gồm: + 03 Lable, bạn đặt tên tùy ý. Thuộc tính Text của mỗi Lable bạn thiết lập nhƣ trên hình + 03 TextBox, bạn đặt tên lần lƣợt là txtNum, txtResult, txtSortedArray theo thứ tự từ trên xuống dƣới. + 02 nút lệnh, bạn đặt tên lần lƣợt là cmdInitArray và cmdSortArrayASC theo thứ tự từ trái sang phải. Cài đặt lớp doubleArray Bạn hãy định nghĩa một Class Module, đặt tên là doubleArray nhƣ trong đoạn mã dƣới đây. Các thuộc tính và hàm của lớp đã đƣợc mô tả chi tiết trong ví dụ của phần Class 164 Module. Ở đây, chúng ta định nghĩa thêm một hàm toString() trả về một chuỗi là các phần tử của mảng đƣợc phân tách nhau bởi dấu cách (space). Option Compare Database Option Base 1 Option Explicit Private a(350) As Double Private n As Integer Public Property Get count() As Integer count = n End Property Public Property Let count(ByVal value As Integer) n = value End Property Sub initArray() Dim i As Integer Randomize For i = 1 To n a(i) = Rnd * 100 a(i) = Round(a(i), 2) Next End Sub Function toString() Dim st As String st = "" Dim i For i = 1 To n If (i < n) Then st = st + CStr(a(i)) + " " Else st = st + CStr(a(i)) End If Next 165 toString = st End Function Sub showArray() Dim i For i = 1 To n MsgBox ("a[" & Str(i) & "] = " & Str(a(i))) Next End Sub Sub sortArrayASC() Dim i, j As Integer For i = 1 To n - 1 For j = i + 1 To n If (a(i) > a(j)) Then Dim tg As Double tg = a(i) a(i) = a(j) a(j) = tg End If Next Next End Sub Cài đặt các hàm cho các sự kiện Ở đây, ta cài đặt 02 sự kiện OnClick của 02 nút lệnh nhƣ sau. Toàn bộ đoạn mã lệnh sau đƣợc đặt trong một MSCO Module có tên là Form_frmArrayCalcute Option Compare Database Option Explicit 'Khai bao mot doi tuong doubleArray su dung chung trong toan chuong trinh Dim DA As doubleArray Private Sub cmdInitArray_Click() Dim stResult As String stResult = "" Dim stNum As String 166 Me.txtResult.SetFocus Me.txtResult = "" Me.txtNum.SetFocus stNum = Me.txtNum.Text 'Kiem tra xem co nhap so cac phan tu cua mang hay khong? If (stNum = "") Then MsgBox "Ban phai nhap so phan tu cua mang!" Exit Sub End If 'Kiem tra xem co nhap vao mot so hay khong? If (IsNumeric(stNum) = False) Then MsgBox "So phan tu cua mang phai la mot so nguyen <= 350!" Exit Sub End If 'Kiem tra xem co la mot so thap phan hay khong? Dim pos1, pos2 As Integer pos1 = -1 pos2 = -1 pos1 = InStr(1, stNum, ",", vbTextCompare) pos2 = InStr(1, stNum, ".", vbTextCompare) If (pos1 > 0) Or (pos2 > 0) Then MsgBox "So phan tu cua mang phai la mot so nguyen <= 350!" Exit Sub End If Dim num As Integer num = CInt(stNum) If (num = Null) Or (num > 350) Then MsgBox "So phan tu cua mang phai la mot so nguyen <= 350!" Exit Sub End If Set DA = New doubleArray DA.count = num 167 DA.initArray Me.txtResult.SetFocus Me.txtResult.Text = DA.toString End Sub Private Sub cmdSortArrayASC_Click() Me.txtSortedArray.SetFocus Me.txtSortedArray.Text = "" If (DA Is Nothing) Then MsgBox "Ban can khoi tao ngau nhien mang truoc!" Exit Sub End If If (IsEmpty(DA) = True) Then MsgBox "Ban can khoi tao ngau nhien mang truoc!" Exit Sub End If If (IsNull(DA) = True) Then MsgBox "Ban can khoi tao ngau nhien mang truoc!" Exit Sub End If DA.sortArrayASC Me.txtSortedArray.SetFocus Me.txtSortedArray.Text = DA.toString End Sub Private Sub Form_Load() Set DA = Nothing 168 End Sub Kết quả Bạn hãy nhập vào ô “Số phần tử của mảng” (ví dụ: 10) sau đó click vào nút lệnh “Khởi tạo ngẫu nhiên mảng”. Kết quả khởi tạo sẽ đƣợc hiển thị trong ô “Kết quả khởi tạo ngẫu nhiên mảng”. Bạn tiếp tục click vào nút lệnh “Sắp xếp mảng ASC”. Kết quả sắp xếp mảng đƣợc hiển thị trong ô “Kết quả sắp xếp mảng”. Bạn hãy tự thử nghiệm các trƣờng hợp khác của chƣơng trình. Đây là một trƣờng hợp chạy chƣơng trình. 3. Kiểu dữ liệu, hằng và biến 3.1 Kiểu dữ liệu Một kiểu dữ liệu là tập hợp các giá trị mà một biến thuộc về kiểu đó có thể nhận đƣợc. Kiểu dữ liệu đƣợc đặc trƣng bởi hai yếu tố:  Tập các giá trị thuộc về nó  Tập hợp các phép toán (toán tử) có thể đƣợc thực hiện trên nó VBA cung cấp các kiểu dữ liệu sau đây: Kiểu dữ liệu Kích thƣớc (byte) Ký tự phân loại Có thể chứa Byte 1 Không có Số nguyên từ 0 đến 255 Integer 2 % Số nguyên từ -215 đến 215 – 1 Long 4 & Số nguyên từ 231 đến 231 – 1 169 Single 4 ! Số dấu chấm động từ -3.4*1038 đến 3.4*1038 Double 8 # Số dấu chấm động từ -1.79*10308 đến 1.79*10 308 Currency 8 @ Số nguyên đƣợc chia tỷ lệ với 4 số lẻ phần thập phân từ -922,337,203,685,477.5808 đến 922,337,203,685,477.5807 String 10 + 2 byte cho mỗi ký tự $ Chuỗi ký tự dài tối đa 2 tỷ byte, chuỗi có độ dài cố định có thể dài tối đa 65400 ký tự Boolean 2 Không có True hoặc False Date 8 Không có Giá trị ngày giờ từ 1/1/100 đến 31/12/9999 Object 4 Không có Bất kỳ tham chiếu đối tƣợng nào Variant 16 đến khoảng 2 tỷ byte Không có Bất kỳ loại dữ liệu nào Có thể ngầm định định nghĩa kiểu dữ liệu cho biến bằng cách nối thêm một ký tự phân loại dữ liệu vào sau tên biến trong lần đầu tiên sử dụng biến. Ngoài những kiểu dữ liệu VBA đã xây dựng sẵn, VBA còn cung cấp cho ngƣời dùng cú pháp để họ tự xây dựng kiểu dữ liệu của riêng mình, phù hợp với yêu cầu của ứng dụng. Đó là kiểu dữ liệu do ngƣời dùng định nghĩa (Kiểu cấu trúc). Cú pháp khai báo: [Private | Public] Type varname elementname [([subscripts])] As type [elementname [([subscripts])] As type] . . . End Type Trong đó: Public | Private: từ khóa xác định phạm vi truy xuất của kiểu dữ liệu tƣơng ứng cho tất cả các thủ tục trong tất cả các module thuộc tất cả các dự án | chỉ trong phạm vi module chứa nó. varname: tên kiểu dữ liệu đƣợc đặt theo quy tắc đặt tên của VB elementname: tên các thành phần của kiểu dữ liệu 170 subscript: đặt chỉ số cho mảng nếu thành phần dữ liệu tƣơng ứng là mảng type: là một trong những kiểu của VBA hoặc kiểu đã đƣợc định nghĩa trƣớc. Ví dụ: Type StateData CityCode (1 To 100) As Integer 'Thành phần dữ liệu là một mảng 100 phần tử có chỉ số từ 1 đến 100. County As String * 30 ‘Thành phần dữ liệu là một xâu tối đa là 30 ký tự End Type ‘Khai báo một biến mảng 100 phần tử có chỉ số từ 1 đến 100, mỗi phần tử là một cấu trúc kiểu StateData. Dim Washington(1 To 100) As StateData Kiểu cấu trúc chỉ đƣợc định nghĩa trong phần khai báo của các loại module, không đƣợc định nghĩa trong các hàm, thủ tục con. Trong Standard Module và Class Module, phạm vi truy xuất của kiểu cấu trúc mặc định là public. Cú pháp truy xuất các thành phần của kiểu cấu trúc: varname.elementname Trong đó: varname: là tên biến kiểu cấu trúc elementname: tên thành phần của kiểu cấu trúc. Ví dụ: Với cấu trúc và khai báo biến của ví dụ trƣớc, ta có các truy xuất hợp lệ sau: Washington(1).CityCode(1) = 1 Washington(1).County = “100” 3.2 Hằng Hằng là đại lƣợng có giá trị không đổi trong suốt thời gian chƣơng trình thi hành. Cú pháp khai báo: [Public | Private] Const constname [As type] = expression Trong đó: Public | Private: từ khóa xác định phạm vi truy xuất của hằng tƣơng ứng cho tất cả các thủ tục trong tất cả các module thuộc tất cả các dự án | chỉ trong phạm vi module chứa nó. Các từ khóa này chỉ đƣợc viết trong phần khai báo của module, không đƣợc viết trong phạm vi thủ tục, hàm (mặc dù hằng có thể khai báo trong thủ tục, hàm). Private là phạm vi truy xuất mặc định 171 Const: từ khóa quy định để khai báo hằng constname: tên hằng type: là một trong các kiểu dữ liệu Byte, Boolean, Integer, Long, Currency, Single, Double, Decimal, Date, String, hoặc Variant. Nếu không có kiểu dữ liệu VBA tự xác định kiểu phù hợp nhất cho hằng dựa vào giá trị của biểu thức. Expression: biểu thức, là một biểu thức tính toán đƣợc giá trị cụ thể tại thời điểm khai báo. Ví dụ: [1] Const pi = 3.14 [2] Const r = 5 [3] Const s = pi*r*r Trong ví dụ trên, dòng [1] và [2] tƣơng ứng là khai báo hằng pi nhận giá trị 3.14 và r nhận giá trị 5. Dòng [3] là hằng s đƣợc tính theo biểu thức của pi và r. Trong trƣờng hợp này pi và r đã đƣợc khai báo và xác định giá trị trƣớc nên dòng [3] là một khai báo đúng. Ví dụ: Public Const pi As Double = 3.14159 Hằng có thể đƣợc khai báo ở trong phạm vi toàn module (khai báo trong phần khai báo của module) hoặc trong thủ tục, hàm con của module. Hằng đƣợc khai báo ở đâu thì có tầm vực hoạt động ở đó. 3.3 Biến Biến là đại lƣợng có thể thay đổi giá trị, biến thực chất là một vùng nhớ gồm các ô liên tiếp (số lƣợng ô nhớ tùy thuộc vào kiểu dữ liệu của biến). VBA cung cấp một số loại biến: biến thông thƣờng khai báo bởi từ khóa Dim, biến công cộng khai báo bởi từ khóa public, biến tĩnh khai báo bởi từ khóa static  Khai báo biến thông thƣờng dùng từ khóa Dim Cú pháp khai báo: Dim [WithEvents] varname[([subscripts])] [As [New] type] [, [WithEvents] varname[([subscripts])] [As [New] type]] Trong đó: WithEvents: biểu thị một biến đối tƣợng trong phạm vi một module lớp (Class Module) hồi đáp các sự kiện do đối tƣợng ActiveX kích hoạt. WithEvents chỉ hợp lệ trong Class 172 Module. Không tạo biến mảng với WithEvents, không sử dụng từ khóa New cùng với WithEvents. varname: tên biến đƣợc đặt theo quy tắc đặt tên của VBA subscripts: khai báo chiều của biến mảng nếu biến là một biến mảng. Tối đa VBA cho phép mảng có tới 60 chiều. New: tạo đối tƣợng một cách không tƣờng minh, về sau không cần dùng lệnh set để gán tham chiếu tới đối tƣợng nữa. type: là một trong các kiểu dữ liệu của VBA hoặc kiểu cấu trúc ngƣời dùng đã định nghĩa. Nếu kiểu dữ liệu không đƣợc chỉ ra khi khai báo, VBA sẽ tự gán kiểu Variant cho các biến đó. Khi biến đƣợc gán giá trị cụ thể, thì kiểu tƣơng ứng với giá trị đó sẽ đƣợc xác định. Biến có thể đƣợc khai báo ở cấp module (khai báo trong phần khai báo biến dùng chung của module), khi đó biến là công cộng có phạm vi hoạt động trong tất cả các thủ tục, hàm trong module. Biến có thể đƣợc khai báo trong thủ tục, hàm để có phạm vi cục bộ, khi đó biến có phạm vi hoạt động là cục bộ trong thủ tục, hàm chứa nó. Chú ý: có thể khai báo các biến có cùng tên ở các mức độ khác nhau, khi đó việc truy xuất đến các biến cùng tên trong các thủ tục, hàm sẽ truy xuất đến biến cục bộ. Ví dụ: Dim i as Integer „Khai báo biến i kiểu Integer Dim d(1 To 10, 1 To 100) „Khai báo mảng 2 chiều d, mỗi phần tử của mảng có kiểu là Variant Dim rec as New ADODB.Recordset „Khai báo và tạo một đối tượng Recordset. Có thể khai báo mảng mà không xác định số chiều, kích thƣớc ngay, khi đó số chiều, kích thƣớc của mảng cần xác định lại bằng câu lệnh ReDim tại thời điểm chạy: Cú pháp của câu lệnh nhƣ sau: ReDim [Preserve] varname(subscripts) [As type] [, varname(subscripts) [As type]] Trong đó: Preserve: yêu cầu VBA không khởi tạo lại các giá trị hiện có trong mảng varname: tên biến subscripts: xác định số chiều của mảng, tối đa là 60. 173 type: kiểu dữ liệu của VBA hoặc kiểu cấu trúc đã đƣợc ngƣời dùng định nghĩa. Ví dụ: Dim b() As Integer ReDim b(1 To 100) As Integer b(1) = 6 Trong đoạn mã trên, trƣớc khi gán giá trị cho b(1) ta cần phải định nghĩa lại số chiều và kích thƣớc cho mảng, nếu không một lỗi sẽ xảy ra. Không dùng ReDim để định nghĩa lại kiểu của các phần tử mảng ngay cả khi kiểu của chúng không đƣợc chỉ rõ trong lệnh Dim.  Khai báo biến công cộng dùng từ khóa public Có thể khai báo biến dùng chung cho tất cả các module thuộc tất cả các dự án theo cú pháp: Public [WithEvents] varname[([subscripts])] [As [New] type][,[WithEvents] varname[([subscripts])] [As [New] type]] Các thành phần trong cú pháp trên tƣơng tự các thành phần trong cú pháp khai báo biến với Dim. Biến đƣợc khai báo theo cú pháp trên sẽ đƣợc truy xuất ở khắp nơi trong cùng ứng dụng với điều kiện không có tùy chọn Option Private Module. Không sử dụng đƣợc từ khóa public để khai báo các biến string có độ dài cố định trong Class Module hoặc khai báo các biến cục bộ trong thủ tục, hàm của module. Để truy xuất đến các biến public trong module khác phải sử dụng cú pháp: modulename.varname Trong đó: Modulename: tên module chứa biến public Varname: tên biến đƣợc khai báo là public trong module Ví dụ: Trong phần khai báo của module3 ta có khai báo sau: Public abc As Double Trong thủ tục sub1() của module1 muốn truy xuất đến biến abc trên ta sử dụng cú pháp: Sub sub1() 174 Module3.abc = 100 MsgBox Module3.abc End Sub  Khai báo biến tĩnh Biến tĩnh là biến không bị khởi động lại khi thủ tục, hàm chứa nó đƣợc gọi ở những lần sau. Biến tĩnh chỉ đƣợc phép khai báo ở phạm vi trong thủ tục, hàm Cú pháp: Static varname[([subscripts])] [As [New] type] [, varname[([subscripts])] [As [New] type]] . . . Trong cú pháp trên các tham số của lệnh tƣơng tự các tham số trong cú pháp Dim. Ví dụ: Sub sub1() Static a As Integer a = a + 1 MsgBox "a = " & a End Sub Trong ví dụ trên, nếu bạn gọi sub1() lần đầu tiên, hộp MsgBox sẽ thông báo a = 1 (VBA khởi tạo các giá trị cho các biến kiểu số khi khai báo là 0). Ta gọi sub1() lần thứ 2, hộp MsgBox sẽ thông báo a = 2 (giá trị của biến tĩnh a không bị khởi tạo lại khi ta gọi ở những lần tiếp theo). Tiếp tục ta gọi sub1() lần thứ 3, hộp MsgBox sẽ thông báo a = 3, Giá trị của biến tĩnh chỉ bị khởi tạo khi module bị reset hoặc restart. Biến tĩnh trong Class Module đƣợc bảo toàn giá trị đối với mỗi thể hiện của lớp và chỉ bị khởi tạo lại giá trị khi thể hiện của lớp bị hủy bỏ. Biến tĩnh trong form/report mudule đƣợc bảo toàn giá trị cho đến khi form/report đóng.  Gán giá trị cho biến Cú pháp gán giá trị cho biến: varname = expression Trong đó: varname: là tên biến 175 expression: biểu thức, nếu giá trị của biểu thức không phù hợp với kiểu của biến VBA sẽ tự ép kiểu cho phù hợp (đối với những trƣờng hợp có thể ép kiểu không tƣờng minh). Ví dụ: Dim i as Integer i = 9.76 „Khi đó i thực sự nhận giá trị là 10 Đối với các biến đối tƣợng (biến đối tƣợng là biến dùng để tham chiếu đến đối tƣợng không phải là bản sao của đối tƣợng), để tham chiếu tới một đối tƣợng thông qua biến thì biến đó phải đƣợc thiết lập tham chiếu đến đối tƣợng theo cú pháp: set varname = object Ví dụ: Dim db As Database Dim rec As Recordset Set db = CurrentDb() Set rec = db.OpenRecordset("SELECT * FROM table1") Trong ví dụ trên db và rec là hai biến đối tƣợng. 4. Các cấu trúc điều khiển 4.1 Cấu trúc tuần tự Cấu trúc tuần tự quy định trình tự thực hiện của các câu lệnh, lệnh nào đƣợc thực hiện trƣớc, lệnh nào đƣợc thực hiện sau. Trong VBA, cấu trúc tuần tự đƣợc thể hiện bằng mã xuống dòng (Hex: 0D0A) đƣợc sinh ra khi ta nhấn phím enter trong khi soạn thảo mã lệnh và dấu hai chấm (:). Trong VBA một dòng văn bản có thể có nhiều câu lệnh, khi đó các câu lệnh đƣợc phân cách nhau bởi dấu hai chấm (:). Một câu lệnh trong VBA cũng có thể đƣợc viết trên nhiều dòng, khi đó để nối câu lệnh đƣợc viết trên các dòng khác nhau ta cần dùng ký tự gạch dƣới (_). Ví dụ: Một dòng văn bản có thể viết nhiều câu lệnh, khi đó giữa các câu lệnh đƣợc phân tách nhau bởi dấu “:” Sub tinhtong(a, b, c) Dim tong As Double : tong = a + b + c MsgBox "Tong = " & tong End Sub 176 Ví dụ: Một câu lệnh đƣợc viết trên nhiều dòng, khi đó các dòng đƣợc nối với nhau bởi dấu “_” Sub tinhtong(a, b, c) Dim tong As Double tong = a + b + _ c MsgBox "Tong = " & tong End Sub 4.2 Cấu trúc rẽ nhánh Cú pháp: If condition Then [statements] [Else statements] Hoặc: If condition Then [statements] [ElseIf condition Then [statements]] [Else [statements]] End If Trong đó: condition: là biểu thức chuỗi hoặc số mà VBA có thể đánh giá là True (khác 0) hoặc False (0 hoặc Null). Condition cũng có thể là phép thử TypeOf objectname Is objecttype. statements: một hoặc nhiều câu lệnh. Nếu condition = True thì khối lệnh sau Then đƣợc thực hiện. Nếu condition = False thì khối lệnh sau Else (nếu có) đƣợc thực hiện. Sau đó VBA thi hành câu lệnh tiếp theo sau if. Ví dụ: Sub ptb1(a, b) If (a = 0) Then If (b = 0) Then 177 MsgBox "Phuong trinh vo so nghiem!" Else MsgBox "Phuong trinh vo nghiem!" End If Else MsgBox "Phuong trinh co 1 nghiem la: " & Format_ (-b / a, "0.00") End If End Sub Thủ tục ptb1(a, b) là để giải phƣơng trình bậc 1. Có thể chạy thủ tục này tại cửa sổ immediate (View/Immediate window) bằng lệnh call ptb1(3, -1) để kiểm tra lời giải. Chú ý: có thể đặt nhiều hơn một câu lệnh sau then trên cùng một dòng với If Then. Khi đó các câu lệnh đƣợc phân cách nhau bởi dấu hai chấm (:). Ví dụ: đổi giá trị của hai biến a và b. Dim a, b, tg a = 5 b = 1 If a > b Then tg = a: a = b: b = tg Sau khi các lệnh trên đƣợc thực hiện, ta có a = 1 và b = 5 Cú pháp if then [else ] end if luôn cho ta rẽ vào một trong hai nhánh dựa vào biểu thức điều kiện. Trong trƣờng hợp muốn rẽ vào một trong nhiều nhánh ta cần sử dụng cấu trúc if then [else ] end if lồng nhau. Tuy nhiên, việc lồng nhau của cấu trúc này làm cho chƣơng trình không minh bạch, dễ bị nhầm lẫn. VBA cung cấp một cú pháp rẽ nhánh khác khắc phục nhƣợc điểm này, đó là cú pháp select case end select Cú pháp: Select Case testexpression [Case expressionlist [statements]] [Case expressionlist [statements]] ... [Case Else [statements]] End Select 178 Trong đó: testexpression: là biểu thức chuỗi hoặc số mà VBA có thể đánh giá là True (khác 0) hoặc False (0 hoặc Null) expressionlist: danh sách các biểu thức, phải có nếu có mệnh đề case. Danh sách các biểu thức có thể là: + Một biểu thức đơn lẻ + Biểu thức To biểu thức (To là từ khóa xác định một miền các giá trị, khi đó các giá trị phải đƣợc sắp theo thứ tự tăng). Ví dụ: 1 To 10: là các giá trị trong đoạn [1, 10] + Is toán tử so sánh biểu thức (Is là từ khóa, kết hợp với toán tử so sánh (trừ Is và Like) để xác định một miền các giá trị). Ví dụ: Is > 100: là các giá trị số phải lớn hơn 100 Mỗi biểu thức trong danh sách các biểu thức đƣợc phân cách nhau bởi dấu phẩy (,). statements: 1 hoặc nhiều lệnh Nếu testexpression bằng (hoặc thuộc) bất kỳ expressionlist trong mệnh đề case nào thì khối lệnh sau mệnh đề case đó đƣợc thực hiện. Trƣờng hợp ngƣợc lại khối lệnh trong mệnh đề else (nếu có) đƣợc thực hiện, sau đó VBA sẽ thi hành câu lệnh tiếp theo sau select case. Nếu có nhiều expressionlist trong các mệnh đề case thỏa mãn testexpression thì chỉ có khối lệnh trong mệnh đề case đầu tiên đƣợc thực hiện. Ví dụ: hàm tính số ngày của một tháng trong năm Function songay(th, nam) Dim sn Select Case th Case 1, 3, 5, 7, 8, 10, 12 sn = 31 Case 4, 6, 9, 11 sn = 30 Case Else If (nam Mod 4 = 0) Then sn = 29 Else sn = 28 End Select 179 songay = sn End Function Có thể chạy hàm này ngay trong cửa sổ immediate bằng lệnh: ?songay(2, 2000). Kết quả bạn nhận đƣợc sẽ là 29. 4.3 Cấu trúc lặp Có hai cấu trúc lặp: lặp với số lần xác định và lặp dựa trên biểu thức điều kiện  Lặp với số lần xác định Cú pháp 1: For counter = start To end [Step step] [statements] [Exit For] [statements] Next [counter] Trong đó: counter: biến đếm, kiểu số start: giá trị khởi tạo của biến đếm end: giá trị cuối cùng mà biến đếm có thể nhận step: sau mỗi lần lặp biến đếm đƣợc cộng thêm vào giá trị trong step. Nếu không đƣợc thiết lập, mặc định step = 1. statements: 1 hoặc nhiều lệnh Khi counter còn thuộc phạm vi giữa start và end thì khối lệnh statements còn đƣợc thực hiện. Sau mỗi bƣớc của vòng lặp, biến đếm sẽ đƣợc tăng (giảm) step giá trị. Chú ý: Nếu bỏ qua counter trong mệnh đề Next, vòng lặp vẫn đƣợc thực thi bình thƣờng. Tuy nhiên, nếu có nhiều for lồng nhau, counter trong mệnh đề Next không tƣơng ứng với counter trong for thì một lỗi sẽ xảy ra khi biên dịch. Ví dụ: hàm tính n! Function gt(n) Dim t As Double t = 1 Dim i As Long 180 For i = 1 To n Step 1 t = t * i Next gt = t End Function Cú pháp 2: For Each element In group [statements] [Exit For] [statements] Next [element] Trong đó: element: biến đƣợc sử dụng để lặp thông qua các phần tử của mảng hay tập hợp. Đối với tập hợp, biến có thể là biến Variant hoặc biến đối tƣợng. Đối với mảng, biến chỉ có thể là biến Variant. group: tên tập hợp hoặc mảng statements: một hoặc nhiều lệnh Khối lệnh statements trong for đƣợc thực hiện khi có ít nhất 1 element thuộc group. Khối lệnh này đƣợc lặp lại cho đến khi không còn element nào thuộc group. Lần đầu vào vòng lặp, element đƣợc gán bằng phần tử đầu tiên của tập hợp, mảng. Sau mỗi lần lặp, element lại đƣợc gán bằng phần tử tiếp theo của tập hợp, mảng. Cú pháp này rất hữu ích khi ta không biết số lƣợng các phần tử của mảng, tập hợp. Ví dụ: Dim a(10) As Integer Randomize (100) Dim i As Integer For i = 0 To 9 a(i) = Rnd * 100 Next For Each e In a MsgBox e Next 181 Đoạn mã trên tạo một mảng a gồm 10 phần tử và điền các giá trị ngẫu nhiên nhỏ hơn 100 vào mảng, sau đó in ra mỗi phần tử của mảng  Lặp dựa trên biểu thức điều kiện Cú pháp 1: Do [{While | Until} condition] [statements] [Exit Do] [statements] Loop Hoặc: Do [statements] [Exit Do] [statements] Loop [{While | Until} condition] Trong đó: Condition: biểu thức số hoặc chuỗi mà VBA có thể đánh giá đƣợc là True (khác 0) hoặc False (0 hoặc Null) Statements: một hoặc nhiều lệnh Khối lệnh trong Do đƣợc thực hiện trong khi điều kiện (condition) còn đúng (True) hoặc cho đến khi đúng. Ví dụ: Function ngT(n As Long) if (n = 1) Or (n = 2) Then ngT = True Dim i, t As Long Dim found As Boolean i = 2: t = Sqr(n): found = False Do While (i <= t) And (Not found) If n Mod i = 0 Then found = True i = i + 1 Loop If found Then ngT = False Else ngT = True 182 End Function Hàm kiểm tra ngT(), kiểm tra một số nguyên n có phải là nguyên tố hay không. Nếu n là nguyên tố, hàm trả về giá trị True, ngƣợc lại, hàm trả về giá trị False. Cú pháp 2: While condition [statements] Wend Trong đó 2 tham số condition và statements nhƣ trong cú pháp Do. Khối lệnh trong while còn đƣợc thực hiện khi điều kiện còn đúng. Ví dụ: viết lại hàm kiểm tra số nguyên tố ở ví dụ trƣớc Function ngT(n As Long) If (n = 1) Or (n = 2) Then ngT = True Dim i, t As Long Dim found As Boolean i = 2: t = Sqr(n): found = False While (i <= t) And (Not found) If n Mod i = 0 Then found = True i = i + 1 Wend If found Then ngT = False Else ngT = True End Function 5. Hàm và thủ tục VBA cung cấp hai loại chƣơng trình con: hàm (function) và thủ tục (sub). Mỗi dạng chƣơng trình con đều có thể có tham số. Sự khác nhau giữa hàm và thủ tục chỉ là hàm có thể trả về một giá trị còn thủ tục con thì không. Vì vậy, trƣớc khi kết thúc định nghĩa hàm thƣờng có câu lệnh: tên hàm = biểu thức để trả giá trị về cho lời gọi hàm. 5.1 Hàm Khai báo: [Public | Private | Friend] [Static] Function name [(arglist)] [As type] [statements] [name = expression] [Exit Function] 183 [statements] [name = expression] End Function Trong đó: Public: quy định hàm có thể đƣợc truy xuất từ khắp nơi trong tất cả các module thuộc tất cả các dự án. Nếu hàm đƣợc viết trong module có khai báo tùy chọn On Private Module thì không thể đƣợc truy xuất bên ngoài dự án chứa nó. Private: quy định phạm vi truy xuất của hàm là chỉ trong module chứa nó Friend: chỉ đƣợc sử dụng trong Class Module. Quy định hàm đƣợc truy xuất khắp nơi trong dự án. Static: để bảo toàn tất cả giá trị của các biến đƣợc khai báo trong phạm vi hàm tƣờng minh hoặc không tƣờng minh, với điều kiện module chứa hàm còn mở. Điều này tƣơng đƣơng với việc dùng từ khóa static khai báo toàn bộ các biến có trong hàm. name: tên hàm arglist: danh sách các tham số, đƣợc phân cách nhau bởi dấu phẩy (,). Arglist có dạng: [Optional] [ByVal | ByRef] [ParamArray] varname[( )] [As type] [= defaultvalue] Trong đó: Optional: chỉ định rằng tham số là tùy chọn. Nếu khai báo một tham số là Optional thì tất cả các tham số khác thêm sau tham số đó cũng phải đƣợc khai báo là Optional. ByVal: chỉ định tham số đƣợc tham chiếu theo kiểu tham trị (VBA sẽ chuyển bản sao của giá trị của đối số đến hàm khi gọi hàm). ByRef: chỉ định tham số đƣợc tham chiếu theo kiểu tham biến (VBA sẽ chuyển địa chỉ của đối số đến hàm khi gọi hàm). Tham số là mảng luôn đƣợc tham chiếu là ByRef. ParamArray: chỉ đƣợc khai báo cho tham số cuối cùng trong danh sách các tham số của hàm. Chỉ định tham số cuối cùng là một mảng Optional các phần tử kiểu Variant. defaultvalue: giá trị mặc định của tham số. Giá trị ở đây là một hằng hoặc biểu thức hằng. Tùy chọn này chỉ dành cho các tham số Optional, nếu type là Object thì defaultvalue chỉ co thể là nothing. type: là một trong các kiểu của VBA hoặc kiểu cấu trúc do ngƣời dùng định nghĩa. Nên khai báo type trƣớc mỗi tham số của hàm. 184 statements: một hoặc một số lệnh expression: biểu thức, là giá trị trả về của hàm. Hàm có thể đƣợc gọi ngay trong khi định nghĩa nó, đó là định nghĩa hàm kiểu đệ quy. Có thể gọi hàm tại bất kỳ đâu mà phạm vi truy xuất của nó là đƣợc phép. Hàm tham gia vào một thành phần của biểu thức. Khi gọi hàm tại các vị trí trong cùng module, ta chỉ cần chỉ ra tên hàm và danh sách các giá trị, biến tƣơng ứng với các tham số của nó. Khi gọi hàm bên ngoài module, tên hàm đƣợc xác định:  Tên module.tên hàm đối với hàm trong Standard Module  Form_tên form.tên hàm đối với hàm trong form module  Report_tên report.tên hàm đối với hàm trong report module  Tên biến đối tượng.tên hàm đối với hàm trong Class Module. 5.2 Thủ tục Khai báo: [Private | Public | Friend] [Static] Sub name [(arglist)] [statements] [Exit Sub] [statements] End Sub Trong đó các tham số của lệnh tƣơng tự các tham số của hàm. Thủ tục cũng có thể đƣợc gọi đệ quy ngay trong khi định nghĩa. Cách gọi thủ tục con cũng tƣơng tự hàm. Tuy nhiên thủ tục đƣợc gọi nhƣ một lệnh. 6. Các mô hình truy cập cơ sở dữ liệu 6.1 Kiến trúc chương trình ứng dụng MS Access MS Access có hai thành phần: động cơ chƣơng trình ứng dụng (application engine), kiểm soát việc lập trình và giao diện ngƣời dùng; Jet DBEngine, kiểm soát việc lƣu trữ dữ liệu và thủ tục định nghĩa tất cả các đối tƣợng trong cơ sở dữ liệu. VBA hỗ trợ hai mô hình này tƣơng đối độc lập để thao tác những đối tƣợng đƣợc lƣu bởi Jet DBEngine. Khi mở cơ sở dữ liệu, Application Engine nạp tập hợp đối tƣợng thích hợp từ CSDL và file chƣơng trình ứng dụng để liệt kê toàn bộ bảng, truy vấn, mối quan hệ (relationship), mẫu biểu (form), báo biểu (report), macro, module, trang truy cập dữ liệu để hiển thị trong cửa sổ Database. Application Engine thiết lập đối tƣợng Application ở cấp cao nhất chứa một tập hợp Forms (tập các Form đang mở), tập hợp 185 Reports (tập các Report đang mở), tập hợp References (tất cả các phép tham chiếu đến thƣ viện VBA) và tập hợp Modules (tập các module đang mở, bao gồm cả Form/Report Module). Mỗi Form, Report lại chứa tập hợp Controls gồm tất cả các bộ phận điều khiển trên form, report. Trong MS Access 2013, Microsoft cung cấp các công nghệ truy cập CSDL sau đây:  Data Access Object (DAO)  Object Linking and Embedding, Database (OLE DB)  ADO.NET  ActiveX Data Objects (ADO)  Open Database Connectivity (ODBC) Điều đó có nghĩa là, bạn có thể truy cập vào CSDL của MS Access bằng cách sử dụng 1 trong 5 công nghệ trên từ chƣơng trình ứng dụng đƣợc viết bởi các ngôn ngữ lập trình khác nhau nhƣ VBA, .Net Languages (VB.Net, C#, C++, ). Tuy nhiên, từ VBA bạn chỉ có thể dùng 1 trong 2 công nghệ DAO hoặc ADO. Chi tiết về 02 mô hình này sẽ đƣợc trình bày trong phần dƣới đây 6.2 Kiến trúc DAO (Data Access Objects) Mô hình đầu tiên và cũ nhất trong hai mô hình truy xuất vào CSDL là mô hình DAO. Mô hình này đƣợc sử dụng thích hợp nhất trong phạm vi chƣơng trình ứng dụng CSDL MS Access vì nó cung cấp các đối tƣợng, phƣơng thức, thuộc tính phù hợp với cách thức mà MS Access và Jet DBEngine làm việc với nhau. Hiệu quả truy xuất vào CSDL Access của VBA thông qua DAO đạt tốt hơn so với các mô hình khác bởi VBA là một dạng native language đối với Jet DBEngine. Tức là giữa DAO và Jet DBEngine không có cầu trung gian nào, chúng làm việc trực tiếp với nhau nên tốc độ truy xuất sẽ nhanh hơn so với các truy xuất theo mô hình khác DAO. 186 Sơ đồ sau minh họa mối quan hệ giữa DAO và các thành phần dữ liệu khác Hình 7.16. Mối quan hệ giữa DAO và các thành phần khác DAO sử dụng động cơ Microsoft Jet Database Engine để kết nối với server CSDL thông qua Open Database Connectivity (ODBC) driver hoặc driver riêng của server CSDL. Để tham chiếu đến mô hình DAO, ta phải yêu cầu VBA nạp một tham chiếu đến Microsoft DAO 3.6 Object Library bằng cách mở module bất kỳ, trên thanh công cụ chọn lệnh: Tool/Reference, kết quả nhận đƣợc là hộp thoại có dạng nhƣ hình dƣới, sau đó chọn vào mục Microsoft DAO 3.6 Object Library. 187 Hình 7.17. Tham chiếu đến Microsoft DAO 3.6 Object Library 188 Sơ đồ sau minh họa kiến trúc phân tầng gồm nhiều thành phần, đối tƣợng trong mô hình DAO. Hình 7.18. Các đối tượng và tập hợp trong kiến trúc DAO 1. DBEngine DBEngine là một lớp đƣợc dùng nhƣ một cầu nối giữa động cơ chƣơng trình ứng dụng và Jet DBEngine. DBEngine đại diện cho đối tƣợng DBEngine – vốn là đối tƣợng cao cấp nhất trong hệ thống DAO. Đối tƣợng DBEngine kiểm soát tất cả các đối tƣợng CSDL trong CSDL thông qua một hệ thống phân cấp tập hợp, đối tƣợng và thuộc tính. Khi mở một CSDL MS Accessss, trƣớc tiên đối tƣợng DBEngine thiết lập tập hợp Workspace và một đối tƣợng Workspace mặc định. Nếu nhóm làm việc của bạn đƣợc bảo mật, MS Access nhắc bạn nhập UID và Password để đối tƣợng DBEngine có thể tạo một đối tƣợng ngƣời dùng (user) và một đối tƣợng nhóm (group) ở vùng làm việc mặc định. Nếu nhóm làm việc không đƣợc bảo mật, DBEngine tạo một tài khoản ngƣời dùng mặc định là Admin trong nhóm mặc định Admins. Cuối cùng, DBEngine tạo đối tƣợng Database trong phạm vi đối tƣợng Workspace mặc định. DBEngine sử dụng thông tin đối tƣợng User và/hoặc Group hiện hành để quyết định bạn có thẩm quyền truy cập các đối tƣợng trong CSDL hay không. Sau khi DBEngine tạo đối tƣợng Database, động cơ chƣơng trình ứng dụng kiểm tra các tùy chọn khởi động CSDL để hiển thị Form khởi động, thanh menu, thanh tiêu đề hay sử dụng một hoặc nhiều tùy chọn khởi động khác. 2. Errors 189 Chứa tất cả các đối tƣợng Error trong DAO. Khi thi hành bất kỳ một đối tƣợng nào trong mô hình DAO đều có thể sinh ra một hoặc nhiều lỗi. Mỗi khi một lỗi xảy ra, VBA tạo một hoặc nhiều đối tƣợng Error và đƣa vào trong tập Errors. Khi một đối tƣợng trong DAO đƣợc thi hành và sinh ra lỗi thì tập hợp Errors hiện tại bị xóa và các đối tƣợng Error mới đƣợc sinh ra tƣơng ứng với lỗi đó đƣợc đƣa vào trong tập Errors. 3. Workspaces Định nghĩa một tập hợp các vùng làm việc (đang hoạt động hoặc ẩn) của DBEngine. Có thể khai báo nhiều vùng làm việc một lúc. Khi lần đầu tham chiếu hoặc sử dụng đối tƣợng Workspace, VBA sẽ tự động tạo Workspace mặc định DBEngine.Workspaces(0). Để tham chiếu đến đối tƣợng Workspace, bạn sử một trong các cú pháp sau: DBEngine.Workspaces(0) DBEngine.Workspaces(“name”) DBEngine.Workspaces![name] 4. Connections Chứa các đối tƣợng Connection hiện tại của đối tƣợng Workspace. Khi mở một đối tƣợng Connection mới, nó sẽ tự động đƣợc thêm vào tập các Connections của đối tƣợng Workspace. Khi một đối tƣợng Connection bị đóng, nó sẽ đƣợc loại bỏ khỏi tập các Connections. Tại thời điểm mở một Connection thì một đối tƣợng Database tƣơng ứng đƣợc tạo ra và thêm vào tập Databases trong cùng một Workspace và ngƣợc lại. Khi đóng một Connection thì đối tƣợng Database tƣơng ứng với nó bị xóa khỏi tập Databases và ngƣợc lại. Để tham chiếu đến đối tƣợng Connection trong tập Connections chứa nó bạn có thể sử dụng một trong các cú pháp sau: Connections(0) Connections("name") Connections![name] name: là một chuỗi ký tự chứa đƣờng dẫn đến file CSDL. 5. Databases Chứa tập các đối tƣợng Database đƣợc mở hoặc tạo ra thuộc 1 đối tƣợng Workspace. Khi tạo mới hoặc mở một đối tƣợng Database đã có, VBA tự động nó thêm vào tập Databases. Khi xóa một đối tƣợng Database, nó cũng bị xóa bỏ khỏi tập Databases. 190 Có thể tham chiếu đến đối tƣợng Database trong tập Databases chứa nó bởi một trong các cú pháp sau: Databases(0) Databases("name") Databases![name] name: là xâu ký tự chứa đƣờng dẫn đến file CSDL. Ví dụ: Đoạn mã sau để mở 1 CSDL có tên là DB1.MDB trong thƣ mục D:\Data\Mdb Dim db As DAO.Database Set db = DBEngine.OpenDatabase("D:\Data\Mdb\DB1.MDB") Để mở CSDL hiện tại, có thể dùng lệnh: Set db = CurrentDB Đóng CSDL vừa mở bạn dùng lệnh: db.Close 6. Users Chứa các đối tƣợng User trong Workspace hoặc Group. Có thể tham chiếu đến đối tƣợng User bởi một trong các cú pháp sau: [workspace | group].Users(0) [workspace | group].Users("name") [workspace | group].Users![name] name: là tên của user 7. Groups Chứa tập các đối tƣợng Group của Workspace. Có thể tham chiếu đến đối tƣợng Group bởi một trong các cú pháp sau: Groups(0) Groups("name") Groups![name] name: là tên của Group 8. Reccordsets Recordsets chứa tập các đối tƣợng Recordset. Mỗi đối tƣợng Recordset chứa tập các bản ghi của một bảng, truy vấn hoặc tập các bản ghi là kết quả của câu lệnh SQL. 191 Một số thuộc tính, phƣơng thức của đối tƣợng Recordset + AbsolutePosition Thuộc tính này cho biết vị trí tuyệt đối của bản ghi hiện tại tính từ bản ghi đầu tiên có thứ tự là 0. + EOF Thuộc tính này cho biết con trỏ bản ghi có ở vị trí cuối file hay không. Nếu ở cuối file, giá trị trả về là True, ngƣợc lại là False + BOF Thuộc tính này cho biết con trỏ bản ghi có ở vị trí đầu file hay không. Nếu ở đầu file, giá trị trả về là True, ngƣợc lại là False. + RecordCount Trả về một số nguyên là tổng số bản ghi của có thể truy cập đƣợc (access) của Recordset. + Fields Dùng để tham chiếu đến các field trong Recordset Ví dụ: Tham chiếu đến field Hoten Rec.Fields(“Hoten”).Value + OpenRecordset Tạo một đối tƣợng Recorset mới và thêm nó và tập hợp Recordsets + Move n (n là số nguyên) Di chuyển con trỏ bản ghi đến bản ghi thứ n trong tập các bản ghi của Recordset + MoveFirst, MoveLast, MovePrevious, MoveNext Theo thứ tự là các phƣơng thức để di chuyển con trỏ bản ghi đến bản ghi đầu tiên, cuối cùng, bản ghi liền trƣớc bản ghi hiện tại, bản ghi liền sau bản ghi hiện tại. + AddNew, Update Để thêm mới một bản ghi vào Recordset Quá trình thêm mới một bản ghi gồm các thao tác:  Ra lệnh AddNew 192  Gán giá trị cho các field theo cú pháp: rec.Fields(“Tên field”).Value = giá trị  Ra lệnh Update Ví dụ: đoạn mã sau sẽ thêm vào trong bảng Course có mô tả dƣới đây một bản ghi Tên field Kiểu dữ liệu Ràng buộc CID AutoNumber Primary Key Cname Long Text Not null DurationInHour Number Integer, null Description Long Text Null Dim db As DAO.Database Set db = CurrentDb Dim rec As Recordset Set rec = db.OpenRecordset("SELECT * FROM Course") rec.addnew rec.Fields("CName").value = “ASP.Net” rec.Fields(“DurationInHour”).value = 45 rec.Fields(“Description”).value = “Overview of ASP.net. ” rec.Update rec.Close set db = Nothing Chú ý: vì CID là trƣờng AutoNumber nên bạn không thể thiết lập giá trị cho trƣờng này. MS Access sẽ tự động tính toán và thiết lập giá trị cho nó khi bạn thêm một record vào bảng. + Edit, Update Để sửa dữ liệu của một bản ghi đã có trong Recordset Quá trình sửa một bản ghi gồm các thao tác:  Định vị con trỏ bản ghi tới bản ghi cần sửa bằng các phƣơng thức Move, MoveNext, MoveFirst, MoveLast, MovePrevious  Ra lệnh Edit  Gán giá trị cho các field theo cú pháp: rec.Fields(“Tên field”).Value = giá trị  Ra lệnh Update 193 Ví dụ: đoạn mã sau sẽ sửa bản ghi có CID = 1 trong bảng Course ở trên của CSDL hiện tại với các giá trị mới cho các field CName, DurationInHour Dim db As DAO.Database Set db = CurrentDb Dim rec As Recordset Set rec = db.OpenRecordset("SELECT * FROM Course WHERE CID = 1") rec.edit rec.Fields("CName").value = "New Name" rec.Fields(“DurationInHour”).value = 30 rec.Update rec.Close set db = Nothing Vẫn yêu cầu nhƣ trên, bạn có thể sử dụng cách khác nhƣ sau Dim db As DAO.Database Set db = CurrentDb Dim rec As Recordset Set rec = db.OpenRecordset("SELECT * FROM Course") While (rec.EOF = False) If (rec.Fields(“CID”).value = 1) rec.edit rec.Fields("CName").value = "New Name" rec.Fields(“DurationInHour”).value = 30 rec.Update End If rec.MoveNext Wend rec.Close set db = Nothing Bạn có thể thấy về hiệu quả chạy chƣơng trình thì cách thứ nhất hiệu quả hơn do nó chỉ chọn ra 1 bản ghi cần sửa rồi sửa và cập nhật lại. Cách thứ 2 là lấy tất cả các bản ghi ra rồi mới tìm cái record cần sửa và sửa rồi cập nhật lại. + Delete 194 Xóa bản ghi hiện tại ra khỏi Recordset. Quá trình xóa một bản ghi trong Recordset gồm các thao tác:  Định vị con trỏ bản ghi đến bản ghi cần xóa  Ra lệnh Delete Ví dụ: Đoạn mã sau sẽ xóa bản ghi cuối cùng trong bảng Course ở trên của CSDL hiện tại: Dim db As DAO.Database Set db = CurrentDb Dim rec As Recordset Set rec = db.OpenRecordset("SELECT * FROM Course") rec.MoveLast rec.Delete rec.Close set db = Nothing Ví dụ sau sẽ xóa đi tất cả các bản ghi trong bảng Course ở trên thoả mãn điều kiện CName chứa dãy ký tự “Java”. Có nghĩa là các khóa học cứ có tên chứa “Java” nhƣ “Java cơ bản”, “Lập trình Java trên nền Android”, đều bị xóa Dim db As DAO.Database Set db = CurrentDb Dim rec As Recordset Set rec = db.OpenRecordset("SELECT * FROM Course WHERE CName LIKE „*Java*‟") While (Not rec.EOF) rec.Delete rec.MoveNext Wend rec.Close set db = Nothing + Close Đóng đối tƣợng Recordset Để tham chiếu đến đối tƣợng Recordset trong tập Recordsets, ta có thể sử dụng một trong 3 cú pháp sau: 195 Recordsets(0) Recordsets("name") Recordsets![name] 9. QueryDefs QueryDefs chứa tập các đối tƣợng QueryDef. Mỗi đối tƣợng QueryDef dùng để tham chiếu tới các query có sẵn trong CSDL MS Access, hoặc có thể là các query lập trình bằng câu lệnh SQL tạo ra. Sử dụng phƣơng thức CreateQueryDef của đối tƣợng Database để tạo một query mới. Nhƣ vậy có thể dùng QueryDef để chạy các câu nhƣ lệnh SQL nhƣ SELECT, INSERT, DELETE, UPDATE, rất thuận tiện. Quá trình tạo và thi hành một query trong VBA sử dụng đối tƣợng QueryDef gồm các bƣớc:  Tạo một query mới (nếu không đặt tên thì nó chỉ tồn tại trong bộ nhớ) dùng phƣơng thức CreateQueryDef của đối tƣợng Database.  Gán chuỗi lệnh SQL cho query  Ra lệnh thi hành query  Đóng query Lấy các bản ghi trong bảng Đoạn mã sau sẽ cho hiển thị tất cả các bản ghi của Course ở trên trong CSDL hiện tại ra cửa sổ Debug Dim db As DAO.Database Set db = CurrentDb Dim qr As QueryDef Set qr = db.CreateQueryDef("showrec") qr.SQL = "SELECT * FROM Course" Dim rec As DAO.Recordset Set rec = qr.OpenRecordset() While Not rec.EOF Debug.Print CStr(rec.Fields(“CID”).value) & Chr(32) & rec.Fields(“CName”).value & Chr(32) & CStr(rec.Fields(“DurationInHour”).value) & Chr(32) & rec.Fields(“Description”).value rec.MoveNext Wend 196 rec.Close qr.Close Thêm mới một bản ghi vào bảng Đoạn mã sau sẽ thêm một bản ghi mới vào bảng Course ở trên trong CSDL hiện tại: Dim db As DAO.Database Set db = CurrentDb Dim qr As QueryDef Set qr = db.CreateQueryDef("insertRec") qr.sql = "INSERT INTO Course(CName, DurationInHour, Description) Values('Programming Using C++',60, 'Bla bla bla ')" qr.Execute qr.Close set db = Nothing Sửa (cập nhật) các bản ghi đã có thỏa mãn điều kiện Đoạn mã sau sẽ sửa các khóa học có thời gian 30 giờ thành 45 giờ trong bảng Course Dim db As DAO.Database Set db = CurrentDb Dim qr As QueryDef Set qr = db.CreateQueryDef("updatetRec") qr.sql = "UPDATE Course Set DurationInHour = 45 WHERE DurationInHour = 30" qr.Execute qr.Close set db = NoThing Xóa các bản ghi thỏa mãn điều kiện Đoạn mã sau đây sẽ xóa các khóa học có tên chứa chuỗi “Java” Dim db As DAO.Database Set db = CurrentDb Dim qr As QueryDef Set qr = db.CreateQueryDef("deletetRec") qr.sql = "DELETE FROM Course WHERE CName LIKE „*Java*‟" 197 qr.Execute qr.Close set db = Nothing Để tham chiếu đến đối tƣợng QueryDef trong tập QueryDefs, ta có thể sử dụng một trong 3 cú pháp sau: QueryDefs(0) QueryDefs("name") QueryDefs![name] 10. TableDefs TableDefs chứa tập các đối tƣợng TableDef. Mỗi đối tƣợng TableDef dùng để tham chiếu đến các bảng dữ liệu trong CSDL. Có thể chỉnh sửa, thiết kế cấu trúc của bảng trong chế độ Run time nhƣ trong chế độ thiết kế Design View bằng đối tƣợng này. Một số thuộc tính, phƣơng thức cơ bản: + Name: tên bảng đƣợc gán vào biến kiểu TableDef + RecordCount: tổng số bản ghi hiện có trên bảng đƣợc gán vào biến TableDef + DateCreated: thời gian tạo bảng đƣợc gán vào biến TableDef + Fields Để tham chiếu đến các field của bảng, thuộc tính này là một đối tƣợng có các thuộc tính và phƣơng thức khác. + CreateField Dùng để tạo field mới cho bảng kiểu TableDef. Để tạo một đối tƣợng TableDef mới, ta sử dụng phƣơng thức CreateTableDef của đối tƣợng Database. Ví dụ: Đoạn mã sau sẽ tạo ra thêm trong CSDL hiện tại một bảng có tên là Student, gồm các field là SID (Long Integer) và SName (Long Text) Dim db As DAO.Database Dim tbl As DAO.TableDef Set db = CurrentDb Set tbl = db.CreateTableDef("Student") tbl.Fields.Append tbl.CreateField("SID", dbLong) 198 tbl.Fields.Append tbl.CreateField("SName", dbText, 200) db.TableDefs.Append tbl set db = Nothing Phƣơng thức CreateTableDef yêu cầu đƣa vào tham số là tên của bảng, nếu để là rỗng thì bảng đó chỉ tồn tại trong bộ nhớ, không lƣu lại đƣợc trong CSDL. Để tham chiếu đến đối tƣợng TableDef trong tập TableDefs, ta có thể sử dụng một trong 3 cú pháp: TableDefs(0) TableDefs("name") TableDefs![name] Ví dụ: Tham chiểu đến bảng Student vừa tạo ta sử dụng cú pháp TableDefs("Student") 11. Relations Relations chứa một tập các đối tƣợng Relation. Mỗi đối tƣợng Relation dùng để tạo kết nối (Relationship) giữa 2 bảng, truy vấn trong CSDL MS Access. Để tạo một đối tƣợng Relation, ta sử dụng phƣơng thức CreateRelation của đối tƣợng Database. Ví dụ: đoạn mã sau sẽ tạo Relationship giữa 2 bảng Course (CID, CName, DurationInHour, Description) và CourseStudent (CSID, CID, SID, Sdate, Fdate, Venue) trong CSDL hiện tại. Dim db As DAO.Database Set db = CurrentDb Dim rls As DAO.Relation Set rls = db.CreateRelation("SC", "Course", "CourseStudent") rls.Fields.Append rls.CreateField("CID", dbLong) rls.Fields("CID").ForeignName = "CID" db.Relations.Append rls Phƣơng thức CreateRelation yêu cầu đƣa vào các tham số lần lƣợt là: tên, bảng chính, bảng phụ và ràng buộc toàn vẹn tham chiếu của mối quan hệ. Để thiết lập khóa chính của liên kết ta sử dụng phƣơng thức CreateField của đối tƣợng Relation và đƣa vào tham số yêu cầu là tên của field làm khóa chính của mối quan hệ thuộc bảng chính của 199 liên kết (trong ví dụ trên là field CID của bảng Course). Sau đó cần chỉ ra khóa ngoại của liên kết (trong ví dụ là field CID của bảng CourseStudent). Để tham chiếu đến đối tƣợng Relation trong tập Relations ta có thể sử dụng một trong 3 cú pháp: Relations(0) Relations("name") Relations![name] Ví dụ: Tham chiếu đến Relationship SC trên ta sử dụng Relations("SC") 12. Containers Containers chứa tập các đối tƣợng Container. Mỗi đối tƣợng Database chứa một tập các đối tƣợng Container. Đối tƣợng Container có thể đƣợc định nghĩa bởi Microsoft Jet Database Engine hoặc bởi ngƣời dùng (khi đó DAO không hỗ trợ bất kỳ phƣơng thức hay thuộc tính nào). Sau đây liệt kê một số đối tƣợng Container và kiểu thông tin mà nó chứa đƣợc Microsoft Jet Database Engine hỗ trợ: Tên Container Chứa thông tin về Databases Tất cả các đối tƣợng database đƣợc lƣu trong CSDL Tables Tất cả các đối tƣợng table và query đƣợc lƣu trong CSDL Relations Tất cả các đối tƣợng relationship đƣợc lƣu trong CSDL Chú ý: + Đối tƣợng Databases Container tham chiếu đến tất cả các đối tƣợng Database đƣợc lƣu trong CSDL. Còn tập Databases chỉ tham chiếu đến các đối tƣợng Database đang đƣợc mở trong đối tƣợng Workspace cụ thể. Tƣơng tự với các loại đối tƣợng Container khác. + Container là các đối tƣợng có sẵn (built - in), không thể tạo ra hay hủy bỏ chúng. Để tham chiếu đến đối tƣợng Container trong tập các đối tƣợng Containers, ta có thể sử dụng 1 trong 3 cú pháp sau: Containers(0) Containers("name") Containers![name] 200 Ví dụ Mục tiêu Minh họa hoàn chỉnh của một bài tập làm việc với CSDL gồm tất cả các thao tác: XEM, THÊM, SỬA, XÓA và đƣợc thực hiện gắn với giao diện của ngƣời dùng cuối. Nội dung Xây dựng một chƣơng trình gồm các chức năng: + Search (tìm kiếm): thực hiện tìm kiếm chứa (tìm gần đúng) trên một bảng trong CSDL theo các điều kiện nhập từ form. + Add New (thêm mới): thực hiện thêm mới một bản ghi từ form vào 1 bảng trong CSDL + Edit (sửa): sửa thông tin của một bản ghi đã có trong 1 bảng + Delete (xóa): xóa một tập bản ghi trong 1 bảng + Refresh: khởi tạo lại form về giá trị ban đầu Chú ý + Kết quả thao tác của mỗi chức năng đƣợc hiển thị ngay trong lƣới trên cùng form + Chức năng Edit: Yêu cầu ngƣời dùng chọn duy nhất 1 row (record) trong lƣới rồi Click vào nút lệnh Edit. Chƣơng trình sẽ nạp row đó lên các TextBox và sửa nút Edit thành nút Save Edit, ngƣời dùng sửa xong dữ liệu và Click vào nút Save Edit. Chƣơng trình sẽ cập nhật các thay đổi của ngƣời dùng vào CSDL và hiển thị kết quả tại lƣới bên dƣới (xem giao diện phần sau) + Chức năng Delete: Yêu cầu ngƣời dùng chọn 1 hoặc nhiều các row trong lƣới rồi Click vào nút lệnh Delete. Chƣơng trình sẽ xóa tất cả các row đó trong CSDL và hiển thị kết quả trong lƣới bên dƣới. Các bƣớc thực hiện + Tạo CSDL đặt tên là: qldt.accdb + Tạo một bảng Course trong CSDL trên nhƣ sau 201 + Tạo một form đặt tên là frmCM nhƣ sau: Chi tiết về các điều khiển trên form trong thiết kế nhƣ sau: Tên điều khiển Thuộc tính Giá trị Ghi chú Lable Name Lbl1 Caption Course Management Lable Name Lbl2 Caption Course Name: Lable Name Lbl3 Caption Duration (in hour): Lable Name Lbl4 Caption Description: Lable Name lblSearch Caption Đặt dấu cách 202 TextBox Name txtCN TextBox Name txtDurration TextBox Name txtDes TextBox này bạn lấy từ ActiveX Control để nó có thanh cuộn dọc và ngang khi ngƣời dùng nhập lƣợng dữ liệu lớn Command Button Name cmdSearch Caption Search OnClick Event Procedure/Code Builder Command Button Name cmdAddNew Caption Add new OnClick Event Procedure/Code Builder Command Button Name cmdEdit Caption Edit OnClick Event Procedure/Code Builder Command Button Name cmdDelete Caption Delete OnClick Event Procedure/Code Builder Command Button Name cmdRefesh Caption Refresh OnClick Event Procedure/Code Builder ListBox Name lbCourse Column Count 4 4 cột để sau này ứng với 4 field của bảng Course Column Widths 1";2";1";5" Các độ rộng của mỗi cột Column Heads Yes Để quy định hàng đầu tiên là tiêu đề của LB Row Source Type Value List Bound Column 0 Giá trị cột đầu tiên (0) là giá trị của LB + Viết mã lệnh cho các sự kiện trong MACO nhƣ sau 203 Option Compare Database Dim editingCID As Integer Dim db As DAO.Database Private Sub cmdAddNew_Click() 'Lay cac du lieu nguoi dung nhap tu form 'Dua vao bang Course 'Hien thi ket qua ra ListBox cmdEdit.SetFocus: cmdEdit.Caption = "Edit": editingCID = -1 Dim CN As String: txtCN.SetFocus: CN = txtCN.Text Dim Duration As String txtDuration.SetFocus: Duration = txtDuration.Text Dim Des As String: txtDes.SetFocus: Des = txtDes.value If (CN = "") Then MsgBox "The name of the course must be required!" Exit Sub End If Dim rec As Recordset If (db Is Nothing) Then Set db = CurrentDb End If If (IsNull(db)) Then Set db = CurrentDb End If If (IsEmpty(db)) Then Set db = CurrentDb End If Set rec = db.OpenRecordset("SELECT * FROM Course") rec.AddNew rec.Fields("CName").value = CN 'Kiem tra Duration phai la mot so nguyen nho hon 500 If (Duration "") Then If (IsNumeric(Duration) = False) Then MsgBox "Duration must be less than or equal 500!" 204 Exit Sub End If Dim pos1, pos2 As Integer pos1 = -1 pos2 = -1 pos1 = InStr(1, Duration, ",", vbTextCompare) pos2 = InStr(1, Duration, ".", vbTextCompare) If (pos1 > 0) Or (pos2 > 0) Then MsgBox "Duration must be less than or equal 500!" Exit Sub End If Dim num As Integer num = CInt(Duration) If (num = Null) Or (num > 500) Then MsgBox "Duration must be less than or equal 500!" Exit Sub End If rec.Fields("DurationInHour").value = num End If If (Des "") Then rec.Fields("Description").value = Des End If rec.Update Set db = Nothing LoadDataToListBox End Sub Private Sub cmdDelete_Click() 'Lay ra cac dong (item) ma nguoi dung chon trong ListBox 'Xoa ban ghi do trong bang Course 'Hien thi lai ket qua trong ListBox cmdEdit.SetFocus: cmdEdit.Caption = "Edit": editingCID = -1 If (lbCourse.ItemsSelected.count = 0) Then MsgBox "You must select at least 1 item in the ListBox to delete!" 205 Exit Sub End If Dim count As Integer: count = lbCourse.ItemsSelected.count Dim varitem As Variant, i As Integer Dim sql As String sql = "SELECT * FROM Course WHERE (CID = " & CStr(lbCourse.Column(0, lbCourse.ItemsSelected.item(0))) & ")" For i = 1 To lbCourse.ItemsSelected.count - 1 sql = sql & " OR (CID=" & CStr(lbCourse.Column(0, lbCourse.ItemsSelected(i))) & ")" Next Dim rec As Recordset If (db Is Nothing) Then Set db = CurrentDb End If If (IsNull(db)) Then Set db = CurrentDb End If If (IsEmpty(db)) Then Set db = CurrentDb End If Set rec = db.OpenRecordset(sql) While (rec.EOF = False) rec.Delete rec.MoveNext Wend rec.Close LoadDataToListBox End Sub Private Sub cmdEdit_Click() 'Thay lai Text trong Caption cua nut cho phu hop 'kiem tra nguoi dung chi duoc chon 1 item de sua 'tai item do vao cac dieu khien tren form 'nguoi dung sua cac gia tri 206 'cap nhat lai bang Course cac gia tri moi 'Hien thi ket qua sua item do Dim cap, CN, Dur, Des As String CN = "": Dur = "": Des = "" cmdEdit.SetFocus: cap = cmdEdit.Caption cap = Trim(cap): cap = LCase(cap) If (cap = "edit") Then cap = "Save Edit" If (lbCourse.ItemsSelected.count = 0) Then MsgBox "You must select only 1 item in the ListBox to edit!" Exit Sub End If Dim count As Integer: count = lbCourse.ItemsSelected.count If (count > 1) Then MsgBox "You must select only 1 item in the ListBox to edit!" Exit Sub End If editingCID = lbCourse.Column(0, lbCourse.ItemsSelected.item(0)) If (lbCourse.Column(1, lbCourse.ItemsSelected.item(0)) "") Then CN = lbCourse.Column(1, lbCourse.ItemsSelected.item(0)) End If If (lbCourse.Column(2, lbCourse.ItemsSelected.item(0)) "") Then Dur = lbCourse.Column(2, lbCourse.ItemsSelected.item(0)) End If If (lbCourse.Column(3, lbCourse.ItemsSelected.item(0)) "") Then Des = lbCourse.Column(3, lbCourse.ItemsSelected.item(0)) End If txtCN.SetFocus: txtCN.Text = CN txtDuration.SetFocus: txtDuration.Text = Dur txtDes.SetFocus: txtDes.value = Des Else cap = "Edit" txtCN.SetFocus: CN = txtCN.Text 207 txtDuration.SetFocus: Dur = txtDuration.Text txtDes.SetFocus: Des = txtDes.value Dim sql As String: sql = "SELECT * FROM Course WHERE CID=" & CStr(editingCID) Dim rec As Recordset If (db Is Nothing) Then Set db = CurrentDb End If If (IsNull(db)) Then Set db = CurrentDb End If If (IsEmpty(db)) Then Set db = CurrentDb End If If (CN = "") Then MsgBox "The name of the course must be required!" Exit Sub End If Set rec = db.OpenRecordset(sql) rec.Edit rec.Fields("CName").value = CN If (Dur "") Then If (IsNumeric(Dur) = False) Then MsgBox "Duration must be less than or equal 500!" Exit Sub End If Dim pos1, pos2 As Integer pos1 = -1 pos2 = -1 pos1 = InStr(1, Dur, ",", vbTextCompare) pos2 = InStr(1, Dur, ".", vbTextCompare) If (pos1 > 0) Or (pos2 > 0) Then MsgBox "Duration must be less than or equal 500!" Exit Sub 208 End If Dim num As Integer num = CInt(Dur) If (num = Null) Or (num > 500) Then MsgBox "Duration must be less than or equal 500!" Exit Sub End If rec.Fields("DurationInHour").value = num End If If (Des "") Then rec.Fields("Description").value = Des End If rec.Update rec.Close End If cmdEdit.SetFocus cmdEdit.Caption = cap LoadDataToListBox End Sub Private Sub cmdRefresh_Click() txtCN.SetFocus: txtCN.Text = "" txtDes.SetFocus: txtDes.value = "" txtDuration.SetFocus: txtDuration.Text = "" editingCID = -1 cmdEdit.SetFocus cmdEdit.Caption = "Edit" LoadDataToListBox End Sub Private Sub cmdSearch_Click() 'Lay cac du lieu nguoi dung nhap tu form 'SELECT du lieu theo dieu kien tim kiem 'Hien thi ket qua ben duoi ListBox cmdEdit.SetFocus: cmdEdit.Caption = "Edit": editingCID = -1 209 Dim CN As String: txtCN.SetFocus: CN = txtCN.Text Dim Duration As String: txtDuration.SetFocus: Duration = txtDuration.Text Dim Des As String: txtDes.SetFocus: Des = txtDes.value Dim foundCN As Boolean: foundCN = False Dim foundDur As Boolean: foundDur = False Dim foundDes As Boolean: foundDes = False If (CN "") Then foundCN = True End If If (Duration "") Then foundDur = True End If If (Des "") Then foundDes = True End If If (foundCN = False) And (foundDur = False) And (foundDes = False) Then MsgBox "You must input either Name or Duration or Description of the course!" Exit Sub End If Dim sql As String sql = "SELECT * FROM Course WHERE" If (foundCN) And (foundDur) And (foundDes) Then sql = sql & " (CName Like '*" & CN & "*')" sql = sql & " and (DurationInHour = " & Duration & ")" sql = sql & " and (Description LIKE '*" & Des & "*')" Else If (foundCN) And (foundDur) And (Not foundDes) Then sql = sql & " (CName Like '*" & CN & "*')" sql = sql & " and (DurationInHour = " & Duration & ")" Else If (foundCN) And (Not foundDur) And (foundDes) Then 210 sql = sql & " (CName Like '*" & CN & "*')" sql = sql & " and (Description LIKE '*" & Des & "*')" Else If (foundCN) And (Not foundDur) And (Not foundDes) Then sql = sql & " (CName Like '*" & CN & "*')" Else If (Not foundCN) And (foundDur) And (foundDes) Then sql = sql & " (DurationInHour = " & Duration & ")" sql = sql & " and (Description LIKE % '*" & Des & "*')" Else If (Not foundCN) And (foundDur) And (Not foundDes) Then sql = sql & " (DurationInHour = " & Duration & ")" Else If (Not foundCN) And (Not foundDur) And (foundDes) Then sql = sql & "(Description LIKE '*" & Des & "*')" End If End If End If End If End If End If End If Dim rec As Recordset If (db Is Nothing) Then Set db = CurrentDb End If If (IsNull(db)) Then Set db = CurrentDb End If If (IsEmpty(db)) Then Set db = CurrentDb End If Set rec = db.OpenRecordset(sql) 211 If (rec.RecordCount = 0) Then lblSearch.Caption = "There is no item found!" Exit Sub Else rec.MoveLast lblSearch.Caption = "There are " & CStr(rec.RecordCount) & " items found" Dim i As Integer While (lbCourse.ListCount > 0) i = lbCourse.ListCount - 1 lbCourse.RemoveItem Index:=i Wend lbCourse.ColumnCount = 4 lbCourse.AddItem item:="CID;Course Name;Duration (in hour) ; Description;", Index:=0 Dim item As String: item = "" rec.MoveFirst While (rec.EOF = False) item = CStr(rec.Fields("CID").value) If rec.Fields("CName") "" Then item = item & ";" & rec.Fields("CName").value Else item = item & "; " End If If IsNull(rec.Fields("DurationInHour")) = False Then item = item & ";" & CStr(rec.Fields("DurationInHour").value) Else item = item & "; " End If If rec.Fields("Description") "" Then item = item & ";" & rec.Fields("Description").value Else item = item & "; " 212 End If lbCourse.AddItem item:=item rec.MoveNext Wend rec.Close Set db = Nothing End If End Sub Private Sub Form_Load() cmdEdit.SetFocus: cmdEdit.Caption = "Edit": editingCID = -1 LoadDataToListBox End Sub Private Sub LoadDataToListBox() 'Ham nay se tai du lieu tu bang Coourse vao ListBox lbCourse Dim rec As Recordset If (db Is Nothing) Then Set db = CurrentDb End If If (IsNull(db)) Then Set db = CurrentDb End If If (IsEmpty(db)) Then Set db = CurrentDb End If Set rec = db.OpenRecordset("SELECT * FROM Course") If (rec.RecordCount = 0) Then lblSearch.Caption = "There is no item found!" Exit Sub Else rec.MoveLast lblSearch.Caption = "There are " & CStr(rec.RecordCount) & " items found" rec.MoveFirst Dim i As Integer 213 While (lbCourse.ListCount > 0) i = lbCourse.ListCount - 1 lbCourse.RemoveItem Index:=i Wend lbCourse.AddItem item:="CID;Course Name;Duration (in hour) ; Description;", Index:=0 Dim item As String: item = "" While (rec.EOF = False) item = CStr(rec.Fields("CID").value) If rec.Fields("CName") "" Then item = item & ";" & rec.Fields("CName").value Else item = item & "; " End If If IsNull(rec.Fields("DurationInHour")) = False Then item = item & ";" & CStr(rec.Fields("DurationInHour").value) Else item = item & "; " End If If rec.Fields("Description") "" Then item = item & ";" & rec.Fields("Description").value Else item = item & "; " End If lbCourse.AddItem item:=item rec.MoveNext Wend rec.Close Set db = Nothing End If End Sub Chạy thử chƣơng trình và quan sát các kết quả 214 6.3 Kiến trúc ADO (ActiveX Data Objects) Microsoft giới thiệu một tập hợp mô hình đối tƣợng dữ liệu mang tính khái quát hơn nhằm cung cấp phép tham chiếu không chỉ cho đối tƣợng lƣu trong JET DBEngine mà còn cho dữ liệu đƣợc lƣu trữ trong hệ quản trị CSDL khác nhƣ Microsoft SQL Server. Những mô hình này gọi là kiến trúc ADO (ActiveX Data Object). MS Access 2013 có khả năng hỗ trợ trực tiếp cho ADO thông qua các thƣ viện cài đặt sẵn và phép tham chiếu trực tiếp đến đối tƣợng chính trong mô hình từ đối tƣợng Application của MS Access. Vì kiến trúc ADO đƣợc thiết kế là để cung cấp tập hợp các đối tƣợng chung cho bất kỳ hệ thống dữ liệu nào hỗ trợ ActiveX Data Objects nên chúng không nhất thiết phải hỗ trợ tất cả các đặc tính có trong DAO vốn đƣợc thiết thiết chuyên dụng cho Microsoft Jet DBEngine. Khác với DAO, ADO làm việc theo mô hình Client/Server. Chƣơng trình ứng dụng sẽ là client, hệ quản trị CSDL sẽ là server chứa CSDL. Client và Server giao tiếp với nhau qua kết nối mạng. Kiến trúc ADO gồm các thành phần là: + ADO (ActiveX Data Objects): cho phép chƣơng trình ứng dụng phía client có thể truy cập và thao tác với dữ liệu trên server thuộc nhiều nguồn khác nhau thông qua provider OLEDB. Lợi ích quan trọng của ADO là dễ dàng sử dụng, tốc độ truy xuất cao, bộ nhớ chiếm dụng ít. ADO là sự lựa chọn thông minh cho các ứng dụng client/server và web-based cần truy cập CSDL + ADOX (Microsoft ActiveX Data Objects Extensions for Data Definition Language and Security): là một mở rộng của ADO dựa trên cách tiếp cận hƣớng đối tƣợng. ADOX cung cấp thêm một số đối tƣợng trong thƣ viện của nó để thao tác với ngƣời dùng, nhóm ngƣời dùng, cấp/thu hồi quyền của ngƣời dùng trên các đối tƣợng CSDL. + ADOMD (Microsoft ActiveX Data Objects Multidimensional): cung cấp thƣ viện để làm việc với các loại dữ liệu đa chiều. ADOMD cũng dựa trên OLEDB để truy cập vào các nguồn dữ liệu khác nhau + RDS (Remote Data Service): là một tính năng của ADO, cho phép di chuyển dữ liệu từ server CSDL về ứng dụng phía client, xử lý dữ liệu tại client và sau đó cập nhật lại server trong một giao dịch (single round trip). Trong khuôn khổ của tài liệu này, chúng tôi chỉ đề cập chi tiết đến thành phần ADO, các thành phần còn lại bạn có thể xem trong thƣ viện MSDN của Microsoft. Mô hình ADO cơ bản cho phép bạn mở và thao tác với tập các bản ghi thông qua đối tƣợng Recordset, và thi hành các truy vấn thông qua đối tƣợng Command. Hình ảnh sau đây minh họa các đối tƣợng, tập hợp và mối quan hệ giữa chúng trong kiến trúc ADO. 215 Hình 7.19. Các đối tượng và mối quan hệ giữa chúng trong kiến trúc ADO Để tham chiếu đến thành phần ADO, ta phải yêu cầu VBA nạp một tham chiếu đến Microsoft ActiveX Data Objects 6.1 Library bằng cách mở module bất kỳ, trên thanh công cụ chọn lệnh: Tool/Reference, kết quả nhận đƣợc là hộp thoại có dạng nhƣ hình dƣới, sau đó chọn vào mục Microsoft ActiveX Data Objects 6.1 và Click nút OK 216 Hình 7.20. Tham chiếu đến thành phần ADO Kiến trúc ADO cung cấp 9 đối tƣợng (object) và 4 tập hợp (collection) bao gồm: 1. Đối tƣợng Connection Dùng để thiết lập một ph

Các file đính kèm theo tài liệu này:

  • pdfgiao_trinh_access_dhgtvt1_2_3827_2152146.pdf
Tài liệu liên quan