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...
112 trang |
Chia sẻ: quangot475 | Lượt xem: 884 | Lượt tải: 1
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:
- giao_trinh_access_dhgtvt1_2_3827_2152146.pdf