ผู้เขียน หัวข้อ: [VB6] การแสดงรายการขายสินค้าลงใน MS FlexGrid Control (One-To-Many Relation)  (อ่าน 263 ครั้ง)

ออฟไลน์ ทองก้อน ทับทิมกรอบ

  • Administrator
  • *****
  • กระทู้: 245
  • เพศ: ชาย
  • Webmaster G2GNet
[VB6] การแสดงรายการขายสินค้าลงใน MS FlexGrid Control (One-To-Many Relation)

สำหรับผู้เริ่มต้นใหม่ ในการฝึกเขียนโปรแกรมและพัฒนาระบบฐานข้อมูลให้ได้ดีนั้น ควรจะต้องใช้ "เรียนรู้นอกระบบ รบนอกตำรา" ... ดูจากตัวอย่างงานจริงจากหลายๆแหล่งข้อมูล แล้วก็คิดวิเคราะห์ตามไป ไม่นานคุณก็จะหาคำตอบให้กับตัวเองได้แน่นอนครับ ...

ข้อแนะนำก่อนที่จะศึกษาโค้ดโปรแกรมของผม
  • ลืมทฤษฎีที่ศึกษามาจากห้องเรียน หรือ ความรู้จากตำราที่มีอยู่ในอ้อมกอดออกไปให้หมด เพราะสิ่งที่จะเห็นต่อไปนี้ มันต่างกันอย่างสิ้นเชิง
  • ต้องเข้าใจการเขียนโปรแกรมในลักษณะของ 1 : 1 มาก่อน เช่น การบันทึกรายการลูกค้า รายการสินค้า ซึ่งก็มีอยู่มากมายในเว็บไซต์ของผม
  • ต้องเข้าใจการใช้งาน Control พื้นฐานต่างๆ และ MS FlexGrid
  • ต้องใจเย็นๆ ค่อยๆไล่แกะ และ คิดพิจารณาตามไปด้วย ... พยายามตั้งคำถามว่าทำไมให้บ่อยๆ
  • ที่สำคัญ ... ต้องมีมานะ พยายาม ขยัน และ อดทนในการเรียนรู้ฝึกฝน ...
  • วิธีการนี้มันคงไม่ใช่วิธีการที่ดีที่สุด ... แต่มันดีที่สุดสำหรับผม ณ ขณะเวลานี้ (และยังใช้มาถึงทุกวันนี้)


การออกแบบตารางข้อมูลในลักษณะ One To Many ซึ่งเราจะให้ความสำคัญไปที่ตาราง tblInvoiceDetail ... หากมองดูเผินๆ Primary Key จะมีค่าซ้ำกัน ... ทำไม??? ... ผมไม่ขออธิบายแล้วล่ะ เพราะบอกไปหลายครั้งแล้ว จนรู้สึกเฝือๆ ย้ำๆซ้ำๆแต่เรื่องเดิมๆ 5555+


Run Time ... เมื่อรันโปรแกรม

มาดูโค้ดกันเถอะ ...

' / --------------------------------------------------------------------------------------------------------
' / Developer : Mr.Surapon Yodsanga (Thongkorn Tubtimkrob)
' / Visual Basic 6 Programmer from Khon Kaen, Thailand.
' / eMail : thongkorn@hotmail.com
' / URL: http://www.g2gnet.com
' / Facebook: www.facebook.com/g2gnet
' / Purpose: Understanding simple One-To-Many Programming.
' / Microsoft Visual Basic 6.0 Service Pack 6
' / --------------------------------------------------------------------------------------------------------
Option Explicit

'// ----------------------------------------------------------------------------------------------------
'// นำรายการสินค้าที่ขายไปสำหรับ Invoice ใบนี้ เข้ามาแสดงผล
'// ----------------------------------------------------------------------------------------------------

Sub RecordToGridProduct(PrimaryKey As Long)
    Dim sRow As Integer
   
    Set DS = New ADODB.Recordset
    ' ปกติแล้วเราจะใช้ InvoicePK หรือ Primary Key สร้างเงื่อนไข
    SQLStmt = "SELECT tblInvoiceDetail.InvoicePK, tblProduct.ProductCode, tblProduct.Description, " & _
                        " tblUnit.UnitName, tblInvoiceDetail.PriceUnit, tblInvoiceDetail.Quantity, " & _
                        " tblProduct.Stocked, tblProduct.ProductPK " & _
                        " FROM (tblUnit INNER JOIN tblProduct ON tblUnit.UnitPK = tblProduct.UnitFK) INNER JOIN " & _
                        " tblInvoiceDetail ON tblProduct.ProductPK = tblInvoiceDetail.ProductFK " & _
                        " WHERE [InvoicePK] = " & PrimaryKey & _
                        " ORDER BY tblProduct.ProductPK "
    DS.CursorLocation = adUseClient
    DS.Open SQLStmt, ConnDB, adOpenForwardOnly, adLockReadOnly, adCmdText
   
    sRow = 1
    fgData.Rows = DS.RecordCount + 1
    Do Until DS.EOF
        fgData.TextMatrix(sRow, 0) = DS("ProductPK")
        fgData.TextMatrix(sRow, 1) = "" & DS("ProductCode")
        fgData.TextMatrix(sRow, 2) = "" & DS("Description")
        fgData.TextMatrix(sRow, 3) = "" & DS("UnitName")
        fgData.TextMatrix(sRow, 4) = Format(DS("PriceUnit"), "#,##0.00")
        fgData.TextMatrix(sRow, 5) = Format(DS("Quantity"), "0")
        ' คำนวณราคาขายสินค้า (จำนวน x ราคา)
        fgData.TextMatrix(sRow, 6) = Format(CDbl(fgData.TextMatrix(sRow, 4)) * CDbl(fgData.TextMatrix(sRow, 5)), "#,##0.00")
        '// เพิ่มจำนวนแถวของตารางกริด
        sRow = sRow + 1
        DS.MoveNext
    Loop
   
    DS.Close:   Set DS = Nothing
End Sub

'// ----------------------------------------------------------------------------------------------------
' คำนวณหาผลรวมของราคาสินค้าทั้งหมด
'// ----------------------------------------------------------------------------------------------------

Sub CalTotalAmount()
    ' หากไม่มีสักรายการก็ให้ออกจากโปรแกรมย่อยไปเลย
    If fgData.Rows = 1 Then Exit Sub
   
    Dim i As Integer
    Dim TotalAmount As Double
   
    ' นับไปตามจำนวนแถวที่มีอยู่ปัจจุบันของตารางกริด จนกว่าจะหมด
    ' แล้วรวมค่าในหลักที่ 6 ไว้ในตัวแปรเวียนแทียน TotalAmount ... 5555+

    For i = 1 To fgData.Rows - 1
        TotalAmount = TotalAmount + CDbl(fgData.TextMatrix(i, 6))
    Next
   
End Sub

'// ----------------------------------------------------------------------------------------------------
'// เลือกหมายเลข Invoice แล้วทำการแสดงผลลงตารางกริด

Private Sub cmbInvoicePK_Click()
'// ----------------------------------------------------------------------------------------------------
    '// หา InvoiceCode
    '// ตัดการเชื่อมต่อเดิมทิ้งไป กรณีเปิดค้างไว้แบบไม่ได้ตั้งใจ

    Set DS = New Recordset
    Statement = "SELECT InvoicePK, InvoiceCode " & _
                            " FROM tblInvoice " & _
                            " WHERE InvoicePK = " & Val(cmbInvoicePK.Text) & _
                            " ORDER BY InvoicePK "
    DS.CursorLocation = adUseClient
    DS.Open Statement, ConnDB, adOpenForwardOnly, adLockReadOnly, adCmdText
    txtInvoiceCode.Text = "" & DS("InvoiceCode")
    DS.Close:   Set DS = Nothing

    '// ทำการแสดงผลรายการขายสินค้าทั้งหมด ตามหมายเลข Primary Key ที่เลือก
    Call RecordToGridProduct(Val(cmbInvoicePK.Text))
    ' คำนวณหาจำนวนเงินทั้งหมดในการขายสินค้า
    Call CalTotalAmount
End Sub

'// ----------------------------------------------------------------------------------------------------
Private Sub Form_Load()
    Me.Move (Screen.Width - Width) \ 2, (Screen.Height - Height) \ 2
   
    Call OpenDataBase
    '// โหลดรายการขายสินค้าจากตาราง tblInvoice ลงใน ComboBox
    '// ปกติส่วนนี้คือการส่งค่า Primary Key จากอีกฟอร์มเข้ามาเพื่อทำการแก้ไข
    '// หากไม่มีค่าใดๆที่ส่งมา ก็แสดงว่าเป็นการทำรายการขายสินค้าใหม่

    Call LoadInvoicePK
    ' ตั้งค่าต่างๆให้กับ MS FlexGrid แบบ Run Time
    Call SetupGrid

End Sub
'// ----------------------------------------------------------------------------------------------------

'// ----------------------------------------------------------------------------------------------------
'// นำหมายเลข InvoicePK เข้ามาเก็บไว้ใน ComboBox แล้วเลือกมาแสดงผล

Sub LoadInvoicePK()
'// ----------------------------------------------------------------------------------------------------
    cmbInvoicePK.Clear
    '// ตัดการเชื่อมต่อเดิมทิ้งไป กรณีเปิดค้างไว้แบบไม่ได้ตั้งใจ
    Set DS = New Recordset
    Statement = "SELECT InvoicePK FROM tblInvoice ORDER BY InvoicePK "
    DS.CursorLocation = adUseClient
    DS.Open Statement, ConnDB, adOpenForwardOnly, adLockReadOnly, adCmdText
    Do While Not DS.EOF
        '// เครื่องหมาย "" Double Quote ใส่ไว้เพื่อดัก Run Time Error 97 กรณีที่ไม่มีข้อมูล หรือ Null Value
        cmbInvoicePK.AddItem "" & DS("InvoicePK")
        DS.MoveNext
    Loop

    DS.Close:   Set DS = Nothing
End Sub

'// ----------------------------------------------------------------------------------------------------
' การตั้งค่าคุณสมบัติของ MS FlexGrid ในลักษณะของ Run Time
'// ----------------------------------------------------------------------------------------------------

Sub SetupGrid()
    With fgData
        .FixedRows = 1
        .FixedCols = 0
        ' กำหนด 7 หลัก จาก Index 0 - 6
        .Cols = 7
        ' กำหนด 1 แถว (เฉพาะ Column Header)
        .Rows = 1
        '// หลักแรกคือหลัก 0 ซ่อนหลักเอาไว้โดยกำหนด Width = 0 นั่นเอง
        '// หลักนี้เป็น Primary Key ของสินค้า โปรแกรมเมอร์ต้องใช้ แต่ User ไม่ได้ใช้งาน

        .ColWidth(0) = 0
        .ColWidth(1) = .Width \ 6 - 100
        .ColWidth(2) = .Width \ 6 + 250
        .ColWidth(3) = .Width \ 6 - 200
        .ColWidth(4) = .Width \ 6 - 100
        .ColWidth(5) = .Width \ 6 - 200
        .ColWidth(6) = .Width \ 6
       
        .TextMatrix(0, 0) = "ProductPK"
        .TextMatrix(0, 1) = "รหัสสินค้า"
        .TextMatrix(0, 2) = "ชื่อสินค้า"
        .TextMatrix(0, 3) = "หน่วยนับ"
        .TextMatrix(0, 4) = "ราคาสินค้า"
        .TextMatrix(0, 5) = "จำนวน"
        .TextMatrix(0, 6) = "รวมจำนวนเงิน"
    End With
   
    With fgData
        .RowHeightMin = 330
        .SelectionMode = flexSelectionByRow ' แสดงแถบทั้งแถว
        .AllowUserResizing = flexResizeColumns 'อนุญาตให้ปรับขนาดของแถวหรือหลักได้
        .HighLight = flexHighlightWithFocus
       
        ' ปรับตำแหน่งของการแสดงผล
        .FixedAlignment(1) = flexAlignLeftCenter
        .ColAlignment(1) = flexAlignLeftCenter
        .FixedAlignment(2) = flexAlignLeftCenter
        .ColAlignment(2) = flexAlignLeftCenter
        .FixedAlignment(3) = flexAlignRightCenter
        .ColAlignment(3) = flexAlignRightCenter
        .FixedAlignment(4) = flexAlignRightCenter
        .ColAlignment(4) = flexAlignRightCenter
        .FixedAlignment(5) = flexAlignRightCenter
        .ColAlignment(5) = flexAlignRightCenter
        .FixedAlignment(6) = flexAlignRightCenter
        .ColAlignment(6) = flexAlignRightCenter
   
        ' ปรับค่าแสดงผลด้านสี
        .BackColorFixed = RGB(133, 175, 255)
        .ForeColorFixed = vbBlack
        .BackColorBkg = RGB(245, 245, 245)
        .BackColorSel = RGB(121, 255, 53)
        .ForeColorSel = vbBlack
        .BackColor = RGB(255, 255, 204)
        .ForeColor = vbBlack
        .GridColor = vbBlack
   
    End With

End Sub

Private Sub Form_Unload(Cancel As Integer)
    Call CloseDataBase
    Set frmInvoiceDetail = Nothing
    End
End Sub

ส่วนของ Module ทำมาหากิน modDataBase.Bas

Option Explicit

Global ConnDB As New ADODB.Connection
Global RS As New ADODB.Recordset    '// เชื่อมต่อ RecordSet ตัวหลัก
Global DS As New ADODB.Recordset    '// ใช้ RecordSet ตัวรอง
Global Statement As String
Global SQLStmt As String

'// โปรแกรมย่อยในการติดต่อกับไฟล์ฐานข้อมูล 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 & "POSDB.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


บันทึกการเข้า
สิ่งที่ดีกว่าการให้ คือการให้แบบไม่มีที่สิ้นสุด

ออฟไลน์ naien

  • Newbie
  • *
  • กระทู้: 36
Re: [VB6] การแสดงรายการขายสินค้าลงใน MS FlexGrid Control (One-To-Many Relation)
« ตอบกลับ #1 เมื่อ: มกราคม 13, 2016, 03:49:46 pm »
ขอบคุณครับ
แต่มีข้อสงสัยครับ ดูจากตาราง tblInvoiceDetail ไม่มี Primary key ใช่ป่าวครับ เพราะปกติ access จะทำ Primary key จะมีรูปกุญแจปรากฏ และตารางแบบนี้ เวลาลบข้อมูล ต้องอ้างอิงค่าไหนครับ หรือต้องอ้างทั้ง InvoicePK และ ProducPK ครับ

บันทึกการเข้า

ออฟไลน์ ทองก้อน ทับทิมกรอบ

  • Administrator
  • *****
  • กระทู้: 245
  • เพศ: ชาย
  • Webmaster G2GNet
Re: [VB6] การแสดงรายการขายสินค้าลงใน MS FlexGrid Control (One-To-Many Relation)
« ตอบกลับ #2 เมื่อ: มกราคม 14, 2016, 12:48:39 pm »
ขอบคุณครับ
แต่มีข้อสงสัยครับ ดูจากตาราง tblInvoiceDetail ไม่มี Primary key ใช่ป่าวครับ เพราะปกติ access จะทำ Primary key จะมีรูปกุญแจปรากฏ และตารางแบบนี้ เวลาลบข้อมูล ต้องอ้างอิงค่าไหนครับ หรือต้องอ้างทั้ง InvoicePK และ ProducPK ครับ

คือเราจะต้องโฟกัสไปทีละจุดครับ หากมีรายการลูกค้า เราก็จะดูเฉพาะตาราง tblCustomer + tblInvoice ในแบบ 1 : 1 หมายความว่าลูกค้า 1 ราย มีใบขายสินค้าให้ลูกค้ารายนี้ 1 ใบ ในรูปไม่มีตารางลูกค้าให้ดู แต่ใน tblInvoice จะมีคีย์ที่เชื่อมไปหาตารางลูกค้า CustomerFK ดังนั้นเราจะยึดตารางการขาย tblInvoice เป็นหลัก นั่นคือ InvoicePK

จากตัวอย่างนี้
ต่อมาเราก็มาดูที่ InvoicePK ตัวเดิม (ที่ยึดกับรายชื่อลูกค้าไว้) โยงไปหาตารางการขายสินค้าย่อย (tblInvoiceDetail) ซึ่งในตารางย่อยจะเป็นรายการขายสินค้า 1 ใบต่อ 1 ชนิด (เช่น สินค้า A, สินค้า ก) เราจึงไม่ใช้ Primary Key แบบเฉพาะเจาะจง แต่ใช้ Primary Key จากตารางหลักคือ InvoicePK ของตาราง tblInvoice นั่นเอง

เวลาลบก็หมูมาก เราก็ใช้ InvoicePK ตัวนี้แหละครับ เพื่อใช้ในเงื่อนไข WHERE ในการวนลูป ... ลบเพื่ออะไร??? ตอนทำการแก้ไขยังไงล่ะครับ ก่อนทำการบันทึก เราก็ลบข้อมูลเดิมจากตารางจริงออกไปก่อน แล้วก็วนรอบกลับมา Add หรือ Insert รายการสินค้าที่เกิดการแก้ไขใหม่เข้าไป

การออกแบบในลักษณะนี้ เราสามารถทำเป็นระบบ Client/Server ไปแบบอัตโนมัติ ไม่ต้องล้งต้องล็อค Record อะไรเลยครับผม
-

บันทึกการเข้า
สิ่งที่ดีกว่าการให้ คือการให้แบบไม่มีที่สิ้นสุด

ออฟไลน์ naien

  • Newbie
  • *
  • กระทู้: 36
Re: [VB6] การแสดงรายการขายสินค้าลงใน MS FlexGrid Control (One-To-Many Relation)
« ตอบกลับ #3 เมื่อ: มกราคม 14, 2016, 02:56:18 pm »
ขอบคุณมากครับ ไว้จะลองทำดูบ้าง

บันทึกการเข้า