แจกฟรี Source Code VB6+Access โปรแกรมระบบฐานข้อมูลครุภัณฑ์ ภาคเขียนโปรแกรม
http://www.g2gnet.com/webboard/images/vb6/AssetMain.pngหน้าตาเมื่อรันโปรแกรม ...
http://www.g2gnet.com/webboard/images/vb6/AssetDetail.png
รายละเอียดของรายการครุภัณฑ์
http://www.g2gnet.com/webboard/images/vb6/AssetDB.png
เวลาผ่านมาอย่างยาวนานถึง 8 ปีแล้ว กับการที่แอดมินได้เริ่มต้นในการแจกโค้ด Visual Basic 6 แบบฟรีๆ โปรเจคระบบฐานข้อมูลครุภัณฑ์ ซึ่งแอดมินใช้ Component มาตรฐานของ Microsoft หากเป็นงานจริงๆจะหรูเริ่ดกว่านี้เยอะครับ 5555+ ... ก่อนอื่นต้องขอชี้แจงเกี่ยวกับการออกแบบในโปรเจคตัวนี้กันสักเล็กน้อย เพราะเมื่อหลายท่านได้ดาวน์โหลดโค้ดโปรแกรมไป ก็คงจะต้องมีคำถามเกิดขึ้นในใจว่า ทำไมแอดมินถึงได้ออกแบบตารางข้อมูลในลักษณะเยี่ยงนี้???
ข้อมูลครุภัณฑ์ในส่วนงานภาครัฐ โดยเฉพาะสถานศึกษา เขามักจะมี Detail หรือรายละเอียดต่างๆของวัสดุครุภัณฑ์ที่ซ้ำซ้อนกันมาก สิ่งที่แตกต่างกันก็คือ รหัสครุภัณฑ์ เท่านั้นเอง เช่น ชื่อครุภัณฑ์ว่า คอมพิวเตอร์แบบตั้งโต๊ะ ซึ่งจัดวางอยู่ใน ห้องปฏิบัติการหมายเลขนี้ มันมีจำนวนอยู่ 40 ชุดที่เหมือนกันเลย เพราะเวลาจัดซื้อจัดจ้างในแต่ละครั้ง ก็มักจะเหมารวมกันมาเป็นแบบเข่งๆ ทีนี้ในส่วนของผู้พัฒนาโปรแกรมต้องคิดอย่างแรกเลยว่า จะทำอย่างไรให้ผู้ใช้งานเขาสะดวกในการคีย์ข้อมูล??? เอ้า แค่นี้ยังไม่พอ เนื่องจากข้อมูลมันเกิดความซ้ำซ้อน แล้วเราจะทำอย่างไรจึงจะสามารถลดความซ้ำซ้อนนี้ลงไปได้ ... ก็เลยเกิดการเชื่อมโยงความสัมพันธ์ของตารางข้อมูลต่างๆตามภาพที่ปรากฏ และนำมาใช้ในงานนี้ ...
แอดมินจะขอตัดบางส่วนที่คิดว่าเป็นจุดสำคัญๆ มาเน้นย้ำเพื่ออธิบายให้เท่านั้นนะครับ ...
การโหลดค่าจากตารางย่อย (Detail) คือพวกกลุ่มตารางข้อมูลต่างๆที่อยู่รายรอบตารางหลัก tblAsset
http://www.g2gnet.com/webboard/images/vb6/Asset-DBLoadComboBox.gif
จากฟอร์ม frmAssetDetail ให้ไปดูโปรแกรมย่อย RecordToScreen ...
' ชื่อหลักของครุภัณฑ์ ...
' ไปโปรแกรมย่อยในการโหลดรายการต่างๆของตารางย่อย (Detail) เข้าสู่ ComboBox ค่าที่ส่งไปมี
' ชื่อ ComboBox, ชื่อตาราง, ชื่อ Field ที่เป็น Primary Key, ชื่อฟิลด์ที่เป็นรายการ
Call LoadComboBox( _
cmbAssetName, _
"tblAssetName", _
"AssetNamePK", _
"AssetName" _
)การเรียกไปยังโปรแกรมย่อยที่ชื่อ LoadComboBox โดยที่มีพารามิเตอร์ต่างๆที่ส่งไปยัง Sub Program
' Load รายการเข้าสู่ ComboBox ค่าที่ต้องส่งมา 4 ชุด คือ
' ชื่อ ComboBox, ชื่อตาราง, ชื่อฟิลด์ Primary Key และ ชื่อฟิลด์รายการ
Sub LoadComboBox( _
cmb As ComboBox, _
tblName As String, _
FieldPK As String, _
FieldName As String _
)
Set DS = New ADODB.Recordset
SQLStmt = "SELECT * FROM " & tblName & " ORDER BY " & FieldName
Set DS = ConnDB.Execute(SQLStmt, , adCmdText)
cmb.Clear
Do Until DS.EOF
cmb.AddItem "" & DS(FieldName)
DS.MoveNext
Loop
DS.Close: Set DS = Nothing
End Sub
การค้นหาคำ และการจำกัดความยาวในการป้อนข้อมูลลงใน ComboBox ...
http://www.g2gnet.com/webboard/images/vb6/Asset-DBKeyPressCombo.gif
' =========================================================
' โปรแกรมย่อยในการค้นหาคำในรายการของ ComboBox
Private Sub SearchComboBox(cmb As ComboBox, KeyAscii As Integer)
' =========================================================
Dim strKey As String, iRet As Long, LenKey As Long
cmb.SelText = ""
strKey = cmb.Text & Chr$(KeyAscii)
iRet = SendMessage(cmb.hwnd, CB_FINDSTRING, -1, ByVal strKey)
If iRet <> CB_ERR Then
LenKey = Len(strKey)
cmb.Text = cmb.List(iRet)
cmb.ListIndex = iRet
KeyAscii = 0
cmb.SelStart = LenKey
cmb.SelLength = Len(cmb.Text) - LenKey
End If
End Sub
' =========================================================การค้นหาคำใน ComboBox ... ในระดับขั้นสูง เราหลีกเลี่ยงไม่พ้นที่จะต้องใช้งาน Win API ... (อ่านรายละเอียดได้ที่นี่)
' =========================================================
' ฟังค์ชั่นที่ช่วยจำกัดความยาวข้อมูลสำหรับ ComboBox
Private Sub MaxComboBox(cmb As ComboBox, MaxChar As Integer, KeyAscii As Integer)
' =========================================================
If Len(cmb.Text) >= MaxChar Then ' ถ้าหากมีความยาวมากกว่า หรือ เท่ากับที่ได้ตั้งไว้
If KeyAscii <> vbKeyBack Then ' เป็นการกดคีย์ Back Space หรือไม่
KeyAscii = 0 ' ไม่ใช่ให้ถือว่าไม่ได้กดคีย์ใดๆเลย
End If
End If
End Sub
' =========================================================การจำกัดความยาวที่จะพิมพ์ลงใน ComboBox
การบันทึกข้อมูล แบ่งออกได้ 2 ลักษณะ คือ
1. การเพิ่มข้อมูลใหม่ ส่วนนี้สาระสำคัญ คือ การหา Primary Key ตัวใหม่ และ AssetID ต้องไม่ไปซ้ำกับของเดิม
2. การแก้ไขข้อมูล สาระสำคัญต้องไม่ให้การแก้ไขแล้ว AssetID มีค่าซ้ำกับของเดิม
Private Sub cmdSave_Click()
' AssetID
If Trim(txtAssetID.Text) = "" Or Len(Trim(txtAssetID.Text)) = 0 Then
MsgBox "กรุณาป้อนทะเบียนครุภัณฑ์ให้เรียบร้อยก่อนด้วย.", vbOKOnly + vbExclamation, "รายงานสถานะ"
txtAssetID.SetFocus
Exit Sub
End If
If Trim(cmbAssetName.Text) = "" Or Len(Trim(cmbAssetName.Text)) = 0 Then
MsgBox "กรุณาป้อนชื่อรายการครุภัณฑ์ให้เรียบร้อยก่อนด้วย.", vbOKOnly + vbExclamation, "รายงานสถานะ"
cmbAssetName.SetFocus
Exit Sub
End If
'
' ตรวจสอบการซ้ำกันของรหัสทะเบียนครุภัณฑ์
' =================================================================
' มันมีโอกาสเป็นได้ 2 กรณี คือ
' เพิ่มข้อมูลใหม่ - ทำให้ txtAssetID.Text จะไม่ตรงกันกับ txtAssetID.Tag (ค่านี้จะต้องว่าง)
' แก้ไขข้อมูล - มีโอกาสได้ 2 ทาง คือ
' 1. ไม่มีการแก้ไขค่าใน txtAssetdID.Text จะทำให้ txtAssetID.Text = txtAssetID.Tag
' ดังนั้นไม่ต้องไปเสียเวลาทำการเปรียบเทียบค่าเดิมในฐานข้อมูล
' 2. มีการแก้ไขค่าใน txtAssetID.Text ดังนั้น txtAssetID.Text <> txtAssetID.Tag ทำให้
' ต้องนำค่าไปตรวจสอบว่ามีค่า txtAssetID.Text (ที่เปลี่ยนไป) ไปซ้ำกับค่าเดิมในฐานข้อมูลหรือไม่
' เขียน VB มานับ 10 ปี ... เทคนิคง่ายๆนี้ ผมก็ยังใช้งานได้ไม่เปลี่ยนแปลงทั้ง VB6 หรือ VB.Net
' =================================================================
If txtAssetID.Text <> txtAssetID.Tag Then
If CheckNewCode > 0 Then
MsgBox "มีทะเบียนครุภัณฑ์: " & Trim(txtAssetID.Text) & " เรียบร้อยแล้ว กรุณาแก้ไขใหม่ด้วย.", _
vbOKOnly + vbExclamation, "รายงานสถานะ"
txtAssetID.SetFocus
Exit Sub
End If
End If
' ================================
' ไปบันทึกข้อมูลได้เลย
Call SaveData
' ================================
End Subก่อนจะทำการบันทึกข้อมูล เราต้องทำการ Validate Data ให้เรียบร้อยก่อน
' ฟังค์ชั่นตรวจสอบการซ้ำกันของทะเบียนครุภัณฑ์ (หรืออื่นๆ) กรณีข้อมูลเป็น Text
' จากนั้นส่งค่ากลับ หากเป็น 0 แสดงว่าไปไม่เกิดการซ้ำกันของข้อมูล
' ค่าส่งกลับมากกว่า 0 ... เกิดการซ้ำกัน จะต้องบังคับไม่สามารถเพิ่ม หรือ แก้ไขข้อมูลได้
Function CheckNewCode() As Long
Set DS = New Recordset
SQLStmt = "SELECT * FROM tblAssetWHERE AssetID = " & "'" & Trim(txtAssetID.Text) & "'" & _
" ORDER BY AssetPK "
' หากไม่ระบุเป็น adUseClient จะใช้ค่าเดิมที่ตั้งต้น (Default) เป็นแบบ adUseServer
' การใช้แบบ adUseClient เพื่อต้องการให้ใช้เมธอดของการนับ Record ได้ นั่นคือ
' DS.RecordCount
DS.CursorLocation = adUseClient
DS.Open SQLStmt, ConnDB, adOpenForwardOnly, adLockReadOnly, adCmdText
CheckNewCode = DS.RecordCount
DS.Close: Set DS = Nothing
End Functionค่าทะเบียนครุภัณฑ์จะมีค่าที่ซ้ำกันไม่ได้
การบันทึกข้อมูล ... (หลักการที่แอดมินใช้ใน VB6 จึงถูกนำไปใช้กับ VB.NET ได้ต่อทันที)
' ==================== การบันทึกข้อมูล =======================
Private Sub SaveData()
Set RS = New Recordset
If NewData Then
' ค้นหาค่า PK ก่อน
Call SetupNewData
'
Statement = "SELECT * FROM tblAsset ORDER BY AssetPK"
RS.Open Statement, ConnDB, adOpenKeyset, adLockOptimistic, adCmdText
RS.AddNew
RS("AssetPK") = PK
RS("DateAdded") = FormatDateTime(Now(), vbShortDate)
RS("DateModified") = FormatDateTime(Now, vbShortDate)
'========== แก้ไขข้อมูล
Else
'
Statement = "SELECT * FROM tblAsset WHERE AssetPK = " & PK
RS.Open Statement, ConnDB, adOpenKeyset, adLockOptimistic, adCmdText
End If
'
RS("AssetID") = "" & Trim(txtAssetID.Text)
RS("SerialNumber") = "" & Trim(txtSerialNumber.Text)
RS("Model") = "" & Trim(txtModel.Text)
RS("Class") = "" & Trim(txtClass.Text)
RS("DateReceived") = FormatDateTime(dtpDateReceived.Value, vbShortDate)
'
' ตรวจสอบค่าใน ComboBox
' ชื่อครุภัณฑ์ โดยการส่งค่าไปตรวจสอบหาค่า Primary Key ของตารางย่อย (Detail) ค่าที่ส่งไป มี
' ชื่อ ComboBox, ชื่อตาราง, Field ที่เป็น PK, Field ที่เป็นรายการ (ค่าที่ต้องทดสอบหา Primary Key)
RS("AssetNameFK") = VerifyComboBox( _
cmbAssetName, _
"tblAssetName", _
"AssetNamePK", _
"AssetName" _
)
' ยี่ห้อ - BrandName
' ชื่อครุภัณฑ์ โดยการส่งค่าไปตรวจสอบหาค่า Primary Key ของตารางย่อย (Detail) ค่าที่ส่งไป มี
' ชื่อ ComboBox, ชื่อตาราง, Field ที่เป็น PK, Field ที่เป็นรายการ (ค่าที่ต้องทดสอบหา Primary Key)
RS("BrandNameFK") = VerifyComboBox( _
cmbBrandName, _
"tblBrandName", _
"BrandNamePK", _
"BrandName" _
)
' กลุ่ม
RS("GroupNameFK") = VerifyComboBox(cmbGroupName, "tblGroup", "GroupNamePK", "GroupName")
' หน่วยนับ
RS("UnitFK") = VerifyComboBox(cmbUnitName, "tblUnit", "UnitPK", "UnitName")
' แหล่งเงิน
RS("SourceFK") = VerifyComboBox(cmbSourceName, "tblSource", "SourcePK", "SourceName")
' สถานะของครุภัณฑ์
RS("StatusFK") = VerifyComboBox(cmbStatusName, "tblStatus", "StatusPK", "StatusName")
' สถานที่ใช้งาน หรือ เก็บ
RS("LocationFK") = VerifyComboBox(cmbLocationName, "tblLocation", "LocationPK", "LocationName")
If txtUnitPrice.Text <> "" And Val(txtUnitPrice.Text) >= 0 Then
RS("UnitPrice") = CCur(txtUnitPrice.Text)
Else
RS("UnitPrice") = 0#
End If
'
RS("Reference") = "" & Trim(txtReference.Text)
RS("Memo") = "" & Trim(txtMemo.Text)
RS("DateModified") = FormatDateTime(Now(), vbShortDate)
RS.Update
'
RS.Close: Set RS = Nothing
'
NewData = False
' ส่งค่าไปบอกฟอร์มหลักให้ Refresh
FormUpdate = True
MsgBox "บันทึกเข้าสู่ระบบงานฐานข้อมูลเรียบร้อย.", vbOKOnly + vbInformation, "รายงานสถานะ"
Unload Me
End Sub
โปรแกรมย่อยในการสร้าง Primary Key ให้กับรายการครุภัณฑ์
' ===================== สร้าง Record ใหม่ ==========================
' ต้องคำนวณหาค่า Primary Key ให้เรียบร้อยก่อน
Sub SetupNewData()
Dim Rec As Long
Set DS = New Recordset
' นำข้อมูลจากตารางมาคำนวณหาค่าสูงสุด
SQLStmt = "SELECT Max(tblAsset.AssetPK) As MaxPK FROM tblAsset "
DS.Open SQLStmt, ConnDB, adOpenForwardOnly, adLockReadOnly, adCmdText
' หากไม่พบข้อมูลจะทำให้เกิดค่า Null ดังนั้นต้องดัก Error เอาไว้ก่อนด้วย
If IsNull(DS("MaxPK")) Then
PK = 1
Else
PK = DS("MaxPK") + 1
End If
DS.Close: Set DS = Nothing
End Sub
จะมีฟังค์ชั่นที่สำคัญอยู่ตัวหนึ่ง VerifyComboBox ซึ่งต้องใช้งานกับ ComboBox หลายๆตัวก่อนทำการบันทึกข้อมูล (แอดมินถึงต้องสร้างฟังค์ชั่นขึ้นมาใหม่)
' ฟังค์ชั่นที่ใช้ในการตรวจสอบค่าที่อยู่ใน ComboBox เพื่อค้นหาค่า Primary Key ในตารางย่อย
Function VerifyComboBox( _
cmb As ComboBox, _
tblName As String, _
FieldPK As String, _
FieldName As String _
) As Integer
Dim CountRec As Integer ' ไว้นับจำนวนของตารางย่อย
' ตรวจสอบว่ามีการป้อนข้อมูลหรือไม่ หากไม่มีให้กำหนดค่า Default เป็น 0
' จากนั้น Return ค่ากลับ และออกจากฟังค์ชั่นไปเลยครับพี่น้อง ... เพื่อเป็นการไม่เสียเวลา
If cmb.Text = "" Or Len(cmb.Text) = 0 Or cmb.Text = "-" Then
VerifyComboBox = 0
Exit Function
End If
'// เป็นการตัดการเชื่อมต่อ RecordSet ของเดิมทิ้ง
Set DS = New Recordset
SQLStmt = "SELECT * FROM " & tblName & " WHERE " & FieldName & " = " _
& "'" & Trim(cmb.Text) & "'" & _
" ORDER BY " & FieldPK
'/ ======================================================================
'/ หลายคนมักทำผิด และมองข้ามมันไป สำหรับการเขียน SQL Statement
'/ SQL Statement ... การค้นหาค่าโดยการเปรียบเทียบกับข้อมูลชนิดข้อความ Text หรือ String
'/ SELECT * FROM ... WHERE ฟิลด์แบบข้อความ = '1020' ... (อ่านว่า หนึ่ง ศูนย์ สอง ศูนย์)
'/ เวลาเขียน Statement จะต้องเขียนค่าที่นำมาเปรียบเทียบให้อยู่ภายใต้เครื่องหมาย Single Quote (') เช่น
'/ "SELECT * FROM ... WHERE AssetID = " & "'" & txtAssetID.Text & "'" ... จดจำรูปแบบนี้ให้ดี
'/ ส่วนกรณีของตัวเลขไม่ต้องมีเครื่องหมาย Single Quote เช่น
'/ SELECT * FROM ... WHERE AssetPK = 1020 (อ่านว่า หนึ่งพันยี่สิบ) เช่น
'/ "SELECT * FROM ... WHERE AssetPK = " & txtAssetPK.Text
'/ ======================================================================
DS.CursorLocation = adUseClient
DS.Open SQLStmt, ConnDB, adOpenForwardOnly, adLockReadOnly, adCmdText
CountRec = DS.RecordCount
' แสดงว่าไม่มีในรายการ ดังนั้นเราต้องเพิ่มรายการเข้าไปใหม่ในตารางย่อย
If CountRec <= 0 Then
Set DS = New Recordset
SQLStmt = "SELECT Max(" & tblName & "." & FieldPK & ") As MaxPK " & " FROM " & tblName
DS.CursorLocation = adUseClient
DS.Open SQLStmt, ConnDB, adOpenForwardOnly, adLockReadOnly, adCmdText
' เพิ่มค่า Primary Key ของตารางย่อย (Detail) ขึ้นอีก 1
CountRec = DS("MaxPK") + 1
' การที่ผมไม่สั่งปิดตารางข้อมูล DS.Close ก็เพราะคำสั่ง Set DS = New Recordset
' มันจะตัดการชื่อมต่อเดิมออกไปในทันทีได้เลยครับ ... ไม่ต้องห่วง
Set DS = New Recordset
SQLStmt = "SELECT * FROM " & tblName & " ORDER BY " & FieldPK
DS.Open SQLStmt, ConnDB, adOpenKeyset, adLockOptimistic, adCmdText
DS.AddNew
DS(FieldPK) = CountRec
DS(FieldName) = cmb.Text
DS.Update
' ส่งค่า PK กลับไปเพื่อบันทึกข้อมูล
VerifyComboBox = CountRec
' มีข้อมูลเดิมอยู่แล้ว
Else
' ส่งค่า PK กลับไปเพื่อบันทึกข้อมูล
VerifyComboBox = DS(FieldPK)
End If
DS.Close: Set DS = Nothing
End FunctionConclusion: แอดมินก็คาดหวังเล็กๆว่า คงพอที่จะทำให้พี่น้องหลายท่านได้แนวคิด ได้มุมมองแปลกๆใหม่ๆเอาไว้ในอ้อมกอด อ้อมใจ กันบ้างพอสมควรนะครับ ... สวัสดี
ดาวน์โหลดโค้ด VB6 (SP6) ต้นฉบับแบบเต็มๆได้ที่นี่
ขอบคุณมากมายครับ ขอบคุณครับ:D:D อาจารย์ครับผมอยากได้ การรับค่าจากเครื่องยิง บาร์โค๊ดพอจะมีไหมครับ Artkummool ตอบกลับเมื่อ 2019-9-16 14:21
อาจารย์ครับผมอยากได้ การรับค่าจากเครื่องยิง บาร์โค๊ดพอจะมีไหมครับ
ปกติเครื่องอ่านบาร์โค้ดแบบแท่ง มันสามารถอ่านรหัสแท่ง แล้วแปลงเป็นชุดรหัสตัวอักษร ตัวเลขได้ทันทีเลยครับ ขอบคุณครับ ขอบคุณครับ
หน้า:
[1]