วันพุธที่ ๒๔ กุมภาพันธ์ พ.ศ. ๒๕๕๓

ส่ง URL Parameter ให้ ClickOnce Application

ผมเคยเขียน blog เรื่อง สั่งรัน Win App บนเครื่อง Client เมื่อเดือนธันวาคม 2009 แล้วก็ติดเรื่องส่ง paramenter ให้ ClickOnce application เอาไว้ครับ วันนี้มีโอกาสก็เลยมาเขียนเรื่องนี้ซะเลย

ก่อนอื่นเราก็มาสร้าง Console Application สำหรับการทดสอบซะก่อนครับ จากนั้นก็ Add .NET Reference เข้ามาในโปรเจคเรา 2 ตัว คือ System.Deployment กับ System.Web ครับ แล้วก็เขียนโค้ด Import NameSpace ที่จำเป็นเข้าในด้วย


Imports System.Collections.Specialized
Imports System.Deployment.Application
Imports System.Web

Module Module1

Sub Main()
Dim params = GetURLParameter()
If params.Count > 0 Then
For i As Integer = 0 To params.Count - 1
Console.WriteLine("Parameter {0} is {1}", params.Keys(i), params.Item(i))
Next
Else
Console.WriteLine("No URL Parameter")
End If
Console.WriteLine("Press any key to quit.")
Console.ReadLine()

End Sub

Private Function GetURLParameter() As NameValueCollection
Dim NameValueTable As New NameValueCollection

If (My.Application.IsNetworkDeployed) Then
Dim QueryString As String = ApplicationDeployment.CurrentDeployment.ActivationUri.Query
NameValueTable = HttpUtility.ParseQueryString(QueryString)
End If

Return NameValueTable
End Function

End Module


โค้ดที่ใช้ทดสอบมีเท่านี้ ไม่ยากใช่ไหมครับ แต่ที่สำคัญอีกอย่างหนึ่งก็คือ เราต้องกำหนดให้ ClickOnce Application ของเราสามารถรับ URL Parameters ได้ก่อน โดยไปที่ Property ของโปรเจค แล้วเลือก Publish Tab ครับ



ทีนี้เราจะเห็นปุ่ม OPTIONS กดเข้าไปได้เลยครับ



แล้วก็ทำการเลือก Manifests และคลิ๊กเครื่องหมายถูกตรง Allow URL parameters

เสร็จแล้วก็ทำการ Publish ครับ ทีนี้เรามาลองทดสอบเรียก ClickOnce Application แบบไม่ส่ง parameter ก่อน



โปรแกรมขึ้นว่า No parameter ถูกต้อง คราวนี้ลองทดสอบแบบส่ง QueryString ไปด้วย
(http://nithi/COParameter/CoParameter.application?param1=test1&param2=test2&name=nithi)



เรียบร้อยครับ คราวนี้เราก็สามารถส่ง URL Parameter ไปให้ Application ของเราได้แล้วครับ

Reference:
How to: Retrieve Query String Information in a ClickOnce Application

วันจันทร์ที่ ๒๒ กุมภาพันธ์ พ.ศ. ๒๕๕๓

สถิติของ blog จาก Google Analytics

หลังจากผมได้ลองใช้ Google Analytics มาประมาณ 6 เดือน พอจะสรุปผลคร่าวๆดังนี้ครับ

1. ผู้เยี่ยมชมส่วนใหญ่มาจาก google ครับ ประมาณ 75%
2. เนื้อหายอดนิยมได้แก่ Crystal Report, RDLC, ITextSharp, jQuery น่าจะมาจากการค้นหาใน google
3. 30 วันแรกของ Blog ผู้เยียมชมโดยตรงมี 78 page view คิดเป็นอัตราส่วนประมาณ 11% ส่วนเดือนล่าสุดเพิ่มเป็น 281 (19.18%)
4. วันที่คนมีอัตราเยี่ยมชมสูงสุดคือวันพฤหัส ตอนนี้ pageview สูงสุดอยู่ที่ 72 pv ต่อวันครับ (ยังน้อยอยู่เทียบกับบาง blog ที่มีผู้เยี่ยมชมหลักพัน) วันที่คนเยี่ยมชมน้อยสุดคือวันอาทิตย์ ประมาณ 15-20 pv รวม 30 วันล่าสุดอยู่ที่ 1,465 pv
5. อัตราการเข้าชมโดยตรง ผมแปลกใจมากเพราะอันดับ 1 มาจาก CodeToday.net ครับ (จำได้ว่าผมไป post url ของ blog แค่ครั้งเดียว) อยู่ที่ 2.8% อันดับสองคือ blogger อยู่ที่ 1.57% ส่วน greatfriends อยู่ที่อันดับ 3 ที่ 0.89%

จากที่ได้ลองใช้มา รู้สึกว่า Google Analytics นี่ใช้ง่ายและเครื่องมือเยอะดีครับ แต่ยังต้องศึกษาอีกเยอะทีเดียว เดี๋ยวรอครบ 1 ปี จะมาลองดูใหม่ว่าอัตราการเยี่ยมชมมีอะไรเปลี่ยนแปลงบ้างครับ

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

ASP.NET สั่งเคลียร์ Session เมื่อเปลี่ยน Page

วันนี้เจอคำถามที่ greatfriends ว่าเมื่อ User ปิด Browser หรือเปลี่ยน Page เราต้องการไปเคลียร์ Session ของ Page เดิมจะทำยังไงดี

ถ้าเป็นเรื่องปิด Browser หรือว่า User ไปเปิดเวบอื่นคงไม่มีปัญหาครับ เพราะ ASP.NET มันจะเคลียร์ค่าให้เมื่อ Session Time Out อยู่แล้ว ซึ่งเราก็สามารถเขียนโค้ดใน Session_End ที่ Global.asax เพิ่มเติมก็ได้ แต่ปัญหาคือถ้า user เปลี่ยนหน้า เราจะทำยังไงดี สิ่งแรกที่ผมคิดคือ เรากำหนดให้มัน Post Back กลับมาหน้าเดิมก่อน เพื่อสั่ง Clear Session จากนั้นเราค่อยสั่ง Navigate ไปยังหน้าที่ต้องการอีกที

เช่นสมมติ เรามี User Control 1 ตัว เป็นเมนูสำหรับ Application เราใช้ <asp: Menu> และสร้าง <asp:MenuItem> เพื่อระบุเมนูย่อย แทนที่เราจะใช้ NavigateUrl Attribute เพื่อสั่ง Redirect ไปหน้าใหม่ทันที่ เราก็มาเขียนโค้ดที่ MenuItemClick Event แทนครับ

พอตอนเย็นก็นึกวิธีใหม่ออก ก็เลยมาลองดูครับ วิธีนี้คือ เราใช้ ASP.NET AJAX มาเรียก WebService เพื่อทำการ Clear Session นั่นเองครับ ลองมาดูโค้ดของ WebService กันก่อน


Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols

Namespace jNithi.WS
<System.Web.Script.Services.ScriptService()> _
<WebService(Namespace:="http://tempuri.org/")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Public Class SessionManager
Inherits System.Web.Services.WebService

<Script.Services.ScriptMethod()> _
<WebMethod()> _
Public Function ClearSession(ByVal sessionName As String) As Boolean
Dim session As System.Web.SessionState.HttpSessionState
session = System.Web.HttpContext.Current.Session

If session(sessionName) IsNot Nothing Then
Select Case sessionName.ToUpper
Case "SESSION_RECEIPT_REPORT"
DirectCast(session(sessionName), CrystalDecisions.CrystalReports.Engine.ReportDocument).Dispose()
Case "SESSION_RECEIPT_DATA"
DirectCast(session(sessionName), IList).Clear()
Case Else
End Select
session(sessionName) = Nothing
session.Remove(sessionName)
End If

End Function

End Class

End Namespace


ทีนี้ในหน้า aspx เราก็เขียน javascript สำหรับมาเรียก Webservice ตัวนี้ครับ

<script type="text/javascript">
function Body_OnUnload() {
jNithi.WS.SessionManager.ClearSession('SESSION_RECEIPT_REPORT');
}

</script>
</head>
<body onunload="Body_OnUnload();">
<form id="form1" runat="server">

<asp:ScriptManager ID = "ScriptManager1" runat="server" >
<services>
<asp:ServiceReference Path="~/wsSession.asmx" />
</services>

</asp:ScriptManager>


เท่าที่ลองทดสอบดู ก็ใช้งานได้ผลโอเคนะครับ เมื่อเราเปลี่ยนหน้า หรือปิด Browser มันจะไปเรียก WebService เพื่อสั่ง Clear Session ให้ แต่มีข้อแม้นิดหนึ่งครับ คือ onunload event ของ Body นั้นมันจะทำงานทุกครั้งที่มีการเปลี่ยนหน้า นั่นคือ ถ้ามีการ PostBack กลับไป มันก็จะเกิด Event นี้ด้วยครับ แต่ถ้าเราออกแบบ ASP.NET AJAX Page ของเราดีๆ ไม่ให้มีการ Post Back กลับไป แนวคิดนี้ก็ทำงานได้โอเคครับ

เดี๋ยวคงต้องขอทดสอบอีกซักพัก ก่อนจะเอาไปใช้จริงครับ

วันอังคารที่ ๙ กุมภาพันธ์ พ.ศ. ๒๕๕๓

Crystal Report - สร้างรายงานให้ user กำหนดเงื่อนไข Group ได้เอง (Dynamic Group By)

วันนี้เรามาดูเทคนิคของ Crystal Report อีกครั้งครับ (เนื่องจากดูใน Google Analytics เห็นคนชอบบทความเกี่ยวกับ Crystal Report เป็นพิเศษ) คราวนี้เราจะมาสร้างรายงานที่สามารถให้ user กำหนด Group By ได้เองตามใจ Blog นี้จะทำแค่ Group by 1 ระดับให้ดู แต่เราสามารถประยุกต์ใช้ให้ user เลือก group ได้มากกว่า 1 ระดับ รวมถึงสามารถกำหนดเงื่อนไข sort by ได้ด้วย

ก่อนอื่นเลยสิ่งที่จำเป็นก็คือ เราต้องสร้าง Parameter Field ขึ้นมาก่อน เพื่อรับค่า parameter จาก user ว่าต้องการให้ group by column ไหนของข้อมูลครับ จากตัวอย่างผมสร้าง Parameter Field ชื่อ pGroupBy




จากนั้นก็สร้าง Formula Field เพื่อนำค่าที่ได้จาก Parameter Field มาคำนวนหา column ที่ต้องการ group ครับ
ในตัวอย่างผมสร้าง Formula Field ชื่อ GroupBy



จากโค้ดใน Formula Editor ผมใช้ IF THEN ELSE ซ้อนกัน (ที่จริงน่าใช้ Switch Case แต่ผมขี้เกียจแก้แล้ว) สังเกตุว่าถ้า user ส่ง parameter มาเป็น Dept ผมจะให้ Group By Formula Field อีกที นอกนั้นก็จะ Group By Database Field ตามปกติครับ

คราวนี้มาดูหน้าเวบกันบ้างครับ ผมก็สร้าง Drop-down List มา 1 ตัว ชื่อ ddlGroupBy และใส่ List ที่ต้องการไว้


</asp:DropDownList ID="ddlGroupBy" runat="server" style="position:absolute;left:100px;top:10px" CssClass="txtOptional" Width="110px" EnableTheming="False" EnableViewState="False" >
</asp:ListItem Text="Client" Value="Client" Selected="True"></asp:ListItem>
</asp:ListItem Text="Most Work Lawyer" Value="MostWorkLawyer"></asp:ListItem>
</asp:ListItem Text="Resp. Lawyer" Value = "RespStaff"></asp:ListItem>
</asp:ListItem Text="Department" Value="Dept" ></asp:ListItem>
</asp:DropDownList>


ตอนสร้างรายงานก็อย่าลืม SetParameterValue ด้วยครับ


Dim reportDoc As New ReportDocument
reportDoc.FileName = Server.MapPath("~/Reports/AR/rptAgingReport.rpt")
reportDoc.SetDataSource(ARReportResults)
reportDoc.SetParameterValue("pGroupBy", ddlGroupBy.SelectedValue)
reportDoc.SetParameterValue("AsOfDate", asOfDate)


เท่านี้ก็เรียบร้อยแล้วครับ ทีนี้ user ก็สามารถเลือก column ที่ต้องการ Group By ได้ตามใจครับ