ปัจจุบันนี้ผมก็ยังคงใช้วิธีการดังกล่าวจากภาค 1 เพื่อใช้ทดสอบการเชื่อมต่อ และ ดูข้อมูล กับ DBMS ตัวอื่นๆ เช่น Pervasive (ชื่อเดิม BTrieve) FireBird หรือ MyODBC เป็นต้น โดยที่ไม่ต้องไปงงน่ะจังงังกับการเขียนโค้ดใดๆเลย ... อย่างไรก็ตามที ตอนที่ฝึกเขียนโค้ด VB4 ผมไม่เคยใช้ (และไม่เคยสอนใคร) ตามวิธีการในหนังสือ ที่ต้องเลื่อน Record แบบเดินหน้า ถอยหลัง แต่อย่างใด ด้วยเพราะมีประสบการณ์กับ FoxPro (DOS) เพื่อเขียนโปรแกรมให้คนอื่นใช้งานมาแล้ว ... ดังนั้นต้องยึดตามหลักความเป็นจริง ในการเข้าถึงข้อมูล (ภาษาอังกฤษเรียก Retrieve หรือ ค้นคืน) มันมีทางเลือก 2 ทาง คือ แสดงผลข้อมูลทั้งหมด หรือ เลือกมาเฉพาะที่ต้องการ (ค้นหานั่นแหละ) ... เมื่อได้ข้อมูลมาแสดงผลแล้ว ก็ขึ้นอยู่กับว่าผู้ใช้ (User) ต้องการทำอะไรต่อไป ซึ่งก็มีทางเลือก 2 ทาง คือ แก้ไขข้อมูล (หรือดูรายละเอียด) กับ ลบข้อมูลทิ้งไป ... นี่คือ ธรรมชาติของการออกแบบโปรแกรมเลยก็ว่าได้
การจะเขียนโค้ดเพื่อควบคุมการทำงานของโปรแกรมของ Visual Basic ก็คือ การเกิดเหตุการณ์ (Event) เช่น มีการกดปุ่ม Enter ในช่อง TextBox (Event) จากนั้นสั่งให้ไปทำการค้นหาข้อมูล ตัวหลังนี่แหละก็คือ Driven ... โค้ดตัวอย่าง Private Sub txtSearch_KeyPress(KeyAscii As Integer) If KeyAscii = vbKeyReturn Then Call SearchData End Sub คำว่าเหตุการณ์ มันก็คือโปรแกรมย่อยนั่นแหละครับ แต่เป็นโปรแกรมย่อยที่ Visual Basic สร้างขึ้นมาให้อัตโนมัติ เช่น KeyPress ของ TextBox ก็คือการกดคีย์ใดๆจากแป้นคีย์บอร์ดเข้าไปใน TextBox หรือ เหตุการณ์ Click ที่ปุ่ม CommandButton ก็คือการนำเมาส์ไปคลิ๊กที่ ปุ่ม CommandButton ... ซึ่งผู้เริ่มต้นฝึก VB ใหม่ๆ ต้องพยายามทำความเข้าใจกับกลไกเหล่านี้ให้ดีเสียก่อน ... เพราะเป็นเรื่องที่สำคัญมากๆๆๆๆๆๆ ... ไม่งั้นโปรแกรมเมอร์ทั้งประเทศจะไม่ยอมให้คุณไปต่อ ... VB STAR 55555+ |
 |
 หากนำ Query ในตัวอย่างจาก MS Access ของผมไปใช้ ต้องตัดหลักแสดง ProvinceFK ออกไปก่อนด้วย
|
ก่อนที่จะทำการลงโค้ดได้ เราจะต้องออกแบบ หรือ รู้ลำดับขั้นตอนการทำงานของโปรแกรม (Flow Control) ให้ได้เสียก่อน
Option Explicit
' #####################################################
' เกิดเหตุการณ์ (Events) โดยการกดปุ่ม Enter (ASCII Code = 13) ในช่อง TextBox
' โปรแกรมย่อยตัวนี้มีการรับค่า ASCII Code เข้ามา โดยรับค่าผ่านทาง KeyAscii
' ดังนั้นมันจะทำการอ่านรหัส ASCII Code ที่รับเข้ามา เช่น A จะมีรหัส ASCII = 65
' แล้วรู้ได้ยังไงล่ะว่ารหัส ASCII ของตัวอักขระแต่ละตัวมีค่าเท่าไร ... ต้องลอง Debug ดูครับ
' ให้ Cursor ชี้ที่บรรทัดคำสั่งด้านล่าง (ตรง Sub) แล้วกด Ctrl + F8 ... เมื่อมันทำงานถึงก็จะหยุดรอก่อน
' เลื่อนเมาส์เข้าไปหาบริเวณ KeyAscii เราก็จะเห็นผลลัพธ์ทันที
' #####################################################
Private Sub txtSearch_KeyPress(KeyAscii As Integer)
' นี่คือเงื่อนไข ...
' ไม่ว่า User จะคีย์อะไรเข้ามาก็ไม่สนใจ จนกว่าจะเจอการกด Enter เท่านั้น จึงจะสั่งการ Driven
If KeyAscii = vbKeyReturn Then
' หรือ หากคุณเข้าใจเรื่องรหัส ASCII ก็สามารถใช้ค่าเลขจำนวนเต็มได้เลย
'If KeyAscii = 13 Then
' เนื่องจากโค้ดคำสั่งอาจจะยาวเฟื้อยหรือไม่ก็ตาม แต่เราควรแยกเป็นโปรแกรมย่อยเอาไว้
' เช่น ตอนหลังอาจจะมีปุ่ม CommandButton มาวางตามหลัง TextBox ตัวนี้
' นั่นคือ จะมีเหตุการณ์ของการ Click ที่ปุ่มเพิ่มขึ้นมา และมันทำการค้นหาข้อมูลเช่นเดียวกัน
' จะได้ไม่ต้องมาเขียนโค้ดซ้ำๆกัน ... ที่สำคัญมันจะดูเป็นระเบียบมากกว่า
' สั่งให้ไปทำงานที่โปรแกรมย่อยที่ใช้ในการค้นหาข้อมูลได้เลยทันที (นี่คือ Driven)
Call SearchData
' พอมันทำงานที่โปรแกรมย่อย SearchData เสร็จเรียบร้อย ก็จะกระโดดกลับมาทำงานต่อ
' นั่นคือ เจอคำสั่ง End If และ End Sub ออกจากโปรแกรมย่อย
End If
End Sub
' #####################################################
' เกิดเหตุการณ์ (Events) ในการกดปุ่ม Enter จากช่อง TextBox (txtSearch)
' Driven ก็คือ ทำ Query ตามเงื่อนไขที่ใช้ในการค้นหา ... ผลลัพธ์ส่งออกไปที่ DataGrid
' #####################################################
Sub SearchData()
Dim Statement As String
' Trim(txtSearch.Text) คือ การตัดช่องว่าง ที่มีอยู่ด้านหน้า ด้านหลัง ของ TextBox ออก
' หากต้องการตัดช่องว่างเฉพาะด้านหน้า จะใช้ LTrim
' หากต้องการตัดช่องว่างเฉพาะด้านหลัง จะใช้ RTrim
' Len(Trim(txtSearch.Text)) = 0 คือ ไม่มีตัวอักขระใดๆเลยอยู่ในช่อง TextBox
' หากไม่มีการป้อนข้อมูลในการค้นหา ให้ออกจากโปรแกรมย่อยทันที
If Trim(txtSearch.Text) = "" Or Len(Trim(txtSearch.Text)) = 0 Then
' SetFocus คือ ให้ Cursor ไปอยู่ในช่องของ TextBox ที่เราต้องการ
' ต้องระวังการใช้ SetFocus ไม่สามารถนำไปใช้ในโปรแกรมย่อยที่เราสร้างขึ้นมาเองได้
txtSearch.SetFocus
' หากเงื่อนไขเป็น True ก็ให้ออกจากโปรแกรมย่อย หรือ เหตุการณ์นี้ทันที
Exit Sub
End If
' ทำ Query เพื่อทำการค้นหาข้อมูล
Statement = "SELECT tblCustomer.CustomerPK, tblCustomer.CustomerCode, " & _
" tblCustomer.CustomerName, tblCustomer.Address, tblCustomer.Amphur, " & _
" tblCustomer.ProvinceFK, tblProvince.ProvinceName, tblCustomer.PostCode " & _
" FROM tblCustomer INNER JOIN tblProvince ON " & _
" tblCustomer.ProvinceFK = tblProvince.ProvincePK " & _
" WHERE " & _
" [CustomerCode] " & " Like '%" & Trim(txtSearch.Text) & "%'" & " OR " & _
" [CustomerName] " & " Like '%" & Trim(txtSearch.Text) & "%'" & " OR " & _
" [Address] " & " Like '%" & Trim(txtSearch.Text) & "%'" & " OR " & _
" [Amphur] " & " Like '%" & Trim(txtSearch.Text) & "%'" & " OR " & _
" [ProvinceName] " & " Like '%" & Trim(txtSearch.Text) & "%'" & _
" ORDER BY CustomerPK "
With Adodc1
' ผูกข้อมูลตาม Query ที่กำหนดให้กับ ADO Data Control ใหม่อีกครั้ง
' จำได้ว่าจุดตรงนี้นี่เอง ที่ทำให้ผมรู้ว่าตอน Design Time เราแค่ทดสอบการ Connect ก็พอ
' จากนั้นก็ปลด SQL Statement ออกไป แล้วค่อยมาเขียนโค้ดใหม่ตอน Form_Load ก็ได้ อิๆๆๆๆ
.RecordSource = Statement
' สั่งทำงานตามคำสั่ง SQL (Query) ... กรณีที่ Design Time ไม่ได้ตั้งค่าเอาไว้ล่วงหน้า
.CommandType = adCmdText
' สามารถอ่านจำนวน Record ได้ ... ไปกำหนดตอน Design Time เอาก็ได้ครั้งเดียว
.CursorLocation = adUseClient
' อ่านเดินหน้าอย่างเดียว กรณีของการแสดงผล
.LockType = adLockReadOnly
' ปรับข้อมูลใหม่ใน Adodc1
.Refresh
End With
' กรณีหาข้อมูลพบ ... จะทำให้ Adodc1.Recordset.EOF = False
' ก่อนมาถึงตรงนี้ได้ ต้องมีทักษะพื้นฐานเรื่องของตรรกศาสตร์มาก่อนล่ะครับ
' แล้วรู้ได้ยังไงล่ะว่ามันมีค่าอะไร ... ต้องลอง Debug ดูครับ
' ให้ Cursor ชี้ที่บรรทัดคำสั่งด้านล่าง (ตรง If) แล้วกด Ctrl + F8 ... เมื่อมันทำงานถึงก็จะหยุดรอก่อน
' เลื่อนเมาส์เข้าไปหาบริเวณ Adodc1.Recordset.EOF เราก็จะเห็นผลลัพธ์ทันที
' ตัวแปลภาษาระดับสูงทุกตัว มันมี Debugger อยู่ในตัวของมันเอง
' เราต้องฝึกใช้งานมันให้เป็นด้วย ซึ่งจะช่วยให้ค้นหาคำตอบต่างๆออกมาได้
If Not Adodc1.Recordset.EOF Then
' หรือเขียนแบบธรรมดาที่มือใหม่จะเข้าใจได้ง่ายๆหน่อย
'If Adodc1.Recordset.EOF = False Then
' ปรับการแสดงผลข้อมูลใหม่ ใน DataGrid ตามข้อมูลที่ค้นหาพบ
DataGrid1.Refresh
' ป้าย Label แสดงผลจำนวนข้อมูลที่ค้นหาพบ
lblCount.Caption = "[จำนวน : " & Adodc1.Recordset.RecordCount & " รายการ.]"
' กรณีหาข้อมูลไม่พบ ... นั่นคือ Adodc1.Recordset.EOF = True
' EOF หรือ End Of File ... คือ สิ้นสุดของข้อมูลแล้ว แปลง่ายๆ ก็หาข้อมูลไม่พบสักรายการนั่นแหละครับ
Else
lblCount.Caption = "[จำนวน : 0 รายการ.]"
' แจ้งให้ผู้ใช้งานทราบด้วยก็ได้ว่าหาข้อมูลไม่เจอ
MsgBox "ขออภัย ... ไม่พบข้อมูลที่ต้องการ.", vbOKOnly + vbInformation, "รายงานสถานะ"
End If
' เคลียร์ค่าการค้นหา
txtSearch.Text = ""
End Sub
' #####################################################
' เกิดเหตุการณ์ (Events) โดยการกดดับเบิ้ลคลิ๊กในแต่ละแถวของ DataGrid
' Driven คือ การแสดงผลค่า Primary Key ที่ซ่อนในหลักแรก (Index = 0) ออกมา
' #####################################################
Private Sub DataGrid1_DblClick()
' หากไม่มีข้อมูลใดๆเลย จะทำให้ค่า Row ของ DataGrid มีค่าติดลบ ก็ให้ออกจากโปรแกรมย่อยไปเลย
' แล้วรู้ได้ยังไงล่ะว่ามันมีค่าอะไร ... ต้องลอง Debug ดูครับ
' ให้ Cursor ชี้ที่บรรทัดคำสั่งด้านล่าง (ตรง If) แล้วกด Ctrl + F8 ... เมื่อมันทำงานถึงก็จะหยุดรอก่อน
' เลื่อนเมาส์เข้าไปหาบริเวณ DataGrid1.Row เราก็จะเห็นผลลัพธ์ทันที
If DataGrid1.Row < 0 Then Exit Sub
' ไปอ่านค่าที่ได้ในหลักแรก (หลัก 0) ... เพราะตัวนี้เราเก็บค่า Primary Key เอาไว้
DataGrid1.Col = 0
' แสดง Primary Key ออกมา เพื่อนำค่านี้ไปใช้เป็นเงื่อนไขในการแสดงผลต่อไป
MsgBox DataGrid1.Text
' รอบหน้าค่อยมาต่อกันใหม่ครับ เราจะนำเอา Primary Key ตัวนี้ไปใช้ในการแสดงผลข้อมูล
End Sub
' #####################################################
' เกิดเหตุการณ์ (Events) โดยการกดปุ่ม Enter (vbKeyReturn) ในแต่ละแถวของ DataGrid
' Driven คือ สั่งให้ไปทำงานต่อที่เหตุการณ์กดดับเบิ้ลคลิ๊กแต่ละแถวของ DataGrid
' นี่คือ เหตุการณ์ที่มันเกิดซ้ำๆกัน ... ผมถึงได้แนะนำให้แยกออกเป็นโปรแกรมย่อย ... จะดีกว่า
' #####################################################
Private Sub DataGrid1_KeyPress(KeyAscii As Integer)
If KeyAscii = vbKeyReturn Then
' ไปโปรแกรมย่อย เหตุการณ์กดดับเบิ้ลคลิ๊กแต่ละแถวของ DataGrid
Call DataGrid1_DblClick
End If
End Sub
' #####################################################
' โปรแกรมย่อยที่สร้างเอง (Sub Program) ในการปรับแต่ง DataGrid แบบ Run Time
' สำหรับมือใหม่ๆ ควรจะฝึกการเขียนเป็นโปรแกรมย่อย ในลักษณะแบบนี้เอาไว้ด้วยน่ะครับ
' เพราะเราจะแยกแต่ละส่วนออกจากกัน เพื่อจะได้เข้าใจ หรือ แก้ไขได้ง่ายๆต่อไป
' #####################################################
Sub SetupDataGrid()
With DataGrid1
' #####################################################
' สูตรสำเร็จเลยครับ ... เรื่องของการซ่อน Primary Key เอาไว้ เพื่อนำไปใช้ประโยชน์ต่อไป
' ซ่อน Primary Key ไว้ในหลักแรกของตารางกริด (Index = 0) เอาไว้
.Columns(0).Width = 0
' ไม่ให้ปรับระยะของหลักแรก (Index = 0) ให้ขยายออกมาได้
.Columns(0).AllowSizing = False
' #####################################################
' #####################################################
' เริ่มการปรับแต่งค่า Column ของ DataGrid แบบ Run Time
' จะมีจำนวนทั้งสิ้น 7 หลัก แต่จะใช้ค่า Index จาก 0 - 6 (คอมพิวเตอร์ จะเริ่มนับจาก 0)
' ในงานจริงๆของแต่ละคน ก็ต้องดูด้วยว่าเรียกฟิลด์ไหนมาแสดงผลข้อมูลด้วยน่ะครับ
With .Columns(1)
.Caption = "รหัสลูกค้า"
.Width = DataGrid1.Width \ 6
End With
With .Columns(2)
.Caption = "ชื่อลูกค้า"
.Width = DataGrid1.Width \ 6
End With
With .Columns(3)
.Caption = "ที่อยู่ลูกค้า"
.Width = DataGrid1.Width \ 6
End With
With .Columns(4)
.Caption = "อำเภอ"
.Width = DataGrid1.Width \ 6
End With
With .Columns(5)
.Caption = "จังหวัด"
.Width = DataGrid1.Width \ 6
' ทดสอบปรับการแสดงผลให้อยู่ชิดขวา
.Alignment = dbgRight
End With
With .Columns(6)
.Caption = "รหัสไปรษณีย์"
.Width = .Width + 170
End With
End With
End Sub
' #####################################################
' Visual Basic ทุกรุ่น จะต้องมาเริ่มต้นที่เหตุการณ์ (หรือโปรแกรมย่อย) Form_Load เสมอ
' #####################################################
Private Sub Form_Load()
' ตั้งตำหน่งกึ่งกลางจอภาพ ... การใช้ \ หรือ การหารตัดเศษ จะทำงานได้เร็วกว่าการหาร /
Me.Move (Screen.Width - Me.Width) \ 2, (Screen.Height - Me.Height) \ 2
' เคลียร์ค่า TextBox ที่ใช้ในการค้นหาข้อมูล
txtSearch.Text = ""
' เนื่องจากเราผูกฐานข้อมูลเข้าสู่ Adodc1 เรียบร้อยแล้ว ... สามารถนับจำนวน Record ได้เลย
' ใน Design Time ต้องปรับ Properties ให้ CursorLocation = 3 - adUseClient ด้วย
lblCount.Caption = "[จำนวน : " & Adodc1.Recordset.RecordCount & " รายการ.]"
' เรียกไปยังโปรแกรมย่อยในการปรับแต่งรูปแบบของ DataGrid
Call SetupDataGrid
End Sub
Private Sub Form_Resize()
' ปรับระยะการแสดงผลของ DataGrid
DataGrid1.Move 0, 540, Me.ScaleWidth - 15, Me.ScaleHeight - 570
End Sub
' #####################################################
' ก่อนจบการทำงานของโปรแกรม ต้องปิดการเชื่อมต่อไฟล์ฐานข้อมูลด้วย
' #####################################################
Private Sub Form_Unload(Cancel As Integer)
' สำหรับมือใหม่ๆ ลองคิด และ ตัดไปทำเป็นโปรแกรมย่อยดูครับ
If Adodc1.Recordset.ActiveConnection.State = adStateOpen Then
Adodc1.Recordset.ActiveConnection.Close
Set Adodc1.Recordset.ActiveConnection = Nothing
End If
' การคืนค่าหน่วยความจำกลับคืนให้กับระบบปฏิบัติการ - Operating System
' โดยการใช้ชื่อฟอร์มตัวมันเอง ... การทำแบบนี้ก็เพื่อให้แน่ใจเท่านั้นเองแหละครับ
Set frmDataControlRetrieve = Nothing
End Sub
|
Conclusion: นี่คือโค้ดตัวอย่างของการผสมผสาน การทำงานระหว่าง Design Time และ สั่ง Run Time เข้าด้วยกัน ... สิ่งที่พี่น้องจะได้รับจากบทความนี้ ก็คือ ได้มองเห็นภาพการทำงานของ Event/Driven สำหรับ Visual Basic ได้อย่างชัดเจนมาก (และนำไปใช้กับ VB.NET ได้ด้วยน่ะครับ) และ ที่สำคัญคือ ได้เรียนรู้ ฝึกฝน วิธีการออกแบบโปรแกรมอย่างถูกต้อง ไม่ใช่ยังคงมาใช้วิธีการ MoveFirst, MoveLast, MoveNext และ MovePrevious หรือ การเลื่อนรายการแบบเดินหน้า ถอยหลัง กันอยู่ ซึ่งทางปฏิบัติมันนำไปใช้งานจริงไม่ได้เลย ... หลงทางเสียเวลา หลงภรรยาเขา เสียชีวิตแน่ๆ 55555+
นี่คือ ... ก้าวย่างเริ่มต้น ก่อนที่จะไปเรียนรู้การเขียนโค้ดแบบ Run Time อย่างเต็มรูปแบบ
|