วันอังคารที่ ๒๗ ตุลาคม พ.ศ. ๒๕๕๒

แนะนำ jnithi.bloggang.com

วันนี้กลับไปอ่าน blog เดิม (โปรแกรมมั่ว พ่อลูกอ่อน) ที่เขียนไว้ที่ jnithi.bloggang.com ครับ ก็เลยนึกขึ้นได้ว่ายังไม่ได้ทำ link ที่ bloggang มาที่ blogger นี่เลย จริงๆแล้วตอนที่เริ่มกลับมาเขียน blog ที่ blogger นี่ก็ไปอัพเดทที่ bloggang แต่ว่าพอกด save มันดันมี error ขึ้นมา เลยเซ็งอารมณ์ปล่อยทิ้งไว้อย่างนั้น จากนั้นก็เลยลืมไปเลยครับ 5555

ทีแรกตั้งใจว่าจะเขียนเรื่องเกี่ยวกับการพัฒนาโปรแกรมที่ blogger ส่วนที่ bloggang จะเขียนเรื่องลูกสาวอย่างเดียว ปรากฏว่าเพื่อนๆบอกว่าอยากให้ไปเขียนเรื่องน้องแพร์ที่ hi5 กับ facebook แทน แต่ด้วยความขี้เกียจก็เลยไม่ได้อัพเดททั้ง bloggang, hi5 และ facebook (จริงๆแล้วผมเล่น hi5 กับ facebook ไม่เป็นอะ เข้าไปแล้วมันงงๆๆๆ)

พอกลับไปอ่าน โปรแกรมมั่ว พ่อลูกอ่อน blog ก็เลยนึกขึ้นได้ว่าอาจจะมีประโยชน์สำหรับชาวโปรแกรมมั่วอีกหลายท่านครับ ก็เลยมาเขียน blog แนะนำที่นี่อีกทีครับ

รู้สึกว่าที่ bloggang คนจะเข้ามา comment เยอะกว่านะ ทั้งๆที่ดูจาก PageView แล้วยังน้อยกว่าคนที่เข้า blog นี้ซะอีก สงสัยว่าคนเข้า blog นี้จะรีบเข้ามาหาวิธีเขียนโปรแกรมเพราะส่วนมากมาจาก Search Engine เลยมาเร็ว อ่านเร็ว ไปเร็ว เพราะต้องรีบกลับไปปั่นงาน 5555 (เป็นเหมือนกัน) แต่ตอนนี้คนที่เข้าซ้ำประจำก็เริ่มมีหลายคนนะครับ

วันพฤหัสบดีที่ ๒๒ ตุลาคม พ.ศ. ๒๕๕๒

ASP.NET รวม Crystal Report 2 รายงานเป็น pdf ด้วย iTexSharp

คราวนี้เป็นตอนที่ 3 สำหรับ iTextSharp ครับ เราจะสร้าง pdf file จาก Crystal Report (ต่อจากนี้จะเรียกย่อๆว่า CR) 2 ไฟล์ มารวมกันให้เป็นไฟล์เดียว ในการทดสอบผมใช้ CR ตัวเดียว แต่สร้างเป็น 2 report สำหรับเอามารวมกันเป็น pdf ลองดูโค้ดกันครับ


Imports CrystalDecisions.CrystalReports.Engine
Imports XXEntity = jnithi.CLASS.XX.Entity
Imports XXManager = jnithi.CLASS.XXManager

Partial Class frmTestITextSharp
Inherits System.Web.UI.Page

Private Sub CreateReport()
'สร้าง CR1
Dim myReport1 As New ReportDocument()
Dim rpt1Data = XXManager.XXOutStandingManager.GetXXOutStandings(0, Nothing, Nothing, "R00939", "")
myReport1.FileName = Server.MapPath("~/Reports/XX/rptXXOutstanding.rpt")
myReport1.SetDataSource(rpt1Data)
myReport1.SetParameterValue(0, Date.Today.ToString("dd-MMM-yyyy"))
myReport1.SetParameterValue(1, "")
myReport1.SetParameterValue(2, "Client")

' สร้าง CR2
Dim myReport2 As New ReportDocument
Dim rpt2Data = XXManager.XXOutStandingManager.GetXXOutStandings(0, Nothing, Nothing, "A01032", "")
myReport2.FileName = Server.MapPath("~/Reports/XX/rptXXOutstanding.rpt")
myReport2.SetDataSource(rpt2Data)
myReport2.SetParameterValue(0, Date.Today.ToString("dd-MMM-yyyy"))
myReport2.SetParameterValue(1, "")
myReport2.SetParameterValue(2, "Client")

' สร้าง pdfReader1 ชื่อ reader1 โดย export stream จาก CR1
Dim s1 As System.IO.Stream = myReport1.ExportToStream(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat)
Dim reader1 As New iTextSharp.text.pdf.PdfReader(s1)

' สร้าง pdfReader2 ชื่อ reader2 โดย export stream จาก CR2
Dim s2 As System.IO.Stream = myReport2.ExportToStream(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat)
Dim reader2 As New iTextSharp.text.pdf.PdfReader(s2)

' สร้าง pdfCopy โดยกำหนด os เป็น output FileStream เพื่อสร้าง File ชื่อ rptMerge.pdf
Dim pdfDoc As New iTextSharp.text.Document()
Dim os As New System.IO.FileStream(Server.MapPath("~/rptMerge.pdf"), IO.FileMode.Create)
Dim pdfCopy As New iTextSharp.text.pdf.PdfCopy(pdfDoc, os)

pdfDoc.Open() ' เปิด pdf Document เพื่อเริ่มการ copy

' วนลูปอ่าน reader1 เพื่อ copy ทีละหน้า
For iPage As Integer = 1 To reader1.NumberOfPages
pdfCopy.AddPage(pdfCopy.GetImportedPage(reader1, iPage))
Next

' วนลูปอ่าน reader2 เพื่อ copy ทีละหน้า
For iPage As Integer = 1 To reader2.NumberOfPages
pdfCopy.AddPage(pdfCopy.GetImportedPage(reader2, iPage))
Next

' Clear Memory
pdfDoc.Close()
pdfCopy.Close()
reader1.Close()
reader2.Close()
os.Close()
os.Dispose()
s1.Close()
s1.Dispose()
s2.Close()
s2.Dispose()

Response.Redirect("~/rptMerge.pdf")

End Sub

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
CreateReport()
End Sub
End Class


เมื่อเราใช้ iTextSharp สร้าง pdf ไฟล์เสร็จก็สั่ง redirect เพื่อเปิดไฟล์ที่ browser ครับ ทีแรกผมทดสอบให้ iTextSharp สร้าง pdf เป็น MemoryStream แล้วลองใช้ Response.BinaryWrite กับ Response.OutputStream.Write เพื่อสร้าง HTTPResponse แต่ไม่สามารถทำได้ครับ พอเปิดไฟล์ Browser จะฟ้องว่าไฟล์เสีย ลอง search ใน google ดูหลายๆคนพบปัญหาเดียวกัน เลยแก้ด้วยการใช้ FileStream เพื่อสร้าง temp file แทนตามตัวอย่างด้านบนครับ

ลองดูโค้ด MemoryStream ที่มีปัญหาดู เผื่อมีคนแก้ได้ครับ

' โค้ดด้านบนเหมือนเดิม เริ่มเปลี่ยนตั้งแต่ Dim os ลงมา
' เปลี่ยนจาก FileStream เป็น MemoryStream
'Dim os As New System.IO.FileStream(Server.MapPath("~/rptMerge.pdf"), IO.FileMode.Create)
Dim os As New System.IO.MemoryStream()

Dim pdfCopy As New iTextSharp.text.pdf.PdfCopy(pdfDoc, os)
pdfDoc.Open() ' เปิด pdf Document เพื่อเริ่มการ copy

' วนลูปอ่าน reader1 เพื่อ copy ทีละหน้า
For iPage As Integer = 1 To reader1.NumberOfPages
pdfCopy.AddPage(pdfCopy.GetImportedPage(reader1, iPage))
Next

' วนลูปอ่าน reader2 เพื่อ copy ทีละหน้า
For iPage As Integer = 1 To reader2.NumberOfPages
pdfCopy.AddPage(pdfCopy.GetImportedPage(reader2, iPage))
Next

Response.Clear()
Response.ContentType = "application/pdf"
Response.AddHeader("content-disposition", "attachment;filename=Export.pdf")
Response.BinaryWrite(os.ToArray)

' Clear Memory
pdfDoc.Close()
pdfCopy.Close()
reader1.Close()
reader2.Close()
os.Close()
os.Dispose()
s1.Close()
s1.Dispose()
s2.Close()
s2.Dispose()
Response.End()


blog ก่อนหน้านี้มีคนบอกอยากให้ comment ด้วย คราวนี้ก็เลยใส่ comment มา แต่ไม่แน่ใจว่าจะละเอียดพอหรือเปล่าครับ

บทความก่อนหน้า
เขียนโปรแกรมจัดการเอกสาร pdf ด้วย iTextSharp
การจัดการเอกสาร pdf ด้วย iTextSharp(2)

วันศุกร์ที่ ๑๖ ตุลาคม พ.ศ. ๒๕๕๒

VBScript อ่านค่า User Account เพื่อเปิด Website อัตโนมัติ

ผมได้รับคำสั่งให้ทำอย่างไรก็ได้ เมื่อ user ทำการ log in เข้า Windows แล้วให้ตรวจสอบว่าเป็น user ที่อยู่ในกลุ่มพิเศษหรือไม่ ถ้าใช่ให้เปิด Web Application ขี้นมา เพื่อแสดงสถานะงานคงค้าง (เรื่องสำคัญมากคือการทวงหนี้ลูกค้า)

ไปนั่งคิดนอนคิดก็เลยคิดว่าน่าจะเขียน Log on script ด้วย VBScript มาทำงานนี้ครับ เริ่มแรกผมเก็บข้อมูลรายชื่อ user ไว้ใน database ก่อน แล้วพอ Log on script ทำงานจะไปตรวจสอบว่า User นั้นตรงกับที่อยู่ใน database หรือไม่ ทีนี้เราต้องใช้ Windows Scripting Host (WScript) มาร่วมด้วยครับ


ON ERROR RESUME NEXT

'*******************************************************
'// GET LOGON USER ACCOUNT
'*******************************************************
Dim WSHNetwork
Set WSHNetwork = CreateObject("Wscript.Network")

Dim userAccount
userAccount = WSHNetwork.UserDomain & "\" & WSHNetwork.UserName
Set WSHNetwork = nothing

'*******************************************************
'// VALIDATE USER
'*******************************************************

Set cnn = CreateObject("adodb.connection")
Set rstUser = CreateObject("adodb.recordset")

cnn.connectionString = "Provider=SQLOLEDB.1;Persist Security Info=False;User ID=xxxx;Password=yyyy;Initial Catalog=UserList;Data Source=xxSQLServer"

cnn.open

Dim strSQL
strSQL = "select employeeid from dbo.tblUser a where aduser = '" & userAccount "'"
rstUser.open strsql, cnn

If not rstUser.eof Then
'// If valid user, execute IE
Dim WSHShell
Set WSHShell = CreateObject("WScript.Shell")
WSHShell.Run "iexplore.exe -new http://myHost/myApplication"
Set WSHShell = nothing
End If


rstUser.close
cnn.close

Set rstUser = Nothing
Set cnn = Nothing



ทดสอบการทำงานก็ได้ผลโอเคครับ แต่โดน Network Admin ติงมานิดหน่อยว่าไม่อยากให้ไปติดต่อ database เพราะมันช้า และต้องใช้ traffic ของ network เนื่องจาก SQLServer กับ AD อยู่คนละ Server กัน ดังนั้นเลยเปลี่ยนเอา list ของ user มาเก็บเป็น Text file แทนครับ ลองดูโค้ดเฉพาะตรงนี้กัน ส่วนหา user log on ก็เหมือนโค้ดด้านบน


const ForReading = 1, TristateTrue = -1

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("\\myserver\netlogon\UserList.txt", ForReading, false, TristateTrue)

Dim user
Dim isFound
isFound = 0

Do Until objFile.AtEndOfStream
user = trim(objFile.ReadLine)
If lcase(user) = lcase(useraccount) Then
isFound = 1
'msgbox isFound
Exit Do
End If
Loop

objFile.Close
Set objFile = Nothing
Set objFSO = Nothing

If isFound = 1 Then
'// If valid user, execute IE
Dim WSHShell
Set WSHShell = CreateObject("WScript.Shell")
WSHShell.Run "iexplore.exe -new http://myHost/myApplication"
Set WSHShell = nothing
End If

วันจันทร์ที่ ๑๒ ตุลาคม พ.ศ. ๒๕๕๒

ลองสร้าง Extension Method ให้ Enum

คราวก่อนตอนที่ผมต้องการหาชื่อของ Enum ได้ลองทำการสร้าง Extension Method ชื่อ ToDictionary ไว้ ตามชื่อเลยครับ Method นี้จะไปสร้าง Dictionary สำหรับเก็บชื่อ (Key) และค่า (Value) ของ Enum ที่ต้องการ


Public Module EnumExtension
<System.Runtime.CompilerServices.Extension()> _
Public Function ToDictionary(ByVal en As System.Enum) As Dictionary(Of String, Integer)
Dim result As New Dictionary(Of String, Integer)
For Each v As Integer In System.Enum.GetValues(en.GetType)
result.Add(System.Enum.GetName(en.GetType, v), v)
Next
Return result
End Function

End Module


เนื่องจากผมใช้กับ ASP.NET ดังนั้น Module นี้ ผมเอาไปไว้ที่ App_Code folder ครับ ทีแรกเอาไปใส่ใน BLL ซึ่งอยู่คนละ project ก็เลยเรียกใช้ใน ASP.NET ไม่ได้ งงตั้งนานเหมือนกัน ลองทดสอบดูก็น่าพอใจครับ ถือว่าเป็นการทดสอบการสร้าง Extension Method ละกัน

วันพุธที่ ๗ ตุลาคม พ.ศ. ๒๕๕๒

ASP.NET ส่ง array จาก Code Behind Page ไปที่ javascript

ปกติเวลาเราเขียน javascript ที่หน้า aspx บางครั้งเราอาจจะต้องการข้อมูลที่อยู่ใน Code Behind Page เช่น clientId (ค่อนข้างใช้บ่อย ถ้ามีการใช้ MasterPage) เช่น

<script type="text/javascript" id="MainScript">
// <![CDATA[
var employeeTextBoxId = "<%=txtEmployee.ClientId%>";
var movementCount = "<%=EmploteeMovements.Count%>";
// ]]>
</script>

แต่ทีนี้ถ้าต้องการข้อมูลที่เป็น Array ละ ก็ไม่ยากครับ สร้าง Function ที่ฝั่ง Code Behind Page ขึ้นมาเพื่อเอา array มาต่อกันเป็น string โดยให้มีตัว separate (ในตัวอย่างใช้ ;) จากนั้นฝั่ง javascript ก็ไปสั่ง split อีกที

Public employeeName(5) As String

Protected Function GetEmployees() As String

Dim sb As New StringBuilder

For i As Integer = 0 To employeeName.Length - 1
sb.Append(employeeName(i) & ";")
Next

sb.Remove(sb.Length - 1, 1)
Return sb.ToString

End Function


Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

employeeName(0) = "Nithi"
employeeName(1) = "Paetree"
employeeName(2) = "Ple"
employeeName(3) = "Suvimol"
employeeName(4) = "Weekit"
employeeName(5) = "Sasawin"

End Sub



<script type="text/javascript" id="MainScript">

// <![CDATA[
var a = "<%=GetEmployees()%>";
var empArray = a.split(";");
// ]]>

</script>



เท่าที่ลองดูหลายๆแบบ วิธีนี้ง่ายสุดครับ (แต่อาจไม่ดีสุดนะ)