หากมีคำถาม ขอให้ไปโพสต์ลง เว็บบอร์ดจีทูจีเน็ตดอตคอม ตัวใหม่แทนน่ะครับ

หรือติดต่อเข้ามาทาง Inbox ที่ เฟซบุ๊ค ผมครับ

หน้าหลัก
ข่าวสาร - บทความ ทั้งหมด
VB 6/VB.Net
ASP/ASP.Net
จับฉ่ายคอมพิวเตอร์
เรียนรู้ผ่าน Flash Movie
บทความที่มีผู้ตอบล่าสุด  
 RSS Feeds
 ดาวน์โหลดโปรแกรม RSS Reader ได้ที่นี่ ...   Download โปรแกรม RSS Reader

Forum - www.g2gnet.com
Webmaster - www.g2gnet.com
Visitors - Session views
 4 7 6 3 6 4 8

7 ธันวาคม พ.ศ.2549
115 Users On-Line.
Visitors - Page views
 7 9 8 8 3 2 0
1 กุมภาพันธ์ พ.ศ.2551

Google   
เว็บ g2gnet.com
ขนาดตัวอักษร:  

VB6 กับการใช้งาน Data Control ภาค 4 (โค้ด Run Time)

Category »  VB 6/VB.Net
โดย : Webmaster เมื่อ 20/10/2553   เวลา: 15:15
(อ่าน : 16863) 
บทความที่เกี่ยวกับ Data Control นี้ ผมแยกออกมาอธิบายเป็นตอนๆ ไล่เรียงตามลำดับมาเรื่อยๆ เพื่อแสดงให้เห็นถึง ขั้นตอนของกระบวนการเรียนรู้ กระบวนการคิด และ พัฒนาการฝึกเขียนโปรแกรม อย่างเป็นระบบ (ไม่ได้นำทางให้ใช้โปรแกรม) ... เมื่อพี่น้องได้ลองกลับไปดูบทความที่ผมเคยเขียนเอาไว้ ก็จะร้อง อ๋อ กันทันทีเลย ... กว่าจะเป็นโค้ดแบบ Run Time (หรือการอ้างอิง Object) อย่างที่เห็นๆกันอยู่ในเว็บไซต์ (ประหลาดๆแห่งนี้) มันที่มา ที่ไป เป็นอย่างนี้นี่เอง 55555+ ...
บทความในตอนนี้ เป็นการใช้งาน Data Control ก็จริงอยู่ ... แต่ทว่าผมจะไม่กำหนดการ Connection กับ DataBase เลย ในระหว่างทำการออกแบบ (Design Time) แต่จะใช้การเขียนโค้ดขึ้นมาแทน เพื่อให้ทำงานในลักษณะ Run Time ซึ่งจะมีความยืดหยุ่น และ มีประสิทธิภาพที่ดีกว่าวิธีการผูกข้อมูล (Bound Control) ในระหว่างขณะออกแบบ ... ซึ่งส่วนนี้จะนำทางให้เราไปเรียนรู้ถึงวิธีการใช้งาน Object แทน Control นั่นยังไงล่ะครับ ... พี่น้อง

ว่าด้วยเรื่องของ Control และ Object

    ทั้ง 2 ตัวมันแทบแยกกันไม่ออกหรอกครับ ก็เลยเหมาเรียกรวมๆกันได้ (ไม่น่าจะผิด 55555+) ... แต่ที่มีความแตกต่างกันอย่างเห็นได้ชัดเจนเลย ก็คือ
  • Control สามารถกำหนดคุณสมบัติ (Properties) ได้ในระหว่างขณะทำการออกแบบฟอร์ม (Design Time) นั่นคือ ต้องจับลาก Control มาวางไว้ในฟอร์มเสียก่อนที่จะกำหนดคุณสมบัติได้ เช่น นำเอา ADO Data Control เพื่อทำการเชื่อมต่อกับฐานข้อมูล (Connection)
  • Object จะต้องสั่งให้ทำงาน หรือ เห็นผลได้ ในขณะ Run Time เท่านั้น (ก็เขียนโค้ดขึ้นมาเองนั่นแหละครับ) เช่น
    Dim ConnDB As New ADODB.Connection
    Set ConnDB = New ADODB.Connection
    ConnDB.ConnectionString = _
        "Provider=Microsoft.Jet.OLEDB.4.0;" & _
        "Data Source=ฐานข้อมูล" & ";" & _
        "Persist Security Info=False"
    เห็นมั้ยครับว่า ทั้งๆที่มันก็จะทำการ Connection กับไฟล์ฐานข้อมูลเหมือนๆกัน

 

ดาวน์โหลด Source Code สำหรับผู้ใช้งาน Visual Basic 6
ดาวน์โหลด Source Code สำหรับ MS Visual Basic 6.0 - Service Pack 6
 ดาวน์โหลด Visual Basic 6.0 SP5: Run-Time Redistribution Pack
 ดาวน์โหลด Microsoft Data Access Object (MDAC) และ Jet 4.0 Update
 ดาวน์โหลด Microsoft Visual Basic Service Pack 6
ข้อมูลเพิ่มเติม
VB6 กับการใช้งาน Data Control ภาค 1 ปฐมบท (Design Time ก็ทำงานได้)
VB6 กับการใช้งาน Data Control ภาค 2 (ลูกผสม Design และ Run Time)
VB6 กับการใช้งาน Data Control ภาค 3 (การแก้ไขข้อมูล)
VB6 กับการใช้งาน Data Control ภาค 5 (ฉบับตราเด็กสมบูรณ์)
วิดีโอสอนขั้นตอนการ Debug Program ด้วย MS Visual Basic 6.0
วิดีโอสอนขั้นตอนการออกแบบความสัมพันธ์ระหว่างตารางแบบ One To One
โค้ดจากฟอร์มหลัก frmMain.frm ... เลือกเฉพาะส่วนสำคัญมาแสดงน่ะครับ

Option Explicit

' #####################################################
' Visual Basic ทุกรุ่น จะต้องมาเริ่มต้นที่เหตุการณ์ (หรือโปรแกรมย่อย) Form_Load เสมอ
' #####################################################
Private Sub Form_Load()
    
    ' ตั้งตำหน่งกึ่งกลางจอภาพ ... การใช้ \ หรือ การหารตัดเศษ จะทำงานได้เร็วกว่าการหาร /
    Me.Move (Screen.Width - Me.Width) \ 2, (Screen.Height - Me.Height) \ 2
    
    ' เคลียร์ค่า TextBox ที่ใช้ในการค้นหาข้อมูล
    txtSearch.Text = ""
    
    ' สังเกตด้วยว่า ผมจะไม่เอาฟิลด์ ProvincePK หรือ ProvinceFK มาด้วย เพราะการแสดงผลจะผิดพลาด
    ' หรือหากอยากจะเอามาก็ได้น่ะครับ แต่ต้องซ่อน Column มันเอาไว้ด้วย
    Statement = "SELECT tblCustomer.CustomerPK, tblCustomer.CustomerCode, " & _
                    " tblCustomer.CustomerName, tblCustomer.Address, tblCustomer.Amphur, " & _
                    " tblProvince.ProvinceName, tblCustomer.PostCode " & _
                    " FROM tblCustomer INNER JOIN tblProvince ON " & _
                    " tblCustomer.ProvinceFK = tblProvince.ProvincePK " & _
                    " ORDER BY CustomerPK "

    ' Data Control กับกำหนดการเชื่อมต่อไฟล์ฐานข้อมูลแบบ Run Time
    With Adodc1
        .ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0; " & _
                    " Data Source=" & App.Path & "\MyDB.MDB; " & _
                    " Persist Security Info=False"
        .CursorLocation = adUseClient
        ' เราสั่งให้อ่าน SQL Statement (Query)
        .CommandType = adCmdText
        ' แสดงผลข้อมูลอย่างเดียว ใช้ LockType แบบ Read Only
        .LockType = adLockReadOnly
        .RecordSource = Statement

        ' สั่งให้ประมวลผมตาม RecordSource หรือ SQL Statement ตัวใหม่
        .Refresh
    End With
    
    ' ผูกฐานข้อมูลเข้าสู่ Adodc1 เรียบร้อยแล้ว ... สามารถนับจำนวน Record ได้เลย
    lblCount.Caption = "[จำนวน : " & Adodc1.Recordset.RecordCount & " รายการ.]"

    ' การ Bound Control ระหว่าง Adodc1 และ DataGrid
    Set DataGrid1.DataSource = Adodc1
    ' จากคำสั่งด้านบนนี้ จะทำให้ DataGrid มี Column ทั้งหมด 7 หลัก (ตามจำนวนฟิลด์ที่กำหนดจาก Query)

    ' เรียกไปยังโปรแกรมย่อยในการปรับแต่งรูปแบบของ DataGrid
    Call SetupDataGrid
    
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

โค้ดจากฟอร์มย่อย frmCustomer.frm ... เลือกเฉพาะส่วนสำคัญมาแสดงน่ะครับ

Option Explicit

' #####################################################
' เริ่มต้นการแสดงรายละเอียดของลูกค้า ตามค่า Primary Key จากฟอร์มหลัก
' #####################################################
Private Sub Form_Load()
    
    ' รับค่า CustomerPK ที่เราซ่อนเอาไว้ จากหลักแรก (Index = 0) มาจากฟอร์มหลัก
    ' ก่อนจะมาถึงฟอร์มนี้ เรากำหนดให้อ่านหลัก 0 ของ DataGrid มาก่อนแล้วล่วงหน้า
    ' หากเอาแบบให้แน่ใจก็กำหนดใหม่ได้ โดยการอ้างถึงฟอร์มหลักโดยตรง
    frmDataControlRetrieve.DataGrid1.Col = 0
    CustomerPrimaryKey = frmDataControlRetrieve.DataGrid1.Text
    
    ' ส่วนนี้จริงๆแล้ว ต้องแยกออกเป็นโปรแกรมย่อย เพราะต้องมีการทดสอบก่อนว่าเป็นการเพิ่ม หรือ แก้ไขข้อมูล
    ' หากเป็นการเพิ่มข้อมูลใหม่ ก็จะเคลียร์ค่า Control ต่างๆเพื่อเริ่มต้นการป้อนข้อมูลใหม่
    ' หากเป็นการแก้ไข ก็จะต้องทำการแสดงผลข้อมูลเดิมขึ้นมา ... นี่แหละคือการออกแบบเป็นขั้นเป็นตอน
    ' ทำ Query เพื่อทำการค้นหาข้อมูล
    Statement = "SELECT * FROM tblCustomer " & _
                    " WHERE " & _
                    " [CustomerPK] = " & CustomerPrimaryKey & _
                    " ORDER BY CustomerPK "
    
    ' หากมีนับเป็นสิบๆฟอร์มก็ต้องมาทำ Connection กันใหม่ในทุกๆฟอร์มกันล่ะครับ
    ' Adodc1 ในฟอร์มหลัก กับในฟอร์มนี้มันคนละตัวกัน ทำให้ต้องทำการ Connect ใหม่
    ' มันเป็นการ Connect เข้าหาไฟล์ก้อนเดียวกัน จึงทำให้เปลืองหน่วยความจำ
    ' และ อาจจะเกิดอันตรายต่อไฟล์ฐานข้อมูลได้ด้วย ... ยิ่งเป็นระบบ LAN หลับตานึกภาพเอาเองเลยครับ
    With Adodc1
        .ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0; " & _
                    " Data Source=" & App.Path & "\MyDB.MDB; " & _
                    " Persist Security Info=False"
        ' ผูกข้อมูลตาม Query ที่กำหนดให้กับ ADO Data Control ใหม่อีกครั้ง
        .RecordSource = Statement
        
        ' สั่งทำงานตามคำสั่ง SQL (Query) ... กรณีที่ Design Time ไม่ได้ตั้งค่าเอาไว้ล่วงหน้า
        .CommandType = adCmdText
        
        ' สามารถอ่านจำนวน Record ได้ ... ไปกำหนดตอน Design Time เอาก็ได้ครั้งเดียว
        .CursorLocation = adUseClient
        
        ' อ่านเดินหน้าอย่างเดียว กรณีของการแสดงผล
        .LockType = adLockReadOnly
        
        ' ปรับข้อมูลใหม่ใน Adodc1
        .Refresh
        
        ' นำเอาข้อมูลจากการ Query เพื่อทำการแสดงผลข้อมูล
        txtCustomerCode.Text = Adodc1.Recordset("CustomerCode")
        txtCustomerName.Text = Adodc1.Recordset("CustomerName")
        txtAddress.Text = Adodc1.Recordset("Address")
        txtAmphur.Text = Adodc1.Recordset("Amphur")
        txtPostCode.Text = Adodc1.Recordset("PostCode")
        
    End With
    
    ' ความจริงต้องประกาศตัวแปรต่างๆ เอาไว้บนสุดของ Sub Program น่ะครับ ... จะได้ไม่สับสน
    ' ประกาศตัวแปรเพื่อรับค่ารายชื่อจังหวัดเอาไว้ก่อน แล้วค่อยนำไปเทียบรายการใน DataCombo
    Dim ProvinceName As String
    ProvinceName = Adodc1.Recordset("ProvinceName")
    
    Adodc1.Recordset.Close
    
    ' #####################################################
    ' หากในฟอร์มมี ComboBox นับ 10 ตัวล่ะ จะมี ADO Data Control ทั้ง 10 ตัวเลยเหรอ
    ' ส่วนนี้คือการนำ Adodc1 กลับมาใช้งานอีกรอบ แต่เปลี่ยน RecordeSource ใหม่
    ' เพื่อ Query หารายชื่อจังหวัด นำมาแสดงผลลงใน DataCombo เสียก่อน
    ' #####################################################
    Statement = "SELECT * FROM tblProvince ORDER BY ProvinceName "
    With Adodc1
        .CursorLocation = adUseClient
        .CommandType = adCmdText
        .RecordSource = Statement
        .Refresh
    End With
    
    Set DataCombo1.RowSource = Adodc1
    DataCombo1.ListField = "ProvinceName"
    
    ' นำค่า ProvinceName จากการ Query เดิมมาเทียบใส่กับ DataCombo
    DataCombo1.Text = ProvinceName
    ' #####################################################
       
End Sub

' #####################################################
' เกิดเหตุการณ์กดปุ่ม (Event) ... สั่งทำ Driven คือ
' - ตรวจสอบการป้อนข้อมูล
' - บันทึกผลเข้าสู่ตารางข้อมูล
' - กลับไปรายการหลัก
' #####################################################
Private Sub cmdSave_Click()

    ' การตรวจสอบค่าว่างในฟิลด์ที่สำคัญๆ และจำเป็น เช่น รหัสลูกค้า ชื่อลูกค้า
    ' ผมจะข้ามการทดสอบหารหัสลูกค้าที่ซ้ำกันออกไป ... สามารถดูรายละเอียดเพิ่มเติมได้จาก
    ' http://www.g2gnet.com/News ... หรือ เอาไปคิดเป็นการบ้านเองด้วยครับ
    If Trim(txtCustomerCode.Text) = "" Then
        MsgBox "กรุณาป้อนรหัสลูกค้าให้เรียบร้อยก่อนด้วย.", vbOKOnly + vbExclamation, "รายงานสถานะ"
        txtCustomerCode.SetFocus
        Exit Sub
    ElseIf Trim(txtCustomerName.Text) = "" Then
        MsgBox "กรุณาป้อนชื่อลูกค้าให้เรียบร้อยก่อนด้วย.", vbOKOnly + vbExclamation, "รายงานสถานะ"
        txtCustomerName.SetFocus
        Exit Sub
    
    End If
    
    ' #####################################################
    ' สั่งบันทึกข้อมูล ... ส่วนนี้ควรแยกออกเป็นโปรแกรมย่อย ... จะดีกว่า
    ' #####################################################
    
    ' ความจริงต้องประกาศตัวแปรต่างๆ เอาไว้บนสุดของ Sub Program น่ะครับ ... จะได้ไม่สับสน
    ' เนื่องเราใช้ ADO Data Control เพียงแค่ตัวเดียว ดังนั้นจะต้องไปหาค่า Primary Key
    ' ของรายชื่อจังหวัดที่เลือกเข้ามาก่อน ... นั่นคือการจัดเรียงลำดับเหตุการณ์เพื่อไม่ให้เกิดข้อผิดพลาด
    Dim ProvincePrimaryKey As Integer
    ProvincePrimaryKey = CompareProvinceName

    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 " & _
                    " [CustomerPK] = " & CustomerPrimaryKey & _
                    " ORDER BY CustomerPK "
    With Adodc1
        .RecordSource = Statement
        ' ต้องใช้ adCmdText แทน เพราะเราใช้ SQL Statement ไม่ได้ใช้ตาราง (adCmdTable)
        ' หากกำหนดเป็น adCmdTable จะเกิด Syntax error in FROM clause
        .CommandType = adCmdText
        .CursorLocation = adUseClient
        ' เมื่อต้องการบันทึกผล หรือ เขียนข้อมูลลงไปในตารางข้อมูล ต้องปรับใหม่
        .CursorType = adOpenKeyset
        .LockType = adLockOptimistic
        .Refresh
        
        .Recordset.Fields("CustomerCode") = "" & txtCustomerCode.Text
        .Recordset.Fields("CustomerName") = "" & txtCustomerName.Text
        .Recordset.Fields("Address") = "" & txtAddress.Text
        .Recordset.Fields("Amphur") = "" & txtAmphur.Text
    
        .Recordset.Fields("ProvinceFK") = ProvincePrimaryKey
    
        .Recordset.Fields("PostCode") = "" & txtPostCode.Text
    
        ' บันทึกข้อมูลใหม่ (Update)
        .Recordset.Update
        
        ' ปิดการเชื่อมต่อกับตารางข้อมูล
        .Recordset.Close
    
    End With
    
    MsgBox "บันทึกข้อมูลเรียบร้อย.", vbOKOnly + vbInformation, "รายงานสถานะ"
    
    ' ปิดฟอร์มกลับไปที่โปรแกรมรายการหลัก
    Unload Me
    
End Sub

' #####################################################
' เนื่องจากต้องมีการส่งค่า Primary Key ของจังหวัดนั้นๆ กลับคืนไปเราจึงต้องใช้ฟังค์ชั่นแทน
' แต่ไม่มีการรับค่าเข้ามา นำค่า Text ที่อยู่ใน DataCombo มาทดสอบหาค่า Primary Key
' #####################################################
Function CompareProvinceName() As Integer
    
    Statement = "SELECT tblProvince.ProvincePK, tblProvince.ProvinceName " & _
                    " FROM tblProvince " & _
                    " WHERE ProvinceName = " & "'" & DataCombo1.Text & "'"
                            
    With Adodc1
        .RecordSource = Statement
        .CommandType = adCmdText
        .CursorLocation = adUseClient
        .LockType = adLockReadOnly
        .Refresh
    End With
    
    ' ส่งค่าคืนกลับ (แบบเลขจำนวนเต็ม) โดยใช้ชื่อฟังค์ชั่นของตัวมันเอง
    CompareProvinceName = Adodc1.Recordset("ProvincePK")
    
    ' ปิดการเชื่อมต่อกับตารางข้อมูล
    Adodc1.Recordset.Close
    
End Function

Conclusion:
คราวนี้คงพอเริ่มมองเห็นภาพกันขึ้นมาบ้างแล้ว ... การเขียนโค้ด (แปลกๆ) ของผมในปัจจุบัน มันก็ไล่เรียงกันมาเป็นขั้น เป็นตอน ในลักษณะแบบนี้นั่นแหละครับ นั่นหมายความว่าเป็นการพัฒนาต่อ-ยอดกันมาอย่างเป็นระบบ เมื่อเราสามารถเรียนรู้ถึงที่มา ที่ไปของมันได้ ก็จะทำให้เกิดความเข้าใจได้ลึกซึ้งมากยิ่งขึ้น ... การเขียนโปรแกรม ไม่ใช่การมานั่งท่องจำเอา ต้องฝึกคิด ฝึกทำ จินตนาการไปก่อนล่ะ ว่ามันต้องทำได้ แล้วค่อยคิดหากลวิธีในการแก้ปัญหาออกมา ...

จี ทู จี เน็ต ดอต คอม - g2gNet Dot Com
เลขทะเบียนพาณิชย์อิเล็กทรอนิกส์ 0407314800231
CopyLeft © 2004 - 2099 g2gNet.Com All rights reserved.
Email: [email protected] หรือ โทร. 08-6862-6560