Thứ Tư, 27 tháng 1, 2010

Bắt lỗi khi nhập liệu sai và người dùng đóng form

Hỏi: Tôi tạo form nhập liệu, và muốn bắt lỗi khi người dùng nhập sai nhưng muốn đóng form thì hiện ra thông báo : " Có dữ liệu bị lỗi, Bạn có muốn đóng không?" Vậy tôi phải bắt lỗi thế nào?

Đáp:
(theo hướng dẫn anh phatnq2002 trên DKT)
Để bắt được lỗi này, bạn phải kết hợp hai sự kiện Form_Error và Form_UnLoad.


Bạn nên tạo một biến dùng chung cho module (nghĩa là khai báo ở vùng Declaration của Form Module), ví dụ Dim hasError As Boolean chẳng hạn

Cái sự kiện bạn bắt bao gồm 2 cái:

1. là Form_Unload: sự kiện này xảy ra trước Close. Do vậy nếu Cancel=True ở đây đồng nghĩa với việc hủy Close.

Private Sub Form_Unload(Cancel As Integer)
If hasError Then
If MsgBox("Có dữ liệu bị lỗi, Bạn có muốn đóng không?", vbYesNo) = vbNo Then
Cancel = True
Else
DoCmd.RunCommand acCmdUndo
End If
End If
End Sub

2. là Form_Error

Private Sub Form_Error(DataErr As Integer, Response As Integer)
Respponse = acDataErrContinue
hasError = True
Select Case DataErr
Case 2169
MsgBox "Co loi khi luu du lieu."
Case Else
MsgBox "Err No." & DataErr & vbCrLf & "Err Mess: " & Error(DataErr)
End Select
End Sub

Gọi 1 đoạn code từ menu tự tạo

Hỏi: Tôi có 1 đoạn code trước đây tôi gọi từ form. Nhưng giờ chuyển sang menu thì làm sao gọi được.
Đáp: Để gọi 1 đoạn code từ menu, yêu cầu đoạn code của bạn phải là 1 function.
Ví dụ:
Function QuanlyUser()
If GetUserLevel > 1 Then
DoCmd.Close
DoCmd.OpenForm "frmQuanlyUser", , , "[UserLevel] <" & GetUserLevel
Else
msgBoxOK DLookup("[NDUNG1]", "tblTHONGBAO", "[SOTB] = 9") & vbCrLf & DLookup("[NDUNG2]", "tblTHONGBAO", "[SOTB] = 9"), vbCritical, DLookup("[TIEUDE]", "tblTHONGBAO", "[SOTB] = 9")
End If

End Function

Sau đó, tạo 1 macro và gọi function này bằng hàm runcode . Rồi lưu lại đầy đủ trường name, action...
(trong ví dụ mình lưu Macro tên Dulieuhethong)



Giờ bạn có thể tạo 1 memu gọi macro này bằng cách click chuột phải lên thanh menu, chọn custonmise, Sau đó chọn như hình, lôi Item muốn run lên vị trí tùy ý trên thanh bar tự tạo.


Chương trình demo xin phép modifield lại của bạn HaQuocQuan. :
ID: Admin
Pass: Admin
Pass enable/disable phím shift và VBA : ladieskiller
Demo

Các bạn có thể tham khảo thêm bài:
Thiết kế chương trình phân quyền  để biết thêm về cơ chế phân quyền trong demo này.
Cũng tham khảo thêm bài: Hướng dẫn tạo menu để biết cách tự tạo 1 menu

Chủ Nhật, 24 tháng 1, 2010

Cách lấy (copy)database tại một máy trên mạng Lan

Hỏi:
Mong được sự giúp đỡ của các cao thủ lập trình Access :
_ Đến 1 ngày nhất định trong tháng phải copy database Access từ một máy tính trên mạng Lan; tên máy tính là computerA và địa chỉ IP tĩnh 192.168.1.30, máy tính A share thư mục với tên "Baocao" và tên file cần lấy là "Nhaplieu.mdb".
( tức là \\computerA\Baocao\nhaplieu.mdb hoặc \\192.168.1.30\Baocao\nhaplieu.mdb )
+ Mình muốn tạo một button khi click vào sẽ tự động lấy file ấy về và để tại máy tính mình tại thư mục gốc ổ D.

Đáp:

Trong sự kiện Click, bạn cho dòng lệnh sau vào
Dim RCopy as String
Rcopy= "Copy \\computerA\Baocao\nhaplieu.mdb D:\nhaplieu.mdb"

Shell RCopy,vbHide

Hoặc:
Call FileCopy("\\computerA\Baocao\nhaplieu.mdb", "D:\nhaplieu.mdb")

Trong trường hợp máy copy yêu cầu nhập userName/ pass để được vào copy thì bạn dùng cách Map 1 ổ đĩa mạng, rồi copy từ đó về máy.
Xem thêm cách map ổ đĩa mạng với UserName/ Pass

Map Network Drive

Hỏi: Mình có làm 1 chương trình file Data để trên mạng LAN, do đó nếu người dùng không map ổ mạng thì sẽ không thể chạy chương trình được. Các bác cho em hỏi có đoạn code nào gọi cửa sổ Map network Drive lên được không?
Đáp:
Điều kiện là thư mục của bạn phải được share trước.
Sub MapNetwork(D As String, s As String, UserName As String, Password As String)
'd : diver
's: Map Address
Dim p As String
p = "net use " & D & ": \\" & s
Shell p, vbHide
MsgBox "Map " & D & " successful"
End Sub

Bây giờ, giả sử dữ liệu mình nằm trên server trong thư mục DuLieu. UserName Mình là Noname. Pass Của mình là khpt. Mình muốn gán ổ đĩa G làm ổ mạng. mình sẽ gọi Thủ tục map network Drive như sau:
MapNetwork "G", "server\dulieu", "Noname", "khpt"

Thứ Năm, 14 tháng 1, 2010

Chặn ghi trùng dữ liệu với Validaction

Hỏi:
Trong Validation Ruler của Table hoặc Form có cách nào để chặn không cho ghi dữ liệu trùng không các bạn.
Vì mình có 1 Form nhập thông tin khách hàng,mình không muốn số Mobile trùng nhau ,nên mình muốn đặt Validation Ruler không cho nhập trùng có được không,để nếu mình gõ sai nó báo luôn.Các bạn đừng bảo mình thiết lập khóa chính hay Index (No duplicates) nhé ,vì như thế phải nhập hết thông tin của 1 bản ghi thì nó mới báo trùng,như thế mất công lắm.
 Đáp:
Giả sử bạn có 1 table là Customers(maKH,phone,...), 1 form tên là Customers
Trong form nhập thông tin khách hàng, textbox phone Bạn đặt các thuộc tính như sau:
ValidationRule property:
DLookUp("[Phone]","Customer","[Phone] = '" & [Forms]![Customer]![Phone] & "' and [CusID] <>'" & [Forms]![Customer]![CusID] & "'") Is Null


ValidationText property : "Số phone này đã được nhập , vui lòng xem lại"

Demo

Thứ Tư, 13 tháng 1, 2010

Tùy Biến Các Cột thay đổi trong Report

Bài này được Copy của tác giả paulsteigel bên Giải Pháp Excel. Thấy hay nên Copy về đây cho mọi người tham khảo.
Đó là 1 report có format tương đối giống nhau, nhưng có các cột thay đổi tùy ý. Xin copy lại nguyên gốc tác giả. Và thắc mắc có thể vào link gốc để hỏi trực tiếp tác giả!
Link Gốc

Trích:
Lâu lắm rồi không làm việc với Access, hôm nay có việc phải quay lại làm mấy việc với công cụ báo cáo Report của Access, tiện thể tôi xin chia sẻ một kỹ năng nho nhỏ với các bạn đã và đang, có thể sẽ sử dụng nhiều Access trong công việc.
Đã bao giờ các bạn nghĩ đến việc sử dụng một báo cáo cho nhiều mục đích, hiển thị nhiều báo cáo có bố cục tương đối giống nhau không?
Nếu bạn phải trình bày bảng dữ liệu có quá nhiều cột, bạn muốn báo cáo tự động chia số cột cho đều theo khổ giấy A4, phần còn thừa sẽ chuyển sang báo cáo mới và căn chỉnh độ rộng cột cho phù hợp?
...vv
Thường thì mọi người sẽ xác định giải pháp là: thôi thì tạo ra hàng loạt mẫu báo cáo để có thể dự phòng được trường hợp số cột là nhiều nhất, các báo cáo này giống nhau căn bản và chỉ khác nhau mỗi cái tên.
Với yêu cầu này, tôi xin giới thiệu một cách sử dụng tính năng thừa kế lớp của đối tượng Report (chắc các cao thủ thì đều đã làm rồi, mạo muội xin giới thiệu lại và hy vọng sẽ giúp ích được các bạn).
Đầu bài là:
Tôi có bảng danh sách có 15 cột dữ liệu và cần in ra trên 2 hoặc nhiều hơn báo cáo trên khổ A4 trong đó 4 cột đầu thì giữ nguyên trong khi các cột khác thay đổi và tự điều chỉnh kích thước để phù hợp với khổ giấy. (Chẳng hạn mỗi báo cáo sẽ hiển thị 6 cột thì sẽ có 2 báo cáo là đủ 6 cột còn báo cáo thứ 3 chỉ có 3 cột. Vì thế với báo cáo thứ 3, ta sẽ cần hiệu chỉnh kích thước cột để tràn ra toàn khổ giấy).
Vậy tôi cần làm gì?
1.Thiết kế mẫu báo cáo có số cột lớn nhất có thể thể hiện trên khổ giấy A4 (đảm bảo kích thước vừa đẹp).
2.Viết Code để sử dụng tính năng thừa kế lớp của các đối tượng báo cáo.
3.Viết code để gọi và hiển thị báo cáo.
Với mẫu báo cáo, chúng ta cần có các thủ tục giúp vẽ đường lưới ngang dọc, ở đây tôi dùng lệnh draw trong Access và các bạn có thể quan sát cách gọi thủ tục DrawGrid tại phần format section của báo cáo.
If PrintCount = 1 Then DrawGrid Me, RptCol + 4
Sử dụng biến PrintCount để bỏ qua việc vẽ lại, tham số RptCol + 4 thể hiện yêu cầu sẽ chỉ bắt đầu vẽ kể từ cột thứ tư.
Các bạn cần chú ý cách đặt tên các textbox và label để tiện tham chiếu ("txt" & i và "lbl" & i) trong đó txt và lbl là tiền tố đầu của textbox và nhãn.
Tiếp theo tôi xử lý phần điều chỉnh độ rộng cột bằng cách tính toán số cột tối đa và tính ra độ rộng phù hợp của báo cáo trong sự kiện Report_open.
Có khá nhiều code nhưng nói chung phần này giúp giải quyết việc xác định kích thước các cột và đặt tên nhãn cũng như xử lý về chuỗi cung cấp dữ liệu cho báo cáo.
Các bạn có thể xem qua ví dụ tôi gửi kèm để tiện tham khảo.
Nếu có điều kiện tôi sẽ viết dài dòng hơn để mọi người đều có thể chia sẻ.
Các bạn mở frmReport và nhấn nút, chúng ta sẽ thấy kết quả là có 3 báo cáo được mở cùng lúc trong khi tôi chỉ sử dụng 1 báo cáo mẫu duy nhất.
Thành thật xin lỗi các bạn về việc hơi ẩu khi lạm dụng comment bằng tiếng Anh. - Nếu có điều kiện tôi sẽ viết lại bằng tiếng Việt
Chúc các bạn một buổi tối vui vẻ! 

DownLoad Demo

Thứ Ba, 12 tháng 1, 2010

Sort List theo Alphabe

Mới lang thang tìm thấy 1 đoạn code cho phép Sort 1 Listbox theo Alphabe. Mời mọi người tham khảo, có gì thắc mắc cứ comment hỏi nhé!
Function SortListBox(objListBox As ListBox)
Dim intFirst As Integer
Dim intLast As Integer
Dim intNumItems As Integer
Dim i As Integer
Dim j As Integer
Dim strTemp As String
Dim MyArray() As Variant

'Re-Dim the array
ReDim MyArray(objListBox.ListCount - 1)

'Get upper and lower boundary
intFirst = LBound(MyArray)
intLast = UBound(MyArray)

'Set array values
For i = LBound(MyArray) To UBound(MyArray)
MyArray(i) = objListBox.ItemData(i)
Next i

'Loop through array values to determine sort
For i = intFirst To intLast - 1
For j = i + 1 To intLast
If MyArray(i) > MyArray(j) Then
strTemp = MyArray(j)
MyArray(j) = MyArray(i)
MyArray(i) = strTemp
End If
Next j
Next i

'Remove all items
For i = intLast - 1 To intFirst Step -1
objListBox.RemoveItem i
Next i

'Add all items in order
For i = intFirst To intLast - 1
objListBox.AddItem MyArray(i), i
Next

End Function
 Bạn tạo 1 module mới rồi copy đoạn code trên vào.
tạo 1 form, tạo 1 listbox và 1 nút nhất. Để tên mặc định hết cho nhanh nha.
Row source type của lis bạn chọn Value list
Row Source bạn điền: "Rượu;Socola;Bánh;Bao Bì;Bột Ngọt;Cà Chua;Chanh;Đường;Gạo;Hành;Kem Đánh Răng;Khoai Tây;Mì Gói;Mức;Muối;Ớt;Sữa;Tiêu;Hộp quẹt"

hoặc gì tùy bạn

Trong event click nút nhấn, bạn gọi:
Private Sub Command1_Click()
Call SortListBox(List0)
End Sub

Mời xem demo:
SortList

Tùy biến kích thước Form


Bạn hoaipnb vừa sưu tầm 1 đoạn code rất thú vị có thể tăng/ giảm kích thước form theo 1 hằng số cho trước trong table.
Tuy nhiên, vì thiếu thông tin nên bạn ấy chưa biết sử dụng thế nào.
Nhân đó mình đã chế biến lại thành 1 form có 2 nút cho phép tăng/giảm kích thước.

Đầu tiên, ta qua cửa sổ VBA thêm 1 module mới. Copy đoạn code sau vào:
Private Sub FormReSize(giatri As Single)

Me.InsideHeight = Me.InsideHeight / giatri
Me.InsideWidth = Me.InsideWidth / giatri
Dim ctrl As Control
For Each ctrl In Me.Controls
ctrl.Height = ctrl.Height / giatri
ctrl.Width = ctrl.Width / giatri
ctrl.Left = ctrl.Left / giatri
ctrl.Top = ctrl.Top / giatri
Next
Me.Repaint

End Sub
Sau đó, trong form, ta tạo hai nút tăng/ giảm kích thước, để cho nhanh mình để tên mặc định . Và gọi thủ tục trên :
Private Sub Command0_Click()
FormReSize 1.1
End Sub

Private Sub Command1_Click()
FormReSize 0.9
End Sub

Bạn có thể thay thế chỗ mình tô đỏ cho phù hợp với sở thích của mình. Mời Xem chương trình demo:
DownLoad

Thứ Hai, 11 tháng 1, 2010

Trình tự các sự kiện khi mở-đóng 1 form/report

Khi ta mở/ đóng form thì có 1 chuỗi các sự kiện diễn ra, Nếu biết điều đó ta có thể chặn các sự kiện để cài mã vào cho thích hợp.
Theo đó, khi bạn mở form/report thì trình tự  các sự kiện như sau:
Open → Load → Resize → Activate → Current

Khi đóng form, trình tự các sự kiện  như sau:

Unload → Deactivate → Close

 ------------------
Từ điều này cho thấy, nếu ta muốn chỉnh kích thước các đối tượng thì phải gọi trước sự kiện resize. Tức là Load hoặc open, nếu ta gọi sau (active) thì không có tác dụng.
Tương tự, ta muốn gọi 1 form, điền vào 1 tham số và nhảy setforcut 1 form khác, lấy giá trị từ form này thì phải gọi nó ở sự kiện Current. Vì chưa Active thì nó chưa được điền giá trị vào đó.
....
Tương tự, khi đóng form/report. Ta cũng có thể bẫy chặn các sự kiện nhằm lưu hệ thống . Cụ thể khi form không còn hiện nữa (unload), ta vẫn có thể lưu dữ liệu vì nó chưa hoàn toàn close...

Thứ Năm, 7 tháng 1, 2010

Làm sao để mở data Access ở chế độ Exclusively

Hỏi: Trong Access 2003/XP/2000/97, tôi đã cố gắng sửa đổi một số các đối tượng trong cơ sở dữ liệu Access của tôi.Và thường nhận 1 báo lỗi "database exclusively". Vậy làm thế nào để mở Database ở chế độ Exclusively.
Đáp: Bởi vì trong Advanced Option của chương trình, bạn đang để chế độ Share cho nhiều người dùng. Vì vậy,để chỉnh sửa Form/report/table , đôi khi bạn bị buộc phải mở Database ở chế độ Exclusively.

Để làm việc này, bạn phải đảm bảo file Access của bạn hiện không có ai đang mở.
Mở Ms Access. Vào menu File--> Open
Chọn file dữ liệu mà bạn muốn mở ở chế độ  Exclusively. Bấm vào mũi tên bên phải của nút Open. Một danh sách các tùy chọn sẽ xuất hiện (Open, Open Read-Only, Open Exclusive, Open Exclusive Read Only). Bạn chọn vào Open Exclusive.


Chúc Thành Công


Thứ Hai, 4 tháng 1, 2010

Tìm ngày có thứ đầu tiên trong tháng

Hỏi: Công ty tôi họp thường kỳ vào thứ 3 tuần đầu tiên của tháng. Xin giúp 1 đoạn code để tôi biết chính xác thứ 3 đầu tiên của tháng là ngày mấy!


Đáp: bạn có thể tạo 1 function như sau:
Public Function FirstDayofMonth(intYr As Integer, intMth As Integer, intWeekday As Integer) As Date
Dim dteMonthStart As Date
Dim intDayofWeek As Integer, intIncrement As Integer

dteMonthStart = DateSerial(intYr, intMth, 1)
intDayofWeek = Weekday(dteMonthStart)

If intDayofWeek <= intWeekday Then
   intIncrement = intWeekday - intDayofWeek
   FirstDayofMonth = DateAdd("d", intIncrement, dteMonthStart)
Else
   intIncrement = (7 - intDayofWeek) + intWeekday
   FirstDayofMonth = DateAdd("d", intIncrement, dteMonthStart)
End If
End Function
Bây giờ bạn có thể gọi function trên, truyền tham số
FirstDayofMonth(Năm xem xét, tháng xem xét, Thứ trong tuần)
 Ví dụ muốn biết ngày thứ 3 của tháng 1 năm 2010 bạn gọi:
msgbox FirstDayofMonth(2010,1,3)

mời xem demo