วันอังคารที่ ๒๒ กันยายน พ.ศ. ๒๕๕๒

ASP.NET AJAX 4.0 Preview 5

ตอนนี้ที่ Codeplex ได้มีการ release ASP.NET AJAX 4.0 Preview 5 แล้วครับ (อัพเดทล่าสุดวันที่ 14 กันยายน แต่ผมเพิ่งเห็น) ที่จริงว่าจะลองโหลดมาเล่นตั้งแต่ Preview ก่อนหน้านี้แล้ว เพราะว่า banpote_tt MVP หนุ่มหล่อจาก GreatFriends ได้ MSN มาคุย (โม้ติเวท) บรรยายถึงสรรพคุณถึง Microsoft Ajax Template ให้ฟังจนเคลิ้ม แต่ติดว่าช่วงนี้ยุ่งมากเลยไม่มีเวลาโหลดมาลองเล่นครับ คิดว่าอีก 2 อาทิตย์น่าจะว่างแล้ว คงได้ลองดาวน์โหลดมาเล่นดูบ้าง

วันก่อนเข้าไปอ่าน ScottGu's Blog
พบ entry หนึ่งน่าสนใจ (จริงๆก็น่าสนใจทุกอันแหละครับ) นั่นคือ Announcing the Microsoft AJAX CDN ถือว่าเป็นไอเดียที่ดีมากครับ

แต่สงสัยอย่างหนึ่ง ถ้า MS ทำการ update libraries ใน CDN ใหม่ จะมั่นใจได้ไงว่าจะไม่กระทบกับ App เดิมที่เราใช้งานอยู่เนี่ย เช่นอาจเกิด link ตาย หรือว่า library ตัวใหม่ไม่ compat กับของเก่า เราจะเจอ library hell หรือเปล่านะ

วันอังคารที่ ๑๕ กันยายน พ.ศ. ๒๕๕๒

VB.NET ICloneable

วันนี้ (15 ก.ย. 2552) ได้เข้าไปอ่านกระทู้ที่ GreatFriends เจอคำถามหนึ่งน่าสนใจครับ

การให้ค่ากับ object
http://greatfriends.biz/?113627

คำถามมีดังนี้ครับ
ผมสร้าง class Node กับ Variable

ซึ่ง class Node ก็ประกอบไปด้วยตัวแปรต่าง
และก็มี Variable
ส่วน class Variable ก็ประกอบไปด้วยตัวแปรต่าง

เมื่อผมต้องการกำหนดค่าให้กับ Node ก็สร้าง Node node = new Node;
Variable variable = new Variable;
variable. = .....;ก็กำหนดค่าให้

แล้วก็ node.Variable = variable;

ที่นี้พอผมเปลี่ยนค่า ใน
variable variable. = .....;

ที่นี้node.Variable ก็เลยเปลียนตามไปด้วย
แต่ผมไม่ต้องการให้เปลี่ยน
ผมเลยอยากทราบว่าต้องเซตค่าnode.Variable =
variableยังไงถึงค่าในnode.Variable กับ variable
แยกออกจากกันไม่สัมพันธ์กันอ่ะครับ


ลักษณะนี้ก็คือ เราต้องการให้ Variable Object ที่ถูกกำหนดค่าให้ Node Object เป็นคนละตัวกับ Variable Object ที่เราสร้างไว้แต่แรก ผมเลยเสนอให้ใช้ ICloneable Interface ครับ ก็เลยคิดว่าน่าจะลองเขียนทดสอบเล่นๆดู มาลองดูโค้ดกันครับ

ส่วนของ Variable Class ที่เราทำการ Implement ICloneable ครับ

Public Class Variable
Implements ICloneable

Private _variableName As String
Private _value As String

Public Property Value() As String
Get
Return _value
End Get
Set(ByVal value As String)
_value = value
End Set
End Property

Public Property VariableName() As String
Get
Return _variableName
End Get
Set(ByVal value As String)
_variableName = value
End Set
End Property

Public Function Clone() As Object Implements System.ICloneable.Clone
Return TryCast(MemberwiseClone(), Variable)
End Function

End Class


ส่วนของ Node Class

Public Class Node

Private _nodeId As Integer
Private _nodeName As String
Private _variable1 As Variable
Private _variable2 As Variable

Public Property Variable2() As Variable
Get
Return _variable2
End Get
Set(ByVal value As Variable)
_variable2 = value
End Set
End Property

Public Property Variable1() As Variable
Get
Return _variable1
End Get
Set(ByVal value As Variable)
_variable1 = value
End Set
End Property

Public Property NodeName() As String
Get
Return _nodeName
End Get
Set(ByVal value As String)
_nodeName = value
End Set
End Property

Public Property NodeId() As Integer
Get
Return _nodeId
End Get
Set(ByVal value As Integer)
_nodeId = value
End Set
End Property

End Class


และทีนี้ก็มาทดสอบกัน

Dim myNode As New Node
Dim myVar1 As New Variable
Dim myVar2 As New Variable

myNode.NodeId = 1
myNode.NodeName = "Test"
myVar1.VariableName = "EmployeeName"
myVar1.Value = "Nithi"

myVar2.Value = "EmployeeSurname"
myVar2.Value = "Juabsamai"

myNode.Variable1 = myVar1.Clone
myNode.Variable2 = myVar2

Debug.Print("{0} - {1} {2}", myNode.NodeName, myNode.Variable1.Value, myNode.Variable2.Value)

myVar1.Value = "Wanwimon"
myVar2.Value = "Permpanit"

Debug.Print("{0} - {1} {2}", myNode.NodeName, myNode.Variable1.Value, myNode.Variable2.Value)

myNode.Variable1.Value = "Nithi"
myNode.Variable2.Value = "Juabsamai"
Debug.Print("{0} - {1} {2}", myNode.NodeName, myNode.Variable1.Value, myNode.Variable2.Value)
Debug.Print("Variable2 = " & myVar2.Value)


ที่น่าสนใจคือ .NET ได้เตรียม MemberwiseClone Function มาให้เราเรียบร้อยแล้ว ไม่ต้องเขียนเอง สะดวกดีครับ หลังจากทดสอบดูแล้ว คิดว่าน่าจะตรงกับที่เจ้าของกระทู้ที่ GreatFriends ต้องการนะครับ

วันอาทิตย์ที่ ๑๓ กันยายน พ.ศ. ๒๕๕๒

VB.NET กับ ENUM

วันก่อนเขียนโปรแกรมเพื่อไป Execute StoreProcedure ตัวหนึ่ง ซึ่งใน SP ตัวนี้จะมีการ Return Value กลับมาให้ด้วยเพื่อบอกสถานะของการทำงาน ดังนั้นในส่วนของ VB.NET ผมก็เลยสร้าง Enum ขึ้นมาเพื่อใช้ระบุสถานะดังกล่าว


Public Class abcManager

Public Enum xxxResult
Success = 0
BillNotFound = 100
NoOutstandingAmount = 200
SQLException = 300
UnknownException = 900
End Enum

Public Shared Function xyz() as xxxResult
Public Shared Function .......

End Class



ทีนี้ตอนฝั่ง UI (Presentation Layer) เรียก xyz function มาใช้งานก็ได้ xxxResult กลับมา ทีนี้ตอนจะแสดงผลลัพธ์ให้ user เห็น ถ้าเป็นตัวเลขก็คงไม่ได้ ก็ต้องแสดงเป็นข้อความ ความคิดแรกคือ ใช้ Select Case ในการตรวจสอบ xxxResult เพื่อแสดงข้อความ แต่คิดอีกทีมันไม่ยืดหยุ่นแฮะ ดังนั้นคิดอีกที เอาชื่อของ Enum มาโชว์เลยก็น่าจะได้ ทีแรกผมเขียนแบบนี้ครับ


Dim result As xyzBLL.abcManager.xxxResult
result = xyzBLL.abcManager.xyz()
MsgBox("Error - " & [Enum].GetName(result.GetType, result), MsgBoxStyle.Exclamation + MsgBoxStyle.OkOnly, My.Application.Info.ProductName)


ซักพักนึกขึ้นมาได้เลยว่าน่าจะทำ Overrides Function ToString ของ Enum แต่อยากรู้ว่า default มันได้ค่าอะไร เลยลองทดสอบ result.ToString เอ่อ ปรากฎได้ผลเหมือนกัน เพิ่งรู้นะเนียว่า ToString ของ Enum มันคืนค่าเป็น Name อุตสาห์ไปใช้ GetName Function เฮ้อ เข้าใจคำว่า ฉลาดเรื่องโง่ๆ ละ

-*-"

เคยได้ยินมาว่าคนเก่งมักเรียนรู้จากประสบการณ์ตนเอง ส่วนคนฉลาด มักเรียนรู้จากประสบการณ์คนอื่น
เลยเอาเรื่องนี้มาเขียน blog จะได้มีคนฉลาดเยอะๆ ^_^

วันพฤหัสบดีที่ ๑๐ กันยายน พ.ศ. ๒๕๕๒

VB.NET จัด format ตัวอักษรใน PrintDocument

ช่วง 1-2 ปีมานี้ จะมีน้องๆนักศึกษาเข้ามาถามเรื่อง PrintDocument ใน VB.NET หลายคนทีเดียวครับ สังเกตุอย่างหนึ่งคือการเขียนใช้สไตล์เดียวกัน ดังนั้นเลยเดาว่าคงใช้หนังสือภาษาไทยเล่มเดียวกันในการศึกษา คำถามหนึ่งที่พบบ่อยคืออยากจัดตัวอักษรชิดขวาหรือกึ่งกลางทำอย่างไรดี

ในโค้ดที่แนบมาให้ดู จะมี AnyString Sub ในการวาดตัวอักษรลงบน PrintDocument ครับ ซึ่ง Sub นี้มันจะวาดตัวอักษรชิดซ้ายตลอด ดังนั้นผมเลยเสนอให้สร้าง Sub ที่สามารถกำหนด StringFormat ได้ว่าจะให้ชิดซ้าย ชิดขวา หรือกึ่งกลาง


Public Sub FormatString(ByVal g As Graphics, ByVal printString As String, ByVal xPos As Integer, ByVal yPos As Integer, ByVal align As StringAlignment)

Dim f As New StringFormat
f.Alignment = align
g.DrawString(printString, useFont, Brushes.Black, xPos, yPos, f)

End Sub


สังเกตุว่าจะมีการใช้ตัวแปรชื่อ useFont ด้วยครับ ซึ่ง useFont ก็ต้องถูกประกาศในระดับ Page และกำหนด Font ที่ต้องการใช้วาด PrintDocument ที่ต้องการ

เวลาเรียกใช้ให้จัดชิดขวาก็

FormatString(e.Graphics, "test123", 100, currentYPosition, StringAlignment.Far)

ถ้าต้องการให้อยู่ตรงกลางก็

FormatString(e.Graphics, "test123", 100, currentYPosition, StringAlignment.Center)

วันอังคารที่ ๘ กันยายน พ.ศ. ๒๕๕๒

VB.NET เคลียร์ Excel ไม่ให้เหลือซาก

ปกติเวลาผมเขียนโปรแกรมเพื่อออกรายงานมักใช้ Crystal Report เป็นหลัก เพราะว่าสามารถให้ user preview ได้ รวมถึง export เป็น PDF, Excel, word ได้สะดวกดีครับ โดยเฉพาะเมื่อ Export เป็น Excel ผมพบว่ามันทำงานได้เร็วกว่าการสั่งเปิด Excel เพื่อใส่ข้อมูลตรงๆมากครับ แต่บางครั้งก็จำเป็นจริงๆที่ต้องใช้ Excel ในการออกรายงาน

เวลาออกรายงานบน Excel ผมมักจะเขียนโค้ดประมาณนี้ครับ (ใช้ BackgroundWorker ด้วย)

Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim wk As BackgroundWorker = DirectCast(sender, BackgroundWorker)
Try
isSuccess = False
CreateExcel(wk, e)
CreateReportByLocation(wk, e)
CreateReportByTitle(wk, e)
CreateReportList(wk, e)
isSuccess = True
Catch ex As Exception
isSuccess = False
Finally
FinishCreatingReport(wk, e)
End Try
End Sub


คำสั่ง CreateExcel จะเป็นการ New Excel.Application รวมถึงสร้าง WorkBook สำหรับเริ่มต้นการออกรายงาน
ส่วน CreatReportXXX จะเป็นการสร้างรายงานแต่ละ WorkSheet ตามต้องการครับ สุดท้ายก็ FinishCreatingReport จะเป็นการคืน memory ให้กับระบบ เราจะมาลองดู function นี้กันครับ


Private Sub FinishCreatingReport(ByVal wk As BackgroundWorker, ByRef e As DoWorkEventArgs)
Try
xlBook.SaveAs(fileName)
Catch ex As Exception
MessageBox.Show(ex.Message & vbCrLf & "Please contact ITD.", My.Application.Info.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
Finally
'// Release memory
wk.ReportProgress(95)
xlApp.Quit()
GC.SuppressFinalize(xlSheet)
GC.SuppressFinalize(xlBook)
GC.SuppressFinalize(xlApp)
GC.Collect()
xlSheet = Nothing
xlBook = Nothing
xlApp = Nothing
End Try
End Sub


พอรันโปรแกรมและเปิด Task Manager ดูพบว่า
1. พอ BackgroundWorker1_RunWorkerCompleted ทำงานเสร็จ (แน่นอนว่า เรียก FinishCreatingReport ซึ่งอยู่ใน BackgroundWorker1_DoWork แล้ว) ใน Task Manager ยังมี Excel.exe ค้างอยู่ครับ
2. ถ้ากดปุ่มเพื่อสั่งออกรายงานใหม่อีกครั้ง Excel.exe จะหายไปจาก Task Manager พอโปรแกรมสั่ง CreateExcel ถึงจะสร้างขึ้นมาใหม่
3. เมื่อ BackgroundWorker ทำงานเสร็จ ออกรายงานเรียบร้อย Excel.exe ยังคงอยู่ ถ้าผมปิด form นี้ Excel.exe จะหายไปจาก Task Manager แต่ถ้าผม stop debugging ใน Visual Studio หรือโปรแกรมที่เขียนมี bug และ shutdown ไป Excel.exe จะยังคงค้างอยู่ใน Task Manager

ทีนี้ผมลองเพิ่มคำสั่ง ReleaseComObject ครับ


Private Sub FinishCreatingReport(ByVal wk As BackgroundWorker, ByRef e As DoWorkEventArgs)
Try
xlBook.SaveAs(fileName)
Catch ex As Exception
MessageBox.Show(ex.Message & vbCrLf & "Please contact ITD.", My.Application.Info.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
Finally
'// Release memory
wk.ReportProgress(95)
xlApp.Quit()
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheet)
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBook)
System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp)
GC.SuppressFinalize(xlSheet)
GC.SuppressFinalize(xlBook)
GC.SuppressFinalize(xlApp)
GC.Collect()
xlSheet = Nothing
xlBook = Nothing
xlApp = Nothing
End Try
End Sub


ลองรันโปรแกรมดู พบว่าเมื่อ BackgroundWorker ทำงานเสร็จ Excel.exe จะถูกลบทิ้งไปทันที ไม่เหลือใน Task Manager แล้ว (พอไล่ดูอย่างละเอียด พบว่า Excel.exe จะหายไป ก่อน BackgroundWorker1_RunWorkerCompleted จะทำงานเสร็จครับ)

ดังนั้นนอกจากจะใช้งาน Excel แล้ว ถ้าเรามีการทำ COM Marshal (InterOp) กรุณาเรียกใช้ System.Runtime.InteropServices.Marshal.ReleaseComObject เพื่อจะได้มั่นใจว่า Unmanaged Memory ได้ถูก Release จริงๆครับ

วันศุกร์ที่ ๔ กันยายน พ.ศ. ๒๕๕๒

BlueprintCSS

วันนี้ได้อ่าน forum ต่างประเทศ เจอคำถามโลกแตกที่ว่าทำไมตอน design เวบ แล้วทดสอบใน IE7 ก็ดูดีแต่พอดูด้วย browser อื่นมันเละไปหมด ซึ่งปัญหานี้คาดว่าคงเจอกันหลายคน แต่โชคดีครับที่ผมทำ Web Application สำหรับใช้ภายในบริษัท ดังนั้นจึงสามารถบอกให้พนักงานใช้เฉพาะ IE เท่านั้นได้ เลยไม่ค่อยเจอปัญหานี้ มีบ้างที่ IE6 กับ IE7 ไม่เหมือนกัน เป็นเพราะว่า IE7 นั้นทาง Microsoft เริ่มกลับมาใช้มาตรฐานสากลแล้ว ซึ่ง IE ก่อนหน้านี้ใช้มาตรฐานของ Microsoft (คือเมื่อก่อนใช้กติกูเป็นหลัก)

หลายๆคนแก้ปัญหานี้โดยการใช้เทคนิคที่เรียกว่า CSS Hacked (สำหรับภาษาไทยศึกษาเพิ่มเติมได้ที่ www.divland.com ครับ) หรืออาจจะเขียน javascript สำหรับโหลด CSS โดยตรวจสอบ browser ก่อนว่าเป็นเวอร์ชันไหน แล้วก็ไปอ่านข้อมูล CSS ที่เขียนสำหรับเวอร์ชันนั้นๆมาใส่ใน webpage ซึ่งแน่นอนว่ามันต้องถึกพอดู และก็ต้องทดสอบหลายๆครั้ง เพื่อให้มั่นใจว่าการแสดงผลถูกต้องตามต้องการ

พอดีเจอคำตอบหนึ่งแนะนำให้ลอง BlueprintCSS ซึ๋งเป็น CSS Framework ตัวหนึ่งที่นอกจากจะช่วยแก้ปัญหานี้ยังมีความสามารถอื่นๆอีกเพียบ ผมก็ลองดาวน์โหลดมาทดสอบตามระเบียบครับ ไว้มีโอกาสจะมาลองเขียน blog เพิ่ม สำหรับผู้สนใจลองไปศึกษาหรือดาวน์โหลดได้ที่ http://www.blueprintcss.org/
มีทั้ง Demo, Wiki, Discussion และอื่นๆครบถ้วน น่าสนใจจริงๆครับ