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

|
|
|
Visitors - Session views |       
7 ธันวาคม พ.ศ.2549 37 Users On-Line. |
|
Visitors - Page views |        1 กุมภาพันธ์ พ.ศ.2551 |
|
|
|
 |
|
พื้นฐานในการใช้งาน MS FlexGrid ก่อนสร้างความสัมพันธ์แบบ One To Many (ภาค 2) |
Category »
VB 6/VB.Net โดย : Webmaster เมื่อ 4/3/2553 เวลา: 12:54 | (อ่าน : 16731) |
บทความสำหรับเรื่องนี้ ซึ่งจะมีตามมาอีกในหลายๆตอน ... ผมนำมาเสนอในแง่มุม 2 ด้าน คือ ด้านแรกสำหรับคนที่กำลังพัฒนา ฝึกฝน ในสายงานโปรแกรมมิ่งอยู่ เพื่อให้เข้าใจกระบวนการคิด และ การออกแบบ และ อีกด้าน คือ การนำเสนอแนวทาง หรือ วิธีการถ่ายทอดความรู้ที่มีอยู่ออกไป ในรูปแบบที่เป็นเอกลักษณ์เฉพาะของตัวเอง เพื่ออยากจะบ่งบอกให้รู้กันทั่วไปว่า "การเรียนรู้งานพวกนี้ มันไม่ได้ยากเกินไปตามที่หลายๆคนคิด" ... คงจะมีคนถามว่า ทำไมผมไม่สอนให้ออกแบบ หรือ ทำใบขาย กับ ลูกค้า มาก่อน ... ตอบได้เลยครับว่า กรณีนี้มันยังไม่ใช่ประเด็นหลักๆที่สำคัญ เมื่อเทียบกับ การทำใบขาย กับ รายการสินค้าก่อนหรอกครับ เพราะขั้นตอนนี้ เรายังไม่จำเป็นต้องไปออกแบบตารางข้อมูลด้วยซ้ำไป แต่โปรแกรมจะสามารถค้นหารหัสสินค้าจากตารางข้อมูลออกมาได้ พร้อมกับ การที่ผู้ใช้สามารถเข้าไปแก้ไขข้อมูลในตารางกริดโดยตรงได้ทันที ... การทำแบบนี้ เราเรียกว่า Friendly Use หรือ เป็นมิตรกับผู้ใช้งาน ... ซึ่งจะแตกต่างไปจากหลายๆโปรแกรมที่เขาทำขายกัน หรือ ตามหนังสือ ... ไม่เชื่อต้องดาวน์โหลด Source Code มาทดสอบดูน่ะครับผม |
เริ่มต้นกระบวนการทำงาน
Projects --> References ...
Projects --> Component ...
การออกแบบ Design Time
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
' เชื่อมเข้าสู่ไฟล์ฐานข้อมูล 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
|
Query - การเชื่อมความสัมพันธ์ของตารางสินค้า (tblProduct) กับ ตารางหน่วยนับ (tblUnit) ... งานจริงๆเลยน่ะครับ
จากนั้นไปเลือกมุมมอง SQL แล้วตัด Query Statement มาแปะลงใน Visual Basic 6 ได้เลย ... หมูอู๊ดๆเลย SELECT tblProduct.ProductPK, tblProduct.ProductCode, tblProduct.Description, tblUnit.UnitName, tblProduct.PriceUnit FROM tblProduct INNER JOIN tblUnit ON tblProduct.UnitFK = tblUnit.UnitPK
- อย่างที่บอกกันมาตลอดครับ หลักแรก (0) ต้องนำเอาค่า Primary ไปซ่อนไม่ให้ User เห็น - Primary Key ต้องไม่ใช่รหัสสินค้า (ProductCode) ด้วยครับ
โค้ดในการค้นหารหัสสินค้าจากตารางข้อมูล tblProduct ... แสดงเฉพาะส่วนสำคัญน่ะครับ
' ======================================================
' การตั้งค่าคุณสมบัติของ MS FlexGrid ในลักษณะของ Run Time
' ======================================================
Sub SetupGrid()
With fgData
.FixedRows = 1
.FixedCols = 0
' กำหนด 7 หลัก
.Cols = 7
' กำหนด 1 แถว (เฉพาะ Column Header)
.Rows = 1
.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) = "PK"
.TextMatrix(0, 1) = "รหัสสินค้า"
.TextMatrix(0, 2) = "ชื่อสินค้า"
.TextMatrix(0, 3) = "หน่วยนับ"
.TextMatrix(0, 4) = "ราคาสินค้า"
.TextMatrix(0, 5) = "จำนวน" ' ป้อนข้อมูลได้ในหลักนี้
.TextMatrix(0, 6) = "รวมจำนวนเงิน"
End With
End Sub
' ======================================================
' การเกิดเหตุการณ์ค้นหาข้อมูลรหัสสินค้า โดยการกดปุ่ม Enter
' ======================================================
Private Sub txtSearch_KeyPress(KeyAscii As Integer)
If KeyAscii = vbKeyReturn Then
Call CheckDataRow
End If
End Sub
' ======================================================
' การตรวจสอบค่าใน MS FlexGrid ก่อนที่จะแสดงผลรายการใหม่
' ======================================================
Private Sub CheckDataRow()
Dim CurrentRow As Integer
' ตรวจสอบว่าเป็นค่าว่างหรือไม่ หากใช่ก็ออกจากโปรแกรมย่อย (User ไม่มีการคีย์ข้อมูลใดๆ)
If Trim(txtSearch.Text) = "" Or Len(Trim(txtSearch.Text)) = 0 Then
txtSearch.SetFocus
Exit Sub
End If
' ค้นหาข้อมูลรหัสสินค้าในตารางสินค้า (tblProduct)
' บางคนอาจจะไม่เข้าใจในส่วนนี้ ขอให้กลับไปอ่านบทความเรื่อง VB6 กับ Access ' ตั้งแต่แรกก่อนน่ะครับ เพราะนี่ไม่ได้มาจากหนังสือ แต่มาจากประสบการณ์ล้วนๆของผม
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 = ""
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
' =============================================
' คำนวณหาผลรวมของราคาสินค้าทั้งหมด
' =============================================
Sub CalTotalAmount()
' หากไม่มีสักรายการก็ให้ออกจากโปรแกรมย่อยไปเลย
If fgData.Rows = 1 Then Exit Sub
Dim i As Integer
Dim TotalAmount As Double
' นับไปตามจำนวนแถวที่มีอยู่ปัจจุบันของตารางกริด จนกว่าจะหมด
' แล้วรวมค่าในหลักที่ 6 ไว้ในตัวแปรเวียนแทียน TotalAmount ... 55555+
For i = 1 To fgData.Rows - 1
TotalAmount = TotalAmount + Val(fgData.TextMatrix(i, 6))
Next
lblTotalAmount.Caption = Format(TotalAmount, "#,##0.00")
End Sub
|
ลองย้อนกลับไปที่ภาค 1 ดูเลยครับ ... มันยังคงหลักการ วิธีคิดเช่นเดิม แต่สิ่งหลักๆที่เพิ่มเข้ามาแทน (ตามที่เคยบอกไว้ล่วงหน้าแล้ว) ก็คือ การสร้าง RecordSet เพื่อทำการค้นหารหัสสินค้าจริงๆ ที่อยู่ในตารางข้อมูล เพื่อนำมาเปรียบเทียบค่า (เครื่องหมาย = น่ะครับ) เพราะในกรณีนี้เราจะใช้ LIKE ไม่ได้ (รอบหน้าก็จะมีบอกอีกแหละครับ ... พี่น้อง) ... ไหนๆก็ไหนแล้ว แบบนี้มันยังไม่เท่ กิ๊บเก๋พอ เราต้องเสกให้ตารางกริดมันสามารถ ป้อนข้อมูลลงไปในหลัก และ แถวที่เราต้องการได้ด้วยจะดีกว่า ... ก็เอามาจากบทความที่ผมเคยเขียนไปแล้วนั่นแหละครับ ... อิอิอิอิอิ
โค้ดในการเสกเด็กเข้าท้อง เอ้ย ทำให้ตารางกริดธรรมดาๆ สามารถป้อนข้อมูลลงไปได้ อ่านแนวทางของการออกแบบ และ จินตนาการให้ตารางกริดสามารถป้อนข้อมูลได้ ... ที่นี่
' =============================================
' พิจารณาจากเหตุการณ์ (Event) ในการ Focus ตรงตำแหน่งใน Cell
' =============================================
Sub fgData_EnterCell()
Select Case fgData.Col ' เลือก Column ที่ต้องการ
Case 0, 1, 2, 3, 4, 6: ' เราไม่ได้ป้อนข้อมูลในหลักที่ระบุ
txtData.Visible = False ' จึงต้องสั่งให้ปิดการมองเห็น txtData
Case 5: ' โอเค ถูกต้อง Cell ที่กำลัง Focus มาอยู่หลักที่ 5 ก็เริ่มต้นกระบวนการทำงานได้
txtData.Visible = True ' เปิดให้ txtData มองเห็นได้
txtData.Text = fgData.Text ' ค่าเดิมที่อยู่ใน Cell จะถูกส่งต่อไปให้ txtData เพื่อทำการแก้ไขต่อไป
' ขั้นตอนนี้คือการเลื่อนตำแหน่งของ txtData ให้ไปทับอยู่บนตำแหน่งของ Cell (หลักที่กำหนด) ตามที่เราต้องการ
' การเคลื่อนที่ โดย fgData.Move ตำแหน่งทางซ้าย, ตำแหน่งบน, ความกว้างของเซลล์, ความสูงของเซลล์
txtData.Move fgData.CellLeft + 60, fgData.Top + fgData.CellTop, fgData.CellWidth, fgData.CellHeight
' เมื่อ txtData เคลื่อนที่ไปทับตำแหน่ง Cell ที่เราต้องการแล้ว ให้ Focus ไปที่ txtData เพื่อที่จะสามารถแก้ไขข้อมูลลงใน TextBox ได้
txtData.SetFocus
End Select
End Sub
' เมื่อค่าใน TextBox เปลี่ยน ค่าใน Cell ที่ TextBox ทับเอาไว้ ก็ต้องเปลี่ยนตาม
Private Sub txtData_Change()
Dim sRow As Integer
' หาค่าตำแหน่งของแถว
sRow = fgData.Row
' เมื่อ txtData เกิดการเปลี่ยนแปลง ก็จะส่งค่าให้กับ Cell ที่เราต้องการด้วย
' แต่ ณ ขณะนี้เรายังมองไม่เห็นหรอกครับ เพราะ txtData มันทับ Cell ไว้อยู่ -- สำมะคัญน่ะตัวนี้
' ค่าใน TextBox จะต้องไม่เป็นค่าว่าง หรือ 0 ... หากเป็นใส่ 1 ให้มันซ่ะ
If Trim(txtData.Text) = "" Or Len(Trim(txtData.Text)) = 0 Or txtData.Text = "0" Then txtData.Text = "1"
fgData.TextMatrix(sRow, 5) = txtData.Text
' คำนวณราคา และ จำนวนสินค้า
fgData.TextMatrix(sRow, 6) = Format(fgData.TextMatrix(sRow, 4) * fgData.TextMatrix(sRow, 5), "0.00")
' ไปโปรแกรมย่อยเพื่อคำนวณจำนวนเงินทั้งหมด
Call CalTotalAmount
End Sub
Private Sub txtData_GotFocus()
' โปรแกรมย่อยในการทำ High Light ใน TextBox
Call HLText(txtData)
End Sub
' การดักคีย์ที่กดลงบน TextBox
Private Sub txtData_KeyDown(KeyCode As Integer, Shift As Integer)
Select Case KeyCode
Case vbKeyDown
SendKeys "{DOWN}"
Call txtData_KeyPress(vbKeyReturn)
Case vbKeyUp
SendKeys "{UP}"
Call txtData_KeyPress(vbKeyReturn)
Case vbKeyEscape
Call txtData_KeyPress(vbKeyReturn)
End Select
End Sub
Private Sub txtData_KeyPress(KeyAscii As Integer)
If KeyAscii = vbKeyReturn Then
txtData.Visible = False
KeyAscii = 0
Else
' ตรวจสอบการกดคีย์ตัวเลขได้เท่านั้น
KeyAscii = CheckDigitOnly(KeyAscii)
End If
End Sub
' ==========================================================
' พวกฟังค์ชั่นเหล่านี้เราต้องใช้งานบ่อยๆ ควรนำไปเก็บไว้ใน Module จะดีกว่าน่ะครับ
' ==========================================================
' ตรวจสอบการป้อนค่าได้เฉพาะตัวเลข
Function CheckDigitOnly(Index As Integer) As Integer
Select Case Index
Case 48 To 57 ' 0 - 9
Case 8 ' Back Space
Case 13 ' Enter
Case Else
Index = 0
End Select
CheckDigitOnly = Index
End Function
' โปรแกรมย่อยในการทำ High Light ใน TextBox ... ใช้ร่วมกับ GotFocus
Public Sub HLText(ByRef sText)
On Error Resume Next
With sText
.SelStart = 0
.SelLength = Len(sText.Text)
End With
End Sub
|
Conclusion: หากพี่น้องท่านใด เคยพบเห็นวิธีการสอนแบบนี้ ขอให้รับรู้ไว้เลยว่า คนผู้นั้นต้องฟันฝ่าปัญหา อุปสรรค สำหรับการพัฒนาฝึกฝนตนเอง ในงานเขียนโปรแกรมต่างๆ มาอย่างทรหด อดทนเอามากๆ ... คิกๆๆๆๆ ... เอ้า ไม่ได้ล้อเล่นน่ะครับ เพราะสิ่งเหล่านี้มันไม่เคยปรากฏอยู่ตามหนังสือ ตามตำราเลยสักเล่ม หรือเว็บไซต์ใดๆ ... แต่ผมก็หวังไว้บ้างว่า คงจะแรงบันดาลใจ ให้กับพี่น้องหลายๆท่าน (ที่คิดว่าการเขียนโปรแกรมมันเป็นเรื่องยากเย็นเอาซ่ะเหลือเกิน) จงมีความพยายาม ฟันฝ่าอุปสรรค ขวากหนามทั้งหลาย เหมือนที่ผมเคยสัมผัสมาแล้ว
|
|