Tìm thủ thuật nhanh hơn với chức năng tìm trong Blog

4/2/10

Tính giá trị 1 công thức lưu trong table

Hỏi: Mình có table tblData thế này
  tblData
tblData
TK NODAU CODAU PSNO PSCO NOCUOI COCUOI
110 50000 0 20000 6000 24000 0
111 150000 0 10000 2000 138000 0
112 245000 0 15000 3000 227000 0
113 15000 0 2000 11000 2000 0
114 85000 0 3000 4000 78000 0
211 0 20000 6000 3000
11000
212 0 135000 7000 8000
120000
213 0 36000 5000 1000
30000
214 0 42000 6000 6000
30000

tblketqua( Chimuc,Congthuc,giatri)
Mình có các công thức lấy giá trị trên table tblData để cộng trừ nhân chia, và công thức này được lưu trên tblKetqua. Mình muốn tính toán và điền giá trị cuối cùng vào ô giatri trên tblketqua.


Đáp:
Lấy giá trị thông qua hàm Dlookup. Tổng quát, ta viết 1 function để lấy giá trị trên tblData như sau:
Function myIndex(Dong As String, Cot As String) As Long
 myIndex = DLookup(Cot, "tblData", "[TK]='" & Dong & "'")
End Function

Công thức lưu của bạn phải phát biểu dạng:
myIndex("110", "nodau")-(myIndex("113", "PSno") + myIndex("114", "PSCo"))
Lưu các công thức này vào field Congthuc trên tblketqua.

Ví dụ:
TBLKETQUA
TBLKETQUA
CHITIEU CONGTHUC SOTIEN
I. TÀI SẢN myIndex("110", "coCuoi")+ myIndex("111", "coCuoi")+ myIndex("113", "coCuoi") 0
1. Tài sản cố định myIndex("111", "noCuoi")-myIndex("112", "noCuoi") 0
2. Vật liệu myIndex("110", "nodau")-(myIndex("113", "PSno") + myIndex("114", "PSCo")) 0
II. NGUỒN VỐN myIndex("211", "Codau")+myIndex("212", "Codau")+myIndex("213", "Codau") 0
1. Tỷ lệ vốn chủ sở hữu myIndex("111", "NoCuoi") 0
Dùng hàm eval để thực thi các công thức dạng chuỗi. Eval(StringExp)
Run 1 query update để cập nhật giá trị công thức vào ô giá trị:
Private Sub cmdTinhKQ_Click()
SQL = "UPDATE TBLKETQUA SET SOTIEN = Eval([congthuc])"
DoCmd.SetWarnings False
DoCmd.RunSQL SQL
MsgBox " Da update thanh cong"
DoCmd.SetWarnings True
DoCmd.OpenTable "TBLKETQUA"
End Sub
Chúc thành công:
Mời xem demo: Download
____________________________________________________________________________________
Thảo luận thêm: http://thuthuataccess.co.cc/forum

3/2/10

Set Mặc Định định dạng ngày tháng

Hỏi: Nếu như trong phần mềm của mình định dạng Format cho trường Date là dd/mm/yyyy mà ngày tháng hệ thống là mm/dd/yyyy thì sẽ dẫn đến lẫn lộn ngày tháng khi nhập vào CSDL .Nếu như mình nhập 12/06/2009 thì nó sẽ hiển thị 6/12/2009.
Vậy có cách nào để khi mình mở phần mềm lên nó sẽ tự kiểm tra ngày hệ thống để trả về định dạng dd/mm/yyyy không ?
Đừng bảo mình chuyển trong Control Panel nhé ,vì như thế cứ mang pm đi máy nào cũng phải chỉnh lại mất công lắm,mà không pro

Đáp: ( dongnamnb- Danketoan)
Bạn có thể giải quyết bằng hàm API. Cách làm như sau:

1. Copy đoạn code sau vào 1 module

Option Compare Database

Public Const LOCALE_SSHORTDATE = &H1F
Public Const WM_SETTINGCHANGE = &H1A
'same as the old WM_WININICHANGE
Public Const HWND_BROADCAST = &HFFFF&

Public Declare Function SetLocaleInfo Lib "kernel32" Alias _
"SetLocaleInfoA" (ByVal Locale As Long, ByVal LCType As _
Long, ByVal lpLCData As String) As Boolean
Public Declare Function PostMessage Lib "user32" Alias _
"PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
Public Declare Function GetSystemDefaultLCID Lib "kernel32" _
() As Long
Public Declare Function GetLocaleInfo Lib "kernel32" Alias _
"GetLocaleInfoA" (ByVal Locale As Long, ByVal LCType As Long, ByVal _
lpLCData As String, ByVal cchData As Long) As Long

Sub SetSysDate()
Dim lLocal As Long
Dim Length As Long
Dim dwLCID As Long
Dim Buf As String * 1024

On Error GoTo SetSysDate_Error
lLocal = GetSystemDefaultLCID()
Length = GetLocaleInfo(lLocal, LOCALE_SSHORTDATE, Buf, Len(Buf))
If Not Left$(Buf, Length - 1) = "dd/MM/yyyy" Then
dwLCID = GetSystemDefaultLCID()
If SetLocaleInfo(dwLCID, LOCALE_SSHORTDATE, "dd/MM/yyyy") = False Then
MsgBox "Khong doi duoc dinh dang ngay he thong.", 64, "Thong bao"
Exit Sub
End If
PostMessage HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0

End If


Exit Sub

SetSysDate_Error:
MsgBox "Unexpected Error No. " & Err.Number & _
" in procedure SetSysDate of Form Main. " _
& vbCrLf & vbCrLf & Err.Description, 64, "Dish Care Centre"
End Sub
Ở sự kiện Form_Load của form startup của bạn, bạn gọi thủ tục SetSysDate như sau:
Private Sub Form_Load()

SetSysDate

End Sub
 Như vậy mỗi lần chạy chương trình của bạn thì định dạng ngày hệ thống sẽ tự động chuyển thành dạng dd/mm/yyyy
____________________________________________________________________________________
Thảo luận thêm: http://thuthuataccess.co.cc/forum

Dùng các phương thức Find của Recordset

Tác giả: Phatnq2002- DanKeToan
Trong bài này, tôi sẽ nói việc sử dụng các phương thức Find của một đối tượng Recordset thuộc thư viện DAO.

Trước tiên, xin lưu ý trong thư viện ADO cũng có đối tượng Recordset, nhưng phương thức Find của đối tượng này sẽ khác với phương thức Find của DAO.Recordset.

Có bốn phương thức Find của DAO.Recordset:
- FindFirst: khi gọi phương thức này, con trỏ record sẽ nhảy ngay về record đầu tiên và tiến hành việc tìm kiếm record đầu tiên thỏa điều kiện tìm. Trong trường hợp recordset không có record nào (RecordCount = 0) thì phương thức này sẽ gây ra một lỗi khi gọi nó.
- FindNext: khi gọi phương thức này, việc tìm kiếm sẽ thực hiện bắt đầu từ vị trí record mà con trỏ record đang đứng đến record cuối cùng. Cũng tương tự như FindFirst, Trong trường hợp recordset không có record nào (RecordCount = 0) thì phương thức này sẽ gây ra một lỗi khi gọi nó. Ngoài ra nếu khi đã con trỏ record duyệt tới record EOF (End of File) mà phương thức này được gọi thì sẽ xảy ra một lỗi.
- FindPrevious: khi gọi phương thức này, việc tìm kiếm sẽ thực hiện bắt đầu từ vị trí record mà con trỏ record đang đứng trở về record đầu tiên. Cũng tương tự như FindFirst, Trong trường hợp recordset không có record nào (RecordCount = 0) thì phương thức này sẽ gây ra một lỗi khi gọi nó. Ngoài ra nếu khi đã con trỏ record duyệt tới record BOF (Begin of File) mà phương thức này được gọi thì sẽ xảy ra một lỗi.
- FindLast: khi gọi phương thức này, con trỏ record sẽ nhảy ngay về record cuối cùng và tiến hành việc tìm kiếm record đầu tiên thỏa điều kiện tìm nhưng ngược từ dưới lên. Trong trường hợp recordset không có record nào (RecordCount = 0) thì phương thức này sẽ gây ra một lỗi khi gọi nó.

Cách gọi phương thức:
đối_tượng_recordset.FindFirst điều_kiện
đối_tượng_recordset.FindNext điều_kiện
đối_tượng_recordset.FindPreviuos điều_kiện
đối_tượng_recordset.FindLast điều_kiện

Trong đó điều_kiện là chuỗi thể hiện nội dung tìm kiếm.

Để minh họa cho cách sử dụng các phương thức trên, chúng ta đặt ra một bài toán sau:
Giả sử chúng ta thiết kế một form nhập liệu danh sách khách hàng (makh, tenkh, diachi). Trong form này có một nút Tìm (cmdFind). Khi nhấn nút này sẽ cho hiện ra một form khác để người sử dụng nhập vào tên khách hàng cần tìm (textbox tenkh) và 3 nút: Thi hành (cmdFind), Tìm tiếp (cmdFindNext) và Thôi (cmdClose).
Khi nhấn nút Thi hành sẽ cho thực hiện một thủ tục, trong đó tìm kiếm xem có khách hàng nào thỏa mãn điều kiện tìm không. Nếu có thì cho hiển thị thông tin của khách hàng đó ở form KhachHang. Nếu không thì báo là không tìm thấy.
Khi nhấn nút Tiếp, sẽ cho thực hiện một thủ tục xem có khách hàng nào còn thỏa mãn điều kiện tìm không. Nếu có thì cho hiển thị thông tin của khách hàng đó ở form KhachHang. Nếu không thì báo là không còn tìm thấy.
Nút Thôi chủ yếu là đóng form Tim lại mà thôi.

Bây giờ vào code đây:

Bước 1: ở form KhachHang, thiết lập thủ tục sự kiện Click cho nút lệnh cmdFind như sau:
DoCmd.OpenForm "TIM" ' Để cho hiển thị form Tim

Bước 2: Ở form Tim, trong khu vực Declaration (tức là dưới dòng lệnh Option Explicit và/hoặc Option Compare Database), cho dòng lệnh khai báo sau:
Dim st As String, rs As DAO.Recordset

Thiết lập các thủ tục sự kiện sau:


Private Sub Form_Load ()
Set rs = Forms("KHACHHANG").RecordsetClone ' gán dữ liệu nguồn của form KhachHang vào recordset
End Sub
Private Sub cmdFind_Click ()
st = "tenkh LIKE '*" & tenkh & "*'" 'dùng toán tử LIKE để tìm kiếm tương đối
On Error GoTo loi_Find
rs.FindFirst st
If rs.NoMatch Then ' nếu không tìm thấy
MsgBox "Không tìm thấy."
Else
Forms("KHACHHANG").Bookmark = rs.Bookmark 'cho hiển thị lên form KhachHang thông tin khách đã tìm thấy
End If
thoat_Find:
Exit Sub
loi_Find:
MsgBox "Lỗi. Có thể danh sách khách hàng đang trống."
Resume thoat_Find
End Sub

Private Sub cmdFindNext_Click ()
On Error GoTo loi_FindNext
rs.FindNext st
If rs.NoMatch Then ' nếu không tìm thấy nữa
MsgBox "Không còn tìm thấy."
Else
Forms("KHACHHANG").Bookmark = rs.Bookmark 'cho hiển thị lên form KhachHang thông tin khách đã tìm thấy
End If
thoat_FindNext:
Exit Sub
loi_FindNext:
MsgBox "Lỗi. Có thể danh sách khách hàng đang trống."
Resume thoat_FindNext
End Sub
Private Sub cmdClose_Click ()
DoCmd.Close acForm, Me.Name
End Sub

Trên chỉ là những đoạn code chính, các bạn có thể thêm mắm muối vào cho hợp ý mình.
____________________________________________________________________________________
Thảo luận thêm: http://thuthuataccess.co.cc/forum

Các sự kiện xảy ra trên report

Sự kiện (event) và các thủ tục sự kiện (event procedure) - phần 2
Tác giả: phatnq2002- Danketoan

Ngoài các sự kiện xảy ra tương tự như với form, report cũng có một số sự kiện mà bạn có thể điều khiển nó nhằm làm cho report của bạn có thể "linh hoạt" hơn.

1. Sự kiện NoData:
Sự kiện xảy ra sau khi report đã được định dạng để chuẩn bị in nhưng không có dữ liệu nào được tập hợp (recordsource của nó có record count = 0).

Dựa trên sự kiện này, chúng ta có thể cho hủy in ra một report trắng.

Thuộc tính sự kiện có tên là OnNoData.
Thủ tục sự kiện có tên là Report_NoData. Có một tham số quen thuộc là Cancel.
Khi Cancel = True thì việc in report sẽ được hủy. Tuy nhiên các bạn cần lưu ý. Việc hủy in ở đây sẽ có tác động gây ra một lỗi và sẽ xuất hiện một thông báo lỗi. Để tắt thông báo này, bạn nên gọi report trong code và dùng mẫu sau:

On Error Resume Next
DoCmd.OpenReport "reportname", acViewNormal (hoặc acViewPreview)
On Error Goto 0
Dưới đây là một đoạn code dùng để thông báo cho người sử dụng biết là việc in sẽ bị hủy vì không có dữ liệu để in:

Private Sub Report_NoData(Cancel As Integer)
MsgBox "Bản in không có dữ liệu, việc in sẽ bị hủy. " & _
chr(13) & "Xin vui lòng kiểm tra dữ liệu nguồn của bản in, " & _
"và chắc chắn là điều kiện lọc (nếu có) được nhập đúng." vbOKOnly + vbInformation
Cancel = True
End Sub
2. Sự kiện Page:
Sự kiện xảy ra sau khi trang đã định dạng để chuẩn bị in.
Điều khiển sự kiện này, chúng ta có thể thêm thắt vào một vài chi tiết gì đó cho trang khi in ra.
Thuộc tính sự kiện có tên là OnPage.
Thủ tục sự kiện có tên là Report_Page. Không có tham số.
Sau đây là ví dụ để vẽ một đường viền quanh trang:

Private Sub Report_Page()
Me.Line(0, 0)-(Me.ScaleWidth, Me.ScaleHeight), , B
End Sub
Đối với các vùng của report: Report Header/Footer, Detail, Page Header/Footer, ..., chúng ta còn có một số sự kiện tác động lên chúng.

3. Sự kiện Format:
Sự kiện xảy ra khi Access đang "thu xếp" xem cái gì sẽ được in ra trong khu vực, nhưng trước khi khu vực đó được định dạng để in ra.
Khi điều khiển sự kiện này, bạn có thể thực hiện những tính toán nào đó, định dạng lại những gì sẽ được in ra, ví dụ sẽ in logo công ty ở tất cả các trang, trừ trang đầu, khu vực sẽ có màu gì khi thỏa mãn điều kiện gì, ...
Thuộc tính sự kiện có tên OnFormat.
Thủ tục sự kiện có tên sectionname_Format. Thủ tục này có hai tham số:
Cancel: dễ hiểu quá rồi.
FormatCount: một số xác định xem sự kiện xảy ra mấy lần trên khu vực đó. mặc định là 1, nó chỉ lớn hơn 1 khi giả định nội dung của khu vực in không thể in cùng một trang, ví dụ nếu nội dung khu vực phải in lấn sang trang sau thì nó sẽ có giá trị là 2.

VD sau sẽ cho hiển thị một hình nào đó ví dụ như :biggrin: chẳng hạn (giả sử control chứa hình có tên là hinh) nếu một đại diện bán hàng đạt doanh thu từ 10 triệu/ tháng, ngược lại thì không:

Private Sub Detail_Format(Cancel As Integer, FormatCount As Integer)
If doanhthu > 10000000 Then
hinh.Visible = True
Else
hinh.Visible = False
End If
End Sub
4. Sự kiện Print:
Sự kiện xảy ra khi Access đã định dạng xong những gì sẽ được in ra trong khu vực, nhưng trước khi khu vực đó được để in ra.
Sự kiện chỉ xảy ra cho những khu vực thực sự sẽ được in ra.
Với khu vực Detail, sự kiện Print xảy ra cho mỗi record trong khu vực ngay trước khi Access in dữ liệu của record.
Với khu vực Group header, sự kiện xảy ra cho mỗi nhóm mới, và thủ tục sự kiện sẽ truy cập đến dữ liệu của group header và dữ liệu trong record đầu tiên của khu vực Detail.
Với group footer, và thủ tục sự kiện sẽ truy cập đến dữ liệu của group footer và dữ liệu trong record cuối cùng của khu vực Detail.
Bạn có thể điều khiển sự kiện Print khi bạn muốn tính toán tổng số trang thật sự được in và cho in lên page header hoặc footer.
Thuộc tính sự kiện có tên là OnPrint.
Thủ tục sự kiện có tên là sectionname_Print. Có hai tham số:
Cancel: thôi khỏi nói nữa.
PrintCount: một số xác định xem sự kiện xảy ra mấy lần trên khu vực đó. mặc định là 1, nó chỉ lớn hơn 1 khi giả định nội dung của khu vực in không thể in cùng một trang, ví dụ nếu nội dung khu vực phải in lấn sang trang sau thì nó sẽ có giá trị là 2.
____________________________________________________________________________________
Thảo luận thêm: http://thuthuataccess.com/forum

Bẫy lỗi trong code

Tác Giả: phatnq2002 - Danketoan

Trong quá trình thao tác nhập liệu trên form, hoặc khi thực hiện một thủ tục nào đó trong code, có thể sẽ phát sinh ra lỗi. Khi một lỗi xảy ra, hệ thống (Access) sẽ thông báo và khi đó tiến trình sẽ bị ngắt, các giá trị đã gán trước đó sẽ bị hủy và như vậy nếu cho thực thi tiếp lại xảy ra những lỗi khác.

Lỗi được tạm chia làm hai dạng:
- Dạng xảy ra khi có một sự cố về việc cập nhật dữ liệu: thường là lỗi Primary key bị trùng hoặc để trống, lỗi field được xác lập thuộc tính Required là Yes nhưng không nhập gì cả, …
- Dạng xảy ra do một câu lệnh khi thực thi đã sản sinh ra một lỗi: ví dụ câu lệnh a = b/c, trong đó c = 0, …

Để có thể tiếp tục thực hiện tiếp những công việc sau đó mà ứng dụng không bị ngắt, chúng ta phải thực hiện một công việc được gọi là bẫy lỗi (trap errors).

Trường hợp 1:
Để bẫy lỗi do hệ thống trả về, chúng ta có thể điều khiển sự kiện OnError của Form. Sự kiện này xảy ra khi có bất kỳ một lỗi nào được sản sinh trong quá trình nhập liệu trên form, nó được “lẩy” khi record hiện thời chuẩn bị được lưu vào table. Chúng ta có thể code cho thủ tục sự kiện Form_Error để bẫy những lỗi này.

Ví dụ sau đây dùng để bẫy một số lỗi cơ bản:


Private Sub Form_Error (DataErr As Integer, Response As Integer)
Response = acDataErrContinue ‘ tắt thông báo của Access
Select Case DataErr
Case 3022
MsgBox “Trùng khóa chính.”
Case 3058
MsgBox “Khóa chính để trống.”
Case 3314
MsgBox “Có ít nhất một field chưa nhập theo yêu cầu bắt buộc.”
Case Else
MsgBox “Có lỗi xảy ra.” & Chr(13) & “Chỉ số lỗi: “ & DataErr & Chr(13) & _
“Nội dung lỗi: “ & Err.Description
End Select
End Sub

Trường hợp 2:
Để bẫy lỗi trong code, chúng ta sẽ sử dụng cấu trúc sau:

Cấu trúc 1: Bỏ qua câu lệnh sản sinh ra lỗi, tiếp tục thực hiện lệnh kế sau.
Cấu trúc này thường dùng khi những lỗi xảy ra không phải là lỗi nghiêm trọng, không ảnh hưởng đến dữ liệu và các xử lý tiếp theo.


On Error Resume Next
Lệnh 1
Lệnh 2

On Error Goto 0

Ví dụ: Khi nhấn vào nút In, sẽ cho thi hành lệnh mở report rptIN ở chế độ Preview.


Private Sub cmdIn_Click ()
DoCdm.OpenReport “rptIN”, acViewPreview
End Sub

Sẽ không có gì xảy ra khi người sử dụng nhấn nút. Nhưng nếu trong rptIN, có đặt một đoạn code sau cho sự kiện OnNoData để hiển thị thông báo “Không có dữ liệu để in.”, đồng thời không cho mở report.


Private Sub Report_NoData (Cancel As Integer)
MsgBox “Không có dữ liệu để in.”
Cancel = True
End Sub

Câu lệnh DoCdm.OpenReport “rptIN”, acViewPreview trong thủ tục cmdIn_Click sẽ gây ra một lỗi ngay. Tuy nhiên lỗi này không phải là lỗi nghiêm trọng gì, chẳng qua là không có gì để in thôi.

Do đó chúng ta sẽ thiết kế lại thủ tục cmdIn_Click như sau:


Private Sub cmdIn_Click ()
On Error Resume Next
DoCdm.OpenReport “rptIN”, acViewPreview
On Error Goto 0
End Sub

Cấu trúc 2: Chuyển hướng xử lý.
Cấu trúc này thường dùng khi có lỗi nghiêm trọng ảnh hưởng đến việc nhập liệu và xử lý.


Sub [Function] rouName (………) [As ….]
Lệnh k
Lệnh k + 1

On Error Goto Nhãn_2
Lệnh 1
Lệnh 2

[Nhãn_3:]
Lệnh n – 2
Lệnh n – 1
Lệnh n
Nhãn_1:
Exit Sub ‘hoặc Exit Function tùy theo đây là thủ tục hay hàm
Nhãn_2:
‘ Kiểm tra xem lỗi gì xảy ra bằng Err.Number và Err.Description, sau đó thông báo
‘ cho người dùng biết.
‘ Đến đây, có thể cho chuyển hướng xử lý bắng một trong hai cách:
‘ Resume Nhãn_3: nếu muốn trước khi ngưng thủ tục hoặc hàm, phải thực hiện tiếp một số lệnh
‘Resume Nhãn_1: ngưng ngay thủ tục hoặc hàm
End Sub [hoặc Function]

Ví dụ: Trong một thủ tục, có yêu cầu thi hành một lệnh SQL để insert dữ liệu vào một từ table từ dữ liệu của table khác. Để tránh trường hợp nơi nhận có nhiều field hơn hoặc nơi nhận có ít field hơn hoặc tên field không tồn tại, chúng ta sẽ bẫy lỗi để phòng.


Private Sub Test ()
On Error Goto loi
CurrentDB.Execute “INSERT INTO tblA (fieldA, fieldB) SELECT fieldA, fieldB, fieldC FROM tblB” ‘Câu lệnh insert có số filed ở SELECT nhiều hơn số field nhận
DoCmd.OpenReport “rptInDuLieuTableA”
thoat:
Exit Sub
loi:
MsgBox “Có lỗi xảy ra khi có ý định chèn dữ liệu vào table A. Vui lòng liên hệ lập trình viên.”
Resume thoat
End Sub
____________________________________________________________________________________
Thảo luận thêm: http://thuthuataccess.com/forum

Sự kiện và các thủ tục sự kiện

Event and Event procedure
Tác Giả: phatnq2002 - Web DanKeToan


CÁC KHÁI NIỆM

Sự kiện là gì?
Sự kiện (event) là một hành động xác định xảy ra trên hoặc với một đối tượng nào đó. MS Access có thể đáp lại nhiều sự kiện: nhấn chuột, thay đổi dữ liệu, mở hoặc đóng form, ... Sự kiện thường là kết quả tạo ra bởi một hành động của người sử dụng.

Thủ tục là gì?
Thủ tục (procedure) là một đơn vị code của VB. Một thủ tục bao gồm một dãy các lệnh (statement) hoặc các phương thức (method) để thực thi một hành động hoặc tính toán một giá trị.

Thủ tục sự kiện là gì?

Thủ tục sự kiện (event procedure) là một thủ tục tự động thực thi để đáp lại một sự kiện được khởi xướng từ một hành động của người dùng hoặc từ code của chương trình.
VD: Thủ tục sự kiện sau đây sẽ thực thi một lệnh mở câu thông báo chào khi form được mở ra:
Private Sub Form_Load()
MsgBox "Xin chào các bạn."
End Sub


MỘT SỐ SỰ KIỆN THƯỜNG GẶP VÀ CÁCH ĐIỀU KHIỂN SỰ KIỆN

Các sự kiện xảy ra với đối tượng Form:

1. Sự kiện Open:
Sự kiện xảy ra khi form được mở, nhưng trước khi record đầu tiên được hiển thị.
(Ghi chú: ở đối tượng Report cũng có sự kiện Open, trong report sự kện Open xảy ra trước khi report được preview hoặc được in)

Từ khi gọi form cho đến khi form xuất hiện lần đầu trên màn hình, một loạt các sự kiện xảy ra, trong đó sự kiện Open là sự kiện được xảy ra đầu tiên. Các sự kiện này và thời điểm xảy ra của nó được thể hiện bằng chuỗi sau:
Open --> Load --> Resize --> Activate --> Current
Thuộc tính gắn với sự kiện Open có tên là OnOpen. (Các bạn lưu ý: với những thuộc tính bắt đầu bằng On có nghĩa là sự kiện đó đang diễn tiến,Before là trước khi sự kiện xảy ra, After là sau khi sự kiện xảy ra).
Thủ tục sự kiện gắn với sự kiện này là Form_Open. Sự kiện có một tham số có tên là Cancel kiểu Integer (thực chất nó chỉ nhận hai giá trị True và False). Tham số này các bạn có thể định lại giá trị cho nó.
Nếu định là True, sự kiện sẽ bị hoãn, nghĩa là sự kiện sẽ không hoàn tất, nhưng không xảy ra lỗi. Mặc nhiên là False.
VD: Bạn muốn rằng khi form A mở ra thì form B sẽ mở ra nếu người sử dụng đồng ý, bạn có thể code cho thủ tục sự kiện như sau:
Private Sub Form_Open(Cancel As Integer)
If MsgBox("Ban co muon form B mo ra dong th oi luon khong?", vbYesNo) = vbYes Then
DoCmd.OpenForm "form B"
Else
Cancel = True
End If
End Sub
Ghi chú: Bất kỳ một thủ tục sự kiện nào nó thông số là Cancel, thì sự kiện tương ứng đều có thể cho ngưng không cho hoàn tất. Trong trường hợp bạn sử dụng một macro hoặc một phương thức của DoCmd thì nếu hoãn một sự kiện, sẽ có một error trả về. Để tránh xuất hiện lỗi thì nế dùng macro, bạn có thể đặt trước macro đó một macro SetWarnings và gán cho thuộc tính WarningOn là No, dùng DoCmd trong code thì trước đó bạn đặt câu On Error Resume Next.
2. Sự kiện Load:
Sự kiện Load xảy ra bởi tác động của người dùng như:
- Bắt đầu một ứng dụng.
- Mở một form bằng cách nhắp vào Open trên cửa sổ Database.
- Thực thi một hành động OpenForm trong macro.
- Gọi form bằng DoCmd.OpenForm trong thủ tục.

Thuộc tính sự kiện tương ứng có tên là OnLoad.

Thủ tục sự kiện tương ứng có tên là Form_Load, không có tham số.
Khi điều khiển sự kiện này, bạn có thể thực hiện những công việc sau:
Thiết lập các giá trị mặc định cho các control hoặc thể hiện các giá trị tính toán tùy thuộc vào dữ liệu có trên form.

VD: Trong public module bạn có một biến language. Nếu biến này là "V" thì bạn cho hiển thị các caption của các Label là tiếng Việt, "E" thì hiển thị là tiếng Anh. Biến này sẽ tùy thuộc vào việc người ta chọn trong một thủ tục nào đó trước khi form bạn được gọi vào.
Trước tiên, khi thiết kế form, ở các label, bạn đặt các từ "ính lịt" tương ứng ở property Tag của nó.
Sau đó bạn thiết kế thủ tục Form_Load như sau:
Private Sub Form_Load ()
Dim ctl As Control
For Each ctl In Detail.Controls ' or FormHeader.Controls, FormFooter.Controls
If TypeOf ctl Is Label Then
x = ctl.Caption
ctl.Caption = ctl.Tag
ctl.Tag = x
End If
Next
End Sub

3. Sự kiện Resize:
Sự kiện xảy ra khi form được gọi và bất cứ khi nào kích thước của form thay đổi.

Thuộc tính sự kiện tương ứng có tên là OnResize.

Thủ tục sự kiện tương ứng có tên là Form_Resize. Thủ tục này không có tham số.

Khi điều khiển sự kiện này, bạn có thể:
- Di chuyển hoặc định lại kích thước của một control.
- Tính toán lại các biến.
- Reset lại các thuộc tính có liên quan đến kích thước của form.

VD: Khi có sự thay đổi kích thước của form sẽ cho hoàn tất những cập nhật màn hình còn đang treo hoặc những tính toán chưa thực hiện xong trên các control bằng phương thức Repaint.
Private Sub Form_Resize()
Me.Repaint
End Sub


4. Sự kiện Activate:
Sự kiện xảy ra khi một form (cũng đúng với report) nhận một focus (cái này không dám dịch ra tiếng việt, bởi dịch ra thì hổng ai hiểu, còn để nguyên thì ai cũng hiểu !!! :sifone:) và trở thành cửa sổ hoạt động.

Thuộc tính sự kiện tương ứng có tên là OnActivate.

Thủ tục sự kiện tương ứng có tên là Form_Activate. Thủ tục không có tham số.

VD: Bây giờ bạn có thiết kế một cái toolbar chỉ dành riêng cho form A. Như vậy nếu form A được kích hoạt thì mới hiện toolbar này (ví dụ toolbar này có tên là FormA_Toolbar).
Private Sub Form_Activate()
DoCmd.ShowToolbar "FormA_Toolbar", acToolbarYes
End Sub

5. Sự kiện Current:
Sự kiện xảy ra khi một focus được di chuyển đến một record, làm cho nó trở thành record hiện hành, hoặc khi form được "làm tươi mát" (refresh) hoặc được gán lại source (requery).

Thuộc tính sự kiện tương ứng có tên là OnCurrent.

Thủ tục sự kiện tương ứng có tên là Form_Current. Thủ tục này không có tham số.

VD: Bạn có một form hiển thị một số thông tin của một table, trong đó có 3 textbox ứng với 3 field(tạm là text1, text2, text3) và một checkbox cũng ứng với một field Yes/No(check1).
Yêu cầu đặt ra là nếu check1 bằng True thì không cho thay đổi nội dung trong 3 textbox.
Private Sub Form_Current ()
text1.Locked = check1
text3.Locked = check1
text3.Locked = check1
End Sub

6. Sự kiện UnLoad:
Sự kiện Unload xảy ra khi:
- Người sử dụng nhấn nút Close trên cửa sổ Form.
- Người sử dụng gọi menu File - Close.
- Gọi một macro Close một cửa sổ có thuộc tính Form.
- Gọi một lệnh DoCmd.Close với kiểu cửa sổ là acForm.
- Đóng Windows trong khi ứng dụng Access đang chạy.

Unload là sự kiện đầu tiên của chuỗi sự kiện sau:
Unload --> DeActivate --> Close

Thuộc tính sự kiện tương ứng là OnUnload.

Thủ tục sự kiện tương ứng là Form_Unload. Thủ tục này có một thuộc tính Cancel. Như ở bài trước, bạn đã biết nếu Cancel = True thì có nghĩa gì rồi phải không?

VD: Giả sử trong Form bạn có một biến tên là danglam kiểu Boolean. Nếu biến là True thì có nghĩa bạn đang nhập liệu và đang có thay đổi dữ liệu. Bạn muốn rằng khi đóng form, nó sẽ hỏi lưu thay đổi không, nếu có thì trước khi đóng, bạn cho lưu. (Code dưới đây chỉ là dàn ý, bạn phải tùy biến để cho phù hợp với ứng dụng mà bạn thiết kế).

Private Sub Form_Unload (Cancel As Integer)
If danglam Then
If MsgBox ("Du lieu da co thay doi. Co muon luu truoc khi dong khong?", vbYesNo) = vbYes then
DoCmd.SaveRecord
Else
DoCmd.Undo
End If
End Sub
7. Sự kiện DeActivate:
Sự kiện xảy ra khi bạn rời focus ra khỏi form (hoặc report). Nó cũng xảy ra sau khi form (report) được Unload.

Thuộc tính sự kiện tương ứng có tên OnDeactivate.

Thủ tục sự kiện tương ứng có tên Form_Deactivate. Không có tham số.

VD: Tiếp theo ví dụ của mục 4 phía trên (bài trước), khi chuyển focus ra khỏi form A, bạn cho ẩn tool box đi.
Private Sub Form_Deactivate()
DoCmd.ShowToolbar "FormA_Toolbar", acToolbarNo
End Sub
8. Sự kiện Close:
Sự kiện xảy ra khi form đã thực sự được đóng lại nhưng chưa rời hỏi màn hình. Về cơ bản xem như nó đã được đóng lại, nhưng nếu bạn có "lưu luyến" gì với người dùng, bạn có thể gửi một thông điệp gì đó ở đây đại loại như ... xem ví dụ bên dưới.

Tên thuộc tính sự kiện tương ứng là OnClose.

Tên thủ tục sự kiện là Form_Close. Không có tham số.

VD: Gửi một thông điệp "lưu luyến"
Private Sub Form_Close ()
MsgBox "See you soon."
End Sub
9. Sự kiện BeforeInsert:
Sự kiện này xảy ra khi người sử dụng gõ vào ký tự đầu tiên trong record mới, nhưng trước khi record ấy thực sự được tạo.

Sự kiện này bắt đầu một dãy sự kiện sau đây:
BeforeInsert --> BeforeUpdate --> AfterUpdate --> AfterInsert

Thuộc tính sự kiện có tên là BeforeInsert.

Thủ tục sự kiện có tên là Form_BeforeInsert. Có một tham số quen thuộc là Cancel.

VD: Giả sử bạn là người rất cẩn thận. Bạn muốn rằng khi một người sử dụng gõ một ký tự bất kỳ vào record mới, bạn sẽ lưu ý họ liền, sợ rằng họ vô tình nhấn phím mà thôi. Bạn có thể code nhẹ nhàng như sau:

Private Sub Form_BeforeInsert(Cancel As Integer)
If MsgBox("Them record moi ha ban?", vbOKCancel) = vbCancel Then
MsgBox "Lan sau nho can than khi su dung ban phim nghen !!!"
Cancel = True
End If
End Sub

10. Sự kiện BeforeUpdate:
Sự kiện xảy ra khi có một sự thay đổi dữ liệu trong một control thuộc form hoặc khi record được cập nhật (update).
Đúng ra đây đang liệt kê các sự kiện của Form, nhưng sự kiện này cũng ảnh hưởng đến một control, vậy có nên nói luôn ở đây không ta? Thôi chơi luôn há?

Đối với form, sự kiện này sẽ xảy ra khi bạn dời focus từ record hiện hành sang một record khác nhưng chưa dời thật sự, hoặc bạn đã gọi lệnh Save Record trong menu Records, hoặc bạn gọi macro SaveRecord hoặc bạn dùng lệnh DoCmd.RunCommand acCmdSaveRecord.
Đối với control, sự kiện này sẽ xảy ra khi bạn dời focus từ control đó sang một control khác mà ở control hiện hành bạn có thay đổi dữ liệu.
Sự kiện BeforeUpdate của form xảy ra sau sự kiện BeforeUpdate của control. Bạn nên chú ý điều này để việc điều khiển bằng thủ tục sự kiện cho phù hợp.

Thuộc tính sự kiện có tên là BeforeUpdate.

Thủ tục sự kiện có tên là:
Form_BeforeUpdate đối với form
controlname_BeforeUpdate đối với một control

Cả hai đều có tham số quen thuộc Cancel. Với form, khi Cancel = True, nó sẽ không cho bạn rời khỏi record đó, trừ khi bạn nhấn ESC để hủy thay đổi (hoặc một macro hay lệnh tương ứng) hay nhập liệu cho chính xác với yêu cầu.
Đối với control, khi Cancel = True, nó sẽ không cho bạn rời khỏi control đó trừ khi bạn cũng nhấn ESC để hủy bỏ thay đổi (hoặc nhập dữ liệu phù hợp).

VD: Giả sử bạn nhập dữ liệu điểm thi 3 môn học A, B, C cho các thí sinh. Trong đó nếu khối của thí sinh là 1 thì môn A nhân hệ số 3, khối của thí sinh là 2 thì môn B nhân hệ số 3, khối của thí sinh là 3 thì môn C nhân hệ số 3.
Điểm trung bình là tổng điểm 3 môn sau khi đã nhân với hệ số chia cho 5. Các điểm chỉ được từ 0 đến 10. Điểm trung bình sẽ tính ngay sau khi 3 môn được nhập. Điểm từng môn sẽ kiểm sau khi nhập xong điểm môn đó.
Giả sử các control của bạn là: mahs, hotenhs, khoi, monA, monB, monC, dtb
Bây giờ bạn sẽ "chế" 2 cái thủ tục BeforeUpdate cho form và cho các control điểm như sau:

Làm một cái cho điểm môn A, điểm môn B và môn C tương tự:
Private Sub monA_BeforeUpdate (Cancel As Integer)
If monA < 0 Or monA > 10 Then
MsgBox "Diem mon chi tu 0 den 10 thoi."
Cancel = True
End If
End Sub

Làm cho form:
Private Sub Form_BeforeUpdate (Cancel As Integer)
dim diem as double ' Lay theo kieu chuan, dung Single cung duoc tuy ban
diem = IIf(khoi = 1, monA*3, IIf(khoi = 2, monB*3, monC*3))/5
dtb=diem
End Sub
11. Sự kiện AfterUpdate:
Sự kiện xảy ra sau khi dữ liệu trong control đã có thay đổi, hoặc khi record đã được cập nhật vào table.
Nó xảy ra sau sự kiện BeforeUpdate.
Trong một record, dữ liệu có thay đổi trong mỗi control sẽ được cập nhật khi rời focus khỏi control đó bằng ENTER hoặc TAB.
Còn nếu khi focus rời khỏi record hoặc người sử dụng gọi menu Records - Save Record, hay dùng lệnh DoCmd.RunCommand acCmdSaveRecord trong code.
Lưu ý: sự kiện này không xảy ra khi có sự thay đổi giá trị trong các control tính toán hoặc control Unbound.
Từ sự kiện này, bạn có thể điều khiển nó để thực hiện một vài tác vụ tương tự như sau:
- Cho mở ra một form khác
- Di chuyển sang một control khác với control default (control mặc định khi bạn nhấn ENTER, TAB sẽ chuyển focus đến nó).
...
Thuộc tính sự kiện có tên là AfterUpdate.
Thủ tục sự kiện có tên:
Với control: controlname_AfterUpdate
Với form: Form_AfterUpdate.
Thủ tục này không có tham số.

12. Sự kiện AfterInsert:
Sự kiện xảy ra sau khi recordmới được thêm vào.
Thuộc tính sự kiện có tên là AfterInsert.
Thủ tục sự kiện có tên là Form_AfterInsert, không có tham số.

13. Sự kiện Current:
Như đã nói ở các bài trước, sự kiện Current là một trong chuỗi sự kiện có liên quan của form.
Sự kiện xảy ra khi focus di chuyển đến một record, biến record đó trở thành record hiện hành, hoặc khi form được làm tươi nội dung - refresh hoặc làm tươi dữ liệu - requery.
Thuộc tính sự kiện có tên là OnCurrent.
Thủ tục sự kiện có tên là Form_Current. Không có tham số.
Khi điều khiển sự kiện này, bạn có thể:
- Cho thay đổi thuộc tính của một hay nhiều control nào đó, ứng với giá trị của một hay nhiều control khác.
- Cho Requery lại dữ liệu trong Sub form khác.
- ...

VD: Thường trong khi thiết kế, bạn sẽ gặp tình huống sẽ thiết kế một form trong đó chứa 2 subform hoặc hơn.
Khi chuyển đến một record nào đó trên subform 1 thì dữ liệu trong subform 2 sẽ thay đổi tương ứng, và rồi cũng thay đổi dữ liệu luôn trong subform 3 nếu nó có.
Vậy chúng ta làm điều đó như thế nào?
Trước tiên các bạn cần lưu ý, dữ liệu của các subform phải có quan hệ với nhau thì mới áp dụng được cách này.
Bây giờ giả sử ta có 3 table:
KHOI (makhoi, tenkhoi)
LOP (malop, tenlop, makhoi)
HOCSINH (mahs, hoten, malop)
Quan hệ: KHOI --> LOP --> HOCSINH
Bây giờ xem như bạn đã design 3 cái form tương ứng cho từng table (frmKHOI, frmLOP, frmHOCSINH).
Bạn làm một cái form mới trắng.
Nhúng 3 cái subform:
- subform thứ nhất: Name: subKHOI, Source Object: frmKHOI
- subform thứ hai: Name: subLOP, Source Object: frmLOP, Link Child Fields: makhoi, Link Master Fields: subKHOI.Form!makhoi
- subform thứ ba: Name: subHOCSINH, Source Object: frmHOCSINH, Link Child Fields: malop, Link Master Fields: subLOP.Form!malop
Bây giờ bạn vào frmKHOI. Bạn code cho Form_Current một câu thôi: Parent.subLOP.Requery.
Vào frmLOP. Bạn code cho Form_Current: Parent.subHOCSINH.Requery.

14. Sự kiện BeforeDelConfirm:
Sự kiện này xảy ra khi có một hành động xóa một hay nhiều record được gọi, nhưng trước khi chúng thật sự bị xóa khỏi table (những record này tạm thời được giữ trên vùng nhớ đệm - buffer) và trước khi MS Access hiển thị một hộp thoại yêu cầu xác nhận hành động xóa này.

Nếu bạn gỡ bỏ check box Record Changes trong nhóm Confirm của trang Edit/Find của hộp thoại Options (Tools - Options ...), sự kiện này sẽ không xảy ra và tất nhiên hộp thoại xác nhận cũng không hiển thị. Record được xóa ngay lập tức! (Đừng nên làm như thế).

Bạn có thể điều khiển sự kiện này để thay thế hộp thoại thông báo của riêng bạn trước khi người sử dụng đồng ý xóa.

Thuộc tính sự kiện tương ứng có tên BeforeDelConfirm.
Thủ tục sự kiện tương ứng có tên Form_BeforeDelConfirm. Thù tục này có hai tham số.
Tham số Cancel quá quen thuộc và
Tham số Response: xác định xem điều gì sẽ thực hiện khi sự kiện xảy ra.
- Nếu Response = acDataErrDisplay (mặc định), sau khi thủ tục sự kiện thi hành xong, Access cho hiện hộp thoại xác nhận xóa của chính nó.
- Nếu Response = acDataErrContinue, Access sẽ bỏ qua hộp thoại xác nhận.

VD: Bạn muốn cho người sử dụng thấy hộp thoại riêng của bạn khi xóa, bạn có thể code cho thủ tuc sự kiện như sau:
Private Sub Form_BeforeDelConfirm(Cancel As Integer, Response As Integer)
Response = acDataErrContinue
If MsgBox("Xóa record đã chọn chứ?", vbOKCancel) = vbCancel Then
Cancel = True
End If
End Sub
15. Sự kiện AfterDelConfirm:
Sự kiện xảy ra sau khi hộp thoại xác nhận xóa đã hiển thị và nhận thông tin phản hồi từ người sử dụng. Nếu sự kiện BeforeDelConfirm có bị hủy thì sự kiện này vẫn xảy ra. Nó cho biết tình trạng của việc xóa xảy ra thế nào.

Thuộc tính sự kiện có tên: AfterDelConfirm.
Thủ tục sự kiện có tên: Form_AfterDelContfirm. Thủ tục có một tham số Status để nhận tình trạng hiện thời của thao tác xóa.
- Nếu Status = acDeleteOK thì việc xóa xảy ra bình thường.
- Nếu Status = acDeleteCancel thì việc xóa được lập trình viên tự động cho hoãn.
- Nếu Status = acDeleteUserCancel thì việc xóa là do người dùng thực hiện.

VD:
Private Sub Form_AfterDelConfirm(Status As Integer)
Select Case Status
Case acDeleteOK
MsgBox "Xóa bình thường."
Case acDeleteCancel
MsgBox "Bị hủy do lập trình viên."
Case acDeleteUserCancel
MsgBox "Bạn đã hủy lệnh xóa."
End Select
End Sub

16. Sự kiện Delete:
Với sự kiện Delete, việc xóa xem như "trọn bộ".
Sự kiện xảy ra khi người sử dụng đặt một hành động xóa record, nhưng trước khi record thực sự được xóa.

Sự kiện Delete xảy ra cho từng record. Do vậy nếu bạn chọn nhiều record để xóa cùng lúc, sự kiện Delete sẽ xảy ra bấy nhiêu lần.
Thông thường khi điều khiển hành động xóa, người ta ít thực hiện trên sự kiện này.

Thuộc tính sự kiện có tên OnDelete.
Thủ tục sự kiện có tên: Form_Delete. Có một tham số quen thuộc là Cancel.

Với form, còn nhiều sự kiện nữa, tuy nhiên những sự kiện kể trên là thông dụng và thường được điều khiển "bẫy bắt" nhiều nhất.
Nếu bạn nào chịu khó, bạn đó có thể mở Help của thằng Access ra luyện công. Bảo đảm không lên không ăn tiền.
Lần tới tôi sẽ post tiếp các sự kiện có liên quan đến report, ít thôi nhưng đôi khi cũng hữu dụng.


17/Các sự kiện phím:
Thông thường thì mỗi control sẽ tiếp nhận 3 trạng thái phím khi nó được focus. KeyDown, KeyUp và KeyPress.
KeyDown xảy ra khi người dùng nhấn một phím trong khi control đang focus. Nó cũng xảy ra khi bạn dùng hành động SendKeys (trong macro) hay lệnh SendKeys trong code.
KeyPress cũng vậy, nhưng có một chút khác biệt: nó xảy ra khi nhấn và nhả phím trong khi control đang focus. Và trong khi với KeyDown, bạn có thể kiểm tra được là nếu người dùng nhấn tổ hợp phím thì phím kết hợp sẽ là gì: CTRL, ALT, SHIFT một cách riêng biệt. Trong khi KeyPress thì không.
KeyUp là sự kiện xảy ra khi người dùng nhả phím ra. Nó giống như KeyDown, nghĩa là có thể kiểm tra được là nếu người dùng nhấn tổ hợp phím thì phím kết hợp sẽ là gì: CTRL, ALT, SHIFT một cách riêng biệt.
Tùy theo tình hình cụ thể, các bạn có thể điều khiển sự kiện phím thích hợp.

Với sự kiện KeyDown, bạn có thuộc tính sự kiện OnKeyDown.
Thủ tục sự kiện tương ứng là control_KeyDown. Thủ tục này có 2 tham số:
KeyCode: tiếp nhận mã phím mà người sử dụng nhấn (không tính các phím kết hợp). Bạn có thể dùng các hằng định sẵn để xác định phím mà người dùng nhấn (vbKeyA - phím A, vbkeyReturn - phím Enter, ...). Các bạn chịu khó mở cửa sổ Object Browser (lúc đang ở cửa sổ VB Editor) xem lib VBA, mục KeyCode constants để biết nhiều hơn.
Nếu bạn ngăn không tiếp nhận sự kiện nhấn phím, bạn gán tham số này bằng 0.
Shift: xác định xem phím kết hợp nào được nhấn cùng.
Nhấn SHIFT: acShiftMask
Nhấn CTRL: acCtrlMask
Nhấn ALT: acAltMask

Một ví dụ để điều khiển sự kiện KeyDown:
VD: Kiểm tra xem, có nhấn phím CTRL xuống không, nếu không thì thoát thủ tục. Nếu có thì coi có nhấn phím F3 không. Nếu có thì cho hiển thị một cái form gì đó.


Private Sub mahocsinh_KeyCode(KeyCode As Integer, Shift As Integer)
If (Shift And acCtrlMask) > 0 Then
If KeyCode = vbKeyF3 Then
DoCmd.OpenForm "frmLylichHocsinh", acNormal
End If
End If
End Sub

VD2: Còn đây là ví dụ để bắt sự kiện KeyPress nghen. Thông thường thì nếu bạn dùng Format cho text box với ký tự > thì nội dung trong text box hiển thị dạng chữ hoa hết sau khi bạn rời focus khỏi text box, nhưng thực tế thì nội dung trong textbox vẫnlà chữ thường. bây giờ bạn muốn đổi sang Hoa thật sự thì bạn sẽ nhờ đến KeyPress. Chú ý: bạn cẩn thận khi dùng font Unicode.


Private Sub hoten_KeyPress(KeyAscii As Integer)
Dim strCharacter As String
strCharacter = Chr(KeyAscii)
KeyAscii = Asc(UCase(strCharacter))
End Sub

18. Sự kiện Not In List:
Sự kiện này dành riêng cho control Combo Box. Sự kiện xảy ra khi người sử dụng nhập vào ô text của combo box với nội dung không khớp với dữ liệu có sẵn trong danh sách nguồn của combo box và thuộc tính Limit to List của combo box được định là Yes.

Các bạn có thể điều khiển sự kiện này để thông báo cho người sử dụng nội dung mà người đó nhập vào không đúng với quy định hay thậm chí bạn có thể cho "tự động" hay "bán tự động" add thêm dữ liệu vào bảng dữ liệu nguồn (Row Source) của combo box.

Thuộc tính sự kiện có tên là OnNotInList.
Thủ tục sự kiện có tên là comboboxname_NotInList. Thủ tục có hai tham số:
NewData: tham số lưu trữ giá trị của nội dung được nhập mới mà không khớp với danh sách hiện có của combo box.
Response: tham số chỉ định cách thức "hồi đáp" lại sự kiện này.
Có 3 "vé":
Response = acDataErrDisplay (cái này mặc định): Khi xảy ra sự kiện, Access sẽ mở ra hộp thông báo của chính nó. (Cái này thường ít ai muốn, vì nó chơi tiếng tây trong khi chương trình lại toàn là tiếng ta !!!).
Response = acDataErrContinue: Nếu định như thế này thì Access sẽ không cho hiện hộp thông báo của nó nữa. Bạn có thể chêm vào thông báo của mình.
Response = acDataErrAdded: Bạn có thể thêm vào danh sách hiện có của combo box nội dung không khớp đó như là dữ liệu thêm mới. Access sẽ chấp nhận nó và cho qua, không thông báo lỗi.

VD1: Thông báo lỗi khi dữ liệu nhập vào không khớp

Private Sub combo_NotInList(NewData As String, Response As Integer)
Response = acDataErrContinue
MsgBox "Xin vui long chon trong danh sach hien co.", vbExclamation, "Thong Bao"
End Sub
VD2: Thêm vào danh sách của combo box trong trường hợp Row Source Type của combo box là Value List.

Private Sub dvt_NotInList(NewData As String, Response As Integer)
Response = acDataErrAdded
dvt.RowSource = dvt.RowSource & ";" & NewData
End Sub
VD3: Giả sử bạn có table tblDVT chứa các đơn vị tính, và chỉ có một field là dvt (thể có nhiều hơn, nhưng field dvt là field chính). Trong một form, bạn tạo một combo box có Row Source là tblDVT. Bây giờ khi người sử dụng nhập thêm một đơn vị tính mới, lập tức nó sẽ được add ngay vào table. Cái vụ này sướng à.

Private Sub dvt_NotInList(NewData As String, Response As Integer)
Response = acDataErrAdded
CurrentDB.Execute "INSERT INTO tblDVT(dvt) VALUES('" & NewData & "')"
End Sub


____________________________________________________________________________________
Thảo luận thêm: http://thuthuataccess.co.cc/forum

1/2/10

Mở Form từ listbox, combobox

ạo một combo box từ mẫu biểu nào mà bạn muốn chọn để mở mẫu biểu đầu tiên. Combo box được thiết lập các thuộc tính sau:
Name: List of forms
Data: Value list (để cài đặt thuộc tính Row Suorce type)
Event: [On Enter]
' Đoạn mã xử lý như sau:
Private Sub Listofforms_enter()
   Dim MyDb as Database
   Dim MyContainer as Container
   Dim I as integer
   Dim list as string
   Set MyDb = DBEngine.Workspace(0).Database(0)
   Set My Container = MyDb.Containers("Forms")
   List = " "
   For I=0 to MyContainer.Documents.count - 1
   List = List & MyContainer.Documents(I).name & ";"
   Next I
   me![List of Forms].Row Suorce = Left(List, Len(list)-1)
End sub
' Nhập tiếp thuộc tính After Update của Combo box
Private Sub ListofForms_AfterUpdate()
   Docmd.OpenForm me![ListofForms)
End Sub

Sưu Tầm
____________________________________________________________________________________
Thảo luận thêm: http://thuthuataccess.com/forum

Liên kết tới CSDL là Foxpro,dBase,Access...

Function ConnectSource(DBF As String, Tenbang As String)
    Dim db As Database, tb As TableDef
    Set db = CurrentDb
    On Error Resume Next
    DoCmd.RunSQL ("drop table " + Tenbang)
    On Error GoTo 0
    Set tb = db.CreateTableDef(Tenbang)
    tb.Connect = ("FoxPro 2.6;Database=path")    tb.SourceTableName = DBF
    db.TableDefs.Append tb
    Set db = Nothing
End Function
Tương tự, nếu muốn nối tới dbase,bạn thay Foxpro bằng Dbase..
- Nếu muốn link tới một File Access có mật khẩu bạn thay dòng in đậm như sau:
tb.Connect = (";pwd=******;Database=path")

nguồn: blog suu tam thu thuat

Sửa lỗi Tiếng Việt khi lưu file dbf vào Access

Khi import file .dbf vào access thì phần Data không hiển thị được tiếng Việt
Bạn làm như sau: Mở Regedit của windows và theo path này
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\Engines
tìm đến XBase và Paradox rồi sửa lại Value cho phần DataCodePage từ OEM thành ANSI. Rồi import lại xem, Chúc thành công.

Tác giả: Smartman
____________________________________________________________________________________
Thảo luận thêm: http://thuthuataccess.co.cc/forum

Lấy về địa chỉ tập tin

Hỏi: Tôi muốn lấy địa chỉ tập tin file *.mdb (*.mde) để sử dụng như một tham số vào vấn đề khác. Làm sao lấy được địa chỉ một cách tự động khi tập tin cứ luôn bị người dùng thay đổi?

Trả lời: Bạn tạo một hàm GetappPath() trong module với tham số là GetappPath để có thể sử dụng vào mục đích khác. Nội dung như sau:

Function GetAppPath() As String
On Error Resume Next

Dim DBName As String
Dim DBPath As String
Dim intCurrVal As Integer
Dim intLastPosition As Integer

DBName = CurrentDb.Name

intCurrVal = InStr(DBName, "\")
Do Until intCurrVal = 0
intLastPosition = intCurrVal
intCurrVal = InStr(intLastPosition + 1, DBName, "\")
Loop

DBPath = left(DBName, intLastPosition)
GetAppPath = DBPath

End Function
Nếu cần bạn có thể thêm hàm Msgbox() để thông báo đường dẫn hiện tại của tập tin mà ko phu thuộc vào sự thay đổi vị trí tập tin, tức là tham số GetAppPath cũng thay đổi theo sự thay đổi địa chỉ do người dùng.

MsgBox "Duong dan hien tai cua file nay la " & GetAppPath, vbOKOnly, "Thong bao"

Tác giả: PhilongXPCT
____________________________________________________________________________________
Thảo luận thêm: http://thuthuataccess.com/forum

Hiển thị user và computer name log in vào chương trình

Hỏi: Nếu mình muốn biết được hiện máy nào (theo tên máy) đang sử dụng chương trinh Access của mình hoặc số lượng người hiện đang sử dụng chương trình của mình thì có làm được không?

Đáp:

1) Để hiển thị user và computer name log in vào chương trình bạn phải dùng ADO recordset (không dùng DAO) do vậy bạn nhớ tham chiếu đến thư viện ADO 2.x

2) Dùng 1 loại ADO recordset đặc biệt , có tên là Schema Information để lấy thông tin user và computer name log in đang sử dụng chương trình.

3) Cách làm như sau :
- Bạn tạo 1 Form (F_TrackUsers) có 1 listbox và 2 command button.
+ Listbox tên : lboConnections
Đây là unbound Listbox, lưu ý : Row Source Type : value list.
+ 1 command button tên : CmdRefresh
+ 1 command button tên : CmdClose

- Ở cửa sổ viết code, nhập các đoạn code sau :

Private Sub cmdClose_Click()
DoCmd.Close
End Sub

Private Sub cmdRefresh_Click()
ListConnections
End Sub

Private Sub Form_Load()
ListConnections
End Sub

Private Sub ListConnections()
Dim cnn As ADODB.Connection
Dim rst As ADODB.Recordset
Dim strComputerName As String

Set cnn = CurrentProject.Connection
Set rst = cnn.OpenSchema(adSchemaProviderSpecific, , _
"{947bb102-5d43-11d1-bdbf-00c04fb92675}")
lboConnections.RowSource = vbNullString
lboConnections.AddItem "Computer Name;Login Name"

Do While Not rst.EOF
If rst("Connected") Then
strComputerName = rst("Computer_Name")
lboConnections.AddItem Left(strComputerName, _
InStr(strComputerName, vbNullChar) - 1) & ";" & rst("Login_Name")
End If
rst.MoveNext
Loop
rst.Close
Set rst = Nothing
Set cnn = Nothing
End Sub

4) Nguyên lý : Khi bạn mở form F_TrackUsers, Sub ListConnections() sử dụng ADO record đăc biệt đã nói ở trên để lấy thông tin và hiển thị trong listbox lboConnections.

tác giả: hungtano
____________________________________________________________________________________
Thảo luận thêm: http://thuthuataccess.com/forum

KeyCode các phím trên bàn phím

Một số bạn trong lập trình thường có nhu cầu bắt sự kiện các phím đặt biệt như Shift, ALT, Enter, Tab... và quan trọng là phải biết mã của chúng để chặn sự kiện. Thường ta ứng dụng việc này vào việc gán phím tắt cho 1 số hành động như thêm mới, sửa , xóa, in...
Ta có thể thiết kế 1 form để liệt kê code các phím thông thường như sau:
bạn vẽ lên form 1 ListBox với tên là List1, 1 commandButton tên Command1 và copy code sau vào form.

Đoạn code này sẽ ứng với các mã các ký tự trên bàn phím. Lưu Ý sẽ không lấy được các mã của phím đặt biệt do đó bạn phải nhớ các mã đó như: Enter: 13, Esc: 27, KeyUp -> Key Down: 37 ->40 ...

Code:
'Đoạn code này sẽ liệt kê mã của các ký tự trên bàn phím
Private Sub Command1_Click()
Dim i As Integer

For i = 1 To 255
List1.AddItem i & vbTab & Chr(i)
Next
End Sub

Để lấy được Các phím đặt biệt, bạn vẽ 1 text box. ví dụ tên text1
Ta có sự kiện keydow như sau:

Private Sub Text1_KeyDown(KeyCode As Integer, Shift As Integer)
Select Case (KeyCode)

Case "8"
MsgBox "Backspace Keyis " & KeyCode

Case "9"
MsgBox "Tap Key is " & KeyCode

Case "12"
MsgBox "Clear Key is " & KeyCode

Case "13"
MsgBox "Enter Key is " & KeyCode

Case "16"
MsgBox "Shift Key is " & KeyCode

Case "17"
MsgBox "Control Key is " & KeyCode

Case "27"
MsgBox "Esc Key is " & KeyCode

Case "32"
MsgBox "SpaceBar Key is " & KeyCode

Case "112"
MsgBox "F1 Key is " & KeyCode

Case "113"
MsgBox "F2 Key is " & KeyCode

Case "114"
MsgBox "F3 Key is " & KeyCode

Case "115"
MsgBox "F4 Key is " & KeyCode

Case "116"
MsgBox "F5 Key is " & KeyCode

Case "117"
MsgBox "F6 Key is " & KeyCode

Case "118"
MsgBox "F7 Key is " & KeyCode

Case "119"
MsgBox "F8 Key is " & KeyCode

Case "120"
MsgBox "F9 Key is " & KeyCode

Case "121"
MsgBox "F10 Key is " & KeyCode

Case "122"
MsgBox "F11 Key is " & KeyCode

Case "123"
MsgBox "F12 Key is " & KeyCode

Case Else
MsgBox "you is press " & KeyCode

End Select

End Sub

Chú ý: bạn có thể thay các Messege box bằng các đoạn code/lệnh mình muốn khi có sự kiện đó xảy ra trên form textbox đó. Thủ thuật này có thể áp dụng với các Object khác của Access có hỗ trợ event key enter/ keyup/ keydown...
____________________________________________________________________________________
Thảo luận thêm: http://thuthuataccess.com/forum

Tạo combo box để chọn các ổ đĩa có trên máy tính

Cách tạo combo box để chọn ổ đĩa trên máy tính (tự động đưa tất cả các ổ đĩa trong máy vào combo box)
đầu tiên tạo 1 combo box tên là (ví dụ là thế) : Mydrive

Private Sub MyDrive_GotFocus()
    Dim fs, d, dc, s, n
    Set fs = CreateObject("Scripting.FileSystemObject")
    Set dc = fs.Drives
    n = 0
    For Each d In dc
        s = s & d.DriveLetter & ":,"
        n = n + 1
    Next
MyDrive.ListRows = n
MyDrive.RowSourceType = "Value list"
MyDrive.RowSource = s
End Sub

xem demo: DownLoad
Thủ Thuật này được sưu tầm từ blog sưu tầm bài viết
____________________________________________________________________________________
Thảo luận thêm: http://thuthuataccess.com/forum

Vài nét về Access 2000


Vài nét về Access 2000


PCWorld VN 5/99
    Từ đầu 1999, Microsoft giới thiệu bản beta 3 của Office 2000, trong đó chiếm vị trí xứng đáng là Access 2000 hay còn gọi là Access 9. Những tin tức đầu tiên về công cụ phát triển ứng dụng khách/chủ này chủ yếu tập trung vào 2 công nghệ mới với tên khá giống nhau là Microsoft Access Data Projects (ADPs) và Data Access Pages (DAPs). Cơ sở dữ liệu client/server dựng bằng Access bây giờ có thể được lưu trong tập tin với dạng thức kiểu mới gọi là .ADP (người dùng muốn dùng dạng thức .MDB cũng vẫn được). Tập tin ADP chứa các mẫu biểu (form), báo biểu (report) và các thành phần khác của giao diện, trong khi bảng và query (còn gọi là câu hỏi hoặc bảng truy vấn) được đưa vào tập tin dữ liệu của SQL Server 7. Nếu như trước kia, Access dùng Jet engine để truy xuất dữ liệu, thì nay có thể thay cơ cấu này bằng SQL Server 7, một cơ cấu CSDL mới, rất mạnh của Microsoft. Tất nhiên, vẫn có thể dùng Jet engine, nhưng cơ cấu này cũng được nâng cấp (phiên bản mới có số hiệu 4.x): ngôn ngữ SQL theo chuẩn ANSI 92, thêm khái niệm VIEW (khung nhìn), Procedure (thủ tục lưu sẵn), khái niệm giao tác phía Server, rồi một loạt kiểu dữ liệu mới như của SQL Server như Decimal,... Access 2000 hỗ trợ bộ ký tự Unicode mà không cần đến các phần mềm bổ trợ (add-on). Công cụ Access Upsizing Wizard nổi tiếng nay đã trở thành bộ phận cấu thành Access 2000 mà không cần phải tải xuống (download) từ cơ sở Web của Microsoft (đây là công cụ dùng để nâng cấp CSDL Jet hiện có lên SQL Server). Tập tin .MDB của Access 2000 không đọc được trong các phiên bản cũ của Access. Bạn có thể chuyển đổi tập tin .MDB thành .ADP một cách dễ dàng, tuy nhiên ngược lại thì không được. Tập tin .ADP cho phép vận hành Access như một ứng dụng mặt trước (front-end) trong khi SQL Server 7.0 dùng làm back-end (mặt sau, hậu trường). Không cần phải nạp cơ cấu Jet khi thực hiện ADP.
    Còn DAP (Data Access Pages) thì sao? Nó cho phép dùng Access để tạo các ứng dụng Web động trên nền ASP (Active Server Pages). Truy xuất dữ liệu bây giờ chủ yếu thực hiện qua giao diện mới là ADO (Active Data Object). Có thể coi đây là mô hình đối tượng được sử dụng để làm việc với dữ liệu qua công nghệ OLE DB của Microsoft.
    Bài báo này đề cập đến những tính năng mới được coi là thú vị nhất của Access 2000.
Đặc tính Dirty
    Chắc bạn đã làm quen với đặc tính Dirty của mẫu biểu Access (chỉ có từ Access 97). Nó cho phép từ chương trình viết bằng VBA (Visual Basic for Applications) có thể xác định được dữ liệu trong bản ghi hiện thời (gắn với mẫu biểu) có thay đổi và đã được ghi chưa. Bạn có thể sửa đặc tính này thành giá trị False từ mã VBA để ghi bản ghi đó ra đĩa. Thế nhưng nếu ta cần thực hiện thao tác gì đó khi mẫu biểu bị thay đổi thì làm thế nào? Access 97 không có sự kiện gọi là Dirty. Chắc bạn đã đoán ra: Access 2000 cuối cùng đã có sự kiện (event) như vậy. Thủ tục xử lý sự kiện Dirty có tham biến Cancel. Khi cần có thể thực hiện lệnh Cancel = True để thôi (undo) không làm những gì vừa thay đổi với bản ghi hiện thời nữa. Nhược điểm lớn của sự kiện Dirty là nó chỉ xảy ra khi người nhập liệu bắt đầu sửa nội dung mẫu biểu, chứ không xảy ra khi đặc tính Dirty bị sửa thành False từ chương trình. Nên có thêm sự kiện, chẳng hạn với tên là Undo để xử lý tình huống đó (bạn có thể lấy trình FrmSmp 97.exe từ cơ sở Web của Microsoft để tham khảo cách mô phỏng sự kiện After undo, nó sử dụng một điều khiển tính toán với biểu thức là một hàm người dùng, và hàm này thực hiện mỗi khi đặc tính Dirty của Access 97 thay đổi. Dùng kỹ thuật này để mô phỏng sự kiện Dirty, nhưng không hiệu quả bằng sự kiện Dirty cài sẵn trong Access 2000).
Định dạng có điều kiện
    Đây là một trong những tính năng mà người lập trình Access khao khát nhất: làm thế nào để chỉ một số dòng nào đó (ví dụ: các dòng với doanh số lớn hơn 10.000 USD) của mẫu hoặc báo biểu hiện lên với dạng thức theo ý muốn (ví dụ: có màu đậm hơn các dòng khác, để dễ đọc, dễ nhấn mạnh). Với report thì có thủ thuật thêm cấu trúc IF - Endif vào thủ tục xử lý sự kiện Format của vùng (section) chứa hộp văn bản cần kiểm tra và nhấn mạnh. (Nếu thỏa điều kiện nào đó thì sửa các đặc tính Font, Fontweight,... của điều khiển cần định dạng riêng.) Tương tự, đối với mẫu biểu kiểu Single (đơn bản ghi) đưa nhóm lệnh kể trên vào thủ tục xử lý Current. Thế nhưng với kiểu mẫu biểu liên kế (Continuous Form, mẫu biểu đa bản ghi), làm như vậy sẽ thay đổi dạng thức cho tất cả các bản ghi! Nghĩa là không đạt được mục đích đặt ra. Vâng, bây giờ thì nhóm Access của Microsoft đã lắng nghe nguyện vọng của người sử dụng: có cách dễ dàng định dạng có điều kiện (conditional formatting) cho từng hộp văn bản, từng hộp chọn combo, từng dòng một. Có thể xác lập nhiều điều kiện định dạng cho mỗi điều khiển (dưới dạng biểu thức luận lý hay trạng thái điều khiển đang trong focus hay không). Nếu có vài điều kiện đúng, sẽ chọn điều kiện đúng đầu tiên kể từ đầu danh sách. Bạn đặt điều kiện trong cả chế độ Design (khi thiết kế) hoặc chế độ Form (khi mở mẫu biểu để làm việc, tức là định dạng động, trong thời gian vào hoặc xem số liệu). Hình 1 cho thấy hộp thoại Conditional Formatting mở ra khi người dùng nhập liệu hoặc xem form bằng cách vào thực đơn Format, sau đó chọn Conditional Formatting.

Hình 1. Hộp thoại định dạng có điều kiện mở trong Form View.
    Người lập trình VBA có thể kiểm soát việc định dạng có điều kiện bằng cách thêm hoặc loại bỏ các đối tượng kiểu FormatCondition từ sưu tập (collection) FormatConditions, và đặt lại các đặc tính của các đối tượng đó. Ví dụ, để doanh số hàng tháng trở nên đậm và có màu xanh dương nếu lớn hơn hạn mức nào đó (nhập sẵn vào hộp văn bản textMonthQuota của mẫu biểu), ta dùng đoạn mã sau:
With Forms!frmSales!txtMonthSales. FormatConditions
.Add(acFieldValue, acGreaterthan, "=[txtMonthQuota]")
.FontBold = True
.ForeColor = vbBlue
End With
Gắn mẫu biểu với bộ bản ghi
    Ngay trong Access 2.0, người lập trình đã có nhiều cách để kiểm soát đặc tính Record Source của mẫu biểu. Bạn có thể gửi tên bảng, tên câu hỏi (query) hoặc cả một lệnh SELECT của SQL vào đặc tính này để đặc tả nguồn bản ghi. Với bộ bản ghi (recordset) tính được trong VBA thì rắc rối hơn: phải tạo bảng trung gian hoặc bảng truy vấn chứa dữ liệu cần thiết. Trong Access 2000 có thể gắn recordset tính được với mẫu biểu. Access 97 truy cập bộ bản ghi của mẫu biểu bằng đặc tính RecordsetClone, nhưng vẫn chưa có đặc tính Recordset để ấn định bộ bản ghi nguồn.
    Tính năng mới này của Access 2000 rất linh hoạt, nhất là khi tạo recordset trong một giao tác (transaction). Khi quay lui (roll back) toàn giao tác, tất cả các thay đổi làm với mẫu biểu dựa trên bộ dữ liệu tạo trong giao tác, kể cả các thay đổi tác động đến nhiều bản ghi, sẽ được hủy. Trong quá khứ thì cực kỳ vất vả: thông thường phải dùng bảng trung gian hoặc mảng dữ liệu.

Hình 2: Đăng ký chế độ Compact on Close và tập tin Access sẽ được nén tự động khi đóng (miễn là không có ai khác vẫn còn mở nó).
    Có thể gắn một recordset với vài mẫu biểu một lúc. Dữ liệu trong mẫu biểu này sẽ tự động đồng bộ với dữ liệu trong form khác, mà quay lui giao tác cũng sẽ quay lui đối với dữ liệu trong tất cả các biểu mẫu. Ngay cả khi dùng các recordset với nhiều mẫu biểu mà các recordset đó được tạo trong một giao tác, chúng cũng rollback được cùng nhau.
    Trong các tập tin ADP, các bộ bản ghi ADO có thể cập nhập (sửa) được qua form nếu các ADP này được tối ưu để dùng OLE DB tương tác với SQL server. Nếu dùng Jet (tức dùng tập tin .MDB) vẫn phải sử dụng DAO (Data Access Object) để tạo recordset kiểu read/write cho mẫu biểu, còn dùng bộ bản ghi ADO cho mẫu biểu của .MDB thì nó chỉ có tính read-only mà thôi. Có lẽ Microsoft phải khắc phục nhược điểm này của ADO trong MDB khi đưa ra phiên bản cuối cùng của Access 2000.
Tự động nén tập tin
    Đây là tính năng rất hữu ích, và để Access làm được điều đó khi đóng tập tin, chỉ việc vào trang General của hộp thoại Options, sau đó đánh dấu chọn ô Compact on Close (xem hình 2).
    Thông thường ứng dụng Access được đưa vào MDB mặt trước (chứa query, form, report, module), dữ liệu tách ra tập tin MDB riêng (mặt sau). Đóng tự động được thực hiện cho MDB mặt trước, và cần một thủ thuật nhỏ để đảm bảo nén cả tập tin dữ liệu ở mặt sau. Access đòi hỏi tập tin này phải được mở trong chế độ Exclusive (độc chiếm) thì mới thực hiện compact được, tuy nhiên, ít nhất là từ nay không cần phải lo nén tập tin mặt trước nữa.
Tại sao lại phải thường xuyên nén dữ liệu, trong khi bạn không tạo bảng tạm thời nào cả? Trên thực tế, Access tạo rất nhiều đối tượng trung gian và làm cho CSDL liên tục phình ra, do đó bị phân đoạn và giảm hiệu năng truy xuất.

Hình 3. Từ nay Access 2000 dùng chung VBE với các thành phần khác của Office 2000.
    Cần lưu ý rằng, nén tập tin có tác dụng đánh dấu các query cần được biên dịch lại trong lần vận hành kế tiếp. Khi bảng truy vấn lần đầu thực hiện sau khi thiết kế, cơ cấu Jet thiết lập một lược đồ truy vấn dựa trên các số liệu thống kê thu thập được. Nếu có ít số liệu (thường ban đầu dùng số liệu thử do người lập trình tự tạo), lược đồ truy vấn không sử dụng các chỉ mục mà trực tiếp quét các bản ghi trong bảng từ đầu đến cuối vì như thế sẽ nhanh hơn. Khi hệ thống áp dụng cho số liệu thực, liên tục biến đổi tăng dần, lược đồ truy vấn cũ không còn thích hợp nữa. Chính việc nén tập tin có tác dụng thiết lập lược đồ truy vấn mới cho query, dựa trên tình trạng mới nhất của số liệu. Nén cũng kéo theo việc sửa chữa tập tin, do đó không cần thực hiện thao tác repair như trong Access 2.0. Nén tự động sẽ không thực hiện nếu không tiết kiệm thêm ít nhất 256 KB.
Bộ soạn thảo VBE
    Làm việc với Word, Excel chắc bạn đã quen thuộc với Visual Basic Editor (VBE). Bản thân Access 95 đã chấp nhận VBA nhưng đến Access 97 vẫn trung thành với giao diện soạn thảo chương trình theo kiểu riêng. Kể từ Office 2000, tất cả các ứng dụng cấu thành của nó, trong đó có Access, đều chung VBE với giao diện thống nhất (Hình 3).
Dường như đây là một bước thụt lùi của Access 2000. Trong phần lớn các phần mềm dùng VBA, các mẫu biểu được thiết kế ngay trong VBE. Người lập trình Access 2000 buộc phải tiến/lui giữa cửa sổ VBE và cửa sổ Access nơi có các mẫu biểu đang mở.
Nếu là người lập trình từng trải, bạn có thể đóng cửa sổ Properties trong VBE vì nó chỉ lặp lại những gì có trong các hộp thoại Access, không có cả các đặc tính của điều khiển. Cửa sổ Project Explorer theo phong cách treeview (dạng cây) thật hữu ích khi làm việc với các CSDL thư viện. Ngoài ra, Project Explorer còn có khả năng mở rộng, cho thấy cả những gì không có trong cửa sổ CSDL của Access. Bạn có thể làm cho cửa sổ Immediate di động tự do giống như cửa sổ Debug trước kia. Nếu không thấy cánh Locals hoặc Watch, có thể mở chúng trong cửa sổ riêng nằm trên phần còn lại là cửa sổ soạn thảo mã VBA, không khác gì mấy so với trong Access 97, ngoại trừ việc các đơn thể chỉ mở trong khuôn khổ của cửa sổ VBE mà thôi. Bản thân VBE có gì mới? Hấp dẫn nhất là thanh công cụ Edit: có cặp nút lệnh cho phép thêm/bớt câu chú thích cho toàn khối mã chỉ bằng một thao tác nhấp chuột. Điểm mới nữa là thanh Edit cho phép đặt thẻ đánh dấu (bookmark) vào văn bản mã trình. Khi cần thiết có thể dễ dàng quay lại những nơi đã đặt dấu, hoàn toàn chỉ bằng cách nhấp vào lệnh tương ứng trong thanh công cụ. Thực ra thì bookmark đã có từ thời Access 97 nhưng thao tác đặt lại nằm sâu trong hệ thống thực đơn, do đó nhiều người không để ý và chẳng bao giờ dùng đến.

Hình 4. CurrentProject cho phép nhận được nhiều thông tin mới, rất hữu ích.
    VBE còn hỗ trợ mô hình các công cụ bổ trợ linh hoạt, cho phép tùy biến môi trường mã hóa của người lập trình. Bạn tự động thêm khối chú thích hoặc đoạn mã xử lý sự kiện trong thủ tục hoặc hàm mới? Có thể tự tạo add-in để làm điều đó hoặc dùng các công cụ do người khác cung cấp. Dự đoán sẽ có nhiều phương tiện bổ trợ cho VBE xuất hiện trên thị trường.
AutoCorrect
    Giả sử bạn dùng trường tên là CustomerID trong một hoặc nhiều bảng. Một ngày nào đó bạn muốn đổi tên nó thành ClientID. Như trước kia thì thật vất vả: phải rà soát tất cả bảng truy vấn (query) để tìm các trường CustomerID đang sử dụng và đổi nó thành tên mới. Cũng làm như vậy với các form và report để thay lại các nguồn điều khiển gắn với trường cũ. Tin tốt lành cho chúng ta: Access 2000 tự động đổi tên trường trong tất cả các thành phần liên quan kể trên, nhưng đừng vội hân hoan: hệ chưa mò được tới các module, các đoạn mã lập trình và recordset. Bạn vẫn phải sửa bằng tay như thường làm, do đó đừng vội vứt bỏ SpeedFerret, một sản phẩm thứ ba, vì chắc nó vẫn còn hữu hiệu cho Access 2000. Tuy nhiên, dùng AutoCorrect của Access 2000 một cách thận trọng cũng giúp được nhiều việc.

Hình 5: Jet 4.0 bây giờ hỗ trợ khóa chốt đến từng bản ghi.
CurrentProject
    Có một đối tượng mới trong Access 2000 gọi là CurrentProject.
    Những ai từng lập trình DAO chắc khá quen thuộc với hàm CurrentDb () để tham chiếu tới CSDL đang mở. CurrentProject cũng có mục đích tương tự nhưng áp dụng cho cả ADO, ví dụ CurrentProject.Connection trả lại đối tượng kết nối ADO. Ngoài ra còn có CodeProject gần giống với hàm CodeDb () trong DAO để dùng với các add-in. Ta thực hiện một số lệnh sau để kiểm tra trong cửa sổ Immediate:
?CurrentProject.FullName
D:\Temp\db1.mdb
?CurrentProject.Name
db1.mdb
?CurrentProject.Path
D:\Temp
    Đối tượng CurrentProject có các sưu tập (collection) chứa tất cả các đối tượng khác của Access, chẳng hạn AllForms, AllReports, AllMacros, AllModules và AllDataAccessPages. Mỗi sưu tập này chứa các đối tượng kiểu AccessObject với nhiều đặc tính (Property) rất hữu ích:
?CurrentProject.AllForms.Count
6
?CurrentProject.AllForms (5) .Name
frmQuery1
?CurrentProject.AllForms (5) .IsLoaded
False
    Còn với các bảng và query thì sao? Có đối tượng khác gọi là CurrentData với đặc tính là các sưu tập AllTables, AllQueries, rồi cả AllViews, AllStoredProcedures và AllDatabaseDiagrams.
    Tuy CurrentProject.Connection trả về đối tượng kết nối ADO, đặc tính ngầm định của đối tượng kết nối này là một chuỗi kết nối (Connect String):
?CurrentProject.Connection
    Kết quả là một chuỗi ký tự bao gồm các tham trị chia tách nhau bằng dấu chấm phẩy (;). Dùng các hàm mới của Access 2000, trong đó có Split() và Filter() để tách và lấy ra các tham trị cần thiết.
Các hàm chuẩn mới
    Một loạt hàm mới được bổ sung so với Access 97, dùng để xử lý xâu ký tự: Replace(), InstrRev(), StrReverse(), Split(), Join() và Filter().
    Hàm Replace() dùng để thay xâu con của xâu nào đó bằng xâu con mới:
?Replace ("a1a2a3a4a5", "a", "b")
b1b2b3b4b5
?Replace ("a1a2a3a4a5", "a", "b", 3)
b2b3b4b5
?Replace ("a1a2a3a4a5", "a", "b", 3, 2)
b2b3a4a5
    Trong 2 ví dụ cuối, số 3 ở tham số thứ tư dùng chỉ vị trí từ đó bắt đầu cần thay thế (và kết quả cũng từ đó luôn). Tham số thứ 5 trong ví dụ cuối cùng để đặc tả số lần phải thay (xâu con có thể hiện diện nhiều lần).
    Hàm StrReverse() trả về chuỗi ký tự đảo ngược (không rõ thực tế ứng dụng CSDL có cần đến hàm này không?).
?StrReverse ("abcdef")
fedcba
    Hàm InstrRev() có cơ chế làm việc như Instr() nhưng tìm kiếm ngược từ ký tự cuối đến ký tự đầu (hàm này thì thật sự cần thiết):
?InstrRv ("C:\MyPath\MyFile.ext", "\")
10
    Các hàm Split(), Join() và Filter() cũng rất hữu ích. Split() chuyển đổi xâu ký tự có chia tách (bởi một ký tự nào đó, ví dụ dấu chấm phẩy) thành mảng các giá trị. Join() thực hiện thao tác ngược lại, từ mảng trở về xâu ký tự có chia tách. Filter() trích từ mảng các dòng thỏa mãn điều kiện nào đó.
? Filter(Split(CurrentProject.Connection, ","), _
"Data Source = ") (0)
Data Source=D:\Temp\db1.mdb
    Thêm một bước nữa thì nhận được trị của Data Source.
? Split (Filter(Split(CurrentProject .Connection, ";"), _
"Data Source = ") (0) ,"=") (1)
D:\Temp\db1.mdb
    Nếu đã quen thuộc với ngôn ngữ Lisp, chắc bạn rất hài lòng với bộ hàm trên và tự hỏi: tại sao đến bây giờ VBA mới có được khả năng phân tích xâu ký tự như vậy?
Ngang ngửa với VB 6
    Những năm qua, người lập trình Access dùng VBA luôn lạc hậu so với Visual Basic, do đó có ý nghĩ cho rằng VBA là một tập con của VB. Sự thực thì VBA và VB luôn là một, có điều là người lập trình Visual Basic có được phiên bản mới hơn một chút. Giờ đây Microsoft đảm bảo Visual Basic và VBA đồng bộ với nhau. Như vậy, Office 2000 có ngôn ngữ lập trình hoàn toàn ngang ngửa với VB 6.0. Dưới đây tóm lược một số tính năng nâng cao của VBA mới.
- Bộ chuyển AddressOf cho phép truyền địa chỉ hàm gọi ngược cho thủ tục khác.
- Lệnh Implements được dùng với các đơn thể lớp để đặc tả lớp (class) thực thi giao diện (tập hợp các đặc tính và phương thức) định nghĩa trong một lớp khác. Như vậy, VBA bây giờ có tính năng đa hình (polymorphism), nghĩa là giao diện chuẩn có hành vi khác trong các lớp khác nhau.
- Với Access 97, bạn có thể dùng khai báo With Events trong đơn thể lớp để tạo khoang sự kiện, do đó đơn thể lớp đó có thể bắt được sự kiện phát sinh trong đối tượng. Từ nay có thể dùng lệnh Event để khai báo sự kiện theo ý muốn và lệnh RaiseEvent để làm cho nó xảy ra bất cứ lúc nào.
- Từ khóa Enum cho phép khai báo các hằng liệt kê, chẳng hạn như các hằng bắt đầu bằng ac, vb hoặc db. Các biến và tham số sau đó có thể được đăng ký với kiểu mới.
- Từ khóa Friend thêm lựa chọn thứ ba vào danh sách các khai báo (trước đó có Public và Private) khi cần định nghĩa phạm vi của thủ tục trong đơn thể lớp. Các thủ tục có tính Friend thấy được từ tất cả các thủ tục khác trong dự án. Khác với Public, chúng không thấy được bên ngoài dự án. Nếu dùng VBA để dựng các Com server cho các trình khác sử dụng, phạm vi kiểu Friend rất hữu ích.
- Đối tượng Debug có thêm phương thức Assert dùng đặt điểm ngắt vào mã trình. Ngắt sẽ xảy ra khi tại điểm đó biểu thức đặc tả trong Assert cho trị False. Lại thêm một cách mới để gỡ rối chương trình khi khó tìm được lỗi.
Jet Engine 4.0
    Gần đây có nhiều tin đồn về giờ tận thế của Jet, một cơ cấu CSDL vốn dùng từ lâu nay cho Access (ở ta hay lẫn Jet với Access và một vài cổ động viên còn sót lại của FoxPro for DOS đã vội hoan hỉ là Access sắp chết - ND). Trên thực tế, người dùng được lựa chọn trong 2 khả năng: dùng Jet Engine 4.0 hoặc SQL Server 7.0 làm động cơ cho Access. Phiên bản 4.0 của Jet mạnh mẽ hơn bao giờ hết. Nhiều ứng dụng Access có thể không cần đến cơ cấu SQL Server 7 quá mạnh, và nhóm Jet của Microsoft vẫn nỗ lực hoàn thiện sản phẩm truyền thống của mình.
    Động cơ Jet mới cho phép khóa chốt đến từng bản ghi (xem tùy chọn tương ứng trong trang Advanced thuộc hộp thoại Options ở hình 5).
    Người dùng các phần mềm CSDL khác thường chê Access (đúng hơn: cơ cấu Jet của nó) là chỉ khoá chốt được toàn trang (page-level locking) khi bản ghi nào trong đó cần khóa. Từ Jet 3.x trở về trước một trang CSDL có dung lượng 2 KB. Jet 4.0 dùng trang 4 KB do đó nếu không hỗ trợ khóa chốt đến từng bản ghi thay cho việc khóa chốt toàn trang (gấp đôi so với trước), nhiều bản ghi sẽ bị "khóa lây". Dung lượng trang 4 KB là để hỗ trợ bộ mã Unicode, vì theo bộ này mỗi ký tự sẽ chiếm 2 byte. Giờ đây kích cỡ tối đa của tập tin MDB được nâng lên 2 GB so với 1 GB trước đây.
    Việc khóa chốt theo kiểu nào cũng được Jet xác định một cách thông minh. Nếu có nhiều bản ghi trong trang 4 KB nào đó được khóa chốt, Jet tự động khóa cả trang cho hiệu quả, thậm chí nó khóa chốt cả bảng luôn nếu không có người dùng nào khác trên mạng truy cập bảng đó (đúng hơn: đang sửa - ND)
    Nhiều đặc tính mới khác của Jet chủ yếu định hướng cho mô hình đối tượng ADO mà Microsoft khuyến nghị sử dụng thay cho mô hình DAO. Mô hình DAO cũ sẽ vẫn còn và giữ được giá trị sử dụng, nhất là về phương diện bảo mật và nhân bản (replication).
    Dùng ADO nghĩa là sử dụng ngôn ngữ SQL đổi mới. Dưới đây liệt kê một số điểm mới của SQL theo chuẩn ANSI-92 mà Jet 4.0 hỗ trợ.
- Thêm các lệnh GRANT, REVOKE cho mục đích bảo mật
- Thêm các lệnh CREATE và DROP để tạo và loại bỏ câu hỏi (VIEW và PROCEDURE)
- CASCADE dùng để định nghĩa tính toàn vẹn tham chiếu
- Ràng buộc CHECK dùng để đặc tả biểu thức luận lý mà các bản ghi trong một hoặc nhiều bản phải thỏa.
- Các lệnh ALTER TABLE và ALTER COLUMN để sửa cấu trúc bảng mà không phải thêm cột mới, sao chép cột cũ sang và loại bỏ cột cũ như trước kia.
- IDENTITY là từ khóa đặc tả tính tăng tự động của cột
- Thêm một số kiểu cột mới để tương hợp với SQL Server, ví dụ kiểu Decimal
Có nên nâng cấp?
    Khi bài báo này đến tay bạn đọc, vẫn còn quá sớm để phán xét về phiên bản mới của Access. Một số tính năng đề cập ở trên có thể không có trong bản chính thức của Access 2000 hoặc khác đi một chút. Việc tích hợp VBE với Access còn gây nhiều bàn cãi nhưng những bước tiếp theo có thể làm cho tính năng này sáng giá hơn. Microsoft rõ ràng là đã chuyển hẳn sang hướng xây dựng phần mềm tận dụng tối đa các thành phần dùng chung, tất nhiên có lúc phải trả giá về hiệu năng.
    Ai đã từng trải qua thời kỳ lập trình trong Access 2.0 mới càng thấu hiểu giá trị của các thành phần nhiều mega đó, tuy chúng còn ở mức sơ khai và hy vọng hoàn chỉnh hơn theo đà hoàn thiện mô hình đối tượng chung của Microsoft. Sản phẩm mới chịu búa rìu dư luận là chuyện thường tình, và Access 2000 không là ngoại lệ.
Tô Tuấn
Theo Access-Office-VB Advisor 4/1999

____________________________________________________________________________________
Thảo luận thêm: http://thuthuataccess.co.cc/forum