ดาวน์โหลดโปรแกรม RSS Reader ได้ที่นี่ ...

|
|
|
Visitors - Session views |       
7 ธันวาคม พ.ศ.2549 90 Users On-Line. |
|
Visitors - Page views |        1 กุมภาพันธ์ พ.ศ.2551 |
|
|
|
 |
|
พื้นฐานในการใช้งาน MS FlexGrid ก่อนสร้างความสัมพันธ์แบบ One To Many (ภาค 3) |
Category »
VB 6/VB.Net โดย : Webmaster เมื่อ 7/4/2553 เวลา: 16:31 | (อ่าน : 16145) |
ตอนนี้จะเป็นการค้นหาสินค้า (หรืออื่นๆ) เพื่ออำนวยความสะดวกให้กับผู้ใช้งานมากขึ้น การค้นหาจะทำได้นอกจากรหัสสินค้าแล้ว ยังค้นหาด้วยชื่อสินค้า หรืออื่นๆก็ได้อีก กระบวนการขั้นตอนก็ไม่มีอะไรมาก หรือ ยากจนเกินเหตุ เริ่มต้นด้วยการคลิ๊กปุ่มเพื่อเปิดฟอร์มรายการค้นหาสินค้าขึ้นมา จากนั้นผู้ใช้ก็จะทำการค้นหาข้อมูลตามที่ต้องการ กรณีหากหาข้อมูลสินค้าไม่พบ ก็สามารถเพิ่มขึ้นมาใหม่ได้เลยทันที หากหาข้อมูลพบ ก็จะส่ง Primary Key ของสินค้านั้นๆกลับไปยังฟอร์มหลัก (รายละเอียดได้แจ้งไว้ในโค้ดแล้ว) เมื่อกลับมายังหน้าจอหลักการขาย ก็จะทำการค้นหาสินค้าอีกรอบ ... ทำไมต้องใช้การค้นหาถึง 2 รอบ ... หาคำตอบได้จากโค้ดทางด้านล่างเลยครับพี่น้อง ... อิอิอิอิอิ ... แหม การเข้าเรื่องในรอบนี้มันช่างน่าติดตามจังเลย เหอๆๆๆๆ |
เริ่มต้นกระบวนการทำงาน modDataBase.bas โมดูลหากิน (มันมาอีกแล้ว) ... 55555+
Option Explicit
Global ConnDB As New ADODB.Connection
Global RS As New ADODB.Recordset
Global DS As New ADODB.Recordset
Global Statement As String
Global SQLStmt As String
'
' กำหนดว่าเป็นการเพิ่ม หรือ แก้ไขข้อมูล
Global blnNewData As Boolean
' ให้เกิดการ Update ในฟอร์มที่มีการเปลี่ยนแปลง
Global FormUpdate As Boolean
' ตัวแปรแบบ Global เพื่อส่งค่า Primary Key ไปมาระหว่างฟอร์มการขาย และ ค้นหาสินค้า
' นอกจากนี้ยังสามารถนำไปใช้กับส่วนอื่นๆเช่น ค้นหาลูกค้า ผู้ขาย หรือ อื่นๆได้อีก ... เอาให้คุ้ม
Global gPK As Long
' เชื่อมเข้าสู่ไฟล์ฐานข้อมูล MS Access
Public Sub OpenDataBase()
On Error GoTo Err_Handler
Dim DB_File As String
DB_File = App.Path
If Right$(DB_File, 1) <> "\" Then DB_File = DB_File & "\"
DB_File = DB_File & "ProductDB.MDB"
' Open a connection.
Set ConnDB = New ADODB.Connection
ConnDB.ConnectionString = _
"Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & DB_File & ";" & _
"Persist Security Info=False"
ConnDB.Open
Exit Sub
Err_Handler:
MsgBox "Error : " & Err.Number & " " & Err.Description
End
End Sub
Public Sub CloseDataBase()
' ตรวจสอบว่ามีการเชื่อมโยง - Connect ข้อมูลหรือไม่
If ConnDB.State = adStateOpen Then
ConnDB.Close
Set ConnDB = Nothing
End If
End Sub
|

การเลือกคลิ๊กที่ปุ่มค้นหา cmdSearchData (จากฟอร์มหลักการขาย frmSearchDB)
Private Sub cmdSearchData_Click()
' กำหนดค่า Primary Key แบบ Global ไว้ล่วงหน้า ตัวแปรนี้อยู่ใน modDataBase.bas น่ะครับ
gPK = -1
' vbModal คือการที่ให้ฟอร์มอยู่เหนือฟอร์มอื่นๆทั้งหมด และ โฟกัสมาที่ฟอร์มนี้เท่านั้น
' เรียกฟอร์มค้นหาสินค้าขึ้นมา
frmSearchProduct.Show vbModal
' เมื่อปิดฟอร์มที่ค้นหาข้อมูลแล้ว จะเกิดการส่งค่า ProductPK มาทาง gPK มี 2 กรณี
' 1. gPK = -1 แสดงว่าไม่มีข้อมูลที่ต้องการ จบออกจากโปรแกรมย่อยได้ทันที
' 2. gPK <> -1 นั่นคือ ส่งค่า Primary Key กลับมา เราก็ต้องค้นหารายการสินค้าอีกรอบ
' ผมใช้ Primary Key กลับมาค้นหาอีกรอบ ซึ่งหากบางคนไม่ชอบวิธีนี้ก็ใช้ตัวแปรกำหนดแทนก็ได้น่ะครับ
' แต่มันต้องใช้เยอะทั้งรหัสสินค้า ชื่อสินค้า จำนวน ราคา อื่นๆอีก ซึ่งไม่สะดวก และ ยุ่งยากไปครับ
' แล้วทำไมไม่ให้คืนค่ากลับด้วยรหัสสินค้ามาเลย ...
' - การค้นหาข้อมูลด้วยตัวเลข จะเข้าถึงข้อมูลได้เร็วกว่าแบบ String
' - สำคัญ ... หากเรามีรหัสสินค้า หรือ รหัสบาร์โค้ดหลายชุดในสินค้าตัวเดียวกัน จะยุ่งยากมาก ... แจ่มมั้ยครับ
' ตรวจสอบรายการสินค้าอีกรอบ
If gPK <> -1 Then
' คราวนี้ต้องส่งค่าไปบอกโปรแกรมย่อยด้วยว่าค้นหาด้วยอะไร
' 0 คือ ค้นหาจาก txtSearch
' 1 คือ ค้นหาจากปุ่ม cmdSearchData
Call CheckDataRow(1)
End If
End Sub
|
เปิดฟอร์มที่ใช้ค้นหารายการสินค้าขึ้นมา (frmSearchProduct)
โค้ดในการค้นหาข้อมูลในฟอร์ม frmSearchProduct และส่งค่า Primary Key (gPK) ของสินค้ากลับคืนไป
Private Sub Form_Load()
' ตั้งค่าฟอร์มกึ่งกลางจอภาพ
Me.Move (Screen.Width - Width) \ 2, (Screen.Height - Height) \ 2
txtSearch.Text = ""
' แหมก็หาไอคอนกราฟิคมาใส่ให้สวยงามหน่อยล่ะ
With Me
cmdNew.Picture = .i16x16.ListImages(1).Picture
cmdEdit.Picture = .i16x16.ListImages(2).Picture
cmdDelete.Picture = .i16x16.ListImages(3).Picture
cmdRefresh.Picture = .i16x16.ListImages(5).Picture
End With
' จัดระเบียบให้ ListView
With lvwData
' หลักแรกให้ซ่อน Primary Key ของสินค้าไว้ (ตลอดเลย)
.ColumnHeaders.Add 1, , "PK", 0
.ColumnHeaders.Add 2, , "รหัสสินค้า", 1600
.ColumnHeaders.Add 3, , "รายการสินค้า", .Width \ 2 + 950
End With
' แสดงผลข้อมูลใน ListView
Call ShowListView(False)
End Sub
' ======================================
' ให้ตัวแปร blnSearch เป็นตัวรับค่าที่ใช้ในการแสดงผล
' ======================================
Sub ShowListView(blnSearch As Boolean)
Dim LV As Variant
Set RS = New Recordset
Select Case blnSearch
' จริง คือการค้นหาข้อมูล
Case True
SQLStmt = "SELECT tblProduct.ProductPK, tblProduct.ProductCode, tblProduct.Description " & _
" FROM tblProduct " & _
" WHERE " & _
" [ProductCode] " & " Like '%" & Trim(txtSearch.Text) & "%'" & " OR " & _
" [Description] " & " Like '%" & Trim(txtSearch.Text) & "%'" & _
" ORDER BY ProductPK "
' เท็จ คือการสั่งรีเฟรชการแสดงผลข้อมูลใหม่
' Case Else ก็ได้น่ะครับ เพราะค่ามันจะส่งมาได้ 2 ค่า คือ จริง กับ เท็จ เท่านั้นเอง
Case False
SQLStmt = "SELECT tblProduct.ProductPK, tblProduct.ProductCode, tblProduct.Description " & _
" FROM tblProduct " & _
" ORDER BY ProductPK "
End Select
RS.CursorLocation = adUseClient
' การอ่านแบบเดินหน้าอย่างเดียว adOpenForwardOnly เพื่อให้เข้าถึงข้อมูลได้เร็วขึ้น
RS.Open SQLStmt, ConnDB, adOpenForwardOnly, adLockReadOnly, adCmdText
lblCount.Caption = "[จำนวน : " & RS.RecordCount & " รายการ]"
lvwData.View = lvwReport
lvwData.ListItems.Clear
' ทำลูปเพื่อนำค่าจากตารางย่อย (Detail) มาแสดงผลใน ListView ให้หมด
Do While Not RS.EOF
' ทำโหนด (Node) ให้มันก่อนล่ะ
Set LV = lvwData.ListItems.Add(, , RS("ProductPK"), 0, 0)
LV.SubItems(1) = "" & RS("ProductCode")
LV.SubItems(2) = "" & RS("Description")
RS.MoveNext
Loop
RS.Close: Set RS = Nothing
End Sub
' ======================================
' เลือกรายการสินค้าตามที่ต้องการ
' ======================================
Private Sub lvwData_DblClick()
' หากมีการเลือกรายการก็ส่งค่า PrimaryKey (ซ่อนเอาไว้ในหลักแรก) ที่ได้กลับไปยังฟอร์มที่เรียกมา
' กรณีของการยกเลิก หรือ ไม่เลือกรายการใดๆ ค่า gPK = -1 เหมือนเดิม
' จะอ่านค่าจาก Node ตัวมันเองครับ นั่นก็คือหลักแรกที่เรานำค่า Primary Key ไปซ่อนไว้นั่นเอง
gPK = Val(lvwData.SelectedItem.Text)
' หากเป็นข้อมูลในหลักอื่นๆใช้ Method --> lvwData.SelectedItem.SubItems(1)
Unload Me
End Sub
Private Sub cmdExit_Click()
' ส่งค่ากลับให้น้อยกว่า 0 เพื่อแจ้งว่าไม่มีการเลือกรายการใด
gPK = -1
Unload Me
End Sub
' สั่งแสดงผลรายการสินค้าใหม่ทั้งหมด
Private Sub cmdRefresh_Click()
txtSearch.Text = ""
' ไม่ใช่การค้นหา เลยต้องค่าเท็จไปโปรแกรมย่อย
Call ShowListView(False)
lvwData.SetFocus
End Sub
' ======================================
' การค้นหารายการสินค้าได้ทั้งรหัสสินค้า และ ชื่อสินค้า
' ======================================
Private Sub cmdSearch_Click()
If Trim(txtSearch.Text) = "" Or Len(Trim(txtSearch.Text)) = 0 Then
txtSearch.SetFocus
Exit Sub
End If
' ต้อง Repace ค่า ' (Single Quote) ออกก่อนน่ะครับ ... มิฉะนั้นมันจะเกิด Error ไม่เชื่อผมก็ลองดูซิครับ ... พี่น้อง
' อีกตัวหนึ่งที่ต้องเอาออกด้วยก็คือ ... แอ่น แอ้น ... % ครับ เพราะใน Access มันเสมือนหนึ่งว่าทำการค้นหาข้อมูลทั้งหมด
txtSearch.Text = Replace(txtSearch.Text, "'", "")
txtSearch.Text = Replace(txtSearch.Text, "%", "")
' ส่งค่า True เพื่อไปบอกโปรแกรมย่อย ShowListView ว่าเป็นการค้นหาข้อมูลเด้อ
Call ShowListView(True)
lvwData.SetFocus
End Sub
|
กลับมาหน้าฟอร์มการขายหลัก (frmSearchDB) อีกรอบ เพื่อทำการค้นหาสินค้าด้วย Primary Key
การเลือกคลิ๊กที่ปุ่มค้นหา cmdSearchData
Private Sub cmdSearchData_Click()
' กำหนดค่า Primary Key แบบ Global ไว้ล่วงหน้า ตัวแปรนี้อยู่ใน modDataBase.bas น่ะครับ
gPK = -1
' vbModal คือการที่ให้ฟอร์มอยู่เหนือฟอร์มอื่นๆทั้งหมด และ โฟกัสมาที่ฟอร์มนี้เท่านั้น
' เรียกฟอร์มค้นหาสินค้าขึ้นมา
frmSearchProduct.Show vbModal
' เมื่อปิดฟอร์มที่ค้นหาข้อมูลแล้ว จะเกิดการส่งค่า ProductPK มาทาง gPK มี 2 กรณี
' 1. gPK = -1 แสดงว่าไม่มีข้อมูลที่ต้องการ จบออกจากโปรแกรมย่อยได้ทันที
' 2. gPK <> -1 นั่นคือ ส่งค่า Primary Key กลับมา เราก็ต้องค้นหารายการสินค้าอีกรอบ
' ผมใช้ Primary Key กลับมาค้นหาอีกรอบ ซึ่งหากบางคนไม่ชอบวิธีนี้ก็ใช้ตัวแปรกำหนดแทนก็ได้น่ะครับ
' แต่มันต้องใช้เยอะทั้งรหัสสินค้า ชื่อสินค้า จำนวน ราคา อื่นๆอีก ซึ่งไม่สะดวก และ ยุ่งยากไปครับ
' แล้วทำไมไม่ให้คืนค่ากลับด้วยรหัสสินค้ามาเลย ...
' - การค้นหาข้อมูลด้วยตัวเลข จะเข้าถึงข้อมูลได้เร็วกว่าแบบ String
' - สำคัญ ... หากเรามีรหัสสินค้า หรือ รหัสบาร์โค้ดหลายชุดในสินค้าตัวเดียวกัน จะยุ่งยากมาก ... แจ่มมั้ยครับ
' ตรวจสอบรายการสินค้าอีกรอบ
If gPK <> -1 Then
' คราวนี้ต้องส่งค่าไปบอกโปรแกรมย่อยด้วยว่าค้นหาด้วยอะไร
' 0 คือ ค้นหาจาก txtSearch
' 1 คือ ค้นหาจากปุ่ม cmdSearchData
Call CheckDataRow(1)
End If
End Sub
' ======================================================
' การตรวจสอบค่าใน MS FlexGrid ก่อนที่จะแสดงผลรายการใหม่
' แก้ไขจากเดิม เพื่อไม่ให้มีโปรแกรมย่อยคล้ายๆกันทำงานซ้ำซาก โดย
' หากส่งค่า 0 มา ให้เป็นการค้นหาด้วยรหัสสินค้า (ProductCode) จาก txtSearch
' หากส่งค่า 1 มา ให้เป็นการค้นหา Primary Key จากการกดปุ่ม cmdSearchData
' ======================================================
Private Sub CheckDataRow(SearchProduct As Byte)
Dim CurrentRow As Integer
' หากค้นหาข้อมูลรหัสสินค้าจาก txtSearch
If SearchProduct = 0 Then
' ตรวจสอบว่าเป็นค่าว่างหรือไม่ หากใช่ก็ออกจากโปรแกรมย่อย (User ไม่มีการคีย์ข้อมูลใดๆ)
If Trim(txtSearch.Text) = "" Or Len(Trim(txtSearch.Text)) = 0 Then
txtSearch.SetFocus
Exit Sub
End If
' ค้นหาข้อมูลรหัสสินค้าในตารางสินค้า (tblProduct) ... ใช้ = (Equal) ได้เท่านั้น
Set RS = New Recordset
Statement = "SELECT tblProduct.ProductPK, tblProduct.ProductCode, " & _
" tblProduct.Description, tblUnit.UnitName, tblProduct.PriceUnit " & _
" FROM tblProduct INNER JOIN tblUnit ON tblProduct.UnitFK = tblUnit.UnitPK " & _
" WHERE ProductCode = " & "'" & Trim$(txtSearch.Text) & "'"
' เคลียร์ค่าไว้รอเลย
txtSearch.Text = ""
' หากค้นหาข้อมูลด้วยการเลือกจากปุ่ม cmdSearchData
Else
' ค้นหาข้อมูล Primary Key ของสินค้า ในตารางสินค้า (tblProduct) ... ใช้ = (Equal) ได้เท่านั้น
' และเป็นการค้นหาด้วยตัวเลขน่ะครับ สังเกตความแตกต่างจากการค้นหาด้วย String
Set RS = New Recordset
Statement = "SELECT tblProduct.ProductPK, tblProduct.ProductCode, " & _
" tblProduct.Description, tblUnit.UnitName, tblProduct.PriceUnit " & _
" FROM tblProduct INNER JOIN tblUnit ON tblProduct.UnitFK = tblUnit.UnitPK " & _
" WHERE ProductPK = " & gPK
End If
RS.CursorLocation = adUseClient
RS.Open Statement, ConnDB, adOpenForwardOnly, adLockReadOnly, adCmdText
' ไม่พบข้อมูล ให้ออกจากโปรแกรมย่อยไปได้เลย
If RS.RecordCount = 0 Then
RS.Close: Set RS = Nothing
Exit Sub
End If
' เริ่มต้นการค้นหาข้อมูลในตารางกริด
For CurrentRow = 1 To fgData.Rows - 1
' ค้นหารหัสสินค้าที่อยู่ในหลักที่ 1
If fgData.TextMatrix(CurrentRow, 1) = RS("ProductCode") Then
' หากพบรายการสินค้าเดิมใส่จำนวนสินค้าเพิ่มเข้าไปอีก 1 ทันที ในแถวที่อยู่ ณ ปัจจุบัน
fgData.TextMatrix(CurrentRow, 5) = Val(fgData.TextMatrix(CurrentRow, 5)) + 1
' คูณราคาสินค้า กับจำนวนสินค้าเข้าไปใหม่
fgData.TextMatrix(CurrentRow, 6) = Format(Val(fgData.TextMatrix(CurrentRow, 4)) * _
Val(fgData.TextMatrix(CurrentRow, 5)), "0.00")
RS.Close: Set RS = Nothing
' ไปโปรแกรมย่อยเพื่อคำนวณจำนวนเงินทั้งหมด
Call CalTotalAmount
' จบจากการวนรอบ FOR และ ออกจากโปรแกรมย่อยไปเลยครับ
Exit Sub
End If
Next
' =================================================
' การนำรายละเอียดของสินค้าเข้าสู่ตารางกริดนั่นเอง
' =================================================
' เพิ่มจำนวนแถวใน MS FlexGrid อีก 1 แถว
fgData.Rows = fgData.Rows + 1
' ให้แถวปัจจุบันลดลง 1 (เพื่อไม่ให้นับ Column Header)
CurrentRow = fgData.Rows - 1
' แถวใหม่ที่เพิ่มเข้ามา ทำการอ่านข้อมูลจาก RecordSet เข้ามา
' ปกติหลักแรก (หลัก 0) ต้องนำเอาค่า Primary Key ไปซ่อนเอาไว้น่ะครับ ... อย่าลืม
fgData.TextMatrix(CurrentRow, 0) = RS("ProductPK")
' แสดงรหัสสินค้า
fgData.TextMatrix(CurrentRow, 1) = "" & RS("ProductCode")
' แสดงชื่อสินค้า
fgData.TextMatrix(CurrentRow, 2) = "" & RS("Description")
' แสดงหน่วยนับสินค้า
fgData.TextMatrix(CurrentRow, 3) = "" & RS("UnitName")
' แสดงราคาสินค้า
fgData.TextMatrix(CurrentRow, 4) = Format(RS("PriceUnit"), "0.00")
' ใส่จำนวนสินค้าครั้งละ 1
fgData.TextMatrix(CurrentRow, 5) = 1
' คำนวณราคาขาย กับ จำนวน
fgData.TextMatrix(CurrentRow, 6) = Format(fgData.TextMatrix(CurrentRow, 4) * _
fgData.TextMatrix(CurrentRow, 5), "0.00")
RS.Close: Set RS = Nothing
' ไปโปรแกรมย่อยเพื่อคำนวณจำนวนเงินทั้งหมด
Call CalTotalAmount
End Sub
|
Conclusion: มาถึงตอนที่ 3 แล้ว หลายคนที่ว่ามันยาก ก็คงจะได้กระจ่างแจ้งในใจกันแล้ว (มั้ง) น่ะครับ "ไม่เห็นยากสักกะหน่อยเลย" หากเรารู้จักกลวิธีในการออกแบบ และ การจัดเรียงลำดับของงานให้เป็นแล้ว นอกจากนี้ก็ยังได้รู้จักเทคนิคบางอย่างของการเขียนโปรแกรม ตามที่ผมได้บรรยายให้รับรู้กันไปแล้ว แหละนี่คือแนวคิด และ วิธีการที่ผมนำไปใช้กับงานจริงๆของผม และยังใช้เพื่อนำมาสอนคน (เพราะผมไม่ได้สอนหนังสือครับ) ให้กับลูกศิษย์ท้ายกุฏิ (ดื้อ มึน ด้าน) ทั้งหลายของผม แต่เวลาที่เขาทั้งหลายเหล่านี้ พร้อมออกไปเผชิญชีวิตในโลกการทำงานได้เองโดยลำพังแล้ว ... เหงาเหมือนกันครับ เพราะไม่มีม้าเร็วไปซื้อกับแกล้ม โซดา น้ำแข็ง มาให้ผมเลยครับพี่น้อง ... 55555+
|
|