<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5588108423363673584</id><updated>2012-02-11T09:27:09.105+07:00</updated><category term='VBScript'/><category term='Crystal Report'/><category term='LINQ'/><category term='WebServices'/><category term='ASP.NET AJAX'/><category term='jQuery'/><category term='javascript'/><category term='Talk'/><category term='VB6'/><category term='VB.NET'/><category term='Tips'/><category term='SQLServer'/><category term='ASP.NET'/><category term='.NET'/><title type='text'>jnithi</title><subtitle type='html'>.net Developer</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>63</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-8113648739684867030</id><published>2011-11-10T22:29:00.000+07:00</published><updated>2011-11-10T22:29:00.389+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer'/><title type='text'>SQLSERVER: Format Number with comma</title><content type='html'>&lt;pre class='brush:sql'&gt;&lt;br /&gt;-- ตัวอย่าง&lt;br /&gt;SELECT CONVERT(varchar,CONVERT(money,1234567.89),1)&lt;br /&gt;&lt;br /&gt;-- ทดสอบ SIMPLE SELECT&lt;br /&gt;SELECT CONVERT(varchar,CONVERT(money,SUM(bcbill)),1)&lt;br /&gt;FROM ARDebit&lt;br /&gt;WHERE year(billdate) = 2011 and month(billdate) = 10&lt;br /&gt;&lt;br /&gt;-- ทดสอบการลบกันระหว่าง 2 query&lt;br /&gt;SELECT CONVERT(varchar,CONVERT(money,&lt;br /&gt; (&lt;br /&gt; (SELECT SUM(bcBill) FROM ARDebit WHERE Year(BillDate)=2011 and Month(BillDate) = 10)&lt;br /&gt; -&lt;br /&gt; (SELECT SUM(BcPayment) FROM ARCredit WHERE Year(ReceiptDate) = 2011 and MONTH(ReceiptDate) = 10)&lt;br /&gt; )&lt;br /&gt;),1)&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;เมื่อก่อนตอนเจอการตำนวณหลัก 10 ล้าน 100 ล้าน ต้องมานั่งเพ่งดูตัวเลขว่าเท่าไหร่กันแน่ หรือไม่งั้นก็ copy ไป excel พอตอนนี้สามารถสั่ง format ได้ พอมี comma กับจุดทศนิยมก็ง่ายขึ้นเยอะครับ&lt;br /&gt;&lt;br /&gt;แต่ว่าพอ convert เป็น money แล้วทศนิยมมันปัดเป็น 2 ตำแหน่ง อันนี้ต้องระวังด้วยครับ&lt;br /&gt;&lt;br /&gt;&lt;pre class='brush:sql'&gt;&lt;br /&gt;-- ทดสอบ ได้ผลลัพธ์ 1,234,567.90&lt;br /&gt;SELECT CONVERT(varchar,CONVERT(money,1234567.8987),1)&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-8113648739684867030?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/8113648739684867030/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=8113648739684867030' title='1 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/8113648739684867030'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/8113648739684867030'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2011/11/sqlserver-format-number-with-comma.html' title='SQLSERVER: Format Number with comma'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-5762423376639381695</id><published>2011-10-12T15:54:00.005+07:00</published><updated>2011-10-12T15:59:34.091+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VB.NET'/><title type='text'>Filter DataTable</title><content type='html'>สมมติว่าเรามี DataTable อยู่ 1 ตัว ทีนี้เราต้องการนับจำนวนรายการที่ตรงตามเงื่อนไขที่ต้องการ (หรือกรองเอาเฉพาะรายการใน DataTable ตามเงื่อนไข) เท่าที่นึกออกตอนนี้ทำได้ 3 วิธีครับ ลองดูต้วอย่างกันเลย&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt; &lt;br /&gt;    Private Sub TestCountDataTable()&lt;br /&gt;&lt;br /&gt;        Dim dtEmployee As New Data.DataTable&lt;br /&gt;        dtEmployee.Columns.Add("EmployeeID", GetType(String))&lt;br /&gt;        dtEmployee.Columns.Add("HireDate", GetType(Date))&lt;br /&gt;&lt;br /&gt;        Dim strCommand As String = "SELECT EmployeeID, HireDate FROM tblEmployee WHERE HireDate IS NOT NULL"&lt;br /&gt;        Dim strConnection As String = "Data Source=tg2000;Initial Catalog=ADMIN;Integrated Security=True"&lt;br /&gt;&lt;br /&gt;        Using sqlConnection As New SqlClient.SqlConnection(strConnection)&lt;br /&gt;            sqlConnection.Open()&lt;br /&gt;            Using sqlCommand As New SqlClient.SqlCommand(strCommand, sqlConnection)&lt;br /&gt;                Using sqlDataReader = sqlCommand.ExecuteReader()&lt;br /&gt;                    dtEmployee.Load(sqlDataReader)&lt;br /&gt;                End Using&lt;br /&gt;            End Using&lt;br /&gt;&lt;br /&gt;        End Using&lt;br /&gt;&lt;br /&gt;        ' USE DataView.RowFilter&lt;br /&gt;        Dim dv = dtEmployee.DefaultView&lt;br /&gt;        dv.RowFilter = "HireDate&gt;='2010-01-01' and HireDate&lt;='2010-12-31'"&lt;br /&gt;        Dim countFromDataView = dv.Count&lt;br /&gt;        Debug.Print(String.Format("Count from DataView:{0:#,##0.00}", countFromDataView))&lt;br /&gt;&lt;br /&gt;        ' USE LINQ TO DATASET&lt;br /&gt;        Dim countFromLinqToDataSet = dtEmployee.AsEnumerable.Where(Function(w) w.Field(Of Date)("HireDate").Year.Equals(2010)).Count&lt;br /&gt;        Debug.Print(String.Format("Count from Linq To DataSet:{0:#,##0.00}", countFromLinqToDataSet))&lt;br /&gt;&lt;br /&gt;        ' USE Datatable.Select&lt;br /&gt;        Dim dr() = dtEmployee.Select("HireDate&gt;='2010-01-01' and HireDate&lt;='2010-12-31'")&lt;br /&gt;        Dim countFromSelect = dr.Length&lt;br /&gt;        Debug.Print(String.Format("Count from Dataset.Select:{0:#,##0.00}", countFromSelect))&lt;br /&gt;&lt;br /&gt;    End Sub&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ถ้าใครนึกวิธีอื่นออกที่น่าสนใจ ก็ช่วยเพิ่มให้ด้วยครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-5762423376639381695?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/5762423376639381695/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=5762423376639381695' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5762423376639381695'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5762423376639381695'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2011/10/filter-datatable.html' title='Filter DataTable'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-6590708582580343474</id><published>2011-10-10T20:15:00.006+07:00</published><updated>2011-10-10T20:15:00.637+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>ASP.NET Gridview export UNICODE to Excel (แก้ปัญหา export ภาษาไทยแล้วอ่านไม่ออก)</title><content type='html'>ผมเจอปัญหาว่าพอสั่ง Export Gridview ไปเป็น Excel แล้วภาษาไทยมันอ่านไม่ออกครับ&lt;br /&gt;ลองแก้ Response.Charset แล้วก็ยังไม่ได้ครับ ค้นใน google ก็เลยเจอว่าน่าจะเป็นเพราะคำสั่งที่ export นั้นไม่สนับสนุน UTF8 ผมใช้ StringWriter ครับ พอลองมาค้นดูก็เจอว่า StreamWriter มันรองรับการทำ Encoding ได้ ก็เลยปิ๊งครับ&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;           &lt;br /&gt;gvContact.DataSource = mlSummaries&lt;br /&gt;gvContact.DataBind()&lt;br /&gt;&lt;br /&gt;Response.Clear()&lt;br /&gt;&lt;br /&gt;Response.AddHeader("content-disposition", "attachment;filename=EventMailingList.xls")&lt;br /&gt;Response.Charset = "tis-620"&lt;br /&gt;Response.ContentType = "application/vnd.ms-excel"&lt;br /&gt;&lt;br /&gt;If Request("EventName") IsNot Nothing Then lblCaption.Text = Server.UrlDecode(Request("EventName"))&lt;br /&gt;&lt;br /&gt;' โค้ดเดิมที่ใช้ไม่ได้&lt;br /&gt;'Dim stringWrite As New System.IO.StringWriter&lt;br /&gt;'Dim htmlWrite As System.Web.UI.HtmlTextWriter = New HtmlTextWriter(stringWrite)&lt;br /&gt;&lt;br /&gt;'divExport.RenderControl(htmlWrite)&lt;br /&gt;'Response.Write(stringWrite.ToString())&lt;br /&gt;&lt;br /&gt;Dim ms As New System.IO.MemoryStream()&lt;br /&gt;Dim streamWrite As New System.IO.StreamWriter(ms, Encoding.UTF8)&lt;br /&gt;Dim htmlWrite As New System.Web.UI.HtmlTextWriter(streamWrite)&lt;br /&gt;divExport.RenderControl(htmlWrite)&lt;br /&gt;&lt;br /&gt;Dim strEncodedHTML As String = Encoding.UTF8.GetString(ms.ToArray)&lt;br /&gt;Response.Write(strEncodedHTML)&lt;br /&gt;Response.End()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ทดลองดูใช้ได้ครับ ออกมาเป็นภาษาไทยถูกต้องแล้ว&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-6590708582580343474?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/6590708582580343474/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=6590708582580343474' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6590708582580343474'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6590708582580343474'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2011/10/aspnet-gridview-export-unicode-to-excel.html' title='ASP.NET Gridview export UNICODE to Excel (แก้ปัญหา export ภาษาไทยแล้วอ่านไม่ออก)'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-8704206040574071619</id><published>2011-10-07T21:00:00.001+07:00</published><updated>2011-10-07T21:00:09.171+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>ASP.NET Dynamic Gridview</title><content type='html'>ผมได้รับ requirement จากยูสเซ่อว่าหน้าสอบถามข้อมูล (Inquiry) อยากให้สามารถเลือกว่าอยากเห็นข้อมูลคอลัมภ์ไหนบ้าง พร้อมทั้งสามารถ export ข้อมูลมาเป็น Excel ได้ (อนาคตจะต้องให้ user สามารถ save format ที่เลือกเก็บไว้ได้ด้วย แต่ผมยังไม่ได้ทำ)&lt;br /&gt;&lt;br /&gt;เนื่องจากหน้าเดิมผมใช้ Gridview ในการแสดงผล ดังนั้นผมเลยเขียนโปรแกรมเพื่อให้ user สามารถเพิ่ม/ลด column ของ Gridview ได้&lt;br /&gt;&lt;br /&gt;ก่อนอื่นเรามาเตรียมโค้ดของ Gridview ก่อนครับ ในเวอร์ชันนี้เราจะให้ Dynamic Gridview รองรับแค่ Label เท่านั้น ผมเริ่มจากการสร้าง GridViewLabelTemplate Class สำหรับ Class ตัวนี้มีไว้สำหรับสร้าง ItemTemplate ซึ่งประกอบด้วย Label 1 อัน&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;  &lt;br /&gt;Public Class GridViewLabelTemplate&lt;br /&gt;    Implements ITemplate&lt;br /&gt;&lt;br /&gt;    Private _lblID As String&lt;br /&gt;&lt;br /&gt;    Public Sub New(ByVal ID As String)&lt;br /&gt;        _lblID = ID&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Public Sub InstantiateIn(ByVal container As System.Web.UI.Control) _&lt;br /&gt;        Implements System.Web.UI.ITemplate.InstantiateIn&lt;br /&gt;&lt;br /&gt;        Dim lbl As New Label&lt;br /&gt;        lbl.ID = _lblID&lt;br /&gt;        container.Controls.Add(lbl)&lt;br /&gt;        AddHandler lbl.DataBinding, AddressOf Me.DataBinding&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Private Sub DataBinding(ByVal sender As Object, ByVal e As EventArgs)&lt;br /&gt;       ' Do nothing but needed for row databinding event &lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;End Class&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;จากนั้นก็มาสร้าง Static Class อีกตัว เพื่อเป็น Gridview Utility สำหรับเพิ่ม Column (ItemTemplate)&lt;br /&gt;&lt;pre class="brush:vb"&gt;  &lt;br /&gt;Public Class GridViewUtility&lt;br /&gt;    Public Shared Sub AddLabelTemplate(ByVal grid As GridView, _&lt;br /&gt;                                                           ByVal labelID As String, _&lt;br /&gt;                                                           ByVal HeaderText As String, _&lt;br /&gt;                                                           ByVal columnWidth As Integer, _&lt;br /&gt;                                                           Optional ByVal fontWeight As Integer = 11)&lt;br /&gt;&lt;br /&gt;        Dim f As New TemplateField&lt;br /&gt;        f.HeaderText = HeaderText&lt;br /&gt;        f.HeaderStyle.Width = Unit.Pixel(columnWidth)&lt;br /&gt;        f.HeaderStyle.HorizontalAlign = HorizontalAlign.Left&lt;br /&gt;        f.HeaderStyle.Font.Name = "Tahoma"&lt;br /&gt;        f.ItemStyle.HorizontalAlign = HorizontalAlign.Left&lt;br /&gt;        f.ItemStyle.Font.Size = Unit.Pixel(fontWeight).Value&lt;br /&gt;        f.ItemStyle.Font.Name = "Tahoma"&lt;br /&gt;&lt;br /&gt;        f.ItemTemplate = New GridViewLabelTemplate(labelID)&lt;br /&gt;        grid.Columns.Add(f)&lt;br /&gt;&lt;br /&gt;    End Sub&lt;br /&gt;End Class&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ขั้นเตรียมตัวเรียบร้อย ก็มาเริ่มเขียน aspx กันครับ ส่วนหน้าจอที่ให้ user เลือก&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-5Kt5iHJ0miQ/To7L64eQ3xI/AAAAAAAAANM/2D8YCAcj5vk/s1600/1.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 248px;" src="http://4.bp.blogspot.com/-5Kt5iHJ0miQ/To7L64eQ3xI/AAAAAAAAANM/2D8YCAcj5vk/s400/1.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5660685994103463698" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ไม่ได้เขียนโค้ดให้ แค่คร่าวๆคือ เมื่อ user กดปุ่ม Advance จะแสดง div ที่ซ่อนไว้ ว่ามีคอลัมภ์อะไรให้เลือกบ้าง โดยจะมี Default อยู่ 8 คอลัมภ์ และ user สามารถเลือกได้ว่าอยากเห็นข้อมูลที่ห้น้าจอหรือ export เป็น excel หรือ pdf พอ user กดปุ่ม Export โปรแกรมก็จะไปสร้าง Gridview ให้ แล้วเอา Gridview ไปแสดงผลตามที่ต้องการ&lt;br /&gt;&lt;br /&gt;คราวนี้มาดูโค้ดส่วนที่สร้าง Gridview บ้างครับ&lt;br /&gt;&lt;pre class="brush:vb"&gt;  &lt;br /&gt;        Dim lst As List(Of CMS.Library.ContactList) = Session(MAILING_LIST_SESSION)&lt;br /&gt;        'Add empty row&lt;br /&gt;        lst.Add(New CMS.Library.ContactList)&lt;br /&gt;&lt;br /&gt;        Dim divReport As New Panel&lt;br /&gt;&lt;br /&gt;        Dim pHeader As New Panel&lt;br /&gt;        Dim lblHeader As New Label&lt;br /&gt;        lblHeader.Text = "List of mailing list by lawyer."&lt;br /&gt;        lblHeader.Font.Bold = True&lt;br /&gt;        lblHeader.Font.Size = Unit.Pixel(16).Value&lt;br /&gt;        pHeader.Controls.Add(lblHeader)&lt;br /&gt;        divReport.Controls.Add(pHeader)&lt;br /&gt;&lt;br /&gt;        Dim pCaption As New Panel&lt;br /&gt;        Dim lblCaption As New Label&lt;br /&gt;        lblCaption.Text = "Lawyer: " &amp; Me.lblLawyerName.Text&lt;br /&gt;        lblCaption.Font.Size = Unit.Pixel(14).Value&lt;br /&gt;        lblCaption.Font.Italic = True&lt;br /&gt;        pCaption.Controls.Add(lblCaption)&lt;br /&gt;        divReport.Controls.Add(pCaption)&lt;br /&gt;&lt;br /&gt;        Dim pLinespace As New Panel&lt;br /&gt;        Dim lblSpace As New Label&lt;br /&gt;        lblSpace.Text = "&lt;br /&gt;"&lt;br /&gt;        pLinespace.Controls.Add(lblSpace)&lt;br /&gt;        divReport.Controls.Add(pLinespace)&lt;br /&gt;&lt;br /&gt;        Dim gvReport As New GridView&lt;br /&gt;        divReport.Controls.Add(gvReport)&lt;br /&gt;&lt;br /&gt;        gvReport.AutoGenerateColumns = False&lt;br /&gt;        gvReport.RowStyle.VerticalAlign = VerticalAlign.Top&lt;br /&gt;        gvReport.BorderStyle = BorderStyle.Solid&lt;br /&gt;        gvReport.BorderWidth = Unit.Pixel(1)&lt;br /&gt;        gvReport.GridLines = GridLines.Both&lt;br /&gt;        gvReport.HeaderStyle.BackColor = Drawing.Color.Silver&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;        AddHandler gvReport.RowDataBound, AddressOf GridView_RowDataBound&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;        If chkPrefix.Checked Then GridViewUtility.AddLabelTemplate(gvReport, "Prefix", "Prefix", 100)&lt;br /&gt;        If chkContactname.Checked Then GridViewUtility.AddLabelTemplate(gvReport, "FirstName", "First Name", 100)&lt;br /&gt;        If chkContactMiddleName.Checked Then _&lt;br /&gt;               GridViewUtility.AddLabelTemplate(gvReport, "MiddleName", "Middle Name", 100)&lt;br /&gt;        If chkContactLastName.Checked Then GridViewUtility.AddLabelTemplate(gvReport, "LastName", "Last Name", 100)&lt;br /&gt;&lt;br /&gt;        If chkCompanyName.Checked Then _&lt;br /&gt;               GridViewUtility.AddLabelTemplate(gvReport, "CompanyName", "Company", 300)&lt;br /&gt;        If chkClientNo.Checked Then GridViewUtility.AddLabelTemplate(gvReport, "ClientNo", "Client No", 100)&lt;br /&gt;        If chkDepartment.Checked Then _ &lt;br /&gt;               GridViewUtility.AddLabelTemplate(gvReport, "DepartmentTitle", "DepartmentTitle", 100)&lt;br /&gt;        If chkTitle.Checked Then GridViewUtility.AddLabelTemplate(gvReport, "JobTitle", "Job Title", 100)&lt;br /&gt;        ...&lt;br /&gt;        ...&lt;br /&gt;        ...&lt;br /&gt;        gvReport.DataSource = lst&lt;br /&gt;        gvReport.DataBind()&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;  &lt;br /&gt;&lt;br /&gt;พอเราสั่ง DataBind โปรแกรมก็จะไปทำ RowDataBinding เราก็ต้องไปเขียนโค้ดเพื่อเอาข้อมูลไปใส่ใน ItemTemplate ให้ถูกต้อง&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;  &lt;br /&gt;    Private Sub GridView_RowDataBound(ByVal sender As Object, ByVal e As GridViewRowEventArgs)&lt;br /&gt;&lt;br /&gt;        'Skip for last empty row&lt;br /&gt;        If e.Row.DataItemIndex = TryCast(Session(MAILING_LIST_SESSION), IList).Count - 1 Then Exit Sub&lt;br /&gt;&lt;br /&gt;        If e.Row.RowType = DataControlRowType.DataRow Then&lt;br /&gt;            Dim ml As CMS.Library.ContactList = TryCast(e.Row.DataItem, CMS.Library.ContactList)&lt;br /&gt;&lt;br /&gt;            If chkPrefix.Checked AndAlso ml.Contact.Prefix IsNot Nothing Then _&lt;br /&gt;                     DirectCast(e.Row.FindControl("Prefix"), Label).Text = ml.Contact.Prefix.PrefixCode&lt;br /&gt;            If chkContactname.Checked AndAlso ml.Contact.FirstName IsNot Nothing Then _&lt;br /&gt;                     DirectCast(e.Row.FindControl("FirstName"), Label).Text = ml.Contact.FirstName.ToString&lt;br /&gt;            If chkContactMiddleName.Checked AndAlso ml.Contact.MiddleName IsNot Nothing Then _&lt;br /&gt;                     DirectCast(e.Row.FindControl("MiddleName"), Label).Text = ml.Contact.MiddleName.ToString&lt;br /&gt;            If chkContactLastName.Checked AndAlso ml.Contact.LastName IsNot Nothing Then _&lt;br /&gt;                     DirectCast(e.Row.FindControl("LastName"), Label).Text = ml.Contact.LastName.ToString&lt;br /&gt; &lt;br /&gt;            If chkClientNo.Checked AndAlso ml.Company.ClientNo IsNot Nothing Then _&lt;br /&gt;                      DirectCast(e.Row.FindControl("ClientNo"), Label).Text = ml.Company.ClientNo.ToString&lt;br /&gt;            If chkDepartment.Checked AndAlso ml.DepartmentTitle IsNot Nothing Then _&lt;br /&gt;                      DirectCast(e.Row.FindControl("DepartmentTitle"), Label).Text = ml.DepartmentTitle&lt;br /&gt;            ...&lt;br /&gt;            ...&lt;br /&gt;            ...&lt;br /&gt;            If ml.Active &lt;&gt; CMS.Library.Constants.ACTIVE Then&lt;br /&gt;                e.Row.ForeColor = Drawing.Color.Red&lt;br /&gt;                e.Row.Font.Italic = True&lt;br /&gt;            End If&lt;br /&gt;        End If&lt;br /&gt;    End Sub&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;โค้ดส่วนที่สำคัญก็มีเท่านี้ครับ ส่วนที่ export ไป excel หรือ pdf ก็ไม่ยากแล้ว&lt;br /&gt;&lt;br /&gt;ถ้าอนาคตมีการ modify หรือรองรับ control อื่นๆนอกจาก label เพิ่มจะมาอัพเดทอีกทีครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-8704206040574071619?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/8704206040574071619/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=8704206040574071619' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/8704206040574071619'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/8704206040574071619'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2011/10/aspnet-dynamic-gridview.html' title='ASP.NET Dynamic Gridview'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/-5Kt5iHJ0miQ/To7L64eQ3xI/AAAAAAAAANM/2D8YCAcj5vk/s72-c/1.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-4010861914501750603</id><published>2011-09-30T21:14:00.003+07:00</published><updated>2011-10-07T17:29:21.235+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Crystal Report'/><category scheme='http://www.blogger.com/atom/ns#' term='VB.NET'/><title type='text'>โค้ดสั่ง Suppress Picture ใน Crystal Report</title><content type='html'>&lt;pre class="brush:vb"&gt;&lt;br /&gt;            Dim picture1 As CrystalDecisions.CrystalReports.Engine.PictureObject&lt;br /&gt;            picture1 = reportDoc.ReportDefinition.ReportObjects("Picture1")&lt;br /&gt;            picture1.ObjectFormat.EnableSuppress = True&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-4010861914501750603?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/4010861914501750603/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=4010861914501750603' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/4010861914501750603'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/4010861914501750603'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2011/09/suppress-picture-crystal-report.html' title='โค้ดสั่ง Suppress Picture ใน Crystal Report'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-5938076369375638822</id><published>2011-09-29T13:56:00.000+07:00</published><updated>2011-09-29T14:09:55.815+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VB.NET'/><title type='text'>แปลงผล LINQ เป็น DATATABLE</title><content type='html'>จากคำถามคุณ witsanukam ที่ GreatFriends นะครับ&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;        Dim dtEmployee As New DataTable()&lt;br /&gt;        Dim dtDepartment As New DataTable()&lt;br /&gt; &lt;br /&gt;        'Define Columns/Data&lt;br /&gt; &lt;br /&gt;        dtEmployee.Columns.Add("EmployeeID", GetType(String))&lt;br /&gt;        dtEmployee.Columns.Add("EmployeeName", GetType(String))&lt;br /&gt;        dtEmployee.Columns.Add("OpenType", GetType(String))&lt;br /&gt;        dtEmployee.Columns.Add("DepartID", GetType(String))&lt;br /&gt; &lt;br /&gt;        dtEmployee.Rows.Add(New Object() {"001", "น้องฟ้า", "สดใส", 2})&lt;br /&gt;        dtEmployee.Rows.Add(New Object() {"002", "น้องนก", "น่ารัก", 2})&lt;br /&gt;        dtEmployee.Rows.Add(New Object() {"003", "ได้หลัง", "แล้วลืมหน้า", 2})&lt;br /&gt; &lt;br /&gt; &lt;br /&gt;        dtDepartment.Columns.Add("DepartID", GetType(String))&lt;br /&gt;        dtDepartment.Columns.Add("DepartName", GetType(String))&lt;br /&gt; &lt;br /&gt;        dtDepartment.Rows.Add(New Object() {1, "นักวิเคราะห์/นักแบบระบบ/อื่นฯ"})&lt;br /&gt;        dtDepartment.Rows.Add(New Object() {2, "โปรแกรมมั่ว"})&lt;br /&gt;        dtDepartment.Rows.Add(New Object() {3, "WOW อรชรอ้อนแอ้น"})&lt;br /&gt; &lt;br /&gt;        '*************************************************************&lt;br /&gt;        Dim dtResult As New DataTable&lt;br /&gt;        dtResult.Columns.Add("EmployeeID", GetType(String))&lt;br /&gt;        dtResult.Columns.Add("EmployeeName", GetType(String))&lt;br /&gt;        dtResult.Columns.Add("OpenType", GetType(String))&lt;br /&gt;        dtResult.Columns.Add("DepartID", GetType(String))&lt;br /&gt;        dtResult.Columns.Add("Department", GetType(String))&lt;br /&gt; &lt;br /&gt;        Dim dr As DataRow = dtResult.NewRow&lt;br /&gt; &lt;br /&gt;        Dim q = From j0 In dtEmployee.AsEnumerable() Join _&lt;br /&gt;                     j1 In dtDepartment.AsEnumerable() _&lt;br /&gt;                On j0.Field(Of String)("DepartID") Equals j1.Field(Of String)("DepartID") _&lt;br /&gt;                Select dtResult.Rows.Add(New Object() {j0("EmployeeID"), j0("EmployeeName"), j0("OpenType"), j1("DepartID"), j1("DepartName")})&lt;br /&gt; &lt;br /&gt; &lt;br /&gt;        q.CopyToDataTable(dtResult, LoadOption.OverwriteChanges)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ลองทำดู คิดว่าเผื่ออนาคตอาจได้ใช้เลยเอามาเก็บไว้ใน blog ซะก่อน&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-5938076369375638822?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/5938076369375638822/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=5938076369375638822' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5938076369375638822'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5938076369375638822'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2011/09/linq-datatable.html' title='แปลงผล LINQ เป็น DATATABLE'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-7989292620651070808</id><published>2010-07-08T19:21:00.003+07:00</published><updated>2010-07-08T19:26:46.713+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Talk'/><title type='text'>เปลี่ยนมาใช้ Syntax Highlighter 3.0.83</title><content type='html'>เพิ่งมาสังเกตุว่า ส่วนที่เป็น source code ที่ใช้ Syntax Highlighter มันไม่แสดงผลอย่างที่ควรจะเป็นครับ พอลองตรวจสอบดูปรากฏว่า ลิงค์ที่ไป URI ของ Syntax Highlighter version 1.5 ที่เคยใช้มัน Access Denied ครับ ลองหลายๆเวอร์ชันก็เป็นเหมือนกัน &lt;br /&gt;&lt;br /&gt;ก็เลยได้โอกาสปรับเปลี่ยนมาลองใช้เวอร์ชันล่าสุด ซึ่งปัจจุบันเป็นเวอร์ชัน 3.0.83 ครับ ปรากฏว่าเวลาเราเขียน &amp;lt;pre&amp;gt; tag ต้องกำหนด attribute ใหม่ จากเดิมเป็น &amp;lt;pre name=code class=vb&amp;gt; เป็น &amp;lt;pre class="brush:vb"&amp;gt; ครับ&lt;br /&gt;&lt;br /&gt;ไว้ว่างๆจะทยอยไล่แก้ที่บทความเก่าๆ ถ้าเป็นไปได้ก็จะหาที่เก็บไฟล์เวอร์ชันนี้เองน่าจะดี ^_^&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-7989292620651070808?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/7989292620651070808/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=7989292620651070808' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/7989292620651070808'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/7989292620651070808'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2010/07/syntax-highlighter-3083.html' title='เปลี่ยนมาใช้ Syntax Highlighter 3.0.83'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-5486793854144361137</id><published>2010-07-08T14:11:00.002+07:00</published><updated>2010-07-08T14:20:41.417+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>Fix: IE8 Stuck on new tab</title><content type='html'>ตอนนี้ที่บริษัทเริ่มใช้ IE8 กันมากขึ้นแล้วครับ ทีนี้ก็เจอปัญหาว่าบางโปรแกรมที่เราเขียน เราสั่งให้มันเป็น Window ใหม่เช่นแสดงรายงาน หรือทำ Popup ปรากฏว่าอยู่ดีๆ ก็ไม่สามารถเปิด Window ใหม่ได้ครับ (มารู้ทีหลังว่าเป็นเพราะเราลงโปรแกรมบางตัว เช่น Image Viewer ครับ)&lt;br /&gt;&lt;br /&gt;ทีแรกลองค้นใน google ดูก็พบว่าวิธีแก้ปัญหาคือต้องลงทะเบียน dll บางตัวใน registry ใหม่ ซึ่งมีหลายตัวมากครับ ให้เราลองสั่ง regsvr32 ทีละตัวดู ซึ่งก็เสียเวลามาก โชคดีที่เพื่อนที่ทำงานได้ bat file มา 1 ตัวซึ่งแก้ปัญหานี้ได้ พอไปดูในโค้ดก็เห็น link ไปเวบ http://iefaq.info ครับ พอเข้าไปดูปรากฏว่าไม่ใช่ภาษาอังกฤษซะงั้น&lt;br /&gt;&lt;br /&gt;แต่สำหรับคนที่มีปัญหาเกี่ยวกับ IE ลองเข้าไปดูได้ครับ โดยเฉพาะ IE8 ถ้าสนใจตัว script สำหรับแก้ปัญหา registry ของ IE8 ให้ไปที่หน้า&lt;br /&gt;&lt;br /&gt;&lt;a href = "http://iefaq.info/index.php?action=artikel&amp;cat=47&amp;id=136&amp;artlang=de"&gt;http://iefaq.info/index.php?action=artikel&amp;cat=47&amp;id=136&amp;artlang=de&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;สำหรับผมใช้ Windows7 32 bit เลยดาวน์โหลดตัว IE8-rereg.zip มาใช้ครับ สุดยอดมากๆๆ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-5486793854144361137?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/5486793854144361137/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=5486793854144361137' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5486793854144361137'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5486793854144361137'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2010/07/fix-ie8-stuck-on-new-tab.html' title='Fix: IE8 Stuck on new tab'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-6456818868668076371</id><published>2010-07-03T17:56:00.001+07:00</published><updated>2010-07-08T19:09:10.692+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>แก้ปัญหา Export to Excel ด้วย GridView: Control 'GridView1' of type 'GridView' must be placed inside a form tag with runat=server.</title><content type='html'>วันก่อนผมเขียนโปรแกรมเพื่อ export ข้อมูลจาก GridView มาเป็น Excel ครับ (ก่อนหน้านี้ใช้ Datagrid ไม่เคยมีปัญหา) ก็เจอ error ดังนี้ครับ&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_N-Phs4y2L44/TC3GboRW-kI/AAAAAAAAAGk/8Z9Q_VOOkBo/s1600/1.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 223px;" src="http://4.bp.blogspot.com/_N-Phs4y2L44/TC3GboRW-kI/AAAAAAAAAGk/8Z9Q_VOOkBo/s400/1.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5489261698803497538" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;นั่งงมอยู่ตั้งนาน ก็เลยเจอวิธีแก้ว่าให้เพิ่มโค้ดดังนี้ครับ&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;    Public Overrides Sub VerifyRenderingInServerForm(ByVal control As System.Web.UI.Control)&lt;br /&gt;        'MyBase.VerifyRenderingInServerForm(control)&lt;br /&gt;&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;แค่นี้ก็ใช้งานได้แล้วครับ&lt;br /&gt;&lt;br /&gt;แต่ถ้าใครเจอปัญหาว่า "RegisterForEventValidation can only be called during Render();"&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_N-Phs4y2L44/TC3HXBO72oI/AAAAAAAAAGs/c515pjWg7Lw/s1600/2.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 210px;" src="http://3.bp.blogspot.com/_N-Phs4y2L44/TC3HXBO72oI/AAAAAAAAAGs/c515pjWg7Lw/s400/2.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5489262719116499586" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ให้เอา AllowSorting กับ AllowPaging ออกครับ&lt;br /&gt;&lt;br /&gt;เท่านี้เราก็สามารถใช้ GridView ในการสร้าง Excel ได้แล้วครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-6456818868668076371?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/6456818868668076371/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=6456818868668076371' title='1 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6456818868668076371'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6456818868668076371'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2010/07/export-to-excel-gridview-control.html' title='แก้ปัญหา Export to Excel ด้วย GridView: Control &apos;GridView1&apos; of type &apos;GridView&apos; must be placed inside a form tag with runat=server.'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_N-Phs4y2L44/TC3GboRW-kI/AAAAAAAAAGk/8Z9Q_VOOkBo/s72-c/1.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-4609026780908814551</id><published>2010-07-02T20:39:00.000+07:00</published><updated>2010-07-02T17:53:04.529+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>ปัญหาเมื่อ export excel: The file you are trying to open, 'name.xls', is in a different format than specified by the file extension</title><content type='html'>ช่วงนี้หลังจากที่ทำงาน migrate Office จาก 2003 เป็น 2007 ก็เจอปัญหาว่าโปรแกรมที่เราสร้างรายงาน Excel ด้วยวิธีการกำหนด Response.ContentType = "application/vnd.ms-excel" ได้ผลลัพธ์ตามปกติ แต่ว่าจะมี alert ขึ้นมาว่า&lt;br /&gt;&lt;br /&gt;The file you are trying to open, 'name.xls', is in a different format than specified by the file extension. Verify that the file is not corrupted and is from a trusted source before opening the file. Do you want to open the file now?&lt;br /&gt;&lt;br /&gt;จริงๆก็ไม่ได้มีปัญหาอะไรมากหรอกครับ เพราะแค่กด OK ก็เปิดดู Excel ได้ตามปกติ แต่หลังๆเริ่มมี user บ่นขึ้นเรื่อยๆ เลยลอง  search ดูก็พบ article ที่เวบไมโครซอฟท์ครับ&lt;br /&gt;&lt;br /&gt;When you open a file in Excel 2007, you receive a warning that the ...&lt;br /&gt;Article ID: 948615 - Last Review: March 4, 2008 - Revision: 1.1&lt;br /&gt;&lt;a href="support.microsoft.com/kb/948615"&gt;support.microsoft.com/kb/948615&lt;br /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;วิธีแก้ก็ง่ายๆคือไปกำหนด registry นั่นเอง ผมก็เลยทำเป็นไฟล์ reg จะได้ส่งให้ user เฉพาะรายที่มีปัญหารันได้เลย (จริงๆแล้วเราสามารถใช้ Group Policy ในการแก้ปัญหานี้ได้ครับ ลองดูใน article ละกัน)&lt;br /&gt;&lt;br /&gt;ก่อนอื่นก็สร้างไฟล์ชื่อ ExcelFileFormat.reg สำคัญตรงนามสกุลไฟล์ให้เป็น .reg นะครับ แล้วก็ใส่เขียนโค้ดดังนี้&lt;br /&gt;&lt;br /&gt;REGEDIT4&lt;br /&gt;[HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Excel\Security]&lt;br /&gt;"ExtensionHardening"=dword:0&lt;br /&gt;&lt;br /&gt;จากนั้นก็ save ไฟล์&lt;br /&gt;&lt;br /&gt;พอต้องการใช้ ก็ double click ที่ไฟล์นี้ได้เลยครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-4609026780908814551?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/4609026780908814551/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=4609026780908814551' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/4609026780908814551'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/4609026780908814551'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2010/07/export-excel-file-you-are-trying-to.html' title='ปัญหาเมื่อ export excel: The file you are trying to open, &apos;name.xls&apos;, is in a different format than specified by the file extension'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-453470523964377704</id><published>2010-06-03T00:04:00.007+07:00</published><updated>2010-06-03T00:32:05.444+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET AJAX'/><title type='text'>ScriptMode ของ ScriptManager ต่างกันอย่างไร</title><content type='html'>วันนี้เจอคำถามที่ GreatFriends เรื่อง ScriptMode ของ ScriptManager ซึ่งผมตอบไปแล้ว แต่ก็จะมาขยายความเพิ่มใน blog นี้ละกันครับ&lt;br /&gt;&lt;br /&gt;ใน ScriptManager จะมี Property ตัวหนึ่งชื่อ ScriptMode ซึ่งมี option ให้เลือก 4 ข้อ คือ Auto, Inherit, Debug, Release เพื่อความเข้าใจที่ง่ายขึ้นมาลองทดสอบกันเลยครับ&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_N-Phs4y2L44/TAaQRjkPWXI/AAAAAAAAAGE/-19cuxFhyaw/s1600/jnithi01060201.bmp"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 176px;" src="http://2.bp.blogspot.com/_N-Phs4y2L44/TAaQRjkPWXI/AAAAAAAAAGE/-19cuxFhyaw/s400/jnithi01060201.bmp" border="0" alt=""id="BLOGGER_PHOTO_ID_5478224628022204786" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;จากรูปผมสร้าง page ใหม่โดยให้มี ScriptManager แค่ 1 ตัว กำหนด ScriptMode เป็น debug แล้วลองรันทดสอบดูครับ (ถ้าใช้ IE ต้องไปเอา Disable Script Debugging ของ IE ใน Option ออกก่อนนะครับ ถึงจะ debug script ได้)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_N-Phs4y2L44/TAaQn3qwJgI/AAAAAAAAAGM/8ajHThREvhU/s1600/jnithi01060202.bmp"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 114px;" src="http://4.bp.blogspot.com/_N-Phs4y2L44/TAaQn3qwJgI/AAAAAAAAAGM/8ajHThREvhU/s400/jnithi01060202.bmp" border="0" alt=""id="BLOGGER_PHOTO_ID_5478225011375351298" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ลองดูใน Script Document ของ VS จะเห็นว่านอกจาก Default.aspx แล้วยังมี WebResource 1 ไฟล์กับ ScriptResource อีก 2 ไฟล์เพิ่มขึ้นมา ตอนนี้เรามา focus ที่ ScriptResource ครับ ลอง double click มาตัวหนึ่ง จะเห็นว่าข้างในมันคือ javascript นี่เอง มีใส่ comment และ เว้นบรรทัดสวยงามอ่านง่ายครับ สังเกตุชื่อใน comment ครับ ว่ามันคือ MicrosoftAjax.debug.js&lt;br /&gt;&lt;br /&gt;คราวนี้ลองเปลี่ยน ScriptMode เป็น Release ดูบ้าง&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_N-Phs4y2L44/TAaSC2UiDoI/AAAAAAAAAGU/-SXvP2ZQEHQ/s1600/jnithi01060203.bmp"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 132px;" src="http://2.bp.blogspot.com/_N-Phs4y2L44/TAaSC2UiDoI/AAAAAAAAAGU/-SXvP2ZQEHQ/s400/jnithi01060203.bmp" border="0" alt=""id="BLOGGER_PHOTO_ID_5478226574381813378" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ลองรันดูครับ&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_N-Phs4y2L44/TAaUfbC-DrI/AAAAAAAAAGc/YHtJzN9o9pk/s1600/jnithi01060204.bmp"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 132px;" src="http://4.bp.blogspot.com/_N-Phs4y2L44/TAaUfbC-DrI/AAAAAAAAAGc/YHtJzN9o9pk/s400/jnithi01060204.bmp" border="0" alt=""id="BLOGGER_PHOTO_ID_5478229264299855538" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;จะสังเกตุว่าไฟล์ที่นำมาทำ ScriptResource จะเปลี่ยนไปเป็น MicrosoftAjax.js แทน ซึ่งมี comment นิดเดียวและก็โค้ดเรียงติดกันเป็นพรืด&lt;br /&gt;&lt;br /&gt;โดยปกติเราไม่ค่อยได้ debug javascript พวกนี้อยู่แล้ว ก็สามารถกำหนด ScriptMode เป็น Release เลยก็ได้ครับ เพราะขนาดไฟล์จะเล็กกว่า ไม่กิน bandwidth ครับ&lt;br /&gt;&lt;br /&gt;ส่วนถ้าเราเลือก ScriptMode เป็น Auto ตอนที่รันจะขึ้นอยู่กับว่า Web เราเลือก Configuration เป็น debug หรือว่า release ครับ WebServer จะไปเลือกไฟล์ js ที่ตรงกับ config ของเรามาสร้าง ScriptResource ให้&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-453470523964377704?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/453470523964377704/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=453470523964377704' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/453470523964377704'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/453470523964377704'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2010/06/scriptmode-scriptmanager.html' title='ScriptMode ของ ScriptManager ต่างกันอย่างไร'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_N-Phs4y2L44/TAaQRjkPWXI/AAAAAAAAAGE/-19cuxFhyaw/s72-c/jnithi01060201.bmp' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-7805310975760533320</id><published>2010-03-27T15:04:00.001+07:00</published><updated>2010-07-08T19:19:19.918+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VB.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>VB.NET ออกรายงาน Excel แบบรวดเร็ว</title><content type='html'>โดยปกติเมื่อเราพัฒนาโปรแกรมทางธุรกิจ ที่หนีไม่พ้นก็คือต้องออกรายงานครับ และโดยส่วนใหญ่เรามักจะใช้ Crystal Report เพราะสะดวก ง่าย และรวดเร็วครับ ที่สำคัญเรายังสามารถ Export ข้อมูลออกเป็น Excel, Word หรือ PDF ก็ยังได้&lt;br /&gt;&lt;br /&gt;แต่ถ้าเราไม่สามารถใช้ Crystal Report ได้ละ เช่นลูกค้าไม่มี License เป็นต้น ทางเลือกหนึ่งก็คือออกรายงานเป็น Excel ครับ ถ้าลูกค้ามี Licese ของ Microsoft Excel อยู่แล้วก็ไม่ยาก หรือเราอาจใช้ OWC (Office Web Componet) แทนก็ได้ ซึ่ง OWC สามารถดาวน์โหลดได้จากเวบของ Microsoft ครับ (ปัจจุบันผมใช้ OWC11) ซึ่งสามารถออกรายงาน รวมถึงสร้างกราฟได้ด้วย&lt;br /&gt;&lt;br /&gt;แต่ข้อเสียของการใช้ Excel หรือ OWC คือ ช้าครับ แถมกิน CPU สูงมากโดยเฉพาะถ้าต้องวนลูปเพื่อใส่ข้อมูล วันนี้ผมจะนำเสนออีกวิธีในการออกรายงาน Excel ครับ นั่นคือเรานำข้อมูลที่ต้องการไปใส่ใน delimited file ก่อนเช่น .csv (CSV file), .txt (Tab delimited file), .prn (Spaced delimited, Formatted Text) หรือ .txt ที่เรากำหนด delimeter เอง เช่น ; หรือ : เป็นต้นครับ จากนั้นค่อยใช้ Excel หรือ OWC เปิดไฟล์พวกนี้ขึ้นมาเพื่อทำการตกแต่งรายงานให้สวยงาม เช่น ใส่ Header, กำหนดขนาด หรือ layout, ตีเส้นตาราง เป็นต้นครับ&lt;br /&gt;&lt;br /&gt;ทีนี้มาลองดูการออกรายงานด้วย Tab Delimited ละกันครับ ผมเริ่มจากสร้าง Console Application มาตัวหนึ่ง แล้วก็ Add Reference ของ Microsoft Excel 12.0 Object Library ครับ&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_N-Phs4y2L44/S7BjFTpJJUI/AAAAAAAAAFc/o8e-exchRkY/s1600/j1.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 334px;" src="http://1.bp.blogspot.com/_N-Phs4y2L44/S7BjFTpJJUI/AAAAAAAAAFc/o8e-exchRkY/s400/j1.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5453968091569005890" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;จากนั้นก็สร้างข้อมูลสำหรับทดสอบ โดยสร้าง Employee Class ขึนมา และ EmployeeManager Class สำหรับ สร้าง List ของ Employee ขนาด 100,000 ข้อมูลครับ&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Public Class Employee&lt;br /&gt;    Public EmployeeID As String&lt;br /&gt;    Public FirstName As String&lt;br /&gt;    Public LastName As String&lt;br /&gt;    Public BirthDay As Date&lt;br /&gt;    Public Salary As Double&lt;br /&gt;End Class&lt;br /&gt;&lt;br /&gt;Public Class EmployeeManager&lt;br /&gt;    Public Shared Function GetEmployees() As IList(Of Employee)&lt;br /&gt;        Dim employees As New List(Of Employee)&lt;br /&gt;        Dim emp As Employee = Nothing&lt;br /&gt;        For iRecord As Integer = 0 To 100000&lt;br /&gt;            emp = New Employee With {.EmployeeID = iRecord, .FirstName = "Test" &amp;  iRecord, .LastName = "LName", .BirthDay = DateSerial(1990, 1, 1), .Salary = 2000.0 + CDbl(iRecord)}&lt;br /&gt;            employees.Add(emp)&lt;br /&gt;        Next&lt;br /&gt;        Return employees&lt;br /&gt;    End Function&lt;br /&gt;End Class&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ทีนี้ก็มาเขียนโค้ดสำหรับการทดสอบกันครับ&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Module Module1&lt;br /&gt;    Const EU_DATE_FORMAT As String = "dd-MM-yyyy hh:mm:ss"&lt;br /&gt;    Dim employees As IList(Of Employee)&lt;br /&gt;    Dim startDate As Date = Now&lt;br /&gt;    Dim endDate As Date = Nothing&lt;br /&gt;&lt;br /&gt;    Private Sub DecorateExcel(ByVal xlsBook As Microsoft.Office.Interop.Excel.Workbook)&lt;br /&gt;&lt;br /&gt;        Dim xlsSheet As Microsoft.Office.Interop.Excel.Worksheet = Nothing&lt;br /&gt;        xlsSheet = xlsBook.ActiveSheet&lt;br /&gt;        xlsSheet.Range("A:E").EntireColumn.AutoFit()&lt;br /&gt;&lt;br /&gt;        With xlsSheet.Range("A1").Font&lt;br /&gt;            .Bold = True&lt;br /&gt;            .Size = "14"&lt;br /&gt;        End With&lt;br /&gt;&lt;br /&gt;        With xlsSheet.Range("A2").Font&lt;br /&gt;            .Bold = True&lt;br /&gt;            .Size = "12"&lt;br /&gt;        End With&lt;br /&gt;&lt;br /&gt;        With xlsSheet.Range("A3:E3")&lt;br /&gt;            .Interior.Color = System.Drawing.Color.Silver.ToArgb&lt;br /&gt;            .Font.Bold = True&lt;br /&gt;        End With&lt;br /&gt;&lt;br /&gt;        xlsSheet.Range("E:E").NumberFormat = "#,##0.00"&lt;br /&gt;&lt;br /&gt;        Dim r As Microsoft.Office.Interop.Excel.Range&lt;br /&gt;        r = xlsSheet.Range("A3:E20004")&lt;br /&gt;&lt;br /&gt;        With r.Borders&lt;br /&gt;            .LineStyle = Microsoft.Office.Interop.Excel.XlLineStyle.xlContinuous&lt;br /&gt;            .Weight = Microsoft.Office.Interop.Excel.XlBorderWeight.xlThin&lt;br /&gt;            .ColorIndex = 0&lt;br /&gt;        End With&lt;br /&gt;&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Private Sub ExportByTAB()&lt;br /&gt;        startDate = Now&lt;br /&gt;        Console.WriteLine("Export by Tab delimited")&lt;br /&gt;        Console.WriteLine("Start at " &amp; startDate.ToString(EU_DATE_FORMAT))&lt;br /&gt;&lt;br /&gt;        Dim _fileNameTAB As String = "C:\test.txt"&lt;br /&gt;        Dim _fileName As String = "c:\testTAB.xlsx"&lt;br /&gt;&lt;br /&gt;        Dim fs As System.IO.FileStream = System.IO.File.Create(_fileNameTAB)&lt;br /&gt;        Dim writer As New System.IO.StreamWriter(fs)&lt;br /&gt;&lt;br /&gt;        writer.WriteLine("List of Employee")&lt;br /&gt;        writer.WriteLine("As of " &amp; Now.ToString("dd MMM yyyy"))&lt;br /&gt;        writer.WriteLine("Employee ID" &amp; vbTab &amp; "First Name" &amp; vbTab &amp; "Last Name" &amp; vbTab &amp; "Birth Date" &amp; vbTab &amp; "Salary")&lt;br /&gt;&lt;br /&gt;        For Each emp As Employee In employees&lt;br /&gt;            writer.WriteLine(String.Concat(emp.EmployeeID, vbTab, emp.FirstName, vbTab, emp.LastName, vbTab, emp.BirthDay.ToString("yyyy-MM-dd"), vbTab, emp.Salary))&lt;br /&gt;        Next&lt;br /&gt;&lt;br /&gt;        writer.Close()&lt;br /&gt;        writer.Dispose()&lt;br /&gt;        fs.Close()&lt;br /&gt;        fs.Dispose()&lt;br /&gt;&lt;br /&gt;        ' Open txt file with Excel for decoration&lt;br /&gt;        Dim xlsAPP As New Microsoft.Office.Interop.Excel.Application&lt;br /&gt;        Dim xlsBook As Microsoft.Office.Interop.Excel.Workbook&lt;br /&gt;        xlsAPP.Visible = False&lt;br /&gt;        xlsBook = xlsAPP.Workbooks.Open(_fileNameTAB)&lt;br /&gt;        DecorateExcel(xlsBook)&lt;br /&gt;&lt;br /&gt;        ' Save as Excel2007&lt;br /&gt;        If System.IO.File.Exists(_fileName) Then System.IO.File.Delete(_fileName)&lt;br /&gt;        xlsBook.SaveAs(_fileName, Microsoft.Office.Interop.Excel.XlFileFormat.xlOpenXMLWorkbook)&lt;br /&gt;        xlsBook.Close(SaveChanges:=False)&lt;br /&gt;&lt;br /&gt;        xlsAPP.Quit()&lt;br /&gt;&lt;br /&gt;        System.Runtime.InteropServices.Marshal.ReleaseComObject(xlsAPP)&lt;br /&gt;        GC.SuppressFinalize(xlsAPP)&lt;br /&gt;        GC.Collect()&lt;br /&gt;        xlsBook = Nothing&lt;br /&gt;        xlsAPP = Nothing&lt;br /&gt;&lt;br /&gt;        System.IO.File.Delete(_fileNameTAB)&lt;br /&gt;&lt;br /&gt;        endDate = Now&lt;br /&gt;        Console.WriteLine("Finish at " &amp; endDate.ToString(EU_DATE_FORMAT))&lt;br /&gt;        Console.WriteLine("Total " &amp; (endDate - startDate).TotalMilliseconds &amp; " milliseconds")&lt;br /&gt;        Console.WriteLine("***********************")&lt;br /&gt;&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Sub Main()&lt;br /&gt;&lt;br /&gt;        employees = EmployeeManager.GetEmployees()&lt;br /&gt;        Console.WriteLine("Testing for " &amp; employees.Count - 1 &amp; " records.")&lt;br /&gt;        Console.WriteLine("")&lt;br /&gt;        ExportByTAB()&lt;br /&gt;        Console.WriteLine("Finish test.")&lt;br /&gt;        Console.ReadLine()&lt;br /&gt;&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;End Module&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ลองรันผลทดสอบดูครับ เครื่องผม Pentium 4 2.66GHz RAM 2 GB ข้อมูลขนาด 100,000 เรคคอร์ดใช้เวลา 7 วินาทีกว่าๆ&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_N-Phs4y2L44/S7BmV8acaeI/AAAAAAAAAFk/1xpwOKOvdzU/s1600/j2.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 244px;" src="http://2.bp.blogspot.com/_N-Phs4y2L44/S7BmV8acaeI/AAAAAAAAAFk/1xpwOKOvdzU/s400/j2.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5453971675925998050" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ข้อมูลขนาด 5,000 เรคอร์ด ใช้เวลา 2.1 วินาที&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_N-Phs4y2L44/S7BpPHpDdBI/AAAAAAAAAF0/qtxTdMFC71M/s1600/j3.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 333px; height: 211px;" src="http://2.bp.blogspot.com/_N-Phs4y2L44/S7BpPHpDdBI/AAAAAAAAAF0/qtxTdMFC71M/s400/j3.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5453974857215865874" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ลองดูผลลัพธ์ครับ&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_N-Phs4y2L44/S7BpkzMnG2I/AAAAAAAAAF8/4LM-m1-Dqe4/s1600/j4.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 294px;" src="http://1.bp.blogspot.com/_N-Phs4y2L44/S7BpkzMnG2I/AAAAAAAAAF8/4LM-m1-Dqe4/s400/j4.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5453975229684980578" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;เรียบร้อยครับ ทีนี้ถ้าเราอยากจัด Layout สวยๆ เราสามารถเปิด Excel แล้วสั่งบันทึก Macro แล้วค่อยเอา source code ที่ได้ไปใส่เพิ่มใน DecorateExcel Function ครับ&lt;br /&gt;&lt;br /&gt;ปล.&lt;br /&gt;1. ผมลอง export โดยใช้ CSV ก็ได้ความเร็วไม่ต่างกันมากครับ&lt;br /&gt;2. ผมลองวนลูปเก็บข้อมูลเข้า StringBuilder ก่อน แล้วค่อยเขียนไฟล์ ปรากฏว่าใช้เวลามากกว่าเปิดไฟล์แล้ววนลูปเขียนเข้าไปตรงๆเลยครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-7805310975760533320?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/7805310975760533320/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=7805310975760533320' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/7805310975760533320'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/7805310975760533320'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2010/03/vbnet-excel.html' title='VB.NET ออกรายงาน Excel แบบรวดเร็ว'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_N-Phs4y2L44/S7BjFTpJJUI/AAAAAAAAAFc/o8e-exchRkY/s72-c/j1.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-489368301231164077</id><published>2010-03-23T21:32:00.003+07:00</published><updated>2010-07-08T19:20:35.583+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WebServices'/><category scheme='http://www.blogger.com/atom/ns#' term='VB6'/><title type='text'>VB6 ติดต่อ ASP.NET WebService</title><content type='html'>ช่วงนี้มีโอกาสกลับไปแก้โปรแกรมเก่าที่เขียนด้วย VB6 ครับ และแน่นอนงานที่แก้ก็คือให้โปรแกรมมันทำงานกับ WebServices ได้นั่นเอง หลังจากลองๆค้นไปค้นมา ก็เลยทำโปรแกรมทดสอบมาก่อนครับ&lt;br /&gt;&lt;br /&gt;เริ่มต้นที่ ASP.NET WebServices กันก่อน แรกสุดก็สร้าง Employee Class ครับ&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;&amp;lt;Serializable()&amp;gt; _&lt;br /&gt;&amp;lt;System.ComponentModel.DefaultProperty("EmployeeID")&amp;gt; _&lt;br /&gt;Public Class Employee&lt;br /&gt;&lt;br /&gt;    Private _employeeId As String&lt;br /&gt;    Private _employeeName As String&lt;br /&gt;    Private _employeeSurnam As String&lt;br /&gt;&lt;br /&gt;    Public Property EmployeeSurname() As String&lt;br /&gt;        Get&lt;br /&gt;            Return _employeeSurnam&lt;br /&gt;        End Get&lt;br /&gt;        Set(ByVal value As String)&lt;br /&gt;            _employeeSurnam = value&lt;br /&gt;        End Set&lt;br /&gt;    End Property&lt;br /&gt;&lt;br /&gt;    Public Property EmployeeName() As String&lt;br /&gt;        Get&lt;br /&gt;            Return _employeeName&lt;br /&gt;        End Get&lt;br /&gt;        Set(ByVal value As String)&lt;br /&gt;            _employeeName = value&lt;br /&gt;        End Set&lt;br /&gt;    End Property&lt;br /&gt;&lt;br /&gt;    Public Property EmployeeId() As String&lt;br /&gt;        Get&lt;br /&gt;            Return _employeeId&lt;br /&gt;        End Get&lt;br /&gt;        Set(ByVal value As String)&lt;br /&gt;            _employeeId = value&lt;br /&gt;        End Set&lt;br /&gt;    End Property&lt;br /&gt;&lt;br /&gt;    Public Overrides Function ToString() As String&lt;br /&gt;        Return EmployeeId&lt;br /&gt;    End Function&lt;br /&gt;&lt;br /&gt;    Public Function Save() As Boolean&lt;br /&gt;        Return True&lt;br /&gt;    End Function&lt;br /&gt;&lt;br /&gt;End Class&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ที่จริงมี EmployeeManager Class ด้วย แต่ว่าขี้เกียจเอามาลงให้ดู ทีนี้ก็มาสร้าง WebServices กันครับ&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Imports System.Web&lt;br /&gt;Imports System.Web.Services&lt;br /&gt;Imports System.Web.Services.Protocols&lt;br /&gt;&lt;br /&gt;&amp;lt;WebService(Namespace:="http://jnithi/")&amp;gt; _&lt;br /&gt;&amp;lt;WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)&amp;gt; _&lt;br /&gt;&amp;lt;Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()&amp;gt; _&lt;br /&gt;Public Class EmployeeWS&lt;br /&gt;    Inherits System.Web.Services.WebService&lt;br /&gt;&lt;br /&gt;    &amp;lt;WebMethod()&amp;gt; _&lt;br /&gt;    Public Function GetEmployee(ByVal empID As String) As Employee&lt;br /&gt;        Dim emp As Employee = Nothing&lt;br /&gt;        emp = EmployeeManager.GetEmployeeByID(empID)&lt;br /&gt;        Return emp&lt;br /&gt;    End Function&lt;br /&gt;&lt;br /&gt;    &amp;lt;WebMethod()&amp;gt; _&lt;br /&gt;    Public Function UpdateEmployee(ByVal empID As String, ByVal empName As String, ByVal empSurname As String) As Boolean&lt;br /&gt;        Dim emp As Employee = Nothing&lt;br /&gt;        Dim result As Boolean = False&lt;br /&gt;        emp = EmployeeManager.GetEmployeeByID(empID)&lt;br /&gt;        emp.EmployeeName = empName&lt;br /&gt;        emp.EmployeeSurname = empSurname&lt;br /&gt;        result = emp.Save&lt;br /&gt;        Return result&lt;br /&gt;    End Function&lt;br /&gt;&lt;br /&gt;End Class&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ลองรัน WebServices ดูครับ ไปที่ http://nithi/TestWeb/EmployeeWS.asmx จะเห็นว่ามี Service 2 ตัว ตามที่เราสร้างไว้ครับ กดเลือก GetEmployee เพื่อดู Service Description&lt;br /&gt;&lt;br /&gt;ดูที่ SOAP 1.1 ครับ เดี๋ยวเราจะนำข้อมูลตรงนี้ไปใช้ในการเรียก WebService&lt;br /&gt;&lt;br /&gt;ส่วนของ SOAP Request&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_N-Phs4y2L44/S6idU-WnBEI/AAAAAAAAAE8/OO5o0iLVzmo/s1600-h/WS1.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 296px;" src="http://1.bp.blogspot.com/_N-Phs4y2L44/S6idU-WnBEI/AAAAAAAAAE8/OO5o0iLVzmo/s400/WS1.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5451780332592759874" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ส่วนของ SOAP Response&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_N-Phs4y2L44/S6iddV3W7zI/AAAAAAAAAFE/FCVBkPhMbxE/s1600-h/WS2.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 296px;" src="http://2.bp.blogspot.com/_N-Phs4y2L44/S6iddV3W7zI/AAAAAAAAAFE/FCVBkPhMbxE/s400/WS2.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5451780476343086898" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;คราวนี้มาที่ VB6 บ้าง เริ่มจากสร้าง Project ใหม่ จากนั้นก็สร้าง Class ชื่อ clsEmployee ครับ ให้มี Property 3 ตัวเหมือนฝั่ง .NET ทีนี้ก็มาสร้าง Class สำหรับจัดการ WebService ครับ ตั้งชื่อ clsEmployeeWS ก่อนอื่นก็ Add Reference ก่อนครับ เลือก Microsoft XML, v4.0 (ผมลองตั้งแต่ v 2.6 ก็ใช้ได้หมดครับ เลยเลือกอันกลางๆ)แล้วก็เขียนโค้ดได้เลย&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Dim asmxURL As String&lt;br /&gt;Dim soapAction As String&lt;br /&gt;Dim soapEnvelop As String&lt;br /&gt;Dim domResult As MSXML2.DOMDocument&lt;br /&gt;&lt;br /&gt;Private Function GetResponse(URL As String, action As String, envelop As String) As MSXML2.DOMDocument&lt;br /&gt;    Dim xmlHTTP As New MSXML2.xmlHTTP&lt;br /&gt;    Dim domReturn As New MSXML2.DOMDocument&lt;br /&gt;    Dim domDOC As New MSXML2.DOMDocument&lt;br /&gt;    &lt;br /&gt;    domDOC.loadXML envelop&lt;br /&gt;    &lt;br /&gt;    xmlHTTP.Open "POST", URL, False&lt;br /&gt;    &lt;br /&gt;    ' Create headings&lt;br /&gt;    xmlHTTP.setRequestHeader "Content-Type", "text/xml; charset=utf-8"&lt;br /&gt;    xmlHTTP.setRequestHeader "SOAPAction", action&lt;br /&gt;    &lt;br /&gt;    ' Send&lt;br /&gt;    xmlHTTP.send domDOC.xml&lt;br /&gt;    domReturn.loadXML xmlHTTP.responseText&lt;br /&gt;   &lt;br /&gt;    &lt;br /&gt;    Set xmlHTTP = Nothing&lt;br /&gt;    Set domDOC = Nothing&lt;br /&gt;    Set GetResponse = domReturn&lt;br /&gt;End Function&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ผมสร้าง Function ชื่อ GetResponse ไว้สำหรับติดต่อกับ WebServices ครับ Function นี้จะคืนค่าเป็น XMLDOMDocument มาให้ครับ ถ้าเราเรียก WebServices สำเร็จ ลอง debug ดูค่า domReturn.xml เพื่อดูผลลัพธ์ได้ครับ &lt;br /&gt;&lt;br /&gt;เสร็จแล้วก็สร้าง Function สำหรับเรียกใช้งาน WebServices ครับ โดยใน GetEmployee Function เราจะระบุ parameter ที่ต้องการได้แก่ asmxURL, soapAction (ดูจาก SOAP Request ในรูปด้านบนครับ) แล้วก็ Envelop (copy มาจาก SOAP Request เหมือนกัน) แต่อย่าลืมเปลี่ยน parameter นะครับ ตรงบรรทัด &lt;empID&gt;" &amp; empID &amp; "&lt;/empID&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Public Function GetEmployee(empID) As clsEmployee&lt;br /&gt;    asmxURL = "http://nithi/testweb/EmployeeWS.asmx"&lt;br /&gt;    soapAction = "http://jnithi/GetEmployee"&lt;br /&gt;    soapEnvelop = "&lt;?xml version=""1.0"" encoding=""utf-8""?&gt; " &amp; _&lt;br /&gt;                  "     &lt;soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/""&gt; " &amp; _&lt;br /&gt;                  "         &lt;soap:Body&gt; " &amp; _&lt;br /&gt;                  "             &lt;GetEmployee xmlns=""http://jnithi/""&gt;" &amp; _&lt;br /&gt;                  "                 &lt;empID&gt;" &amp; empID &amp; "&lt;/empID&gt; " &amp; _&lt;br /&gt;                  "             &lt;/GetEmployee&gt; " &amp; _&lt;br /&gt;                  "         &lt;/soap:Body&gt; " &amp; _&lt;br /&gt;                  "     &lt;/soap:Envelope&gt; "&lt;br /&gt;    Set domResult = GetResponse(asmxURL, soapAction, soapEnvelop)&lt;br /&gt;    &lt;br /&gt;    Dim oNode As MSXML2.IXMLDOMNode&lt;br /&gt;    Dim oNodes As IXMLDOMNodeList&lt;br /&gt;&lt;br /&gt;    'ดึง &lt;GetEmployeeResult&gt;...&lt;/GetEmployeeResult&gt; ผลลัพธ์ได้เป็น List (XMLDOMNodeList)&lt;br /&gt;    Set oNodes = domResult.getElementsByTagName("GetEmployeeResult")&lt;br /&gt;    Dim employee As clsEmployee&lt;br /&gt;&lt;br /&gt;    If oNodes.length &gt; 0 Then&lt;br /&gt;        Set oNode = oNodes.Item(0)&lt;br /&gt;        Set employee = New clsEmployee&lt;br /&gt;        employee.EmployeeID = oNode.selectSingleNode("EmployeeId").Text&lt;br /&gt;        employee.EmployeeName = oNode.selectSingleNode("EmployeeName").Text&lt;br /&gt;        employee.EmployeeSurname = oNode.selectSingleNode("EmployeeSurname").Text&lt;br /&gt;    End If&lt;br /&gt;    Set GetEmployee = employee&lt;br /&gt;End Function&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;คราวนี้มาทำหน้าจอทดสอบครับ&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_N-Phs4y2L44/S6ihNvtVXfI/AAAAAAAAAFM/HmC9lW1SoJQ/s1600-h/WS3.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 291px; height: 183px;" src="http://2.bp.blogspot.com/_N-Phs4y2L44/S6ihNvtVXfI/AAAAAAAAAFM/HmC9lW1SoJQ/s400/WS3.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5451784606448967154" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Private Sub ClearEmployee()&lt;br /&gt;        Me.txtEmpID.Text = ""&lt;br /&gt;        Me.txtEmpName.Text = ""&lt;br /&gt;        Me.txtEmpSurname.Text = ""&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;Private Sub cmdClear_Click()&lt;br /&gt;    ClearEmployee&lt;br /&gt;    Me.txtSearchID.Text = ""&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt;Private Sub cmdSearch_Click()&lt;br /&gt;    Dim ws As New clsEmployeeWS&lt;br /&gt;    Dim employee As clsEmployee&lt;br /&gt;&lt;br /&gt;    'เรียก clsEmployeeWS ให้ติดต่อ WebServices&lt;br /&gt;    Set employee = ws.GetEmployee(Me.txtSearchID.Text)&lt;br /&gt;    If employee Is Nothing = False Then&lt;br /&gt;        Me.txtEmpID.Text = employee.EmployeeID&lt;br /&gt;        Me.txtEmpName.Text = employee.EmployeeName&lt;br /&gt;        Me.txtEmpSurname.Text = employee.EmployeeSurname&lt;br /&gt;    Else&lt;br /&gt;        ClearEmployee&lt;br /&gt;        MsgBox "Cannot find employee.", vbInformation + vbOKOnly, App.Title&lt;br /&gt;    End If&lt;br /&gt;End Sub&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ลองรันดูผลลัพธ์ครับ&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_N-Phs4y2L44/S6ihvKD8UOI/AAAAAAAAAFU/-m35j_TXiMI/s1600-h/WS4.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 291px; height: 183px;" src="http://3.bp.blogspot.com/_N-Phs4y2L44/S6ihvKD8UOI/AAAAAAAAAFU/-m35j_TXiMI/s400/WS4.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5451785180458799330" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;เรียบร้อย ไม่ยากครับ&lt;br /&gt;&lt;br /&gt;จริงๆตอนทดสอบ ผมยังลองทำการ Insert, Update, Delete แล้วก็ SELECT ข้อมูลเป็น Array มาด้วย แต่คงไม่ได้เอามาลง Blog ให้ดูครับ เพราะถ้ารู้ concept แล้วที่เหลือก็ไม่ยากแล้วครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-489368301231164077?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/489368301231164077/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=489368301231164077' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/489368301231164077'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/489368301231164077'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2010/03/vb6-aspnet-webservice.html' title='VB6 ติดต่อ ASP.NET WebService'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_N-Phs4y2L44/S6idU-WnBEI/AAAAAAAAAE8/OO5o0iLVzmo/s72-c/WS1.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-6993865729881115591</id><published>2010-03-09T10:38:00.003+07:00</published><updated>2010-03-09T11:01:31.356+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Talk'/><title type='text'>ลอง Chrome และ FireFox 3.6</title><content type='html'>ผมเคย download หมาย่างมาลองใช้ตั้งแต่เวอร์ชัน 1.x จนล่าสุดในเครื่องเป็นเวอร์ชัน 2.x แต่แค่ลองคลำๆดูก็เลิกกลับมาใช้ IE เหมือนเดิม เพราะว่าใช้ FF แล้วเข้าไป Pantip แล้ว font มันไม่สวย ตัวเล็ก อ่านยากกกกก และเวบไทยหลายๆเวบแนะนำให้ใช้ IE มากกว่า&lt;br /&gt;&lt;br /&gt;จนเมื่อวานนี้ได้ลองดาวน์โหลด Chrome มาใช้ มันเยี่ยมมาก เร็วและดี เลยลอง search ใน google เรื่อง Browser ต่างๆเพิ่มเติม จนเจอว่า FF มันมี option ให้ set font ได้ (จริงๆถ้าตั้งใจเล่นก็น่าจะ set เองได้ตั้งนานแล้ว มัวแต่ติด IE) พอลอง set ตามและเข้า Pantip และเวบอื่นๆที่เล่นบ่อยๆดู โอ้ว จอร์จ ไม่น่าโง่ตั้งนาน ก็เลยรีบไปดาวน์โหลด FF3.6 ภาษาไทยที่ &lt;a href = "http://www.mozilla.com/th/"&gt;http://www.mozilla.com/th/&lt;/a&gt; และก็ติดตั้ง Extension พวก FireBug, WebDeveloper มาลองเล่นดู เพราะเคยอ่านเจอใน Textbook ครับ อยากลองเล่นมานานแล้วแต่ขี้เกียจลง FF&lt;br /&gt;&lt;br /&gt;เมื่อคืนก็เลยลอง Chrome กับ FF อยู่ 3-4 ชั่วโมง สรุปว่าส่วนตัวผมชอบ Chrome มากกว่า ตอนนี้เลยใช้ Chrome เป็นหลัก ยกเว้นเข้าบางเวบก็จะใช้ IE ครับ ส่วน FF ก็ชอบอยู่นะก็จะลองใช้สลับกับ Chrome ดูครับ&lt;br /&gt;&lt;br /&gt;ว่างๆจะลองโหลด IE8 กับ Opera มาเล่นดูบ้าง ไม่ได้เล่น Opera มานานมากแล้ว &lt;br /&gt;&lt;br /&gt;ปล. วันนี้ป่วยลางานอยู่บ้านครับ เพราะเป็นฝีฝักบัวโดนหมอผ่าก้อนฝีออกเท่ามะนาวลูกย่อมๆ (หมอบอก เพราะผมหลับตาปี๋ไม่กล้ามอง) หมอยังไม่เย็บแผลเลย ตอนนี้เอาผ้าก็อซใหญ่กว่าหัวแม่โป้ง (อันนี้เห็นตอนทำแผล) ยัดเข้าไปแล้วปิดแผลไว้ รอดูอาการถ้าดีขึ้นถึงจะเย็บแผล ระหว่างพิมพ์ blog ยังมีรูโบ๋อยู่ที่หน้าท้องอยู่เลย 555&lt;br /&gt;&lt;br /&gt;ปล.2 ตอนทำความสะอาดแผล เจ็บโครตๆๆๆ แต่ยังน่าจะน้อยกว่าผู้หญิงคลอดลูกเยอะ T_T ดังนั้นชาติหน้าขอเกิดเป็นผู้ชายอีกนะคร้าบบบบ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-6993865729881115591?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/6993865729881115591/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=6993865729881115591' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6993865729881115591'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6993865729881115591'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2010/03/chrome-firefox-36.html' title='ลอง Chrome และ FireFox 3.6'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-7026396327011799728</id><published>2010-02-24T12:10:00.004+07:00</published><updated>2010-07-11T00:14:10.563+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VB.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>ส่ง URL Parameter ให้ ClickOnce Application</title><content type='html'>ผมเคยเขียน blog เรื่อง &lt;a href="http://jnithi.blogspot.com/2009/12/aspnet-win-app-client.html"ASP.NET&gt; สั่งรัน Win App บนเครื่อง Client&lt;/a&gt; เมื่อเดือนธันวาคม 2009 แล้วก็ติดเรื่องส่ง paramenter ให้ ClickOnce application เอาไว้ครับ วันนี้มีโอกาสก็เลยมาเขียนเรื่องนี้ซะเลย&lt;br /&gt;&lt;br /&gt;ก่อนอื่นเราก็มาสร้าง Console Application สำหรับการทดสอบซะก่อนครับ จากนั้นก็ Add .NET Reference เข้ามาในโปรเจคเรา 2 ตัว คือ System.Deployment กับ System.Web ครับ แล้วก็เขียนโค้ด Import NameSpace ที่จำเป็นเข้าในด้วย&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Imports System.Collections.Specialized&lt;br /&gt;Imports System.Deployment.Application&lt;br /&gt;Imports System.Web&lt;br /&gt;&lt;br /&gt;Module Module1&lt;br /&gt;&lt;br /&gt;    Sub Main()&lt;br /&gt;        Dim params = GetURLParameter()&lt;br /&gt;        If params.Count &gt; 0 Then&lt;br /&gt;            For i As Integer = 0 To params.Count - 1&lt;br /&gt;                Console.WriteLine("Parameter {0} is {1}", params.Keys(i), params.Item(i))&lt;br /&gt;            Next&lt;br /&gt;        Else&lt;br /&gt;            Console.WriteLine("No URL Parameter")&lt;br /&gt;        End If&lt;br /&gt;        Console.WriteLine("Press any key to quit.")&lt;br /&gt;        Console.ReadLine()&lt;br /&gt;&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Private Function GetURLParameter() As NameValueCollection&lt;br /&gt;        Dim NameValueTable As New NameValueCollection&lt;br /&gt;&lt;br /&gt;        If (My.Application.IsNetworkDeployed) Then&lt;br /&gt;            Dim QueryString As String = ApplicationDeployment.CurrentDeployment.ActivationUri.Query&lt;br /&gt;            NameValueTable = HttpUtility.ParseQueryString(QueryString)&lt;br /&gt;        End If&lt;br /&gt;&lt;br /&gt;        Return NameValueTable&lt;br /&gt;    End Function&lt;br /&gt;&lt;br /&gt;End Module&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;โค้ดที่ใช้ทดสอบมีเท่านี้ ไม่ยากใช่ไหมครับ แต่ที่สำคัญอีกอย่างหนึ่งก็คือ เราต้องกำหนดให้ ClickOnce Application ของเราสามารถรับ URL Parameters ได้ก่อน โดยไปที่ Property ของโปรเจค แล้วเลือก Publish Tab ครับ&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_N-Phs4y2L44/S4Sw4jf1Q0I/AAAAAAAAAEc/CHuu-s46uiA/s1600-h/1.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 234px;" src="http://2.bp.blogspot.com/_N-Phs4y2L44/S4Sw4jf1Q0I/AAAAAAAAAEc/CHuu-s46uiA/s400/1.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5441668735418319682" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ทีนี้เราจะเห็นปุ่ม OPTIONS กดเข้าไปได้เลยครับ&lt;br /&gt;&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_N-Phs4y2L44/S4Sw-sbWOBI/AAAAAAAAAEk/GCy_FGNMSIQ/s1600-h/2.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 244px;" src="http://3.bp.blogspot.com/_N-Phs4y2L44/S4Sw-sbWOBI/AAAAAAAAAEk/GCy_FGNMSIQ/s400/2.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5441668840894642194" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;แล้วก็ทำการเลือก Manifests และคลิ๊กเครื่องหมายถูกตรง Allow URL parameters&lt;br /&gt;&lt;br /&gt;เสร็จแล้วก็ทำการ Publish ครับ ทีนี้เรามาลองทดสอบเรียก ClickOnce Application แบบไม่ส่ง parameter ก่อน&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_N-Phs4y2L44/S4SxEtZo8TI/AAAAAAAAAEs/WzyadA4UcRI/s1600-h/3.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 255px;" src="http://1.bp.blogspot.com/_N-Phs4y2L44/S4SxEtZo8TI/AAAAAAAAAEs/WzyadA4UcRI/s400/3.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5441668944235131186" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;โปรแกรมขึ้นว่า No parameter ถูกต้อง คราวนี้ลองทดสอบแบบส่ง QueryString ไปด้วย&lt;br /&gt;(http://nithi/COParameter/CoParameter.application?param1=test1&amp;amp;param2=test2&amp;amp;name=nithi)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_N-Phs4y2L44/S4SxL5ZL0oI/AAAAAAAAAE0/WRVAmDKltf0/s1600-h/4.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 213px;" src="http://1.bp.blogspot.com/_N-Phs4y2L44/S4SxL5ZL0oI/AAAAAAAAAE0/WRVAmDKltf0/s400/4.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5441669067713532546" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;เรียบร้อยครับ คราวนี้เราก็สามารถส่ง URL Parameter ไปให้ Application ของเราได้แล้วครับ &lt;br /&gt;&lt;br /&gt;Reference:&lt;br /&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/ms172242.aspx"&gt;How to: Retrieve Query String Information in a ClickOnce Application&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-7026396327011799728?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/7026396327011799728/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=7026396327011799728' title='1 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/7026396327011799728'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/7026396327011799728'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2010/02/parameter-clickonce-application.html' title='ส่ง URL Parameter ให้ ClickOnce Application'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_N-Phs4y2L44/S4Sw4jf1Q0I/AAAAAAAAAEc/CHuu-s46uiA/s72-c/1.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-7751436398642219671</id><published>2010-02-22T21:50:00.001+07:00</published><updated>2010-02-22T21:50:00.641+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Talk'/><title type='text'>สถิติของ blog จาก Google Analytics</title><content type='html'>หลังจากผมได้ลองใช้ Google Analytics มาประมาณ 6 เดือน พอจะสรุปผลคร่าวๆดังนี้ครับ&lt;br /&gt;&lt;br /&gt;1. ผู้เยี่ยมชมส่วนใหญ่มาจาก google ครับ ประมาณ 75%&lt;br /&gt;2. เนื้อหายอดนิยมได้แก่ Crystal Report, RDLC, ITextSharp, jQuery น่าจะมาจากการค้นหาใน google &lt;br /&gt;3. 30 วันแรกของ Blog ผู้เยียมชมโดยตรงมี 78 page view คิดเป็นอัตราส่วนประมาณ 11% ส่วนเดือนล่าสุดเพิ่มเป็น 281 (19.18%) &lt;br /&gt;4. วันที่คนมีอัตราเยี่ยมชมสูงสุดคือวันพฤหัส ตอนนี้ pageview สูงสุดอยู่ที่ 72 pv ต่อวันครับ (ยังน้อยอยู่เทียบกับบาง blog ที่มีผู้เยี่ยมชมหลักพัน) วันที่คนเยี่ยมชมน้อยสุดคือวันอาทิตย์ ประมาณ 15-20 pv รวม 30 วันล่าสุดอยู่ที่ 1,465 pv &lt;br /&gt;5. อัตราการเข้าชมโดยตรง ผมแปลกใจมากเพราะอันดับ 1 มาจาก CodeToday.net ครับ (จำได้ว่าผมไป post url ของ blog แค่ครั้งเดียว) อยู่ที่ 2.8% อันดับสองคือ blogger อยู่ที่ 1.57% ส่วน greatfriends อยู่ที่อันดับ 3 ที่ 0.89% &lt;br /&gt;&lt;br /&gt;จากที่ได้ลองใช้มา รู้สึกว่า Google Analytics นี่ใช้ง่ายและเครื่องมือเยอะดีครับ แต่ยังต้องศึกษาอีกเยอะทีเดียว เดี๋ยวรอครบ 1 ปี จะมาลองดูใหม่ว่าอัตราการเยี่ยมชมมีอะไรเปลี่ยนแปลงบ้างครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-7751436398642219671?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/7751436398642219671/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=7751436398642219671' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/7751436398642219671'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/7751436398642219671'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2010/02/blog-google-analytics.html' title='สถิติของ blog จาก Google Analytics'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-5241111873185999501</id><published>2010-02-21T00:46:00.002+07:00</published><updated>2010-07-11T00:15:11.054+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET AJAX'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>ASP.NET สั่งเคลียร์ Session เมื่อเปลี่ยน Page</title><content type='html'>วันนี้เจอคำถามที่ greatfriends ว่าเมื่อ User ปิด Browser หรือเปลี่ยน Page เราต้องการไปเคลียร์ Session ของ Page เดิมจะทำยังไงดี &lt;br /&gt;&lt;br /&gt;ถ้าเป็นเรื่องปิด Browser หรือว่า User ไปเปิดเวบอื่นคงไม่มีปัญหาครับ เพราะ ASP.NET มันจะเคลียร์ค่าให้เมื่อ Session Time Out อยู่แล้ว ซึ่งเราก็สามารถเขียนโค้ดใน Session_End ที่ Global.asax เพิ่มเติมก็ได้ แต่ปัญหาคือถ้า user เปลี่ยนหน้า เราจะทำยังไงดี สิ่งแรกที่ผมคิดคือ เรากำหนดให้มัน Post Back กลับมาหน้าเดิมก่อน เพื่อสั่ง Clear Session จากนั้นเราค่อยสั่ง Navigate ไปยังหน้าที่ต้องการอีกที&lt;br /&gt;&lt;br /&gt;เช่นสมมติ เรามี User Control 1 ตัว เป็นเมนูสำหรับ Application เราใช้ &amp;lt;asp: Menu&amp;gt; และสร้าง &amp;lt;asp:MenuItem&amp;gt; เพื่อระบุเมนูย่อย แทนที่เราจะใช้ NavigateUrl Attribute เพื่อสั่ง Redirect ไปหน้าใหม่ทันที่ เราก็มาเขียนโค้ดที่ MenuItemClick Event แทนครับ &lt;br /&gt;&lt;br /&gt;พอตอนเย็นก็นึกวิธีใหม่ออก ก็เลยมาลองดูครับ วิธีนี้คือ เราใช้ ASP.NET AJAX มาเรียก WebService เพื่อทำการ Clear Session นั่นเองครับ ลองมาดูโค้ดของ WebService กันก่อน&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Imports System.Web&lt;br /&gt;Imports System.Web.Services&lt;br /&gt;Imports System.Web.Services.Protocols&lt;br /&gt;&lt;br /&gt;Namespace jNithi.WS&lt;br /&gt;    &amp;lt;System.Web.Script.Services.ScriptService()&amp;gt; _&lt;br /&gt;    &amp;lt;WebService(Namespace:="http://tempuri.org/")&amp;gt; _&lt;br /&gt;    &amp;lt;WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)&amp;gt; _&lt;br /&gt;    &amp;lt;Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()&amp;gt; _&lt;br /&gt;    Public Class SessionManager&lt;br /&gt;        Inherits System.Web.Services.WebService&lt;br /&gt;&lt;br /&gt;        &amp;lt;Script.Services.ScriptMethod()&amp;gt; _&lt;br /&gt;        &amp;lt;WebMethod()&amp;gt; _&lt;br /&gt;        Public Function ClearSession(ByVal sessionName As String) As Boolean&lt;br /&gt;            Dim session As System.Web.SessionState.HttpSessionState&lt;br /&gt;            session = System.Web.HttpContext.Current.Session&lt;br /&gt;&lt;br /&gt;            If session(sessionName) IsNot Nothing Then&lt;br /&gt;                Select Case sessionName.ToUpper&lt;br /&gt;                    Case "SESSION_RECEIPT_REPORT"&lt;br /&gt;                        DirectCast(session(sessionName), CrystalDecisions.CrystalReports.Engine.ReportDocument).Dispose()&lt;br /&gt;                    Case "SESSION_RECEIPT_DATA"&lt;br /&gt;                        DirectCast(session(sessionName), IList).Clear()&lt;br /&gt;                    Case Else&lt;br /&gt;                End Select&lt;br /&gt;                session(sessionName) = Nothing&lt;br /&gt;                session.Remove(sessionName)&lt;br /&gt;            End If&lt;br /&gt;&lt;br /&gt;        End Function&lt;br /&gt;&lt;br /&gt;    End Class&lt;br /&gt;&lt;br /&gt;End Namespace&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ทีนี้ในหน้า aspx เราก็เขียน javascript สำหรับมาเรียก Webservice ตัวนี้ครับ&lt;br /&gt;&lt;pre class="brush:html"&gt;&lt;br /&gt;&amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;    function Body_OnUnload() {&lt;br /&gt;         jNithi.WS.SessionManager.ClearSession('SESSION_RECEIPT_REPORT');          &lt;br /&gt;    }&lt;br /&gt;        &lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&lt;br /&gt;&amp;lt;body onunload="Body_OnUnload();"&amp;gt;&lt;br /&gt;    &amp;lt;form id="form1" runat="server"&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;asp:ScriptManager ID = "ScriptManager1" runat="server" &amp;gt;&lt;br /&gt;        &amp;lt;services&amp;gt;&lt;br /&gt;            &amp;lt;asp:ServiceReference Path="~/wsSession.asmx" /&amp;gt;&lt;br /&gt;        &amp;lt;/services&amp;gt;&lt;br /&gt;        &lt;br /&gt;    &amp;lt;/asp:ScriptManager&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;เท่าที่ลองทดสอบดู ก็ใช้งานได้ผลโอเคนะครับ เมื่อเราเปลี่ยนหน้า หรือปิด Browser มันจะไปเรียก WebService เพื่อสั่ง Clear Session ให้ แต่มีข้อแม้นิดหนึ่งครับ คือ onunload event ของ Body นั้นมันจะทำงานทุกครั้งที่มีการเปลี่ยนหน้า นั่นคือ ถ้ามีการ PostBack กลับไป มันก็จะเกิด Event นี้ด้วยครับ แต่ถ้าเราออกแบบ ASP.NET AJAX Page ของเราดีๆ ไม่ให้มีการ Post Back กลับไป แนวคิดนี้ก็ทำงานได้โอเคครับ&lt;br /&gt;&lt;br /&gt;เดี๋ยวคงต้องขอทดสอบอีกซักพัก ก่อนจะเอาไปใช้จริงครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-5241111873185999501?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/5241111873185999501/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=5241111873185999501' title='1 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5241111873185999501'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5241111873185999501'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2010/02/aspnet-session-page.html' title='ASP.NET สั่งเคลียร์ Session เมื่อเปลี่ยน Page'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-8877219614113537259</id><published>2010-02-09T21:28:00.003+07:00</published><updated>2010-07-11T00:16:01.747+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Crystal Report'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>Crystal Report - สร้างรายงานให้ user กำหนดเงื่อนไข Group ได้เอง (Dynamic Group By)</title><content type='html'>วันนี้เรามาดูเทคนิคของ Crystal Report อีกครั้งครับ (เนื่องจากดูใน Google Analytics เห็นคนชอบบทความเกี่ยวกับ Crystal Report เป็นพิเศษ) คราวนี้เราจะมาสร้างรายงานที่สามารถให้ user กำหนด Group By ได้เองตามใจ Blog นี้จะทำแค่ Group by 1 ระดับให้ดู แต่เราสามารถประยุกต์ใช้ให้ user เลือก group ได้มากกว่า 1 ระดับ รวมถึงสามารถกำหนดเงื่อนไข sort by ได้ด้วย&lt;br /&gt;&lt;br /&gt;ก่อนอื่นเลยสิ่งที่จำเป็นก็คือ เราต้องสร้าง Parameter Field ขึ้นมาก่อน เพื่อรับค่า parameter จาก user ว่าต้องการให้ group by column ไหนของข้อมูลครับ จากตัวอย่างผมสร้าง Parameter Field ชื่อ pGroupBy&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_N-Phs4y2L44/S3DmvrqJpsI/AAAAAAAAADs/7nJIXCAbCiU/s1600-h/1.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5436098457083946690" style="WIDTH: 400px; CURSOR: hand; HEIGHT: 229px" alt="" src="http://2.bp.blogspot.com/_N-Phs4y2L44/S3DmvrqJpsI/AAAAAAAAADs/7nJIXCAbCiU/s400/1.JPG" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_N-Phs4y2L44/S3DmDGldT3I/AAAAAAAAADk/_AfR74D8vuY/s1600-h/1.JPG"&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;จากนั้นก็สร้าง Formula Field เพื่อนำค่าที่ได้จาก Parameter Field มาคำนวนหา column ที่ต้องการ group ครับ&lt;br /&gt;ในตัวอย่างผมสร้าง Formula Field ชื่อ GroupBy&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_N-Phs4y2L44/S3DnnFue6KI/AAAAAAAAAD0/hDk9mDp-4YM/s1600-h/2.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 147px;" src="http://1.bp.blogspot.com/_N-Phs4y2L44/S3DnnFue6KI/AAAAAAAAAD0/hDk9mDp-4YM/s400/2.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5436099408974244002" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;จากโค้ดใน Formula Editor ผมใช้ IF THEN ELSE ซ้อนกัน (ที่จริงน่าใช้ Switch Case แต่ผมขี้เกียจแก้แล้ว) สังเกตุว่าถ้า user ส่ง parameter มาเป็น Dept ผมจะให้ Group By Formula Field อีกที นอกนั้นก็จะ Group By Database Field ตามปกติครับ&lt;br /&gt;&lt;br /&gt;คราวนี้มาดูหน้าเวบกันบ้างครับ ผมก็สร้าง Drop-down List มา 1 ตัว ชื่อ ddlGroupBy และใส่ List ที่ต้องการไว้&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:html"&gt;&lt;br /&gt;&amp;lt;/asp:DropDownList ID="ddlGroupBy" runat="server" style="position:absolute;left:100px;top:10px" CssClass="txtOptional"  Width="110px" EnableTheming="False" EnableViewState="False" &amp;gt;&lt;br /&gt;     &amp;lt;/asp:ListItem Text="Client" Value="Client" Selected="True"&amp;gt;&amp;lt;/asp:ListItem&amp;gt;&lt;br /&gt;     &amp;lt;/asp:ListItem Text="Most Work Lawyer" Value="MostWorkLawyer"&amp;gt;&amp;lt;/asp:ListItem&amp;gt;&lt;br /&gt;     &amp;lt;/asp:ListItem Text="Resp. Lawyer" Value = "RespStaff"&amp;gt;&amp;lt;/asp:ListItem&amp;gt;&lt;br /&gt;     &amp;lt;/asp:ListItem Text="Department" Value="Dept" &amp;gt;&amp;lt;/asp:ListItem&amp;gt;&lt;br /&gt;&amp;lt;/asp:DropDownList&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ตอนสร้างรายงานก็อย่าลืม SetParameterValue ด้วยครับ&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Dim reportDoc As New ReportDocument&lt;br /&gt;reportDoc.FileName = Server.MapPath("~/Reports/AR/rptAgingReport.rpt")&lt;br /&gt;reportDoc.SetDataSource(ARReportResults)&lt;br /&gt;reportDoc.SetParameterValue("pGroupBy", ddlGroupBy.SelectedValue)&lt;br /&gt;reportDoc.SetParameterValue("AsOfDate", asOfDate)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;เท่านี้ก็เรียบร้อยแล้วครับ ทีนี้ user ก็สามารถเลือก column ที่ต้องการ Group By ได้ตามใจครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-8877219614113537259?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/8877219614113537259/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=8877219614113537259' title='1 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/8877219614113537259'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/8877219614113537259'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2010/02/crystal-report-user-group-dynamic-group.html' title='Crystal Report - สร้างรายงานให้ user กำหนดเงื่อนไข Group ได้เอง (Dynamic Group By)'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_N-Phs4y2L44/S3DmvrqJpsI/AAAAAAAAADs/7nJIXCAbCiU/s72-c/1.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-666303082526353649</id><published>2010-01-29T22:32:00.001+07:00</published><updated>2010-01-29T22:32:00.208+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Crystal Report'/><category scheme='http://www.blogger.com/atom/ns#' term='VB.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>Crystal Report - แสดงข้อความหลายคอลัมภ์ด้วย RTF Text Interpretation</title><content type='html'>วันก่อนได้รับ requirement ว่าในรายงานตัวหนึ่ง ต้องการให้แสดง Report Footer เพื่อแสดงข้อมูลตามเงื่อนไข โดยข้อมูลดังกล่าวต้องสามารถ ทำตัวหนา ตัวเอียง สำหรับข้อมูลที่ต้องการเน้น และจัดรูปแบบเป็น 2 คอลัมภ์ ซึ่งใน Report Footer มีทั้งหมด 5 ส่วน ส่วนดังกล่าวเป็นส่วนที่ 4&lt;br /&gt;&lt;br /&gt;เริ่มแรกผมสร้าง Parameter Field มา 1 ตัว ชื่อ PaymentAccount และสร้าง Text Object มา 1ตัว และลาก Payment Account มาใส่ใน Text Object เพื่อให้เป็น Embedded Field เราจะได้สามารถกำหนด Text Interpretation ได้ครับ (จากรูปสังเกตุว่าเป็น Report Footer d)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_N-Phs4y2L44/S2KNRMEsPMI/AAAAAAAAADM/72z-3sQUOzs/s1600-h/jnithi1.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5432059427000433858" style="WIDTH: 400px; CURSOR: hand; HEIGHT: 311px" alt="" src="http://1.bp.blogspot.com/_N-Phs4y2L44/S2KNRMEsPMI/AAAAAAAAADM/72z-3sQUOzs/s400/jnithi1.JPG" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;เสร็จแล้วคลิ๊กเมาส์ขวาที่ &lt;strong&gt;&lt;span style="color:#3333ff;"&gt;Text Object&lt;/span&gt;&lt;/strong&gt; ที่สร้างขึ้น เลือก Property แล้วคลิ๊กเครื่องหมายถูกที่ &lt;span style="color:#3333ff;"&gt;&lt;strong&gt;Can Grow&lt;/strong&gt;&lt;/span&gt; ใส่ตัวเลข 0 คือไม่จำกัดบรรทัดลงไป เพื่อให้ Object นี้มันยืดได้&lt;br /&gt;&lt;br /&gt;ต่อไปก็กำหนด Text Interpretation โดยการ double click ที่ Text Object แล้วเลือก Embedded field คลิ๊กเมาส์ขวา เลือก Format Embedded Field&lt;br /&gt;&lt;br /&gt;&lt;a href="http://4.bp.blogspot.com/_N-Phs4y2L44/S2KNXcZiDeI/AAAAAAAAADU/tWnjP5dE7Ac/s1600-h/jnithi2.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5432059534462029282" style="WIDTH: 400px; CURSOR: hand; HEIGHT: 212px" alt="" src="http://4.bp.blogspot.com/_N-Phs4y2L44/S2KNXcZiDeI/AAAAAAAAADU/tWnjP5dE7Ac/s400/jnithi2.JPG" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;เสร็จแล้วก็เลือก Paragraph Tab เลือก Text Interpretation เป็น RTF Interpretation&lt;br /&gt;&lt;br /&gt;&lt;a href="http://2.bp.blogspot.com/_N-Phs4y2L44/S2KGEHwBRuI/AAAAAAAAADE/LVW8-BNZzp4/s1600-h/jnithi3.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5432051505920296674" style="WIDTH: 396px; CURSOR: hand; HEIGHT: 400px" alt="" src="http://2.bp.blogspot.com/_N-Phs4y2L44/S2KGEHwBRuI/AAAAAAAAADE/LVW8-BNZzp4/s400/jnithi3.JPG" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;เสร็จขั้นตอนของรายงานแล้ว ทีนี้ก็มาเขียนโค้ดสำหรับสร้างข้อมูล RTF ครับ&lt;br /&gt;&lt;br /&gt;&lt;pre class="vb" name="code"&gt;&lt;br /&gt;Public Shared Function GetAccount(ByVal coCode As String) As String&lt;br /&gt;   Dim sb As New Text.StringBuilder&lt;br /&gt;&lt;br /&gt;   'Set RTF1, ANSI Page and Font-family&lt;br /&gt;   sb.AppendLine("{\rtf1\ansi\ansicpg1252\deff0\deflang1033\deflangfe1033{\fonttbl{\f0\fswiss\fprq2\fcharset0 Arial;}}")&lt;br /&gt;&lt;br /&gt;   'Set Tab (at 0.5 in and 2.5 in) and Font-size&lt;br /&gt;   sb.AppendLine("\viewkind4\uc1\pard\nowidctlpar\sa200\sl276\slmult1\tx720\tx3600\f0\fs20")&lt;br /&gt;&lt;br /&gt;   'Insert first blank line&lt;br /&gt;   sb.AppendLine("\tab \par ")&lt;br /&gt;   Select Case coCode&lt;br /&gt;       Case "Nithi"&lt;br /&gt;           sb.AppendLine("\tab Account Name \tab Nithi Juabsami \par ")&lt;br /&gt;           sb.AppendLine("\tab Savings Account No.: \tab 1-1111-1111, USD account. \par")&lt;br /&gt;           sb.AppendLine("\tab Bank Name: \tab Citibank, N.A. \par ")&lt;br /&gt;           sb.AppendLine("\tab Bank Address: \tab 82 North Sathorn Road \par ")&lt;br /&gt;           sb.AppendLine("\tab \tab Silom, Bangrak, \par ")&lt;br /&gt;           sb.AppendLine("\tab \tab Bangkok 10500, Thailand \par ")&lt;br /&gt;           sb.AppendLine("\tab -or- \par ")&lt;br /&gt;           sb.AppendLine("\tab Account Name: \tab Nithi Juabsamai \par ")&lt;br /&gt;           sb.AppendLine("\tab Savings Account No.: \tab x-xxxx-xxxx \par ")&lt;br /&gt;           sb.AppendLine("\tab Bank Name: \tab The Siam Commercial Bank Plc., Thanon Witthayu Branch \par ")&lt;br /&gt;           sb.AppendLine("\tab Bank Address: \tab 132 Wireless Road, Lumpini, Bangkok 10330 \par ")&lt;br /&gt;       Case "jNithi"&lt;br /&gt;           sb.AppendLine("\tab Account Name: \tab jNithi O.P.  \par  ")&lt;br /&gt;           sb.AppendLine("\tab Bank Account No.: \tab xxx-xxxxxx-xxx (USD), Current Account  \par  ")&lt;br /&gt;           sb.AppendLine("\tab Name of Bank: \tab The Siam Commercial Bank Plc., Thanon Witthayu Branch.  \par  ")&lt;br /&gt;           sb.AppendLine("\tab Bank Address: \tab 132 Wireless Road, Lumpini, Bangkok 10330 \par ")&lt;br /&gt;      Case Else&lt;br /&gt;   End Select&lt;br /&gt;&lt;br /&gt;   ' Insert bottom blank line&lt;br /&gt;   sb.AppendLine("\tab \par ")&lt;br /&gt;&lt;br /&gt;   ' SET end of RTF&lt;br /&gt;   sb.AppendLine("}")&lt;br /&gt;   Return sb.ToString&lt;br /&gt;End Function&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;มาดูรายละเอียดกันครับ&lt;br /&gt;&lt;span style="color:#3333ff;"&gt;{\rtf1\ansi\ansicpg1252\deff0\deflang1033\deflangfe1033{\fonttbl{\f0\fswiss\fprq2\fcharset0 &lt;span style="color:#ff0000;"&gt;Arial&lt;/span&gt;;}}&lt;br /&gt;&lt;/span&gt;บรรทัดนี้เรากำหนด RTF Format และกำหนดให้ใช้ ANSI PAGE 1252 และ font คือ Arial&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#3333ff;"&gt;\viewkind4\uc1\pard\nowidctlpar\sa200\sl276\slmult1\&lt;span style="color:#ff0000;"&gt;tx720&lt;/span&gt;\&lt;span style="color:#ff0000;"&gt;tx3600&lt;/span&gt;\f0\&lt;span style="color:#ff0000;"&gt;fs20&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;บรรทัดนี้เรากำหนดให้เอกสารเรามี tab 2 อัน อันแรกคือ tx720 แปลว่าอยู่ที่ 720 twips หรือ 0.5 นิ้ว (โดยปกติ 1นิ้ว จะเท่ากับ 1440 twips) และอันที่สองคือ tx3600 คือ 3600 twips หรือ 2.5นิ้ว ส่วน fs20 คือขนาดของ font เท่ากับ 10 pt ครับ&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#3333ff;"&gt;\tab Account Name \tab Nithi Juabsami \par&lt;/span&gt;&lt;br /&gt;แปลว่าพิมพ์ tab 1 ครั้ง (คือเลื่อนไปตำแหน่งที่ 0.5 นิ้วที่กำหนด) พิมพ์คำว่า Account Name แล้วพิมพ์ tab (เลื่อนไปยังตำแหน่ง 2.5 นิ้ว พิมพ์ Nithi Juabsamai แล้วขึ้นบรรทัดใหม่ (\par)&lt;br /&gt;&lt;br /&gt;ตอนเขียนโค้ดสั่ง Report ก็เรียกฟังก์ชันนี้เพื่อส่ง parameter ให้ Crystal Report&lt;br /&gt;&lt;pre class="vb" name="code"&gt;&lt;br /&gt;rpt = New rptTestReceipt&lt;br /&gt;rpt.SetDataSource(_receipt)&lt;br /&gt;rpt.SetParameterValue("AsOfDate", AsOfDate.ToString("dd MMM yyyy"))&lt;br /&gt;rpt.SetParameterValue("PaymentAccount", PaymentAccount.GetAccount(_receipt(0).CoCode))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;คราวนี้ลองรันดูผลลัพธ์ครับ&lt;br /&gt;&lt;br /&gt;&lt;a href="http://1.bp.blogspot.com/_N-Phs4y2L44/S2KPKGU0_SI/AAAAAAAAADc/6pGOfSu0P2A/s1600-h/jnithi4.JPG"&gt;&lt;img id="BLOGGER_PHOTO_ID_5432061504221674786" style="WIDTH: 400px; CURSOR: hand; HEIGHT: 340px" alt="" src="http://1.bp.blogspot.com/_N-Phs4y2L44/S2KPKGU0_SI/AAAAAAAAADc/6pGOfSu0P2A/s400/jnithi4.JPG" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;เรียบร้อยครับ ตอนนี้ข้อมูล Payment Account ของเราก็แสดงเป็น 2 column (ซึ่งเราแบ่งด้วย Tab) ถ้าต้องการเพิ่ม Column เราก็สามารถกำหนด tab ที่ตำแหน่งที่ต้องการเพิ่มได้ครับ&lt;br /&gt;&lt;br /&gt;ที่จริงเราสามารถกำหนด HTML Interpretation ได้ครับ แต่ว่า Cystal Report รองรับแค่ subset ของ HTML ครับ คือรองรับได้แค่บาง tag ซึ่ง tag อย่าง table ไม่รองรับครับ ผมเลยเปลี่ยนมาใช้ RTF Interpretation แทน&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="color:#663333;"&gt;หมายเหตุ&lt;br /&gt;&lt;/span&gt;&lt;/strong&gt;&lt;span style="color:#666666;"&gt;จริงๆแล้วเราสามารถใช้ Word Pad ที่มาพร้อม Windows ในการจัดเอกสารตามต้องการ สามารถกำหนดตัวหนา ตัวเอียงได้ตามสะดวก แล้ว save เป็น rtf ไฟล์ จากนั้นก็ใช้ Note Pad เปิดมา copy ใส่ได้เลยครับ แต่อย่าใช้ Microsoft Word นะครับ เพราะมันจะใส่ tag เพิ่มเติมมาเยอะแยะจนน่าเวียนหัว&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-666303082526353649?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/666303082526353649/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=666303082526353649' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/666303082526353649'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/666303082526353649'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2010/01/crystal-report-rtf-text-interpretation.html' title='Crystal Report - แสดงข้อความหลายคอลัมภ์ด้วย RTF Text Interpretation'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_N-Phs4y2L44/S2KNRMEsPMI/AAAAAAAAADM/72z-3sQUOzs/s72-c/jnithi1.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-2066570060107926783</id><published>2010-01-13T23:51:00.001+07:00</published><updated>2010-07-11T00:17:33.816+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer'/><title type='text'>SQL Server - เก็บผลลัพธ์จาก Stored Procedure ลงในตาราง</title><content type='html'>ปกติผมก็ใช้ Stored Procedure ใน SQL Server เสมอๆครับ ซึ่งพอเรา execute stored procedure ก็จะได้ result set มาใช้งาน แต่ทีนี้ถ้าเราต้องการทำ query result set ที่ได้ละจะทำยังไงดี ถ้าเป็นเมื่อก่อนผมก็ copy stored procedure มาสร้างใหม่แล้วแก้คำสั่งข้างในให้ทำ query เพิ่มไปเลย เช่นไป join object อื่น หรือสั่ง where สั่ง group by ฯลฯ แล้วแต่ต้องการ&lt;br /&gt;&lt;br /&gt;หรืออีกวิธีก็สั่ง CREATE TABLE ก่อน แล้วก็ INSERT ข้อมูลที่ได้จาก Stored Procedure ซึ่งวิธีนี้ถ้ามันมีหลาย field ตอนสั่ง CREATE TABLE ก็เหนื่อยไม่ใช่เล่น ซึ่งวิธีนี้ใช้บ่อยโดยเฉพาะอย่างยิ่ง stored procedure ของระบบ เช่นพวก sp_who2, sp_lock เป็นต้น ลองดูตัวอย่างครับ&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:sql"&gt;&lt;br /&gt;CREATE TABLE #locks (spid int, dbid int, objid int,&lt;br /&gt; indid int, [type] varchar(4), resource varchar(50), mode varchar(2), status varchar(10));&lt;br /&gt;&lt;br /&gt;INSERT INTO #locks (spid, dbid, objid, indid, [type], resource, mode, status)&lt;br /&gt;EXEC dbo.sp_lock;&lt;br /&gt;&lt;br /&gt;SELECT * FROM #locks;&lt;br /&gt;&lt;br /&gt;DROP TABLE #locks;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;อันนี้ตัวอย่างแค่ 8 fields ยังเหงื่อตก เพราะเทสกันหลายรอบครับ ทีแรกผมกำหนด datatype ไม่ถูกมันก็ error ใส่ ขนาดน้อยไปเช่น resource varchar(10) ก็ error ครับ&lt;br /&gt;&lt;br /&gt;วันนี้ผมมีวิธีใหม่มาเสนอครับ ลองดูโค้ดละกัน&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:sql"&gt;&lt;br /&gt;SELECT * INTO #tmpWho FROM OPENROWSET('SQLNCLI', 'Server=testSQLServer;Trusted_Connection=yes;', 'EXEC sp_who') ;&lt;br /&gt;&lt;br /&gt;SELECT * FROM #tmpWho Where dbname='master' and loginame = 'sa';&lt;br /&gt;&lt;br /&gt;DROP TABLE #tmpWho;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ครับ โค้ดนี้เราใช้คำสั่ง OPENROWSET ร่วมกับ SELECT INTO นั่นเอง สะดวกดีมากทีเดียว 555 &lt;br /&gt;&lt;br /&gt;คราวนี้มาดูตัวอย่างการใช้งานจริงบ้าง&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:sql"&gt;&lt;br /&gt;SELECT * INTO tempTestOutput FROM OPENROWSET ('SQLNCLI', 'Server=testSQLServer;Trusted_Connection=yes;', 'EXEC testDB.dbo.spTestOutput ''testParameter1'''); &lt;br /&gt;&lt;br /&gt;SELECT t.*, e.Department, e.HiredDate&lt;br /&gt;FROM tempTestOutput t INNER JOIN Employee e ON t.EmployeeId = e.EmployeeId&lt;br /&gt;WHERE e.ResignedDate IS NULL&lt;br /&gt;ORDER BY t.EmployeeId&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;คราวนี้ผมก็ได้ temp table เอาไว้ยำข้อมูลแล้วครับ เสร็จแล้วก็อย่าลืม DROP TABLE ด้วยนะ &lt;br /&gt;&lt;br /&gt;Referrence &lt;br /&gt;&lt;a href = "http://stackoverflow.com/questions/653714/how-to-select-into-temp-table-from-stored-procedure"&gt;www.stackoverflow.com - How to SELECT * INTO [temp table] FROM [Stored Procedure] &lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-2066570060107926783?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/2066570060107926783/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=2066570060107926783' title='1 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/2066570060107926783'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/2066570060107926783'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2010/01/sql-server-stored-procedure.html' title='SQL Server - เก็บผลลัพธ์จาก Stored Procedure ลงในตาราง'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-6070805721672059455</id><published>2010-01-06T22:35:00.001+07:00</published><updated>2010-07-11T00:18:43.583+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Crystal Report'/><category scheme='http://www.blogger.com/atom/ns#' term='VB.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>VB.NET เขียนโค้ดควบคุม Object ของ Crystal Report</title><content type='html'>หลายๆคนที่พัฒนาซอฟท์แวร์ทางธุรกิจก็หนีไม่พ้นที่จะต้องทำรายงาน และที่นิยมมากสำหรับนักพัฒนา .NET ก็คงหนีไม่พ้น Crystal Report นั่นเองครับ ซึ่งใช้งานไม่ยาก ยืดหยุ่น และมีฟังก์ชันมากมายให้ใช้งาน และความยอดเยียมอีกอย่างก็คือ เราสามารถเขียนโค้ดเพื่อควบคุม Crystal Report ได้ด้วย&lt;br /&gt;&lt;br /&gt;ลองมาดูตัวอย่างจากคำถามที่ greatfriends ละกันครับ&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;How to Return total page in crystal report to VB -- HELP ME!!&lt;/strong&gt;&lt;br /&gt;&lt;a href="http://greatfriends.biz/?118103"&gt;http://greatfriends.biz/?118103&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;color:#990000;"&gt;ปัญหามีอยู่ว่า ผมมี crstal report ที่สร้างออกมาเป็น PDF และก็ต้องเอามก Merge กับ File Attach ที่เป็น PDF ให้เป็น ไฟล์เดียว โดย File ที่ Attach นั้นจะต้องอยู่ตรงกลางระหว่างไฟล์ PDF แรก ซึ่งเลขหน้าเวลา Count จะไม่ถูกต้อง เพราะผม แบ่งไฟล์ PDF แรกเป็นสองส่วน โดยเป็นส่วนหัวและท้าย ซึ่งเวลามัน count page ก็จะแยกกันซึ่งไม่ถูกต้อง คือสมมติ ส่วนหัวมี 10 หน้า และท้าย 5 หน้ามันแสดงเป็น 1 of 10 และ ส่วนท้ายแสดงเป็น 1 of 5 ซึ่งส่วนท้ายต้องเป็น 11 of 15&lt;br /&gt;&lt;br /&gt;ผมจึงคิดว่าให้ ส่วนหัว ส่งค่ากลับมาที่ vb ว่ามีจำหน้าทั้งหมดเท่าไหร่ และผมจะส่ง เป็น Parameter ให้กับส่วนท้าย&lt;br /&gt;นะครับ แต่วิธีส่งค่ากลับมาวีบี ทำไงอะครับ ผมรู้สึกว่ามันไม่มีทาง แต่ท่านใดมีแนวทางอื่น ช่วยผมทีครับ&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;color:#006600;"&gt;ผมตอบไว้ดังนี้ครับ&lt;br /&gt;สมมติว่าผมสร้าง ITextObject 1 ตัวเอาไว้ที่ Report Footer ของ Crystal Report ตั้งชื่อ txtTotalPage แล้วเอาค่าของ PageNo มาใส่ไว้&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;ในโค้ด VB&lt;br /&gt;&lt;pre class="brush:vb" &gt;&lt;br /&gt;Dim rptDoc As New ReportDocument&lt;br /&gt;rptDoc.FileName = Server.MapPath("~/Reports/Test.rpt")&lt;br /&gt;rptDoc.SetDataSource(DataSet1)&lt;br /&gt;rptDoc.SetParameterValue(0, CDate(Me.txtAsOfDate.Text).ToString("dd MMMM yyyy"))&lt;br /&gt;&lt;br /&gt;Dim txtTotalPage As TextObject = TryCast(rptDoc.ReportDefinition.ReportObjects("txtTotalPage"),  CrystalDecisions.CrystalReports.Engine.TextObject)&lt;br /&gt;&lt;br /&gt;Dim TotalPage As Integer = 0&lt;br /&gt;If  IsNumeric(txtUSAmount.Text) Then&lt;br /&gt;    TotalPage = CInt(txtUsAmount.Text)&lt;br /&gt;End If&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:verdana;color:#006600;"&gt;เสร็จแล้วก็เอา TotalPage ไปเป็น Parameter ของ report ตัวที่สอง จากนั้นก็ Export รายงานทั้ง 2 ตัว เป็น pdf แล้วเอามา merge กัน&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;ทีนี้นอกจาก เราจะอ่านค่าจาก TextObject เรายังสามารถใส่ค่าเข้าไปให้ TextObject ก็ได้ หรือว่าจะกำหนด property อื่นๆ เช่น Top, Width, EnableSuppress ....&lt;br /&gt;&lt;br /&gt;ลองดูตัวอย่างครับ&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb" &gt;&lt;br /&gt;Dim pos As Integer&lt;br /&gt;Dim iCurrency As Integer&lt;br /&gt;Dim txtEUAmount As TextObject = rptDoc.ReportDefinition.ReportObjects("txtEUAmount")&lt;br /&gt;Dim txtEUSum As ReportObject = rptDoc.ReportDefinition.ReportObjects("txtEUSum")&lt;br /&gt;&lt;br /&gt;'เรียก function เพื่อตรวจสอบเงื่อนไขสกุลเงิน&lt;br /&gt;If IsContainCurrency(ARStatementOfAccounts, "EUR") Then&lt;br /&gt;     iCurrency += 1&lt;br /&gt;     pos = CalculateObjectPosition(iCurrency) ' เรียกฟังก์ชันสำหรับคำนวณตำแหน่งของ Object ใน Detail&lt;br /&gt;     txtEUAmount.Left = pos&lt;br /&gt;     pos = CalculateSumObjectPosition(iCurrency) 'เรียกฟังก์ชันสำหรับคำนวนตำแหน่งของ Object ใน Group Footer&lt;br /&gt;     txtEUSum.Top = pos&lt;br /&gt;Else&lt;br /&gt;     txtEUAmount.Text = ""&lt;br /&gt;     txtEUSum.ObjectFormat.EnableSuppress = True&lt;br /&gt;End If&lt;br /&gt;&lt;br /&gt;pos = 920 + (iCurrency - 1) * 280&lt;br /&gt;DirectCast(rptDoc.ReportDefinition.ReportObjects("LineFV0"), LineObject).Bottom = pos&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;จะสังเกตุว่ามีการประกาศตัวแปร TextObject และ ReportObject ครับ ซึ่ง ReportObject นั้นเป็น Base Class ของ Object ต่างๆ เช่น LineObject, TextObject ใน Crystal นั่นเอง ดังนั้นถ้าเรารู้ Type แน่นอน เราสามารถทำการ Casting เพื่อกำหนด property เฉพาะของ Type นั้นๆได้ครับ&lt;br /&gt;&lt;br /&gt;ก็ไม่ยากใช่ไหมครับ ตอนนี้เราสามารถเขียนโปรแกรมเพื่อทำ Dynamic Layout ของ Crystal Report ได้แล้ว&lt;strong&gt;&lt;/strong&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-6070805721672059455?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/6070805721672059455/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=6070805721672059455' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6070805721672059455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6070805721672059455'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2010/01/vbnet-object-crystal-report.html' title='VB.NET เขียนโค้ดควบคุม Object ของ Crystal Report'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-6599290774639128092</id><published>2009-12-17T22:36:00.001+07:00</published><updated>2010-07-11T00:19:30.119+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VB.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='LINQ'/><title type='text'>เปรียบเทียบ DataTable ด้วย LINQ</title><content type='html'>วันก่อนได้มีโอกาสลองใช้ LINQ กับ DataTable ดูครับ นั่นคือถ้าเรามี data table 2 ตัวที่มี schema เหมือนกัน อยากรู้ว่าข้อมูลใน data table ตัวแรก กับ ตัวที่สองมี row ไหน ที่เหมือนหรือต่างกันบ้าง วิธีเดิมๆคือเราเขียนลูปไล่ตรวจสอบที่ละ row เอง ซึ่งจริงๆก็ไม่ยากครับ แต่สำหรับ .NET Framework 3.5 นั้นง่ายกว่าเดิม เพราะเราสามารถใช้ LINQ ทำงานแทนได้ครับ ลองดูโค้ดกัน ก่อนอื่นเรามาเตรียม DataTable ทั้ง 2 ตัวก่อน&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb" &gt;&lt;br /&gt;'เตรียมข้อมูล Data1 ให้มี 7 row ตั้งแต่ 0-6&lt;br /&gt;Dim dt1 As New Data.DataTable("Data1")&lt;br /&gt;Dim dr1 As DataRow = Nothing&lt;br /&gt;dt1.Columns.Add(New Data.DataColumn("ID"))&lt;br /&gt;dt1.Columns.Add(New Data.DataColumn("Name"))&lt;br /&gt;For iRecord As Integer = 0 To 6&lt;br /&gt;dr1 = dt1.NewRow()&lt;br /&gt;dr1.Item("ID") = iRecord&lt;br /&gt;dr1.Item("Name") = "Test" &amp;amp; iRecord&lt;br /&gt;dt1.Rows.Add(dr1)&lt;br /&gt;Next&lt;br /&gt;&lt;br /&gt;'เตรียมข้อมูล Data2 ให้มี 7 rows ตั้งแต่ 4 - 10&lt;br /&gt;Dim dt2 As New Data.DataTable("Data2")&lt;br /&gt;Dim dr2 As DataRow = Nothing&lt;br /&gt;dt2.Columns.Add(New Data.DataColumn("ID"))&lt;br /&gt;dt2.Columns.Add(New Data.DataColumn("Name"))&lt;br /&gt;For iRecord As Integer = 4 To 10&lt;br /&gt;dr2 = dt2.NewRow()&lt;br /&gt;dr2.Item("ID") = iRecord&lt;br /&gt;dr2.Item("Name") = "Test" &amp;amp; iRecord&lt;br /&gt;dt2.Rows.Add(dr2)&lt;br /&gt;Next&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ทีนี้ถ้าเราอยากจะหาเฉพาะ DataRow ของ Data1 ที่ไม่ซ้ำกับใน Data2 เราสามารถสั่ง Data1 ExceptRow Data2 ได้ครับ แต่ก่อนอื่นเราต้องแปลง DataTable ให้เป็น Enumerable ก่อนเพราะว่าคำสั่ง Except ไม่มีใน Extension Method ของ DataTable ครับ แต่มีใน System.Linq.Enumerable&lt;br /&gt;&lt;br /&gt;&lt;pre class="vb" name="code"&gt;&lt;br /&gt;Dim r1 = dt1.AsEnumerable()&lt;br /&gt;Dim r2 = dt2.AsEnumerable()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ตัว r1 และ r2 จะมี type เป็น System.Data.EnumerableRowCollection(Of System.Data.DataRow) ครับ ทีนี้เราจะเห็นมี Extension Method ชื่อ Except ให้เรียกใช้ได้แล้ว&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb" &gt;&lt;br /&gt;Dim ExceptResult = r1.Except(r2, DataRowComparer.Default)&lt;br /&gt;Dim IntersectResult = r2.Intersect(r1, DataRowComparer.Default)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;จะสังเกตุว่าผมใส่ DataRowComparer.Default เป็น parameter ด้วย สำหรับ Comparer นั้นเราอาจจะเขียนเองก็ได้ครับ ถ้าเรามีเงื่อนไขในการเปรียบเทียบ DataRow ของเราเอง เช่นต้องการเทียบเฉพาะ Primary Key หรือเฉพาะบาง field เป็นต้นครับ ทีนี้ type ของ ExceptResult จะเป็น&lt;br /&gt;System.Collections.Generic.IEnumerable(Of System.Data.DataRow) ครับ จากตัวอย่างข้างต้น ค่าที่ได้ของ ExceptResult จะมี 4 DataRows นั่นคือ ID ตั้งแต่ 0-3&lt;br /&gt;&lt;br /&gt;ส่วนบรรทัดต่อมา ผมลองคำสั่ง Intersect นั่นคือ เอาเฉพาะข้อมูลที่เหมือนกันระหว่าง 2 EnumerableRowCollection ผลลัพธ์ก็จะได้ 3 DataRows คือ ID ตั้งแต่ 4-6 นั่นเอง&lt;br /&gt;&lt;br /&gt;ถ้าผมแก้จาก&lt;br /&gt;dr2.Item("Name") = "Test" &amp;amp; iRecord&lt;br /&gt;เป็น&lt;br /&gt;dr2.Item("Name") = "Test2" &amp;amp; iRecord&lt;br /&gt;&lt;br /&gt;แล้วลองรันใหม่ จะพบว่า ExceptResult.Count = 7 นั่นคือข้อมูลทั้งหมดของ dt1 และ IntersectResult.Count = 0 เพราะว่าเราใช้ DataRowComparer.Default อย่างที่บอกครับ ดังนั้นแต่ละ row ถึงจะมี ID เดียวกัน เป็นข้อมูลใน field อื่นไม่เหมือนกัน ก็จะถือว่าเป็น row นั้น ไม่เหมือนกันครับ&lt;br /&gt;&lt;br /&gt;คราวนี้มาลองเล่นกันต่อครับ กำลังสนุกเลย&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb" &gt;&lt;br /&gt;Dim dtResult = ExceptResult.CopyToDataTable()&lt;br /&gt;dtResult.TableName = "dtResult"&lt;br /&gt;dt1.Rows(0).Delete()&lt;br /&gt;dt2.Rows(1).Delete()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;บรรทัดแรกผมใช้ Extension Method ชื่อ CopyToDataTable ก็ตรงตัวครับ คือมันจะ copy DataRows ไปสร้าง DataTable ใหม่ สำหรับคำส่งนี้ถ้าเราส่ง DataTable ไปเป็น parameter มันจะ copy DataRows ไปยัง DataTable ตัวนั้นๆแทนครับ&lt;br /&gt;&lt;br /&gt;ต่อมาผมลองสั่งลบแถวแรก ของ dt1 และลบแถวที่สองของ dt2 ทีนี้ลองมา debug ดูข้อมูลใน dt1, dt2, r1, r2, ExceptResult, IntersectResult จะพบว่าเมื่อเราสั่งลบ Datarows แล้ว มันจะมีผลต่อ r1, r2, ExceptResult, IntersectResult ครับ แต่มันไม่มีผลต่อ dtResult ครับ ดังนั้นตอนนี้ค่าของ dtResult จะไม่เท่ากับ ExceptResult แล้ว &lt;br /&gt;&lt;br /&gt;ที่เป็นแบบนี้เพราะว่า Except() กับ Intersect() นั้นใช้ความสามารถของ Yield Return นั่นเองครับ ไอ้เจ้า Yield Return นี่มันสุดยอดจริงๆ เสียดายที่ VB2008 ไม่สามารถใช้ Yield Return ได้ครับ สำหรับผู้ที่อยากรู้เรื่อง Yield Return ลอง search ใน google ดูครับ ผมเคยอ่านที่ www.coredeveloper.net แต่วันนี้มันเข้าไปเวบนี้ไม่ได้ซะละ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-6599290774639128092?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/6599290774639128092/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=6599290774639128092' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6599290774639128092'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6599290774639128092'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/12/datatable-linq.html' title='เปรียบเทียบ DataTable ด้วย LINQ'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-5703267258652254683</id><published>2009-12-10T22:43:00.001+07:00</published><updated>2009-12-10T22:43:00.344+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer'/><title type='text'>SQLServer ดึงข้อมูลจาก dbf</title><content type='html'>จริงๆผมต้องเขียนโปรแกรมเพื่อดึงข้อมูลจาก dbf เข้า SQLServer บ่อยๆ ถ้า dbf ตัวไหนที่ต้องใช้ประจำก็จะสร้าง Linked Server เก็บไว้ แต่ถ้าทำเป็น ad hoc ก็จะใช้คำสั่ง OPENROWSET แทน แต่ก็ลืมวิธีทุกครั้ง ต้องเสียเวลาไป search ใน google ทุกที คราวนี้เลยมาเขียนไว้ใน blog ดีกว่า ถ้าลืมอีกคราวหน้าก็มาหาที่นี่ได้เลย 555&lt;br /&gt;&lt;br /&gt;สำหรับการสร้างใช้ OPENROWSET ก็ไม่ยากครับ เขียนแบบนี้&lt;br /&gt;&lt;br /&gt;SELECT * FROM OPENROWSET('MICROSOFT.JET.OLEDB.4.0','dBase 5.0;HDR=NO;IMEX=2;DATABASE={path to dbf}','select * from {filename}.dbf')&lt;br /&gt;&lt;br /&gt;แต่ที่สำคัญคือต้องปิดโปรแกรม foxpro หรือโปรแกรมที่กำลังเปิดไฟล์ dbf นั้นๆไปก่อนครับ ไม่งั้นมันจะขึ้นว่าติดปัญหาเรื่อง permission ไปนั่ง search หาสาเหตุตั้งนาน&lt;br /&gt;&lt;br /&gt;ส่วนสร้าง Linked Server นั้นก็ทำดังนี้ครับ&lt;br /&gt;1. ไปที่ Linked Server คลิ๊กเมาส์ขวาเลือก New Linked Server&lt;br /&gt;2. ใส่ชื่อ Linked Server ที่ต้องการครับ สมมติชื่อ DBLink&lt;br /&gt;3. Provider ให้เลือกเป็น Microsoft Jet 4.0 ครับ (จริงๆมันมี Visual Fox Pro ให้เลือกด้วย และในบอร์ดต่างๆเค้าว่ากันว่าจะทำให้ตอน select ข้อมูล มันเร็วกว่า Jet แต่ผมก็ยังไม่ได้ลองครับ)&lt;br /&gt;4. Product Name ใส่อะไรก็ได้ครับ แต่อย่าทิ้งว่าง สมมติใส่เป็น Microsoft Jet&lt;br /&gt;5. Data source ใส่ path ที่เก็บ dbf ครับ เช่น c:\dbfFiles&lt;br /&gt;6. Provider String ใส่ dBase 5.0&lt;br /&gt;7. เปลี่ยนมาที่ Security Page ครับ ตรง option สำหรับ log in เลือกตัวล่างสุด ที่เขียนว่า Be made using this security context เสร็จแล้วตรง Remote Login ให้ใส่ Admin ส่วน With Password ให้เว้นว่างไว้ครับ&lt;br /&gt;8. กด OK เป็นอันเสร็จพิธี&lt;br /&gt;&lt;br /&gt;ทีนี้เวลาเขียนคำสั่ง SQL ก็เขียนประมาณนี้ครับ สมมติว่าต้องการดูข้อมูลจาก testdata.dbf&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#3333ff;"&gt;SELECT&lt;/span&gt; * &lt;span style="color:#3333ff;"&gt;FROM&lt;/span&gt; DBLink...testdata&lt;br /&gt;&lt;br /&gt;ลองเปรียบเทียบกับการใช้ OPENROWSET&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#3333ff;"&gt;SELECT&lt;/span&gt; * &lt;span style="color:#3333ff;"&gt;FROM OPENROWSET&lt;/span&gt;(&lt;span style="color:#ff0000;"&gt;'MICROSOFT.JET.OLEDB.4.0','dBase 5.0;HDR=NO;IMEX=2;DATABASE=c:\dbfFiles'&lt;/span&gt;, &lt;span style="color:#ff0000;"&gt;'select * from testdata.dbf'&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;ไม่ยากใช่ไหมครับ แต่ทำไมผมลืมทุกทีก็ไม่รู้สิ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-5703267258652254683?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/5703267258652254683/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=5703267258652254683' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5703267258652254683'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5703267258652254683'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/12/sqlserver-dbf.html' title='SQLServer ดึงข้อมูลจาก dbf'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-6762606597358843000</id><published>2009-12-04T21:42:00.001+07:00</published><updated>2009-12-04T21:42:00.577+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Talk'/><title type='text'>เรื่องประหลาดใจ</title><content type='html'>วันนี้มีเรื่องน่าประหลาดใจเรื่องหนึ่งสำหรับผมครับ คือผมได้รับอีเมลล์มาแสดงความขอบคุณและบอกว่าแก้ปัญหาเรื่องการเขียนโปรแกรมได้แล้ว ที่น่าประหลาดใจคือผมได้รับเมลล์มาขอความช่วยเหลือเรื่องเขียนโปรแกรมมากกว่า 5 ปีแล้ว ตั้งแต่เริ่มตอบกระทู้ที่พันธ์ทิพย์และเขียนบทความที่ expert2you เท่าที่จำได้ครั้งนี้เป็นครั้งแรกครับที่พอผมตอบเมลล์ไป แล้ววันรุ่งขึ้นมีเมลล์มาแจ้งว่าทำได้แล้วพร้อมกับขอบคุณ ปกติถ้าได้รับเมลล์จะบอกว่ายังไม่ได้ ช่วยแก้โค้ดหน่อย หรือไม่ก็หายไปเลยครับ จะมีเมลล์มาขอบตคุณบ้างเมื่อมีคำถามใหม่ -*-&lt;br /&gt;&lt;br /&gt;ที่ประหลาดใจกว่าก็คือในหลายๆเวบบอร์ดก็มีการบ่นเรื่องนี้ครับ คือคนตอบกระทู้แล้ว พอคนได้คำตอบก็หายไปเลย อย่างน้อยมาบอกว่าทำได้แล้ว คนอื่นๆที่ search เจอภายหลังจะได้มั่นใจว่า เจอคำตอบที่ถูกต้องก็ยังดี ถ้าคำตอบในกระทู้ยังไม่ถูก แต่ไปหาวิธีแก้ปัญหามาได้แล้ว น่าจะมีอัพเดทในกระทู้นั้นๆบ้าง จะได้มีประโยชน์กับคนอื่นต่อไปครับ&lt;br /&gt;&lt;br /&gt;ก็อยากชักชวนให้เพื่อนๆพี่ๆน้องๆ ที่ได้คำตอบแล้วช่วยมาอัพเดทในกระทู้ที่ตัวเองตั้งด้วยนะครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-6762606597358843000?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/6762606597358843000/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=6762606597358843000' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6762606597358843000'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6762606597358843000'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/12/blog-post.html' title='เรื่องประหลาดใจ'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-3219776894833932855</id><published>2009-12-03T22:35:00.003+07:00</published><updated>2010-07-11T00:28:28.220+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>ASP.NET สั่งรัน Win App บนเครื่อง Client</title><content type='html'>ผมเคยสร้าง Web Application ตัวหนึ่ง เพื่อทำเป็น Application Portal คือถ้า user sign in เข้ามาใน Web นี้ จะมองเห็น link ของโปรแกรมทั้ง Win App และ Web App ที่มีสิทธิ์เปิดใช้งาน รวมถึงทำ Single Sign On ด้วย นั่นคือจะส่ง User Name + Password เพื่อไป log in ที่ app นั้นๆทันทีครับ แต่ใน App นั้นๆก็ต้องรองรับการส่ง Parameter ๆไปด้วยนะ&lt;br /&gt;&lt;br /&gt;ทีนี้ถ้าเป็น Web App ด้วยกันก็ไม่มีปัญหาครับ แค่สร้าง link เท่านั้นเองและก็ส่ง parameter ที่เข้ารหัสไปด้วย แต่ถ้าเป็น Win App นี่แหละที่เป็นเรื่องใหญ่ เพราะว่า ASP.NET นั้นทำงานเป็น Server side script นั่นคือคำสั่งพวก Shell จะทำให้เกิดการรันโปรแกรมที่ฝั่ง Server ครับ เหมือนกรณีเราเขียนคำสั่ง MessageBox.Show นั่นแหละ มันจะขึ้น message box ที่ server เสมอ เราต้องใช้ Client side script แทน ถ้าเป็น javascript ก็คือ alert() นั่นเอง&lt;br /&gt;&lt;br /&gt;ทีนี้ Win App ที่เราต้องการเรียกนั้น ถ้าเราพัฒนาด้วย .NET 2.0 ขึ้นไป สามารถใช้ ClickOnce ในการ Deploy ได้ ถ้าเป็นแบบนี้ก็ง่ายขึ้นครับ เพราะเราจะมี URI ให้เลือกใช้ดังนี้&lt;br /&gt;&lt;ul&gt;&lt;li&gt;http://ClickOnceServer/TestApplication ตัวนี้จะลิงค์ไปหน้าหลักของโปรแกรม ซึ่งจะมีปุ่ม Install และ ปุ่ม Install Prerequisites&lt;/li&gt;&lt;br /&gt;&lt;li&gt;http://ClickOnceServer/TestApplication/TestApplication.Application ตัวนี้จะไปรัน Application ทันทีครับ&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;ดังนั้นเราก็เขียน javascript เรียก .Application เพื่อสั่งรันโปรแกรมได้เลย&lt;/p&gt;&lt;br /&gt;&lt;pre class="brush:javascript"&gt;&lt;br /&gt;function btnAttn_OnClick() {&lt;br /&gt;     window.open("http://ClickOnceServer/TestApplication/TestApplication.Application", "Application");&lt;br /&gt;     intervalId = setInterval(UpdateInfo, 10000);&lt;br /&gt;     event.returnValue = false;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;ในโค้ดผมมีการใช้ setInverval ด้วย เพราะต้องการไปเรียก UpdateInfo function ทุก 10 วินาที function ตัวนี้จะไปเรียก WebService เพื่อทำการ update สถานะบน label ทุก 10 วินาทีครับ &lt;/p&gt;&lt;p&gt;ตัว ClickOnce เราสามารถส่ง Commandline argument (parameter) ไปได้ด้วยครับ แต่ขอข้ามไปก่อนละกัน&lt;/p&gt;&lt;p&gt;ทีนี้ถ้าเป็น Win App ที่ไม่ใช่ ClickOnce ก่อนอื่นเลยจะมีข้อจำกัดดังนี้&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Win App นั้นต้อง Install ที่เครื่อง Client แล้ว และเราต้องรู้ Path ที่แน่นอน&lt;/li&gt;&lt;li&gt;ต้องกำหนด Security Option ของ IE ในเครื่อง Client ด้วย ที่ผมทำคือไปกำหนด Trust Site ครับ เพราะเป็น Intranet อยู่แล้วก็ไม่มีปัญหา เพราะเราต้องใช้ WScript ในการสั่ง Shell application ที่ต้องการนั่นเอง&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;ดังนั้นวิธีนี้เหมาะกับเครื่องภายในเท่านั้นครับ &lt;/p&gt;&lt;p&gt;โค้ดส่วน HTML ซึ่งเก็บ hidden field ไว้สองตัวนั่นคือ User name กับ password อย่าลืมเข้ารหัสด้วยนะครับ&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;pre class="brush:html"&gt;&lt;br /&gt;&amp;lt;body&amp;gt;&lt;br /&gt;     &amp;lt;form id="form1"&amp;gt;&lt;br /&gt;          &amp;lt;div&amp;gt;&lt;br /&gt;               &amp;lt;input type=hidden id='hdnExeCommand' name='hdnExeCommand' value='&amp;lt;%=strExeCommand%&amp;gt;'&amp;gt;&lt;br /&gt;               &amp;lt;input type=hidden id='hdnWinAppLocation' name='hdnWinAppLocation' value='&amp;lt;%=strWinAppLocation %&amp;gt;'&amp;gt;&lt;br /&gt;          &amp;lt;/div&amp;gt;&lt;br /&gt;     &amp;lt;/form&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt&lt;/pre&gt;&lt;p&gt;จะเห็นว่ามีการเอาค่าตัวแปรมาใส่ใน hidden field ด้วย ค่าตัวแปรที่ได้นี้มาจากคำสั่งถอดรหัสในฝั่ง Code Behind ครับ ทีนี้มาดู vbscript กัน (จริงๆจะใช้ javascript ก็ได้และดีกว่าด้วย แต่ผมอยากลอง vbscript ครับ)&lt;/p&gt;&lt;br /&gt;&lt;pre class="brush:vb" &gt;&lt;br /&gt;&amp;lt;script language="vbscript"&amp;gt;&lt;br /&gt;dim WshShell, strExe, fso, strCommand&lt;br /&gt;set WshShell = CreateObject("WScript.Shell")&lt;br /&gt;set fso = CreateObject("Scripting.FileSystemObject")&lt;br /&gt;strExe = document.all("hdnWinAppLocation").value&lt;br /&gt;strCommand = document.all("hdnExeCommand").value&lt;br /&gt;if fso.FileExists(strexe) then&lt;br /&gt;     WshShell.Run strCommand ,1, false&lt;br /&gt;else&lt;br /&gt;     msgbox "File not found.",vbOKOnly+vbInformation,"TG Application Portal"&lt;br /&gt;end if&lt;br /&gt;set fso = nothing&lt;br /&gt;set WshShell = nothing&lt;br /&gt;window.close()&lt;br /&gt;&amp;lt;script&amp;gt&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ส่วนเรื่องการส่ง command line argument กับ Encrypt&amp;Decrpyt ถ้ามีเวลาจะกลับมาเขียนเพิ่มครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-3219776894833932855?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/3219776894833932855/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=3219776894833932855' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/3219776894833932855'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/3219776894833932855'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/12/aspnet-win-app-client.html' title='ASP.NET สั่งรัน Win App บนเครื่อง Client'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-6423695428234156987</id><published>2009-11-16T20:43:00.000+07:00</published><updated>2009-11-16T20:43:00.604+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Talk'/><title type='text'>.NET Framework 4.0</title><content type='html'>วันก่อนเจอคุณมี่ (MVP, GreatFriends Leader) ที่งานอบรมที่ไมโครซอฟท์ เลยมีโอกาสถามเรื่อง .NET Framework 4.0 ครับ เพราะผมไม่ค่อยได้ติดตาม .NET Framework 4.0 และ VS 2010 เลย (ทั้งที่มีแต่คนบอกว่า VS2010 สุดยอดให้ลองโหลดมาเล่น)&lt;br /&gt;&lt;br /&gt;คำถามแรกคือ .NET Framework 4.0 ยังใช้ Core เดียวกันกับ .NET 2.0, 3.0 และ 3.5 หรือไม่&lt;br /&gt;คำตอบคือ ทาง MS พัฒนาขึ้นมาใหม่เป็น Core ตัวใหม่เลย แต่ไม่ต้องห่วงครับ (ยังไม่ได้ถาม และยังไม่ได้ห่วงเลย คุณมี่รีบตอบให้เสร็จ - สงสัยกลัว VS2010 จะขายได้น้อย 555) เพราะ VS2010 สนับสนุนเรื่อง Target Framework เหมือนเดิม ดังนั้นเราสามารถเลือกพัฒนา .NET เวอร์ชัน 2.0,3.0, 3.5 หรือ 4.0 ก็ได้&lt;br /&gt;&lt;br /&gt;อะ ก็ฟังดูดี แต่ว่าพอผมเอา VS2008 ไปเปิด Solution ที่พัฒนาด้วย VS2005 มันจะบังคับให้ Convert ยันเตเลย มันเปิดแล้วเลือก Target Framework เป็น 2.0 อัตโนมัติไม่ได้เหรอ แล้ว VS2010 จะทำได้มั๊ยเนี่ย ไม่อยาก Convert น่ะ (ตอนนั้นยังไม่ได้ห่วง ก็เลยลืมถามคำถามนี้ เซ็งเลยตรู แต่ตอนนี้ยังเป็น Beta ดังนั้นก็ต้องรอดูเวอร์ชันจริงอีกที)&lt;br /&gt;&lt;br /&gt;คำถามที่สองก็คือ แล้ว .NET Framework 4.0 มันใหญ่มั๊ย เพราะว่า .NET Framework 2.0 Redis. มันแค่ 22.4 MB เอง พอใช้ Click Once แล้วต้อง Deploy ตัว .NET Framework ไปด้วย มันไม่ใหญ่เลยทำได้สะดวกครับ แต่ว่าพอมาใช้ .NET Framework 3.5 SP1 มันตั้ง 231 MB แนะวุ้ย ใหญ่มากกกกกกกกกกกกกกก&lt;br /&gt;&lt;br /&gt;คำตอบคือไม่ใหญ่ครับ เพราะพัฒนาขึ้นใหม่ (จบ สั้นๆได้ใจความ)&lt;br /&gt;&lt;br /&gt;ผมเลยลองไปดูในหน้าดาวน์โหลดของ MSDN ถ้าเลือกเป็น .NET Framework 4.0 X86 มันแค่ 37.7 MB เอง ถือว่าขนาดโอเคครับ ถ้าเป็น x64 ก็ 54.6 MB&lt;br /&gt;&lt;br /&gt;ในหน้า MSDN มี Visual Studio Express 2010 Beta ให้โหลดมาลองด้วยครับ ถ้าใครกลัวโหลด VS2010 ตัว Profession หรือ Team Suit มาใช้แล้วติดใจต้องเสียเงินซื้อ ลองใช้ Express เวอร์ชันก็ได้นะครับ พอใช้รับงาน freelance หากินได้ ยกเว้นเรืองออกรายงานที่ต้องใช้ rdlc หรือไม่ก็ PrintDocument ที่ต้องถึกหน่อยครับ&lt;br /&gt;&lt;br /&gt;อีกเรื่องที่สำคัญ คนที่มีลิขสิทธิ์ VS2008 แล้วอยากจะ upgrade ไปใช้ VS2010 ลองติดต่อคุณมี่ดูครับ เพราะเหมือนว่าจะมีโปรโมชันสำหรับคนต้องการ Upgrade ในราคาพิเศษนะ แต่ผมฟังไม่ถนัดเพราะว่าที่ทำงานผมมี Enterprise Agreement อยู่แล้ว มัน upgrade ฟรี (เพราะเสียเงินค่า EA ไปแล้ว)เลยไม่ได้สนใจมาก ที่สำคัญอีกอย่างคือผมกำลังกินขนมกับกาแฟอยู่น่ะครับ ถ้ายังไงคนที่สนใจ VS2010 ลองติดต่อคุณมี่ดูครับ ผมไม่ได้ขออนุญาตเลยไม่กล้าลงอีเมลล์ไว้ ลอง search หาดู หรือไม่ก็แวะไปคุยกับคุณมี่ที่ GreatFriends ครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-6423695428234156987?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/6423695428234156987/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=6423695428234156987' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6423695428234156987'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6423695428234156987'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/11/net-framework-40.html' title='.NET Framework 4.0'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-6319464308437479325</id><published>2009-11-12T21:34:00.005+07:00</published><updated>2010-07-11T00:27:54.518+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>Javascript - สร้าง TextBox เพื่อ filter รายการใน DropDownList</title><content type='html'>ผมได้รับ requirement ใหม่จาก user เจ้ากรรมอีกแล้ว นั่นคือมี DropDownList ตัวหนึ่งใน ASP.NET ที่มีรายการมากกว่า 100 รายการ (อันนี้กรองมาแล้วนะครับ) ซึ่ง option มันเยอะมาก ดังนั้น user ต้องการให้โปรแกรมสามารถ filter รายการใน DropDownList นี้ได้ครับ ทีนี้จะเขียนโปรแกรมที่ฝั่ง server ก็สามารถทำได้ แต่มันจะทำให้มีการ postback ไปหา ซึ่งไม่ค่อยดีเท่าไหร่ ดังนั้นผมเลยใช้ javascript สำหรับ filter แทนครับ&lt;br /&gt;&lt;br /&gt;วิธีก็คือผมสร้าง Textbox ขึ้นมาหนึ่งตัว สำหรับให้ user พิมพ์ข้อความที่ต้องการกรอง และสร้าง drop-down list อีกหนึ่งตัวโดยให้ visibility=hidden คือซ่อนไม่ให้ user เห็น สำหรับเก็บ option ทั้งหมดที่ไม่ได้ทำการกรองครับ ลองมาดูโค้ดกัน&lt;br /&gt;&lt;br /&gt;เริ่มจาก WebControl ก่อน มี DropDownList 2 ตัว และ TextBox  ครับ&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:html"&gt;&lt;br /&gt;&amp;lt;asp:DropDownList ID="ddlWorkCode" runat="server" CssClass="txtRequired"  Width="540px" AutoPostBack="True" style="height:24px"&amp;gt;&amp;lt;/asp:DropDownList"&amp;gt;&amp;nbsp;&lt;br /&gt;&amp;lt;asp:TextBox ID="txtSearchWorkCode" runat="server" Width="421px" style="color: #000099; background-color: #afeeee;""&amp;gt;&amp;lt;/asp:TextBox"&amp;gt;&lt;br /&gt;&amp;lt;asp:DropDownList ID="ddlCache" runat="server" Width="0px" style="visibility:hidden" "&amp;gt;&amp;lt;/asp:DropDownList"&amp;gt;&amp;nbsp;  &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ในหน้า Code Behind ก็ทำการ Add Attribute ให้ txtSearchWorkCode &lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;txtSearchWorkCode.Attributes.Add("onkeyup", "txtSearchWorkCode_OnKeyUp(this);")&lt;br /&gt;txtSearchWorkCode.Attributes.Add("onblur", "txtSearchWorkCode_OnBlur();")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;คราวนี้มาถึง javascript บ้าง คราวนี้เขียนสดๆไม่ได้ใช้ jQuery ครับ &lt;br /&gt;&lt;pre class="brush:javascript"&gt;&lt;br /&gt;        function txtSearchWorkCode_OnKeyUp(obj) {&lt;br /&gt;            if (event.keyCode != 13 || event.keyCode != 9) {&lt;br /&gt;                var str = obj.value.toUpperCase();&lt;br /&gt;                var ddlWorkCode = $get("ddlWorkCode");&lt;br /&gt;                var ddlCache = $get("ddlCache");&lt;br /&gt;                var listWorkCode = ddlCache.getElementsByTagName("OPTION");&lt;br /&gt;&lt;br /&gt;                // ถ้าไม่มี OPTION ใน cache ให้ออกจาก function&lt;br /&gt;                if(listWorkCode.length==0) return false;&lt;br /&gt;&lt;br /&gt;                //clear option ใน DropDownList&lt;br /&gt;                ddlWorkCode.length = 0; &lt;br /&gt;                for (i = 0; i &lt; listWorkCode.length; i++) {&lt;br /&gt;                    if (listWorkCode[i].text.toUpperCase().indexOf(str) &gt; -1) {&lt;br /&gt;                        var o = listWorkCode[i].cloneNode(true);&lt;br /&gt;                        ddlWorkCode.appendChild(o);&lt;br /&gt;                    }&lt;br /&gt;                }&lt;br /&gt;                ddlWorkCode.selectedIndex = 0;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        function txtSearchWorkCode_OnBlur() {&lt;br /&gt;            if ($get("txtSearchWorkCode").value == "") {&lt;br /&gt;                // ถ้าไม่มีคำสั่งกรอง ให้ไปดึง OPTION ทั้งหมดจาก DropDownList2 มาใส่คืน&lt;br /&gt;                                var ddlWorkCode = $get("ddlWorkCode");&lt;br /&gt;                var ddlCache = $get("ddlCache");&lt;br /&gt;&lt;br /&gt;                ddlWorkCode.lenght = 0;&lt;br /&gt;                for (i = 0; i &lt; ddlWorkCode2.options.length; i++) {&lt;br /&gt;                    var o = ddlWorkCode2.options[i].cloneNode(true);&lt;br /&gt;                    ddlWorkCode.appendChild(o);&lt;br /&gt;                }&lt;br /&gt;                ddlWorkCode.selectedIndex = 0;&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ในส่วนคำสั่ง javascript มีสองจุดที่น่าสนใจครับ นั่นคือ&lt;br /&gt;1. ผมใช้ ddlWorkCode.length=0; ในการเคลียร์ option ของ drop-down list ทิ้งครับ ถือว่าง่ายและสะดวกมาก ทีแรกผมใช้ for วนลูปเพื่อ remove element ออก ซึ่งแน่นอนว่าช้ากว่าครับ&lt;br /&gt;&lt;br /&gt;2. ผมใช้ cloneNode method ในการ copy OPTION จาก drop-down list ตัวที่สอง (ที่เก็บข้อมูลทั้งหมดไว้) แล้วก็เอา OPTION ที่ clone ได้ไปใส่ใน drop-down list ตัวแรกด้วยคำสั่ง appendChild ทีแรกผมใช้วิธีสร้าง new Option ครับ แต่เจอคำสั่ง cloneNode ที่ www.java2s.com ใช้ได้สะดวกดีครับ และที่สำคัญพอลอง search ใน google พบว่าคำสั่ง cloneNode ทำได้เร็วกว่า New element ซะอีก&lt;br /&gt;&lt;br /&gt;ตอนนี้ลองทดสอบโปรแกรมดู ก็ใช้งานได้ดียังไม่เจอ bug แต่เพิ่งทดสอบได้นิดเดียวคงต้องลองเล่นดูอีกสักพักครับ&lt;br /&gt;&lt;br /&gt;ปล. สำหรับ $get() เป็น shortcut ของ ASP.NET AJAX ครับ มีค่าเท่ากับ document.getElementById()&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-6319464308437479325?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/6319464308437479325/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=6319464308437479325' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6319464308437479325'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6319464308437479325'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/11/aspnet-filter-drop-down-list.html' title='Javascript - สร้าง TextBox เพื่อ filter รายการใน DropDownList'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-8776744759121413375</id><published>2009-10-27T22:48:00.003+07:00</published><updated>2009-10-27T23:05:10.941+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Talk'/><title type='text'>แนะนำ jnithi.bloggang.com</title><content type='html'>วันนี้กลับไปอ่าน blog เดิม (โปรแกรมมั่ว พ่อลูกอ่อน) ที่เขียนไว้ที่ &lt;a href="http://jnithi.bloggang.com"&gt;jnithi.bloggang.com&lt;/a&gt; ครับ ก็เลยนึกขึ้นได้ว่ายังไม่ได้ทำ link ที่ bloggang มาที่ blogger นี่เลย จริงๆแล้วตอนที่เริ่มกลับมาเขียน blog ที่ blogger นี่ก็ไปอัพเดทที่ bloggang แต่ว่าพอกด save มันดันมี error ขึ้นมา เลยเซ็งอารมณ์ปล่อยทิ้งไว้อย่างนั้น จากนั้นก็เลยลืมไปเลยครับ 5555&lt;br /&gt;&lt;br /&gt;ทีแรกตั้งใจว่าจะเขียนเรื่องเกี่ยวกับการพัฒนาโปรแกรมที่ blogger ส่วนที่ bloggang จะเขียนเรื่องลูกสาวอย่างเดียว ปรากฏว่าเพื่อนๆบอกว่าอยากให้ไปเขียนเรื่องน้องแพร์ที่ hi5 กับ facebook แทน แต่ด้วยความขี้เกียจก็เลยไม่ได้อัพเดททั้ง bloggang, hi5 และ facebook (จริงๆแล้วผมเล่น hi5 กับ facebook ไม่เป็นอะ เข้าไปแล้วมันงงๆๆๆ)&lt;br /&gt;&lt;br /&gt;พอกลับไปอ่าน โปรแกรมมั่ว พ่อลูกอ่อน blog ก็เลยนึกขึ้นได้ว่าอาจจะมีประโยชน์สำหรับชาวโปรแกรมมั่วอีกหลายท่านครับ ก็เลยมาเขียน blog แนะนำที่นี่อีกทีครับ&lt;br /&gt;&lt;br /&gt;รู้สึกว่าที่ bloggang คนจะเข้ามา comment เยอะกว่านะ ทั้งๆที่ดูจาก PageView แล้วยังน้อยกว่าคนที่เข้า blog นี้ซะอีก สงสัยว่าคนเข้า blog นี้จะรีบเข้ามาหาวิธีเขียนโปรแกรมเพราะส่วนมากมาจาก Search Engine เลยมาเร็ว อ่านเร็ว ไปเร็ว เพราะต้องรีบกลับไปปั่นงาน 5555 (เป็นเหมือนกัน) แต่ตอนนี้คนที่เข้าซ้ำประจำก็เริ่มมีหลายคนนะครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-8776744759121413375?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/8776744759121413375/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=8776744759121413375' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/8776744759121413375'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/8776744759121413375'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/10/jnithibloggangcom.html' title='แนะนำ jnithi.bloggang.com'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-2603319032976551677</id><published>2009-10-22T12:19:00.001+07:00</published><updated>2010-07-11T00:29:02.507+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>ASP.NET รวม Crystal Report 2 รายงานเป็น pdf ด้วย iTexSharp</title><content type='html'>คราวนี้เป็นตอนที่ 3 สำหรับ iTextSharp ครับ เราจะสร้าง pdf file จาก Crystal Report (ต่อจากนี้จะเรียกย่อๆว่า CR) 2 ไฟล์ มารวมกันให้เป็นไฟล์เดียว ในการทดสอบผมใช้ CR ตัวเดียว แต่สร้างเป็น 2 report สำหรับเอามารวมกันเป็น pdf ลองดูโค้ดกันครับ&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Imports CrystalDecisions.CrystalReports.Engine&lt;br /&gt;Imports XXEntity = jnithi.CLASS.XX.Entity&lt;br /&gt;Imports XXManager = jnithi.CLASS.XXManager&lt;br /&gt;&lt;br /&gt;Partial Class frmTestITextSharp&lt;br /&gt;    Inherits System.Web.UI.Page&lt;br /&gt;&lt;br /&gt;    Private Sub CreateReport()&lt;br /&gt;        'สร้าง CR1&lt;br /&gt;        Dim myReport1 As New ReportDocument()&lt;br /&gt;        Dim rpt1Data = XXManager.XXOutStandingManager.GetXXOutStandings(0, Nothing, Nothing, "R00939", "")&lt;br /&gt;        myReport1.FileName = Server.MapPath("~/Reports/XX/rptXXOutstanding.rpt")&lt;br /&gt;        myReport1.SetDataSource(rpt1Data)&lt;br /&gt;        myReport1.SetParameterValue(0, Date.Today.ToString("dd-MMM-yyyy"))&lt;br /&gt;        myReport1.SetParameterValue(1, "")&lt;br /&gt;        myReport1.SetParameterValue(2, "Client")&lt;br /&gt;&lt;br /&gt;        ' สร้าง CR2&lt;br /&gt;        Dim myReport2 As New ReportDocument&lt;br /&gt;        Dim rpt2Data = XXManager.XXOutStandingManager.GetXXOutStandings(0, Nothing, Nothing, "A01032", "")&lt;br /&gt;        myReport2.FileName = Server.MapPath("~/Reports/XX/rptXXOutstanding.rpt")&lt;br /&gt;        myReport2.SetDataSource(rpt2Data)&lt;br /&gt;        myReport2.SetParameterValue(0, Date.Today.ToString("dd-MMM-yyyy"))&lt;br /&gt;        myReport2.SetParameterValue(1, "")&lt;br /&gt;        myReport2.SetParameterValue(2, "Client")&lt;br /&gt;&lt;br /&gt;        ' สร้าง pdfReader1 ชื่อ reader1 โดย export stream จาก CR1&lt;br /&gt;        Dim s1 As System.IO.Stream = myReport1.ExportToStream(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat)&lt;br /&gt;        Dim reader1 As New iTextSharp.text.pdf.PdfReader(s1)&lt;br /&gt;&lt;br /&gt;        ' สร้าง pdfReader2 ชื่อ reader2 โดย export stream จาก CR2&lt;br /&gt;        Dim s2 As System.IO.Stream = myReport2.ExportToStream(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat)&lt;br /&gt;        Dim reader2 As New iTextSharp.text.pdf.PdfReader(s2)&lt;br /&gt;&lt;br /&gt;        ' สร้าง pdfCopy โดยกำหนด os เป็น output FileStream เพื่อสร้าง File ชื่อ rptMerge.pdf&lt;br /&gt;        Dim pdfDoc As New iTextSharp.text.Document()&lt;br /&gt;        Dim os As New System.IO.FileStream(Server.MapPath("~/rptMerge.pdf"), IO.FileMode.Create)&lt;br /&gt;        Dim pdfCopy As New iTextSharp.text.pdf.PdfCopy(pdfDoc, os)&lt;br /&gt;&lt;br /&gt;        pdfDoc.Open() ' เปิด pdf Document เพื่อเริ่มการ copy&lt;br /&gt;&lt;br /&gt;        ' วนลูปอ่าน reader1 เพื่อ copy ทีละหน้า&lt;br /&gt;                For iPage As Integer = 1 To reader1.NumberOfPages&lt;br /&gt;            pdfCopy.AddPage(pdfCopy.GetImportedPage(reader1, iPage))&lt;br /&gt;        Next&lt;br /&gt;&lt;br /&gt;        ' วนลูปอ่าน reader2 เพื่อ copy ทีละหน้า&lt;br /&gt;                For iPage As Integer = 1 To reader2.NumberOfPages&lt;br /&gt;            pdfCopy.AddPage(pdfCopy.GetImportedPage(reader2, iPage))&lt;br /&gt;        Next&lt;br /&gt;&lt;br /&gt;        ' Clear Memory&lt;br /&gt;        pdfDoc.Close()&lt;br /&gt;        pdfCopy.Close()&lt;br /&gt;        reader1.Close()&lt;br /&gt;        reader2.Close()&lt;br /&gt;        os.Close()&lt;br /&gt;        os.Dispose()&lt;br /&gt;        s1.Close()&lt;br /&gt;        s1.Dispose()&lt;br /&gt;        s2.Close()&lt;br /&gt;        s2.Dispose()&lt;br /&gt;&lt;br /&gt;        Response.Redirect("~/rptMerge.pdf")&lt;br /&gt;&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load&lt;br /&gt;        CreateReport()&lt;br /&gt;    End Sub&lt;br /&gt;End Class&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;เมื่อเราใช้ iTextSharp สร้าง pdf ไฟล์เสร็จก็สั่ง redirect เพื่อเปิดไฟล์ที่ browser ครับ ทีแรกผมทดสอบให้ iTextSharp สร้าง pdf เป็น MemoryStream แล้วลองใช้ Response.BinaryWrite กับ Response.OutputStream.Write เพื่อสร้าง HTTPResponse แต่ไม่สามารถทำได้ครับ พอเปิดไฟล์ Browser จะฟ้องว่าไฟล์เสีย ลอง search ใน google ดูหลายๆคนพบปัญหาเดียวกัน เลยแก้ด้วยการใช้ FileStream เพื่อสร้าง temp file แทนตามตัวอย่างด้านบนครับ&lt;br /&gt;&lt;br /&gt;ลองดูโค้ด MemoryStream ที่มีปัญหาดู เผื่อมีคนแก้ได้ครับ&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;' โค้ดด้านบนเหมือนเดิม เริ่มเปลี่ยนตั้งแต่ Dim os ลงมา&lt;br /&gt;' เปลี่ยนจาก FileStream เป็น MemoryStream&lt;br /&gt;'Dim os As New System.IO.FileStream(Server.MapPath("~/rptMerge.pdf"), IO.FileMode.Create)&lt;br /&gt;Dim os As New System.IO.MemoryStream()&lt;br /&gt;&lt;br /&gt;Dim pdfCopy As New iTextSharp.text.pdf.PdfCopy(pdfDoc, os)&lt;br /&gt;pdfDoc.Open() ' เปิด pdf Document เพื่อเริ่มการ copy&lt;br /&gt;&lt;br /&gt;' วนลูปอ่าน reader1 เพื่อ copy ทีละหน้า&lt;br /&gt;For iPage As Integer = 1 To reader1.NumberOfPages&lt;br /&gt;    pdfCopy.AddPage(pdfCopy.GetImportedPage(reader1, iPage))&lt;br /&gt;Next&lt;br /&gt;&lt;br /&gt;' วนลูปอ่าน reader2 เพื่อ copy ทีละหน้า&lt;br /&gt;For iPage As Integer = 1 To reader2.NumberOfPages&lt;br /&gt;    pdfCopy.AddPage(pdfCopy.GetImportedPage(reader2, iPage))&lt;br /&gt;Next&lt;br /&gt;&lt;br /&gt;Response.Clear()&lt;br /&gt;Response.ContentType = "application/pdf"&lt;br /&gt;Response.AddHeader("content-disposition", "attachment;filename=Export.pdf")&lt;br /&gt;Response.BinaryWrite(os.ToArray)&lt;br /&gt;&lt;br /&gt;' Clear Memory&lt;br /&gt;pdfDoc.Close()&lt;br /&gt;pdfCopy.Close()&lt;br /&gt;reader1.Close()&lt;br /&gt;reader2.Close()&lt;br /&gt;os.Close()&lt;br /&gt;os.Dispose()&lt;br /&gt;s1.Close()&lt;br /&gt;s1.Dispose()&lt;br /&gt;s2.Close()&lt;br /&gt;s2.Dispose()&lt;br /&gt;Response.End()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;blog ก่อนหน้านี้มีคนบอกอยากให้ comment ด้วย คราวนี้ก็เลยใส่ comment มา แต่ไม่แน่ใจว่าจะละเอียดพอหรือเปล่าครับ&lt;br /&gt;&lt;br /&gt;บทความก่อนหน้า&lt;br /&gt;&lt;a href="http://jnithi.blogspot.com/2009/07/pdf-itextsharp.html"&gt;เขียนโปรแกรมจัดการเอกสาร pdf ด้วย iTextSharp&lt;/a&gt;&lt;br /&gt;&lt;a href="http://jnithi.blogspot.com/2009/07/pdf-itextsharp-2.html"&gt;การจัดการเอกสาร pdf ด้วย iTextSharp(2)&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-2603319032976551677?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/2603319032976551677/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=2603319032976551677' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/2603319032976551677'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/2603319032976551677'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/10/aspnet-crystal-report-2-pdf-itexsharp.html' title='ASP.NET รวม Crystal Report 2 รายงานเป็น pdf ด้วย iTexSharp'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-7947293511222313265</id><published>2009-10-16T23:28:00.000+07:00</published><updated>2009-10-16T23:28:00.677+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VBScript'/><title type='text'>VBScript อ่านค่า User Account เพื่อเปิด Website อัตโนมัติ</title><content type='html'>ผมได้รับคำสั่งให้ทำอย่างไรก็ได้ เมื่อ user ทำการ log in เข้า Windows แล้วให้ตรวจสอบว่าเป็น user ที่อยู่ในกลุ่มพิเศษหรือไม่ ถ้าใช่ให้เปิด Web Application ขี้นมา เพื่อแสดงสถานะงานคงค้าง (เรื่องสำคัญมากคือการทวงหนี้ลูกค้า)&lt;br /&gt;&lt;br /&gt;ไปนั่งคิดนอนคิดก็เลยคิดว่าน่าจะเขียน Log on script ด้วย VBScript มาทำงานนี้ครับ เริ่มแรกผมเก็บข้อมูลรายชื่อ user ไว้ใน database ก่อน แล้วพอ Log on script ทำงานจะไปตรวจสอบว่า User นั้นตรงกับที่อยู่ใน database หรือไม่ ทีนี้เราต้องใช้ Windows Scripting Host (WScript) มาร่วมด้วยครับ&lt;br /&gt;&lt;br /&gt;&lt;pre name=code class=vb&gt;&lt;br /&gt;ON ERROR RESUME NEXT&lt;br /&gt;&lt;br /&gt;'*******************************************************&lt;br /&gt;'// GET LOGON USER ACCOUNT&lt;br /&gt;'*******************************************************&lt;br /&gt;Dim WSHNetwork &lt;br /&gt;Set WSHNetwork = CreateObject("Wscript.Network")&lt;br /&gt;&lt;br /&gt;Dim userAccount &lt;br /&gt;userAccount = WSHNetwork.UserDomain &amp; "\" &amp; WSHNetwork.UserName&lt;br /&gt;Set WSHNetwork = nothing&lt;br /&gt;&lt;br /&gt;'*******************************************************&lt;br /&gt;'// VALIDATE USER &lt;br /&gt;'*******************************************************&lt;br /&gt;&lt;br /&gt;Set cnn = CreateObject("adodb.connection")&lt;br /&gt;Set rstUser = CreateObject("adodb.recordset")&lt;br /&gt;&lt;br /&gt;cnn.connectionString = "Provider=SQLOLEDB.1;Persist Security Info=False;User ID=xxxx;Password=yyyy;Initial Catalog=UserList;Data Source=xxSQLServer"&lt;br /&gt;&lt;br /&gt;cnn.open&lt;br /&gt;&lt;br /&gt;Dim strSQL&lt;br /&gt;strSQL = "select employeeid from dbo.tblUser a where aduser = '" &amp; userAccount  "'"&lt;br /&gt;rstUser.open strsql, cnn&lt;br /&gt;&lt;br /&gt;If not rstUser.eof Then&lt;br /&gt; '// If valid user, execute IE &lt;br /&gt; Dim WSHShell&lt;br /&gt; Set WSHShell = CreateObject("WScript.Shell")&lt;br /&gt; WSHShell.Run "iexplore.exe -new http://myHost/myApplication"&lt;br /&gt; Set WSHShell = nothing&lt;br /&gt;End If&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;rstUser.close&lt;br /&gt;cnn.close&lt;br /&gt;&lt;br /&gt;Set rstUser = Nothing&lt;br /&gt;Set cnn = Nothing&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ทดสอบการทำงานก็ได้ผลโอเคครับ แต่โดน Network Admin ติงมานิดหน่อยว่าไม่อยากให้ไปติดต่อ database เพราะมันช้า และต้องใช้ traffic ของ network เนื่องจาก SQLServer กับ AD อยู่คนละ Server กัน ดังนั้นเลยเปลี่ยนเอา list ของ user มาเก็บเป็น Text file แทนครับ ลองดูโค้ดเฉพาะตรงนี้กัน ส่วนหา user log on ก็เหมือนโค้ดด้านบน&lt;br /&gt;&lt;br /&gt;&lt;pre name=code class=vb&gt;&lt;br /&gt;const ForReading = 1, TristateTrue = -1&lt;br /&gt;&lt;br /&gt;Set objFSO = CreateObject("Scripting.FileSystemObject")&lt;br /&gt;Set objFile = objFSO.OpenTextFile("\\myserver\netlogon\UserList.txt", ForReading, false, TristateTrue)&lt;br /&gt;&lt;br /&gt;Dim user&lt;br /&gt;Dim isFound&lt;br /&gt;isFound = 0&lt;br /&gt;&lt;br /&gt;Do Until objFile.AtEndOfStream&lt;br /&gt;    user = trim(objFile.ReadLine)&lt;br /&gt; If lcase(user) = lcase(useraccount) Then&lt;br /&gt;  isFound = 1&lt;br /&gt;  'msgbox isFound&lt;br /&gt;  Exit Do&lt;br /&gt; End If &lt;br /&gt;Loop&lt;br /&gt;&lt;br /&gt;objFile.Close&lt;br /&gt;Set objFile = Nothing&lt;br /&gt;Set objFSO = Nothing&lt;br /&gt;&lt;br /&gt;If isFound = 1 Then&lt;br /&gt; '// If valid user, execute IE &lt;br /&gt; Dim WSHShell&lt;br /&gt; Set WSHShell = CreateObject("WScript.Shell")&lt;br /&gt; WSHShell.Run "iexplore.exe -new http://myHost/myApplication"&lt;br /&gt; Set WSHShell = nothing&lt;br /&gt;End If&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-7947293511222313265?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/7947293511222313265/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=7947293511222313265' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/7947293511222313265'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/7947293511222313265'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/10/vbscript-user-account-website.html' title='VBScript อ่านค่า User Account เพื่อเปิด Website อัตโนมัติ'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-2069061052790988499</id><published>2009-10-12T21:49:00.000+07:00</published><updated>2009-10-12T21:49:00.739+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VB.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>ลองสร้าง Extension Method ให้ Enum</title><content type='html'>คราวก่อนตอนที่ผมต้องการหาชื่อของ Enum ได้ลองทำการสร้าง Extension Method ชื่อ ToDictionary ไว้ ตามชื่อเลยครับ Method นี้จะไปสร้าง Dictionary สำหรับเก็บชื่อ (Key) และค่า (Value) ของ Enum ที่ต้องการ&lt;br /&gt;&lt;br /&gt;&lt;pre class=code name=vb&gt;&lt;br /&gt;Public Module EnumExtension&lt;br /&gt;    &amp;lt;System.Runtime.CompilerServices.Extension()&amp;gt; _&lt;br /&gt;    Public Function ToDictionary(ByVal en As System.Enum) As Dictionary(Of String, Integer)&lt;br /&gt;        Dim result As New Dictionary(Of String, Integer)&lt;br /&gt;        For Each v As Integer In System.Enum.GetValues(en.GetType)&lt;br /&gt;            result.Add(System.Enum.GetName(en.GetType, v), v)&lt;br /&gt;        Next&lt;br /&gt;        Return result&lt;br /&gt;    End Function&lt;br /&gt;&lt;br /&gt;End Module&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;เนื่องจากผมใช้กับ ASP.NET ดังนั้น Module นี้ ผมเอาไปไว้ที่ App_Code folder ครับ ทีแรกเอาไปใส่ใน BLL ซึ่งอยู่คนละ project ก็เลยเรียกใช้ใน ASP.NET ไม่ได้ งงตั้งนานเหมือนกัน ลองทดสอบดูก็น่าพอใจครับ ถือว่าเป็นการทดสอบการสร้าง Extension Method ละกัน&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-2069061052790988499?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/2069061052790988499/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=2069061052790988499' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/2069061052790988499'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/2069061052790988499'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/10/extension-method-enum.html' title='ลองสร้าง Extension Method ให้ Enum'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-6344224176472021288</id><published>2009-10-07T20:01:00.002+07:00</published><updated>2009-10-07T20:01:00.403+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>ASP.NET ส่ง array จาก Code Behind Page ไปที่ javascript</title><content type='html'>ปกติเวลาเราเขียน javascript ที่หน้า aspx บางครั้งเราอาจจะต้องการข้อมูลที่อยู่ใน Code Behind Page เช่น clientId (ค่อนข้างใช้บ่อย ถ้ามีการใช้ MasterPage) เช่น&lt;br /&gt;&lt;pre name="code" class="javascript"&gt;&lt;br /&gt;&amp;lt;script type="text/javascript" id="MainScript"&amp;gt;&lt;br /&gt;// &amp;lt;![CDATA[&lt;br /&gt;    var employeeTextBoxId = "&amp;lt;%=txtEmployee.ClientId%&amp;gt;";&lt;br /&gt;    var movementCount = "&amp;lt;%=EmploteeMovements.Count%&amp;gt;";&lt;br /&gt;// ]]&amp;gt;&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;แต่ทีนี้ถ้าต้องการข้อมูลที่เป็น Array ละ ก็ไม่ยากครับ สร้าง Function ที่ฝั่ง Code Behind Page ขึ้นมาเพื่อเอา array มาต่อกันเป็น string โดยให้มีตัว separate (ในตัวอย่างใช้ ;) จากนั้นฝั่ง javascript ก็ไปสั่ง split อีกที&lt;br /&gt;&lt;pre name="code" class="vb"&gt;&lt;br /&gt;Public employeeName(5) As String&lt;br /&gt;&lt;br /&gt;Protected Function GetEmployees() As String&lt;br /&gt;&lt;br /&gt;    Dim sb As New StringBuilder&lt;br /&gt;&lt;br /&gt;    For i As Integer = 0 To employeeName.Length - 1&lt;br /&gt;        sb.Append(employeeName(i) &amp; ";")&lt;br /&gt;    Next&lt;br /&gt;&lt;br /&gt;    sb.Remove(sb.Length - 1, 1)&lt;br /&gt;    Return sb.ToString&lt;br /&gt;&lt;br /&gt;End Function&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load&lt;br /&gt;&lt;br /&gt;    employeeName(0) = "Nithi"&lt;br /&gt;    employeeName(1) = "Paetree"&lt;br /&gt;    employeeName(2) = "Ple"&lt;br /&gt;    employeeName(3) = "Suvimol"&lt;br /&gt;    employeeName(4) = "Weekit"&lt;br /&gt;    employeeName(5) = "Sasawin"&lt;br /&gt;&lt;br /&gt;End Sub&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="javascript"&gt;&lt;br /&gt;&amp;lt;script type="text/javascript" id="MainScript"&amp;gt;&lt;br /&gt;&lt;br /&gt;// &amp;lt;![CDATA[&lt;br /&gt;    var a = "&amp;lt;%=GetEmployees()%&amp;gt;";&lt;br /&gt;    var empArray = a.split(";");&lt;br /&gt;// ]]&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;เท่าที่ลองดูหลายๆแบบ วิธีนี้ง่ายสุดครับ (แต่อาจไม่ดีสุดนะ)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-6344224176472021288?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/6344224176472021288/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=6344224176472021288' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6344224176472021288'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6344224176472021288'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/10/aspnet-array-code-behind-page.html' title='ASP.NET ส่ง array จาก Code Behind Page ไปที่ javascript'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-7862015290840642669</id><published>2009-09-22T21:11:00.000+07:00</published><updated>2009-09-22T21:11:00.164+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><title type='text'>ASP.NET AJAX 4.0 Preview 5</title><content type='html'>ตอนนี้ที่ Codeplex ได้มีการ release ASP.NET AJAX 4.0 Preview 5 แล้วครับ (อัพเดทล่าสุดวันที่ 14 กันยายน แต่ผมเพิ่งเห็น) ที่จริงว่าจะลองโหลดมาเล่นตั้งแต่ Preview ก่อนหน้านี้แล้ว เพราะว่า banpote_tt MVP หนุ่มหล่อจาก GreatFriends ได้ MSN มาคุย (โม้ติเวท) บรรยายถึงสรรพคุณถึง Microsoft Ajax Template ให้ฟังจนเคลิ้ม แต่ติดว่าช่วงนี้ยุ่งมากเลยไม่มีเวลาโหลดมาลองเล่นครับ คิดว่าอีก 2 อาทิตย์น่าจะว่างแล้ว คงได้ลองดาวน์โหลดมาเล่นดูบ้าง&lt;br /&gt;&lt;br /&gt;วันก่อนเข้าไปอ่าน &lt;a href="http://weblogs.asp.net/scottgu/"&gt;ScottGu's Blog&lt;br /&gt;&lt;/a&gt; พบ entry หนึ่งน่าสนใจ (จริงๆก็น่าสนใจทุกอันแหละครับ) นั่นคือ &lt;a href="http://weblogs.asp.net/scottgu/archive/2009/09/15/announcing-the-microsoft-ajax-cdn.aspx"&gt;Announcing the Microsoft AJAX CDN &lt;/a&gt; ถือว่าเป็นไอเดียที่ดีมากครับ &lt;br /&gt;&lt;br /&gt;แต่สงสัยอย่างหนึ่ง ถ้า MS ทำการ update libraries ใน CDN ใหม่ จะมั่นใจได้ไงว่าจะไม่กระทบกับ App เดิมที่เราใช้งานอยู่เนี่ย เช่นอาจเกิด link ตาย หรือว่า library ตัวใหม่ไม่ compat กับของเก่า เราจะเจอ library hell หรือเปล่านะ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-7862015290840642669?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/7862015290840642669/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=7862015290840642669' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/7862015290840642669'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/7862015290840642669'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/09/aspnet-ajax-40-preview-5.html' title='ASP.NET AJAX 4.0 Preview 5'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-6634726721982347574</id><published>2009-09-15T22:11:00.003+07:00</published><updated>2010-10-29T11:04:11.357+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>VB.NET ICloneable</title><content type='html'>วันนี้ (15 ก.ย. 2552) ได้เข้าไปอ่านกระทู้ที่ GreatFriends เจอคำถามหนึ่งน่าสนใจครับ&lt;br /&gt;&lt;br /&gt;การให้ค่ากับ object&lt;br /&gt;&lt;a href="http://greatfriends.biz/?113627"&gt;http://greatfriends.biz/?113627&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;คำถามมีดังนี้ครับ&lt;/strong&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-family:verdana;color:#006600;"&gt;ผมสร้าง class Node กับ Variable&lt;br /&gt;&lt;br /&gt;ซึ่ง class Node ก็ประกอบไปด้วยตัวแปรต่าง&lt;br /&gt;และก็มี Variable&lt;br /&gt;ส่วน class Variable ก็ประกอบไปด้วยตัวแปรต่าง&lt;br /&gt;&lt;br /&gt;เมื่อผมต้องการกำหนดค่าให้กับ Node ก็สร้าง Node node = new Node;&lt;br /&gt;Variable variable = new Variable;&lt;br /&gt;variable. = .....;ก็กำหนดค่าให้&lt;br /&gt;&lt;br /&gt;แล้วก็ node.Variable = variable;&lt;br /&gt;&lt;br /&gt;ที่นี้พอผมเปลี่ยนค่า ใน&lt;br /&gt;variable variable. = .....;&lt;br /&gt;&lt;br /&gt;ที่นี้node.Variable ก็เลยเปลียนตามไปด้วย&lt;br /&gt;แต่ผมไม่ต้องการให้เปลี่ยน&lt;br /&gt;ผมเลยอยากทราบว่าต้องเซตค่าnode.Variable =&lt;br /&gt;variableยังไงถึงค่าในnode.Variable กับ variable&lt;br /&gt;แยกออกจากกันไม่สัมพันธ์กันอ่ะครับ&lt;/span&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;ลักษณะนี้ก็คือ เราต้องการให้ Variable Object ที่ถูกกำหนดค่าให้ Node Object เป็นคนละตัวกับ Variable Object ที่เราสร้างไว้แต่แรก ผมเลยเสนอให้ใช้ ICloneable Interface ครับ ก็เลยคิดว่าน่าจะลองเขียนทดสอบเล่นๆดู มาลองดูโค้ดกันครับ&lt;br /&gt;&lt;br /&gt;ส่วนของ Variable Class ที่เราทำการ Implement ICloneable ครับ&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Public Class Variable&lt;br /&gt;    Implements ICloneable&lt;br /&gt;&lt;br /&gt;    Private _variableName As String&lt;br /&gt;    Private _value As String&lt;br /&gt;&lt;br /&gt;    Public Property Value() As String&lt;br /&gt;        Get&lt;br /&gt;            Return _value&lt;br /&gt;        End Get&lt;br /&gt;        Set(ByVal value As String)&lt;br /&gt;            _value = value&lt;br /&gt;        End Set&lt;br /&gt;    End Property&lt;br /&gt;&lt;br /&gt;    Public Property VariableName() As String&lt;br /&gt;        Get&lt;br /&gt;            Return _variableName&lt;br /&gt;        End Get&lt;br /&gt;        Set(ByVal value As String)&lt;br /&gt;            _variableName = value&lt;br /&gt;        End Set&lt;br /&gt;    End Property&lt;br /&gt;&lt;br /&gt;    Public Function Clone() As Object Implements System.ICloneable.Clone&lt;br /&gt;        Return TryCast(MemberwiseClone(), Variable)&lt;br /&gt;    End Function&lt;br /&gt;&lt;br /&gt;End Class&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ส่วนของ Node Class&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Public Class Node&lt;br /&gt;&lt;br /&gt;    Private _nodeId As Integer&lt;br /&gt;    Private _nodeName As String&lt;br /&gt;    Private _variable1 As Variable&lt;br /&gt;    Private _variable2 As Variable&lt;br /&gt;&lt;br /&gt;    Public Property Variable2() As Variable&lt;br /&gt;        Get&lt;br /&gt;            Return _variable2&lt;br /&gt;        End Get&lt;br /&gt;        Set(ByVal value As Variable)&lt;br /&gt;            _variable2 = value&lt;br /&gt;        End Set&lt;br /&gt;    End Property&lt;br /&gt;&lt;br /&gt;    Public Property Variable1() As Variable&lt;br /&gt;        Get&lt;br /&gt;            Return _variable1&lt;br /&gt;        End Get&lt;br /&gt;        Set(ByVal value As Variable)&lt;br /&gt;            _variable1 = value&lt;br /&gt;        End Set&lt;br /&gt;    End Property&lt;br /&gt;&lt;br /&gt;    Public Property NodeName() As String&lt;br /&gt;        Get&lt;br /&gt;            Return _nodeName&lt;br /&gt;        End Get&lt;br /&gt;        Set(ByVal value As String)&lt;br /&gt;            _nodeName = value&lt;br /&gt;        End Set&lt;br /&gt;    End Property&lt;br /&gt;&lt;br /&gt;    Public Property NodeId() As Integer&lt;br /&gt;        Get&lt;br /&gt;            Return _nodeId&lt;br /&gt;        End Get&lt;br /&gt;        Set(ByVal value As Integer)&lt;br /&gt;            _nodeId = value&lt;br /&gt;        End Set&lt;br /&gt;    End Property&lt;br /&gt;&lt;br /&gt;End Class&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;และทีนี้ก็มาทดสอบกัน&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;        Dim myNode As New Node&lt;br /&gt;        Dim myVar1 As New Variable&lt;br /&gt;        Dim myVar2 As New Variable&lt;br /&gt;&lt;br /&gt;        myNode.NodeId = 1&lt;br /&gt;        myNode.NodeName = "Test"&lt;br /&gt;        myVar1.VariableName = "EmployeeName"&lt;br /&gt;        myVar1.Value = "Nithi"&lt;br /&gt;&lt;br /&gt;        myVar2.Value = "EmployeeSurname"&lt;br /&gt;        myVar2.Value = "Juabsamai"&lt;br /&gt;&lt;br /&gt;        myNode.Variable1 = myVar1.Clone&lt;br /&gt;        myNode.Variable2 = myVar2&lt;br /&gt;&lt;br /&gt;        Debug.Print("{0} - {1} {2}", myNode.NodeName, myNode.Variable1.Value, myNode.Variable2.Value)&lt;br /&gt;&lt;br /&gt;        myVar1.Value = "Wanwimon"&lt;br /&gt;        myVar2.Value = "Permpanit"&lt;br /&gt;&lt;br /&gt;        Debug.Print("{0} - {1} {2}", myNode.NodeName, myNode.Variable1.Value, myNode.Variable2.Value)&lt;br /&gt;&lt;br /&gt;        myNode.Variable1.Value = "Nithi"&lt;br /&gt;        myNode.Variable2.Value = "Juabsamai"&lt;br /&gt;        Debug.Print("{0} - {1} {2}", myNode.NodeName, myNode.Variable1.Value, myNode.Variable2.Value)&lt;br /&gt;        Debug.Print("Variable2 = " &amp;amp; myVar2.Value)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ที่น่าสนใจคือ .NET ได้เตรียม MemberwiseClone Function มาให้เราเรียบร้อยแล้ว ไม่ต้องเขียนเอง สะดวกดีครับ หลังจากทดสอบดูแล้ว คิดว่าน่าจะตรงกับที่เจ้าของกระทู้ที่ GreatFriends ต้องการนะครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-6634726721982347574?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/6634726721982347574/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=6634726721982347574' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6634726721982347574'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6634726721982347574'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/09/vbnet-icloneable.html' title='VB.NET ICloneable'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-4860011081860736859</id><published>2009-09-13T12:46:00.001+07:00</published><updated>2010-10-29T11:20:08.865+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>VB.NET กับ ENUM</title><content type='html'>วันก่อนเขียนโปรแกรมเพื่อไป Execute StoreProcedure ตัวหนึ่ง ซึ่งใน SP ตัวนี้จะมีการ Return Value กลับมาให้ด้วยเพื่อบอกสถานะของการทำงาน ดังนั้นในส่วนของ VB.NET ผมก็เลยสร้าง Enum ขึ้นมาเพื่อใช้ระบุสถานะดังกล่าว&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt; Public Class abcManager&lt;br /&gt;&lt;br /&gt;    Public Enum xxxResult&lt;br /&gt;        Success = 0&lt;br /&gt;        BillNotFound = 100&lt;br /&gt;        NoOutstandingAmount = 200&lt;br /&gt;        SQLException = 300&lt;br /&gt;        UnknownException = 900&lt;br /&gt;    End Enum&lt;br /&gt;&lt;br /&gt;    Public Shared Function xyz() as xxxResult&lt;br /&gt;    Public Shared Function .......&lt;br /&gt;&lt;br /&gt;End Class&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;ทีนี้ตอนฝั่ง UI (Presentation Layer) เรียก xyz function มาใช้งานก็ได้ xxxResult กลับมา ทีนี้ตอนจะแสดงผลลัพธ์ให้ user เห็น ถ้าเป็นตัวเลขก็คงไม่ได้ ก็ต้องแสดงเป็นข้อความ ความคิดแรกคือ ใช้ Select Case ในการตรวจสอบ xxxResult เพื่อแสดงข้อความ แต่คิดอีกทีมันไม่ยืดหยุ่นแฮะ ดังนั้นคิดอีกที เอาชื่อของ Enum มาโชว์เลยก็น่าจะได้ ทีแรกผมเขียนแบบนี้ครับ &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Dim result As xyzBLL.abcManager.xxxResult&lt;br /&gt;result = xyzBLL.abcManager.xyz()&lt;br /&gt;MsgBox("Error - " &amp; [Enum].GetName(result.GetType, result), MsgBoxStyle.Exclamation + MsgBoxStyle.OkOnly, My.Application.Info.ProductName)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ซักพักนึกขึ้นมาได้เลยว่าน่าจะทำ Overrides Function ToString ของ Enum แต่อยากรู้ว่า default มันได้ค่าอะไร เลยลองทดสอบ result.ToString เอ่อ ปรากฎได้ผลเหมือนกัน เพิ่งรู้นะเนียว่า ToString ของ Enum มันคืนค่าเป็น Name อุตสาห์ไปใช้ GetName Function เฮ้อ เข้าใจคำว่า ฉลาดเรื่องโง่ๆ ละ&lt;br /&gt;&lt;br /&gt;-*-"&lt;br /&gt;&lt;br /&gt;เคยได้ยินมาว่าคนเก่งมักเรียนรู้จากประสบการณ์ตนเอง ส่วนคนฉลาด มักเรียนรู้จากประสบการณ์คนอื่น &lt;br /&gt;เลยเอาเรื่องนี้มาเขียน blog จะได้มีคนฉลาดเยอะๆ ^_^&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-4860011081860736859?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/4860011081860736859/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=4860011081860736859' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/4860011081860736859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/4860011081860736859'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/09/vbnet-enum.html' title='VB.NET กับ ENUM'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-5150189348153572054</id><published>2009-09-10T21:28:00.001+07:00</published><updated>2009-09-11T08:47:54.680+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VB.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>VB.NET จัด format ตัวอักษรใน PrintDocument</title><content type='html'>ช่วง 1-2 ปีมานี้ จะมีน้องๆนักศึกษาเข้ามาถามเรื่อง PrintDocument ใน VB.NET หลายคนทีเดียวครับ สังเกตุอย่างหนึ่งคือการเขียนใช้สไตล์เดียวกัน ดังนั้นเลยเดาว่าคงใช้หนังสือภาษาไทยเล่มเดียวกันในการศึกษา คำถามหนึ่งที่พบบ่อยคืออยากจัดตัวอักษรชิดขวาหรือกึ่งกลางทำอย่างไรดี&lt;br /&gt;&lt;br /&gt;ในโค้ดที่แนบมาให้ดู จะมี AnyString Sub ในการวาดตัวอักษรลงบน PrintDocument ครับ ซึ่ง Sub นี้มันจะวาดตัวอักษรชิดซ้ายตลอด ดังนั้นผมเลยเสนอให้สร้าง Sub ที่สามารถกำหนด StringFormat ได้ว่าจะให้ชิดซ้าย ชิดขวา หรือกึ่งกลาง&lt;br /&gt;&lt;br /&gt;&lt;pre name=code class=vb&gt;&lt;br /&gt;Public Sub FormatString(ByVal g As Graphics, ByVal printString As String, ByVal xPos As Integer, ByVal yPos As Integer, ByVal align As StringAlignment)&lt;br /&gt;&lt;br /&gt;     Dim f As New StringFormat&lt;br /&gt;     f.Alignment = align&lt;br /&gt;     g.DrawString(printString, useFont, Brushes.Black, xPos, yPos, f)&lt;br /&gt;&lt;br /&gt;End Sub&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;สังเกตุว่าจะมีการใช้ตัวแปรชื่อ useFont ด้วยครับ ซึ่ง useFont ก็ต้องถูกประกาศในระดับ Page และกำหนด Font ที่ต้องการใช้วาด PrintDocument ที่ต้องการ &lt;br /&gt;&lt;br /&gt;เวลาเรียกใช้ให้จัดชิดขวาก็&lt;br /&gt;&lt;br /&gt;FormatString(e.Graphics, "test123", 100, currentYPosition, StringAlignment.Far)&lt;br /&gt;&lt;br /&gt;ถ้าต้องการให้อยู่ตรงกลางก็&lt;br /&gt;&lt;br /&gt;FormatString(e.Graphics, "test123", 100, currentYPosition, StringAlignment.Center)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-5150189348153572054?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/5150189348153572054/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=5150189348153572054' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5150189348153572054'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5150189348153572054'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/09/vbnet-format-printdocument.html' title='VB.NET จัด format ตัวอักษรใน PrintDocument'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-8475434072344078272</id><published>2009-09-08T21:58:00.002+07:00</published><updated>2010-10-29T11:20:59.637+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VB.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>VB.NET เคลียร์ Excel ไม่ให้เหลือซาก</title><content type='html'>ปกติเวลาผมเขียนโปรแกรมเพื่อออกรายงานมักใช้ Crystal Report เป็นหลัก เพราะว่าสามารถให้ user preview ได้ รวมถึง export เป็น PDF, Excel, word ได้สะดวกดีครับ โดยเฉพาะเมื่อ Export เป็น Excel ผมพบว่ามันทำงานได้เร็วกว่าการสั่งเปิด Excel เพื่อใส่ข้อมูลตรงๆมากครับ แต่บางครั้งก็จำเป็นจริงๆที่ต้องใช้ Excel ในการออกรายงาน &lt;br /&gt;&lt;br /&gt;เวลาออกรายงานบน Excel ผมมักจะเขียนโค้ดประมาณนี้ครับ (ใช้ BackgroundWorker ด้วย)&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;    Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork&lt;br /&gt;        Dim wk As BackgroundWorker = DirectCast(sender, BackgroundWorker)&lt;br /&gt;        Try&lt;br /&gt;            isSuccess = False&lt;br /&gt;            CreateExcel(wk, e)&lt;br /&gt;            CreateReportByLocation(wk, e)&lt;br /&gt;            CreateReportByTitle(wk, e)&lt;br /&gt;            CreateReportList(wk, e)&lt;br /&gt;            isSuccess = True&lt;br /&gt;        Catch ex As Exception&lt;br /&gt;            isSuccess = False&lt;br /&gt;        Finally&lt;br /&gt;            FinishCreatingReport(wk, e)&lt;br /&gt;        End Try&lt;br /&gt;    End Sub&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;คำสั่ง CreateExcel จะเป็นการ New Excel.Application รวมถึงสร้าง WorkBook สำหรับเริ่มต้นการออกรายงาน&lt;br /&gt;ส่วน CreatReportXXX จะเป็นการสร้างรายงานแต่ละ WorkSheet ตามต้องการครับ สุดท้ายก็ FinishCreatingReport จะเป็นการคืน memory ให้กับระบบ เราจะมาลองดู function นี้กันครับ&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Private Sub FinishCreatingReport(ByVal wk As BackgroundWorker, ByRef e As DoWorkEventArgs)&lt;br /&gt;   Try&lt;br /&gt;       xlBook.SaveAs(fileName)&lt;br /&gt;   Catch ex As Exception&lt;br /&gt;       MessageBox.Show(ex.Message &amp; vbCrLf &amp; "Please contact ITD.", My.Application.Info.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation)&lt;br /&gt;   Finally&lt;br /&gt;       '// Release memory&lt;br /&gt;       wk.ReportProgress(95)&lt;br /&gt;       xlApp.Quit()&lt;br /&gt;       GC.SuppressFinalize(xlSheet)&lt;br /&gt;       GC.SuppressFinalize(xlBook)&lt;br /&gt;       GC.SuppressFinalize(xlApp)&lt;br /&gt;       GC.Collect()&lt;br /&gt;       xlSheet = Nothing&lt;br /&gt;       xlBook = Nothing&lt;br /&gt;       xlApp = Nothing&lt;br /&gt;    End Try&lt;br /&gt;End Sub&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;พอรันโปรแกรมและเปิด Task Manager ดูพบว่า&lt;br /&gt;1. พอ BackgroundWorker1_RunWorkerCompleted ทำงานเสร็จ (แน่นอนว่า เรียก FinishCreatingReport ซึ่งอยู่ใน BackgroundWorker1_DoWork แล้ว) ใน Task Manager ยังมี Excel.exe ค้างอยู่ครับ&lt;br /&gt;2. ถ้ากดปุ่มเพื่อสั่งออกรายงานใหม่อีกครั้ง Excel.exe จะหายไปจาก Task Manager พอโปรแกรมสั่ง CreateExcel ถึงจะสร้างขึ้นมาใหม่&lt;br /&gt;3. เมื่อ BackgroundWorker ทำงานเสร็จ ออกรายงานเรียบร้อย Excel.exe ยังคงอยู่ ถ้าผมปิด form นี้ Excel.exe จะหายไปจาก Task Manager แต่ถ้าผม stop debugging ใน Visual Studio หรือโปรแกรมที่เขียนมี bug และ shutdown ไป Excel.exe จะยังคงค้างอยู่ใน Task Manager&lt;br /&gt;&lt;br /&gt;ทีนี้ผมลองเพิ่มคำสั่ง ReleaseComObject ครับ&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Private Sub FinishCreatingReport(ByVal wk As BackgroundWorker, ByRef e As DoWorkEventArgs)&lt;br /&gt;   Try&lt;br /&gt;       xlBook.SaveAs(fileName)&lt;br /&gt;   Catch ex As Exception&lt;br /&gt;       MessageBox.Show(ex.Message &amp; vbCrLf &amp; "Please contact ITD.", My.Application.Info.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation)&lt;br /&gt;   Finally&lt;br /&gt;       '// Release memory&lt;br /&gt;       wk.ReportProgress(95)&lt;br /&gt;       xlApp.Quit()&lt;br /&gt;       System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheet)&lt;br /&gt;       System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBook)&lt;br /&gt;       System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp)&lt;br /&gt;       GC.SuppressFinalize(xlSheet)&lt;br /&gt;       GC.SuppressFinalize(xlBook)&lt;br /&gt;       GC.SuppressFinalize(xlApp)&lt;br /&gt;       GC.Collect()&lt;br /&gt;       xlSheet = Nothing&lt;br /&gt;       xlBook = Nothing&lt;br /&gt;       xlApp = Nothing&lt;br /&gt;    End Try&lt;br /&gt;End Sub&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ลองรันโปรแกรมดู พบว่าเมื่อ BackgroundWorker ทำงานเสร็จ Excel.exe จะถูกลบทิ้งไปทันที ไม่เหลือใน Task Manager แล้ว (พอไล่ดูอย่างละเอียด พบว่า Excel.exe จะหายไป ก่อน  BackgroundWorker1_RunWorkerCompleted จะทำงานเสร็จครับ)&lt;br /&gt;&lt;br /&gt;ดังนั้นนอกจากจะใช้งาน Excel แล้ว ถ้าเรามีการทำ COM Marshal (InterOp) กรุณาเรียกใช้ System.Runtime.InteropServices.Marshal.ReleaseComObject เพื่อจะได้มั่นใจว่า Unmanaged Memory ได้ถูก Release จริงๆครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-8475434072344078272?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/8475434072344078272/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=8475434072344078272' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/8475434072344078272'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/8475434072344078272'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/09/vbnet-excel.html' title='VB.NET เคลียร์ Excel ไม่ให้เหลือซาก'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-2995699286082874339</id><published>2009-09-04T21:28:00.000+07:00</published><updated>2009-09-04T21:28:00.609+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>BlueprintCSS</title><content type='html'>วันนี้ได้อ่าน forum ต่างประเทศ เจอคำถามโลกแตกที่ว่าทำไมตอน design เวบ แล้วทดสอบใน IE7 ก็ดูดีแต่พอดูด้วย browser อื่นมันเละไปหมด ซึ่งปัญหานี้คาดว่าคงเจอกันหลายคน แต่โชคดีครับที่ผมทำ Web Application สำหรับใช้ภายในบริษัท ดังนั้นจึงสามารถบอกให้พนักงานใช้เฉพาะ IE เท่านั้นได้ เลยไม่ค่อยเจอปัญหานี้ มีบ้างที่ IE6 กับ IE7 ไม่เหมือนกัน เป็นเพราะว่า IE7 นั้นทาง Microsoft เริ่มกลับมาใช้มาตรฐานสากลแล้ว ซึ่ง IE ก่อนหน้านี้ใช้มาตรฐานของ Microsoft (คือเมื่อก่อนใช้กติกูเป็นหลัก)&lt;br /&gt;&lt;br /&gt;หลายๆคนแก้ปัญหานี้โดยการใช้เทคนิคที่เรียกว่า CSS Hacked (สำหรับภาษาไทยศึกษาเพิ่มเติมได้ที่ www.divland.com ครับ) หรืออาจจะเขียน javascript สำหรับโหลด CSS โดยตรวจสอบ browser ก่อนว่าเป็นเวอร์ชันไหน แล้วก็ไปอ่านข้อมูล CSS ที่เขียนสำหรับเวอร์ชันนั้นๆมาใส่ใน webpage ซึ่งแน่นอนว่ามันต้องถึกพอดู และก็ต้องทดสอบหลายๆครั้ง เพื่อให้มั่นใจว่าการแสดงผลถูกต้องตามต้องการ &lt;br /&gt;&lt;br /&gt;พอดีเจอคำตอบหนึ่งแนะนำให้ลอง BlueprintCSS ซึ๋งเป็น CSS Framework ตัวหนึ่งที่นอกจากจะช่วยแก้ปัญหานี้ยังมีความสามารถอื่นๆอีกเพียบ ผมก็ลองดาวน์โหลดมาทดสอบตามระเบียบครับ ไว้มีโอกาสจะมาลองเขียน blog เพิ่ม สำหรับผู้สนใจลองไปศึกษาหรือดาวน์โหลดได้ที่ &lt;a href="http://www.blueprintcss.org"&gt;http://www.blueprintcss.org/&lt;/a&gt;&lt;br /&gt; มีทั้ง Demo, Wiki, Discussion และอื่นๆครบถ้วน น่าสนใจจริงๆครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-2995699286082874339?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/2995699286082874339/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=2995699286082874339' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/2995699286082874339'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/2995699286082874339'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/09/blueprintcss.html' title='BlueprintCSS'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-6526061810822708085</id><published>2009-08-21T23:13:00.001+07:00</published><updated>2009-08-21T23:13:00.213+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VB.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='LINQ'/><title type='text'>LINQ To SQL Group by multiple columns</title><content type='html'>วันนี้จะเขียน Linq To SQL เพื่อให้ทำคำสั่งเหมือน&lt;br /&gt;&lt;br /&gt;SELECT cocode, arbook, batchdate, batchno,count(*)&lt;br /&gt;FROM arpendingbill&lt;br /&gt;WHERE cocode = 'TGIP' AND arbook = 'NEWAR' &lt;br /&gt;GROUP BY cocode, arbook, batchdate, batchno&lt;br /&gt;&lt;br /&gt;โดยให้นำข้อมูลไปใส่ Class ชื่อ ARPendingBatch เขียนเป็น LINQ ดังนี้ครับ&lt;br /&gt;&lt;br /&gt;&lt;pre name=code class=vb&gt;&lt;br /&gt;Dim b = From pb In dc.ARPendingBills _&lt;br /&gt;        Where pb.COCode = coCode AndAlso pb.ARBook = arBook _&lt;br /&gt;        Group By key = New With {pb.COCode, pb.ARBook, pb.BatchDate, pb.BatchNo} _&lt;br /&gt;        Into pbGroup = Group _&lt;br /&gt;        Select New ARPendingBatch With {.ARBook = key.ARBook, _ &lt;br /&gt;                  .CoCode = key.COCode, .BatchDate = key.BatchDate, _&lt;br /&gt;                  .BatchNo = key.BatchNo, .BillCount = pbGroup.Count( _&lt;br /&gt;                             Function(c) c.BatchDate = key.BatchDate AndAlso _ &lt;br /&gt;                                         c.BatchNo = key.BatchNo)}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;พอลอง debug เพื่อดู SQLCommand ที่มันสร้างขึ้นมาได้แบบนี้ครับ&lt;br /&gt;&lt;br /&gt;SELECT (     SELECT COUNT(*)     FROM [dbo].[ARPendingBill] AS [t2]     WHERE ([t2].[BatchDate] = [t1].[BatchDate]) AND ([t2].[BatchNo] = [t1].[BatchNo]) AND ([t1].[COCode] = [t2].[COCode]) AND ([t1].[ARBook] = [t2].[ARBook]) AND ([t1].[BatchDate] = [t2].[BatchDate]) AND ([t1].[BatchNo] = [t2].[BatchNo]) AND ([t2].[COCode] = @p0) AND ([t2].[ARBook] = @p1)     ) AS [BillCount], [t1].[BatchDate], [t1].[BatchNo], [t1].[ARBook], [t1].[COCode] AS [CoCode] FROM (     SELECT [t0].[COCode], [t0].[ARBook], [t0].[BatchDate], [t0].[BatchNo]     FROM [dbo].[ARPendingBill] AS [t0]     WHERE ([t0].[COCode] = @p0) AND ([t0].[ARBook] = @p1)     GROUP BY [t0].[COCode], [t0].[ARBook], [t0].[BatchDate], [t0].[BatchNo]     ) AS [t1]&lt;br /&gt;&lt;br /&gt;รู้สึกว่ามันช่างเข้าใจยากพิกล ส่วนหนึ่งเป็นเพราะยังไม่ชินด้วยครับ และควรจะต้องไปจัด Layout ใหม่ให้อ่านง่ายๆก่อน แต่ผลลัพท์ได้ออกมาเหมือนกัน&lt;br /&gt;&lt;br /&gt;ลองเขียนโดยใช้ Lambda Expression ครับ&lt;br /&gt;&lt;pre name=code class=vb&gt;&lt;br /&gt;Dim b = dc.ARPendingBills.Where( _&lt;br /&gt;        Function(a) a.COCode = coCode AndAlso a.ARBook = arBook).GroupBy( _&lt;br /&gt;        Function(g) New With {.coCode = g.COCode, .arBook = g.ARBook, _&lt;br /&gt;                    .batchDate = g.BatchDate, .batchNo = g.BatchNo}).Select( _&lt;br /&gt;        Function(c) New ARPendingBatch With {.ARBook = c.Key.arBook, _ &lt;br /&gt;                    .CoCode = c.Key.coCode, .BatchDate = c.Key.batchDate, _ &lt;br /&gt;                    .BatchNo = c.Key.batchNo, .BillCount = c.Count( _&lt;br /&gt;                    Function(k) k.BatchDate = c.Key.batchDate AndAlso _ &lt;br /&gt;                              k.BatchNo = c.Key.batchNo)})&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ได้ผลลัพท์เหมือนกันครับ (ก็แหงละ ลอง debug ดู SQLCommand ก็เหมือนกับที่เขียนด้านบน)&lt;br /&gt;&lt;br /&gt;รู้สึกขัดใจกับ SQLCommand ที่ Linq To SQL สร้างให้ ดังนั้นเลยเปลี่ยนมา ExecuteQuery ตรงๆแทน&lt;br /&gt;&lt;br /&gt;&lt;pre name=code class=vb&gt;&lt;br /&gt;Dim b = dc.ExecuteQuery(Of ARPendingBatch)( _&lt;br /&gt;        "SELECT cocode, arbook, batchdate, batchno, " &amp; _ &lt;br /&gt;        "BillCount=count(billRecId) " &amp; _&lt;br /&gt;        "FROM arpendingbill &amp; _ &lt;br /&gt;        "WHERE cocode = '" &amp; coCode &amp; "' and arbook = '" &amp; arBook &amp; "' " &amp; _&lt;br /&gt;        "GROUP BY cocode, arbook, batchdate, batchno")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;มันไม่ Typed Save ครับ แต่ก็มีข้อดีคือ ผมไม่ต้องใช้เวลาเป็นชั่วโมงในการลองเขียน LINQ To SQl แล้ว debug ได้ SQLCommand ที่อ่านยาก และก็ต้อง Test เยอะเพื่อความมั่นใจ ดังนั้นช่วงนี้ถ้าเจอกรณีแบบนี้ก็จะใช้ ExecuteQuery ไปก่อน แล้วพอมีเวลาหัดให้คล่องๆแล้วค่อยกลับมาแก้ครับ (แต่มันก็อ่านยากเหมือนกันนะ programmer ที่ต้องมาแก้โค้ดนี้ภายหลัง ไม่แน่ใจว่าจะชอบแบบไหน ดังนั้นผมเลย Comment เก็บไว้ทั้ง 3 แบบเลย)&lt;br /&gt;&lt;br /&gt;ว่าแต่โค้ด Linq To SQL ด้านบน สามารถเขียนได้ง่ายกว่านี้มั๊ยนะ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-6526061810822708085?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/6526061810822708085/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=6526061810822708085' title='3 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6526061810822708085'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6526061810822708085'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/08/linq-to-sql-group-by-multiple-column.html' title='LINQ To SQL Group by multiple columns'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-2739743977212439351</id><published>2009-08-19T21:09:00.000+07:00</published><updated>2009-08-18T13:53:07.742+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET AJAX'/><title type='text'>สร้าง DIV เพื่อบัง WebPage ขณะใช้งาน AJAX</title><content type='html'>ปกติเวลาพัฒนา Web Application ด้วย ASP.NET ผมมักจะใช้ ASP.NET AJAX ร่วมด้วยเสมอ และเมื่อมีการ PostBack หรือเรียกใช้ WebServices ก็จำเป็นต้องเขียน javascript เพื่อโชว์รูปว่ากำลัง Update และ disable controls ต่างๆ &lt;br /&gt;&lt;br /&gt;พอดีได้เข้าไปดูเวบไหนจำไม่ได้แล้วครับ เค้าน่าจะสร้าง Div ที่กำหนด OPACITY ไว้เพื่อให้เป็นกรอบจางๆสำหรับบังหน้าเวบขณะ process งาน ก็สะดวกดีนะครับ เพราะว่าถ้าในหน้ามี control หลายๆตัว จะไปนั่งไล่ disable ทีละตัวก็จะถึกไปหน่อย ยิ่งถ้าใช้พวก asp:menu หรือ Master Page ด้วยแล้ว เสียเวลาน่าดูชม สร้าง Div มาบังมันซะเลย เร็วและได้ผลลัพท์ไม่แตกต่างกัน (อาจจะดีกว่าด้วยครับ) เลยมาลองทำดูบ้างครับ&lt;br /&gt;&lt;br /&gt;&lt;pre name=code class=html&gt;&lt;br /&gt;&amp;lt;div id="divProgress"&amp;gt;&lt;br /&gt;   &amp;lt;div id="divBackground"style="z-index:999; position:absolute;top:0px;left:0px; background-color:#eeeeee;filter:alpha(opacity=40); opacity:0.4;-moz-opacity:0.4"&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;   &amp;lt;div id="divImg" style="z-index:1000;position:absolute;border: 1px solid #800000; top:340px;left:200px;width:300px;text-align:center;background-color:#FFDECE"&amp;gt;&lt;br /&gt;       &amp;lt;img alt="Processing" src="../Images/loading.gif" /&gt;&amp;lt;asp:label ID="lblLoading" CssClass ="textNormal" Text="Preparing reports..." runat="server"&amp;gt;&amp;lt;/asp:label&amp;gt;&lt;br /&gt;   &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;div&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;โดยปกติก็จะกำหนด visibility เป็น hidden ไว้ก่อน พอมีการเรียกใช้ AJAX ก็จะเปลี่ยนเป็น visible มาดูโค้ดกันครับ&lt;br /&gt;&lt;br /&gt;&lt;pre name=code class=javascript&gt;&lt;br /&gt;&amp;lt;script type="text/javascript" id="MSAJAX"&amp;gt;&lt;br /&gt;    var prm = Sys.WebForms.PageRequestManager.getInstance();&lt;br /&gt;    prm.add_beginRequest(onBeginRequest);&lt;br /&gt;    prm.add_endRequest(onEndRequest);&lt;br /&gt;&lt;br /&gt;    function onBeginRequest(sender, e) {&lt;br /&gt;        ShowProgress(true);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    function onEndRequest(sender, e) {&lt;br /&gt;        ShowProgress(false);&lt;br /&gt;    }&lt;br /&gt;        function ShowProgress(isShow) {&lt;br /&gt;        if(isShow == true){&lt;br /&gt;           $get("divProgress").style.visibility = "visible";&lt;br /&gt;           var dv = $get("divBackground")&lt;br /&gt;           dv.style.width = "1200px";&lt;br /&gt;           dv.style.height = "1000px";&lt;br /&gt;        }else{&lt;br /&gt;           $get("divProgress").style.visibility = "hidden";&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;เนื่องจากผมใช้ PageRequestManager ของ ASP.NET AJAX เป็นตัวสั่ง ShowProgress ดังนั้น Script นี้ต้องวางไว้ข้างล่าง ScriptManager control นะครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-2739743977212439351?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/2739743977212439351/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=2739743977212439351' title='2 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/2739743977212439351'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/2739743977212439351'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/08/div-webpage-ajax.html' title='สร้าง DIV เพื่อบัง WebPage ขณะใช้งาน AJAX'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-8248574455506334569</id><published>2009-08-16T23:59:00.000+07:00</published><updated>2009-08-16T23:59:00.217+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VB.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>VB.NET สร้างรายงานด้วย .rdlc (Client Report Definition File)</title><content type='html'>ช่วงนี้ดึกๆหามรุ่งหามค่ำนั่งโม่โปรแกรมอยู่ (เล่นปังยา 2 ชั่วโมง เขียนโปรแกรม ครึ่งชั่วโมง) พอจะสร้างรายงาน ปรากฏว่าตัว Visual Studio 2008 ที่ลงไว้ไม่มี Crystal Report มาด้วย (กรรม หาแผ่นก็ไม่เจอ) ก่อนหน้านี้เคยใช้ Print Document รู้สึกว่ามันถึกมาก (เหมือนใช้ VSPrint ใน VB6 เลย) มานั่งทบทวนจำได้ว่าเคยอ่านบทความเรื่อง rdlc กับ Report Viewer ผ่านตา เลยไปลองใช้บริการอากู๋ดูครับ &lt;br /&gt;&lt;br /&gt;ได้ข้อมูลมาเยอะมาก อ่านผ่านๆไม่ค่อยรู้เรื่อง ก็ลงมือลองเขียนเลยครับ ปรากฏว่าไม่ยากเท่าไหร่ ติดที่ว่าไม่ถนัดครับ แต่ก็พอถูๆไถๆไปได้ ลองเขียน Daily Report ใช้เวลาคลำประมาณครึ่งชั่วโมงก็เสร็จครับ ไว้ถ้ามีเวลาจะศึกษาเพิ่ม แล้วจะมาลงวิธีการสร้างรายงานอีกที&lt;br /&gt;&lt;br /&gt;พอสร้างรายงานเสร็จก็เขียนโค้ดสำหรับ Preview Report ครับ ก็ใช้ ReportViewer Control นั่นแหละ ก่อนอื่นสร้าง form สำหรับ query ข้อมูลก่อน แล้วพอ user กด print ก็ไปเปิด form ที่มี ReportViewer ขึ้นมาแสดง&lt;br /&gt;&lt;br /&gt;&lt;pre name=code class=vb&gt;&lt;br /&gt;    Private Sub btnPreview_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnPreview.Click&lt;br /&gt;&lt;br /&gt;        Dim reports As List(Of Entity.ReportDailySummary) = ReportManager.GetDailySummaryReport(Me.dtpWorkingDate.Value)&lt;br /&gt;&lt;br /&gt;        If reports Is Nothing OrElse reports.Count = 0 Then&lt;br /&gt;            MsgBox("ไม่พบข้อมูล", MsgBoxStyle.Information + MsgBoxStyle.OkOnly, My.Application.Info.Description)&lt;br /&gt;        Else&lt;br /&gt;            Dim ds As New Microsoft.Reporting.WinForms.ReportDataSource("ReportDailySummary", reports)&lt;br /&gt;            Dim params As New List(Of Microsoft.Reporting.WinForms.ReportParameter)&lt;br /&gt;            params.Add(New Microsoft.Reporting.WinForms.ReportParameter("WorkingDate", Me.dtpWorkingDate.Value.ToString("dd MMMM yyyy")))&lt;br /&gt;            frmPreview.ReportViewer1.LocalReport.ReportPath = "rptDailySummary.rdlc"&lt;br /&gt;            frmPreview.ReportViewer1.LocalReport.DataSources.Add(ds)&lt;br /&gt;            frmPreview.ReportViewer1.LocalReport.SetParameters(params)&lt;br /&gt;            frmPreview.ReportViewer1.RefreshReport()&lt;br /&gt;            frmPreview.Text = "รายงานสรุปยอดขายประจำวัน"&lt;br /&gt;            frmPreview.ShowDialog()&lt;br /&gt;        End If&lt;br /&gt;&lt;br /&gt;    End Sub&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;เนื่องจากขี้เกียจ และต้องรีบไปออกรอบ ดังนั้นลงแค่ code พอ เผื่อว่าวันหลังลืมวิธี Preview Report จะได้มาค้นดูได้&lt;br /&gt;&lt;br /&gt;ปล. วันนี้ตอนอยู่ที่ทำงานจะเขียนคำสั่งสำหรับ sort list ว่าจะใช้ Lamda Expression แต่นึกไม่ออก ลองเขียนแล้วก็รันไม่ผ่าน ยังดีที่ได้เอาโค้ดมาลง blog ไว้ เลยมาเปิดหาดู สะดวกดีครับ ดีกว่าไปเปิดโปรเจคเก่าๆมาหามากมายก่ายกอง&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-8248574455506334569?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/8248574455506334569/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=8248574455506334569' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/8248574455506334569'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/8248574455506334569'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/08/vbnet-rdlc-client-report-definition.html' title='VB.NET สร้างรายงานด้วย .rdlc (Client Report Definition File)'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-3859578717733770596</id><published>2009-08-14T23:13:00.006+07:00</published><updated>2009-08-14T23:54:48.466+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VB.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>VB.NET อ่านข้อมูลภาษาไทยจากไฟล์</title><content type='html'>วันก่อนมีโอกาสเขียนโปรแกรมสำหรับทำการอ่านข้อมูลจาก text file แล้วตัดเอาข้อมูลไปเก็บใน database เพื่อประมวลผล จริงๆก็ไม่ยากครับ แต่ปรากฏว่าข้อมูลภาษาไทยมันกลายเป็นอักษรต่างดาว สาเหตุก็คือ text file ที่ว่าเป็น daily report ที่ได้จากการ export มาจากโปรแกรม POS (Point of Sale) และมันถูก encode ด้วย  ANSI (ASCII) ไม่ใช่เป็น UNICODE พอเขียน VB.NET สั่งอ่านไฟล์ตรงๆมันเลย encode ไม่ถูกครับ เพราะว่า .NET ใช้ Unicode (UTF-16) เป็น default encoder&lt;br /&gt;&lt;br /&gt;ดังนั้นตอนใช้ StreamReader ก็กำหนด Encode ไปด้วยแค่นี้เอง (งมตั้งนาน - จริงๆแล้วพอคิดไม่ออก เลยไปเล่นเกมส์ พอเล่นเสร็จ ง่วงแล้วก็เลยรีบนอน 5555) ทีนี้ก็มีให้เลือกว่าจะใช้ Windows-874 หรือว่า TIS-620 ผมก็เลือกใช้ Windows-874 เพราะว่า text file โดน export มาจากโปรแกรมที่เขียนบน Windows ซึ่งเขียนมานานพอสมควร ดังนั้นก็เลยเลือกใช้ Windows-874 ซะเลย (ยังไม่ได้ลอง TIS-620 เลย เพราะขี้เกียจ)&lt;br /&gt;&lt;br /&gt;ลองดูโค้ดกัน ผมสร้าง Singleton Class ชื่อ DBImportManager ขึ้นมา มี Sub อยู่ 1 ตัว สังเกตุว่ามี BackGroundWorker เป็นพารามิเตอร์ด้วย &lt;br /&gt;&lt;br /&gt;&lt;pre name=code class=vb&gt;&lt;br /&gt;Public Sub ImportData(ByVal bk As System.ComponentModel.BackgroundWorker, ByVal fileName As String, ByVal fileImporter As IFileImporter)&lt;br /&gt;&lt;br /&gt;  If Not FileIO.FileSystem.FileExists(fileName) Then Exit Sub&lt;br /&gt;  Dim iLines As Integer&lt;br /&gt;  Try&lt;br /&gt;      Dim fs As New System.IO.StreamReader(fileName, System.Text.Encoding.GetEncoding(874))&lt;br /&gt;      Do While Not fs.EndOfStream&lt;br /&gt;          fileImporter.ParseLine(fs.ReadLine)&lt;br /&gt;          bk.ReportProgress(10, "Reading: " &amp; iLines)&lt;br /&gt;      Loop&lt;br /&gt;      fileImporter.Update()&lt;br /&gt;  Catch exIO As IO.IOException&lt;br /&gt;      bk.ReportProgress(0, exIO.Message)&lt;br /&gt;  Catch ex As Exception&lt;br /&gt;      bk.ReportProgress(0, ex.Message)&lt;br /&gt;  End Try&lt;br /&gt;&lt;br /&gt;End Sub&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;และพารามิเตอร์ตัวสุดท้ายก็เป็น IFlieImporter เนื่องจากว่าเรามีไฟล์หลายตัวที่ต้องทำการ import ข้อมูล ซึ่งแต่ละไฟล์ก็จะมีเงื่อนไขในการตัดไม่เหมือนกัน ผมเลยสร้าง Interface ตัวนี้ขึ้นมาครับ และก็ไปสร้าง FileImporter สำหรับไฟล์แต่ละแบบ เช่น รายงานประจำวัน รายงานลูกค้าใหม่ รายงานรับสินค้าเข้า ฯลฯ ดังนั้นเวลาเปลี่ยน logic ในการตัดไฟล์ เราก็ไปทำการแก้ไขเฉพาะ FileImporter Class  ที่เราสร้างขึ้น โดยจะไม่กระทบกับ Class ตัวนี้เลย&lt;br /&gt;&lt;br /&gt;ลองรันทดสอบดู เรียบร้อยครับ ผลลัพธ์ถูกต้อง&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-3859578717733770596?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/3859578717733770596/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=3859578717733770596' title='3 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/3859578717733770596'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/3859578717733770596'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/08/vbnet.html' title='VB.NET อ่านข้อมูลภาษาไทยจากไฟล์'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-5068543568157450548</id><published>2009-08-09T09:55:00.003+07:00</published><updated>2009-08-09T10:06:45.396+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>วิเคราะห์ WebSite ด้วย Google Analytics</title><content type='html'>หลังจากเขียน Blog มาซักพัก เริ่มอยากรู้ว่ามีคนเข้าชมเยอะสักแค่ไหนครับ ก่อนหน้านี้เขียนที่ Bloggang.com จะมีตัวนับสถิติมาให้ (แต่ข้อมูลผิดบ่อยๆนะ) เลยลอง search ในเนตหาตัวนับสถิติฟรี (เน้น) มาลองใช้ครับ ก็เลยมาสะดุดที่ Google Analytics ครับ รู้สึกว่าเครื่องมือทันสมัยแถมมีเยอะมากมายครับ&lt;br /&gt;&lt;br /&gt;ลองเข้าไปลงทะเบียนที่ &lt;a href="http://www.google.co.th/analytics/"&gt;http://www.google.co.th/analytics&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ข้อมูลเพิ่มเติม ละเอียดน่าสนใจครับ (ภาษาไทย)&lt;br /&gt;&lt;a href="http://tungblog.atikomtrirat.com/2008/07/google-analytics.html"&gt;Google Analytics คืออะไร, Tung's Blog&lt;/a&gt;&lt;br /&gt;&lt;a href="http://gotoknow.org/blog/itgirl/62236"&gt;มารู้จัก Google Analytics กันเถิด [ตอนที่ 1], โลกของคนไอที&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;ตอนนี้ยังใช้งานไม่ค่อยเป็น ต้องลองศึกษาอีกซักพักครับ หวังว่าคงมีคนเข้ามาชม blog เยอะๆ จะได้มีข้อมูลมาลองศึกษาเยอะๆตามไปด้วยครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-5068543568157450548?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/5068543568157450548/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=5068543568157450548' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5068543568157450548'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5068543568157450548'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/08/website-google-analytics.html' title='วิเคราะห์ WebSite ด้วย Google Analytics'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-5860539949130124299</id><published>2009-08-06T23:44:00.001+07:00</published><updated>2010-10-29T10:56:42.435+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>jQuery ลอง Selectors หลายๆแบบ</title><content type='html'>วันก่อนคุณ Nine ได้ post ถามเรื่องการใช้ jQuery สำหรับค้นหาข้อมูลเพื่อซ่อน table โดยหน้า html ได้มาจาก MS SharePoint ครับ ทีแรกผมเข้าใจว่า html จะมี form เป็น pattern เดียวกัน คือมีจำนวนแถวและคอลัมภ์เท่ากันเสมอ ดังนั้นผมจึงเขียนโค้ดดังนี้ครับ&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;var isCompleted = $("table.ms-formtable tr:eq(11) td:eq(1)").html();&lt;br /&gt;if (isCompleted!=null&amp;amp;&amp;amp;isCompleted.indexOf("1 ")&gt;-1) {&lt;br /&gt;     $('table.ms-toolbar').hide();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;คือใช้ Selectors แบบ ancestor descendant ร่วมกับ Filters หรืออธิบายคือ สั่งให้ jQuery ไปหา element ที่ class =table.ms-formtable (ซึ่งเป็น table) จากนั้นก็ไปที่ row ที่ 12 (index=11) ของตารางนี้ และไปที่ column ที่ 2 (index=1) แล้วไปดึงเอา innerHTML ออกมาซะ เพื่อตรวจสอบเงื่อนไขสำหรับ hide ตาราง&lt;br /&gt;&lt;br /&gt;แต่ปรากฎว่าแต่ละหน้า อาจมี row ไม่เท่ากันก็ได้ ดังนั้นคุณ Nine จึงสั่งไปที่ table เดียวกัน (class =table.ms-formtable) แล้วใช้ jQuery Object Accessors ที่ชื่อว่า each ในการวนลูปทีละแถว เพื่อตรวจสอบข้อมูลทุกแถวครับ&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;$(document).ready(function($) {&lt;br /&gt;     var rows = $('table.ms-formtable tr').each(function() {&lt;br /&gt;          var row = $(this);&lt;br /&gt;          var columns = row.children('td');&lt;br /&gt;          if(columns.eq(1).html().indexOf('ProcessCompleted')&gt;-1 &amp;amp;&amp;amp; columns.eq(1).html().indexOf('1 ')&gt;-1)&lt;br /&gt;               $('table.ms-toolbar').hide();&lt;br /&gt;     });&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;จริงๆแล้ว เราควรจะใช้ ID เป็น Selector จะได้ง่าย แต่เพราะว่า MS SharePoint มันดันสร้าง ID ซ้ำกันเยอะแยะนั่นสิ ทำให้เวลาเราใช้ ID Selector แล้วมันจะได้เฉพาะ element แรกเท่านั้นนั่นเอง (เพราะ ID ควรมีแค่ 1 ID ต่อ 1 page)&lt;br /&gt;ใครสนใจจะดูโค้ดเต็มๆลองไปดูที่&lt;br /&gt;&lt;br /&gt;jQuery ค้นหาค่า และซ่อน table ช่วยหน่อยคร้าบ&lt;br /&gt;&lt;a href="http://greatfriends.biz/?110667" target="_blank"&gt;http://greatfriends.biz/?110667&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-5860539949130124299?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/5860539949130124299/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=5860539949130124299' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5860539949130124299'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5860539949130124299'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/07/jquery-selectors.html' title='jQuery ลอง Selectors หลายๆแบบ'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-1327979100172363694</id><published>2009-08-03T21:14:00.001+07:00</published><updated>2009-08-04T13:19:30.085+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VB.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><title type='text'>VB.NET Coding Convention</title><content type='html'>ผมได้เข้าไปค้นหาข้อมูลใน msdn ก็เลยเจอหัวข้อ VB.NET Coding Convention อยู่ภายใน VS2008 พออ่านๆดูก็พบว่ามีหลายอย่างที่ไม่ได้ทำตาม Convention ของ MS ครับ ก่อนอื่นมาดูก่อนว่าถ้าเราเขียนโค้ดตาม guide line นี้จะมีข้อดีอะไรบ้าง&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Coding conventions create a consistent look to the code, so that readers can focus on content, not layout. &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Conventions let the readers understand the code more quickly, because it allows them to make assumptions based on previous experience.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Conventions make copying, changing, and maintaining the code easier. &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Conventions demonstrate Visual Basic "best practices."&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;ทีนี้มาลองดูหัวข้อที่น่าสนใจครับ&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="color:#660000;"&gt;Naming Convention&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;Do not use "My" or "my" as part of a variable name. This creates confusion with the My objects. &lt;em&gt;&lt;span style="font-family:arial;color:#006600;"&gt;ข้อนี้ผิดบ่อยครับ บางทีคิดไม่ออกชอบตั้งชื่อตัวแปรมี My นำหน้า &lt;/span&gt;&lt;/em&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="color:#660000;"&gt;Layout Convention&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;li&gt;Use only one statement per line. Do not use the Visual Basic line continuation character (:).&lt;em&gt;&lt;span style="color:#009900;"&gt; &lt;/span&gt;&lt;span style="font-family:arial;color:#006600;"&gt;ข้อนี้ก็ทำบ้าง แต่ส่วนใหญ่จะใช้กับ 2 statements สั้นๆ โดยเฉพาะอย่างยิ่งเขียนใน SELECT CASE หรือ IF THEN STATEMENT และพอทำแล้วผมว่าโค้ดอ่านง่ายนะ &lt;/span&gt;&lt;/em&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Use only one declaration per line. &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Add at least one blank line between method and property definitions. &lt;em&gt;&lt;span style="font-family:arial;color:#006600;"&gt;ผมทำตามนี้อยู่แล้ว เห็นโค้ดบางคนไม่เว้นบรรทัด อ่านแล้วลายตาครับ&lt;/span&gt;&lt;/em&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;&lt;span style="color:#660000;"&gt;Language Guidelines&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;String Data Type&lt;/strong&gt;&lt;br /&gt;&lt;li&gt;Use &amp;amp; to concatenate strings: &lt;em&gt;&lt;span style="font-family:arial;color:#006600;"&gt;เมื่อก่อนก็เขียนแบบนี้ แต่ตอนหลังเปลี่ยนมาใช้ String.Concat() นะ ว่าแต่ใช้แบบไหนดีเนี่ย&lt;/span&gt;&lt;/em&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;For appending strings in loops, use the StringBuilder object: &lt;em&gt;&lt;span style="font-family:arial;color:#006600;"&gt;อันนี้แน่นอน ใช้ StringBuilder ดีกว่าเห็นๆ&lt;/span&gt;&lt;/em&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Type Inference&lt;/strong&gt;&lt;br /&gt;&lt;li&gt;Take advantage of type inference for local variables: &lt;em&gt;&lt;span style="font-family:arial;color:#006600;"&gt;สำหรับผมกำหนด Type ไปเลยอ่านง่ายกว่า Type inference นะ อันนี้ขอไม่เปลี่ยน&lt;/span&gt;&lt;/em&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Use Type Inference for Loop Variables in For or For Each Statements&lt;br /&gt;Allow type inference to determine the type of the loop range variable.&lt;em&gt;&lt;span style="color:#006600;"&gt; &lt;/span&gt;&lt;span style="font-family:arial;color:#006600;"&gt;เหมือนข้อเมื่อกี้ ส่วนตัวไม่ชอบ Type inference (แต่ก็ใช้บ้าง) ถ้าเป็นไปได้จะหลีกเลี่ยง มันเหมือนคนพูดสั้นๆให้เข้าใจเอาเอง&lt;/span&gt;&lt;/em&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Unsigned Data Type&lt;/strong&gt;&lt;br /&gt;&lt;li&gt;Use Integer rather than unsigned types unless memory is at a premium. &lt;em&gt;&lt;span style="font-family:arial;color:#006600;"&gt;อันนี้รวมถึงไม่ควรใช้ Int16, Int64, Short, Long ด้วย&lt;/span&gt;&lt;/em&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Use the MsgBox Function&lt;/strong&gt;&lt;br /&gt;&lt;li&gt;Use MsgBox instead of MessageBox.Show or Console.WriteLine. In environments that do not support the MsgBox function, such as Silverlight, use an appropriate alternative. &lt;em&gt;&lt;span style="font-family:arial;color:#006600;"&gt;อืม เคยอ่านมาว่าให้ใช้ MessageBox.Show แต่จริงๆชอบ MsgBox มากกว่า แบบนี้ก็กลับมาใช้แบบเดิมดีกว่าครับ &lt;/span&gt;&lt;/em&gt;&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;New Keyword&lt;/strong&gt;&lt;br /&gt;&lt;li&gt;Use short instantiation: &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Use object initializers for new objects instead of the parameterless constructor: &lt;/li&gt;&lt;br /&gt;&lt;br /&gt;ยังมีอีกพอสมควรครับ สำหรับคนที่สนใจเข้าไปศึกษาได้ที่ &lt;a href="http://msdn.microsoft.com/en-us/library/h63fsef3.aspx"&gt;http://msdn.microsoft.com/en-us/library/h63fsef3.aspx&lt;/a&gt; ครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-1327979100172363694?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/1327979100172363694/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=1327979100172363694' title='4 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/1327979100172363694'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/1327979100172363694'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/07/vbnet-coding-convention.html' title='VB.NET Coding Convention'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-3464169441739885430</id><published>2009-08-01T01:10:00.002+07:00</published><updated>2010-10-29T10:57:52.266+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET AJAX'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>jQuery กับการกำหนด Mouse Hover ใน GridView</title><content type='html'>อ้างอิงจากคำถามที่ GreatFriends ครับ&lt;br /&gt;&lt;strong&gt;&lt;span style="color:#3333ff;"&gt;ขอคำแนะนำเรื่อง GridView กับ Jquey จ้า&lt;/span&gt;&lt;/strong&gt;&lt;br /&gt;&lt;a href="http://greatfriends.biz/?110946"&gt;http://greatfriends.biz?110946&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;เจ้าของกระทู้ต้องการทำ Highlight สีของแถวใน GridView เมื่อเอา mouse ไปชี้ที่แถวนั้นๆครับ และพอกด Select ใน GridView ก็ให้นำข้อมูลมาใส่ textbox&lt;br /&gt;&lt;br /&gt;จริงๆแล้วถ้าข้อมูลที่จะใส่ TextBox อยู่ใน GridView ทั้งหมด เราสามารถใช้ jQuery ดึงข้อมูลของแถวมาใช้ได้เลย แต่ผมสมมติว่าเราต้องการข้อมูลอื่นๆ โดยอ้างจาก Id ของข้อมูลที่เลือกละกันครับ ดังนั้นผมก็ต้อง Post back กลับไปเพื่อหาค่ามาใส่ใน textbox ก็เลยทำหน้า page มาลองทดสอบดูครับ&lt;br /&gt;&lt;br /&gt;โค้ดส่วน Head ครับ Style ผม copy มาจากเจ้าของกระทู้เลย ส่วน jQuery ผมใช้ที่อาจารย์สุเทพแก้ไขให้ นำมาแยกออกจาก $document.ready() เป็น LoadHandle function ครับ&lt;br /&gt;&lt;pre class="brush:html"&gt;&lt;br /&gt;&amp;lt;head runat="server"&amp;gt;&lt;br /&gt;    &amp;lt;title&amp;gt;Test&amp;lt;/title&amp;gt;&lt;br /&gt;    &amp;lt;style&amp;gt;&lt;br /&gt;        .trMouseClick {&lt;br /&gt;            background-color:  Transparent;&lt;br /&gt;            color: #A4D1A1 !important;&lt;br /&gt;            text-decoration: none;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;    .trMouseOver {&lt;br /&gt;            background-color: #F2FEF2 !important;&lt;br /&gt;            color: #A4D1A1 !important;&lt;br /&gt;            text-decoration: none;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;    .trMouseOver a  {&lt;br /&gt;            background-color: #F2FEF2 !important;&lt;br /&gt;            color: #A4D1A1 !important;&lt;br /&gt;        }&lt;br /&gt;    &amp;lt;/style&amp;gt;&lt;br /&gt;    &amp;lt;script type="text/javascript" src="../Scripts/jquery-1.3.2-vsdoc.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;    &amp;lt;script type="text/jscript"&amp;gt;&lt;br /&gt;&lt;br /&gt;        $(document).ready(LoadHandle);&lt;br /&gt;    &lt;br /&gt;        function LoadHandle() {&lt;br /&gt;            $("tr:gt{0}").hover(function() {&lt;br /&gt;                $(this).addClass("trMouseOver");&lt;br /&gt;                }, function() {&lt;br /&gt;                $(this).removeClass("trMouseOver");&lt;br /&gt;            });&lt;br /&gt;            $("tr:gt{0}").click(function() {&lt;br /&gt;                $(this).addClass("trMouseClick").siblings().removeClass("trMouseClick");&lt;br /&gt;            });&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;    &amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;โค้ดส่วน Form tag (ใน Body)&lt;br /&gt;&lt;pre class="brush:html"&gt;&lt;br /&gt;&amp;lt;asp:ScriptManager ID="ScriptManager1" runat="server"&gt;:&lt;br /&gt;&amp;lt;/asp:ScriptManager&gt;:&lt;br /&gt;&amp;lt;div id = "main" style="height:700px;z-index:-10"&gt;:&lt;br /&gt;    &amp;lt;div id="text" style="position:absolute"&gt;:&lt;br /&gt;        &amp;lt;asp:UpdatePanel ID ="UpdatePanel1" runat="server" UpdateMode="Conditional" RenderMode="Block"&gt;:&lt;br /&gt;            &amp;lt;ContentTemplate&gt;:&lt;br /&gt;                &amp;lt;div id="Update" style="position:absolute;top:50px; width:700px"&gt;:&lt;br /&gt;                   &amp;lt;asp:GridView ID="gvInvoice" runat="server" AllowPaging="True"&lt;br /&gt;                        AutoGenerateColumns="False" BorderColor="Gainsboro" BorderStyle="None"&lt;br /&gt;                        BorderWidth="1px" CellPadding="0" GridLines="Horizontal"&lt;br /&gt;                        AutoGenerateSelectButton="True" DataKeyNames="ItemId"&gt;:&lt;br /&gt;                        &amp;lt;Columns&gt;:&lt;br /&gt;                            &amp;lt;asp:TemplateField SortExpression="ITEMID"&gt;:&lt;br /&gt;                                &amp;lt;ItemStyle Width="0px"&gt;:&amp;lt;/ItemStyle&gt;:&lt;br /&gt;                                &amp;lt;ItemTemplate&gt;:&lt;br /&gt;                                    &amp;lt;asp:HiddenField id="hdnRecId" runat="server" Value='&amp;lt;%# Bind("ITEMID") %&gt;:'&gt;:&amp;lt;/asp:HiddenField&gt;:&lt;br /&gt;                                &amp;lt;/ItemTemplate&gt;:&lt;br /&gt;                            &amp;lt;/asp:TemplateField&gt;:&lt;br /&gt;                            &amp;lt;asp:TemplateField HeaderText="Item No"&gt;:&lt;br /&gt;                                &amp;lt;HeaderStyle Width="40px" HorizontalAlign="center"&gt;:&amp;lt;/HeaderStyle&gt;:&lt;br /&gt;                                &amp;lt;ItemStyle HorizontalAlign="center" /&gt;:&lt;br /&gt;                                &amp;lt;ItemTemplate&gt;:&lt;br /&gt;                                    &amp;lt;asp:Label id="lblItemNo" runat="server" Text='&amp;lt;%# Eval("ORDERNO") %&gt;:' &gt;:&amp;lt;/asp:Label&gt;:&lt;br /&gt;                                &amp;lt;/ItemTemplate&gt;:&lt;br /&gt;                            &amp;lt;/asp:TemplateField&gt;:&lt;br /&gt;                            &amp;lt;asp:TemplateField HeaderText="Invoice Date"&gt;:&lt;br /&gt;                                &amp;lt;ItemStyle Width="100px"&gt;:&amp;lt;/ItemStyle&gt;:&lt;br /&gt;                                &amp;lt;HeaderStyle HorizontalAlign="Center"&gt;:&amp;lt;/HeaderStyle&gt;:&lt;br /&gt;                                &amp;lt;ItemTemplate&gt;:&lt;br /&gt;                                    &amp;lt;asp:Label style="TEXT-ALIGN: center" id="Label2" runat="server" Width="80px" Text='&amp;lt;%# Bind("InvoiceDate", "{0:dd/MM/yyyy}") %&gt;:'&gt;:&amp;lt;/asp:Label&gt;:&lt;br /&gt;                                &amp;lt;/ItemTemplate&gt;:&lt;br /&gt;                            &amp;lt;/asp:TemplateField&gt;:&lt;br /&gt;                            &amp;lt;asp:TemplateField HeaderText="Product Code"&gt;:&lt;br /&gt;                                &amp;lt;ItemStyle Width="60px"&gt;:&amp;lt;/ItemStyle&gt;:&lt;br /&gt;                                &amp;lt;HeaderStyle HorizontalAlign="Center"&gt;:&amp;lt;/HeaderStyle&gt;:&lt;br /&gt;                                &amp;lt;ItemTemplate&gt;:&lt;br /&gt;                                     &amp;lt;asp:Label style="TEXT-ALIGN: center" id="Label3" runat="server" Width="50px" Text='&amp;lt;%# Bind("ProductCode") %&gt;:'&gt;:&amp;lt;/asp:Label&gt;:&lt;br /&gt;                                &amp;lt;/ItemTemplate&gt;:&lt;br /&gt;                            &amp;lt;/asp:TemplateField&gt;:&lt;br /&gt;                            &amp;lt;asp:TemplateField HeaderText="Amount (THB)"&gt;:&lt;br /&gt;                                &amp;lt;ItemStyle Width="80px"&gt;:&amp;lt;/ItemStyle&gt;:&lt;br /&gt;                                &amp;lt;HeaderStyle HorizontalAlign="Center"&gt;:&amp;lt;/HeaderStyle&gt;:&lt;br /&gt;                                &amp;lt;ItemTemplate&gt;:&lt;br /&gt;                                   &amp;lt;asp:Label style="TEXT-ALIGN: right" id="Label5" runat="server"  Width="70px" Text='&amp;lt;%# Bind("Amount","{0:#,##0.00}") %&gt;:'&gt;:&amp;lt;/asp:Label&gt;:&lt;br /&gt;                                &amp;lt;/ItemTemplate&gt;:&lt;br /&gt;                            &amp;lt;/asp:TemplateField&gt;:&lt;br /&gt;                            &amp;lt;asp:TemplateField ShowHeader="False"&gt;:&lt;br /&gt;                                &amp;lt;ItemStyle Width="40px"&gt;:&amp;lt;/ItemStyle&gt;:&lt;br /&gt;                                &amp;lt;ItemTemplate&gt;:&lt;br /&gt;                                    &amp;lt;asp:ImageButton ID="btnEdit" runat="server" CommandName="Select" ImageUrl="~/Images/icon_edit.gif" ToolTip="Edit" /&gt;:&lt;br /&gt;                            &amp;lt;/ItemTemplate&gt;:&lt;br /&gt;                            &amp;lt;/asp:TemplateField&gt;:&lt;br /&gt;                        &amp;lt;/Columns&gt;:&lt;br /&gt;                        &amp;lt;RowStyle &gt;:&amp;lt;/RowStyle&gt;:&lt;br /&gt;                        &amp;lt;SelectedRowStyle&gt;:&amp;lt;/SelectedRowStyle&gt;:&lt;br /&gt;                        &amp;lt;PagerStyle HorizontalAlign="Right" VerticalAlign="Top"&gt;:&amp;lt;/PagerStyle&gt;:&lt;br /&gt;                        &amp;lt;HeaderStyle &gt;:&amp;lt;/HeaderStyle&gt;:&lt;br /&gt;                        &amp;lt;AlternatingRowStyle &gt;:&amp;lt;/AlternatingRowStyle&gt;:&lt;br /&gt;                    &amp;lt;/asp:GridView&gt;:&lt;br /&gt;                &amp;lt;/div&gt;:     &lt;br /&gt;                &amp;lt;div id="dvContent" style="position:absolute;top:400px"&gt;:&lt;br /&gt;                        &amp;lt;asp:TextBox ID="txtId" runat="server" style="width:100px" MaxLength="6"&gt;:&amp;lt;/asp:TextBox&gt;:&lt;br /&gt;                        &amp;lt;asp:TextBox ID="txtDescription" runat="server" style="width:250px"&gt;:&amp;lt;/asp:TextBox&gt;:&lt;br /&gt;                        &amp;lt;asp:TextBox ID="txtAmount" runat="server" style="width:250px"&gt;:&amp;lt;/asp:TextBox&gt;:&lt;br /&gt;                &amp;lt;/div&gt;:              &lt;br /&gt;            &amp;lt;/ContentTemplate&gt;:              &lt;br /&gt;        &amp;lt;/asp:UpdatePanel&gt;:&lt;br /&gt;    &amp;lt;/div&gt;:&lt;br /&gt;&amp;lt;/div&gt;:&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;โค้ดส่วน Code Behind ครับ&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Imports jnithi.Class.Invoice&lt;br /&gt;&lt;br /&gt;Partial Class InvoiceTest&lt;br /&gt;    Inherits System.Web.UI.Page&lt;br /&gt;#Region "Local fields"&lt;br /&gt;&lt;br /&gt;    Private _inv As List(Of Entity.Invoice) = Nothing&lt;br /&gt;&lt;br /&gt;#End Region&lt;br /&gt;&lt;br /&gt;    Private Sub GetInvoiceByInvoiceNo()&lt;br /&gt;        If _inv IsNot Nothing Then&lt;br /&gt;            _inv.Clear()&lt;br /&gt;            _inv = Nothing&lt;br /&gt;        End If&lt;br /&gt;        _inv = Manager.Invoice.GetInvoiceByInvoiceNo("Test1")&lt;br /&gt;        Session("Invoice") = _inv&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Private Sub BindGridView()&lt;br /&gt;        gvInvoice.DataSource = _inv&lt;br /&gt;        gvInvoice.DataBind()&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Protected Sub form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles form1.Load&lt;br /&gt;&lt;br /&gt;        If Not IsPostBack Then&lt;br /&gt;            GetInvoiceByInvoiceNo()&lt;br /&gt;        Else&lt;br /&gt;            _inv = Session("Invoice")&lt;br /&gt;        End If&lt;br /&gt;        BindGridView()&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;    Protected Sub gvInvoice_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles gvInvoice.SelectedIndexChanged&lt;br /&gt;        Dim id As Integer = CType(gvInvoice.SelectedValue, Integer)&lt;br /&gt;        Dim item As Entity.Invoice = _inv.Find(Function(i) i.ItemID = id)&lt;br /&gt;        Me.txtId.Text = id&lt;br /&gt;        Me.txtDescription.Text = item.ItemWorkDesc&lt;br /&gt;        Me.txtAmount.Text = item.LCCost.ToString("#,##0.00")&lt;br /&gt;        ScriptManager.RegisterStartupScript(Page, Page.GetType, "GVDataBound", "LoadHandle();", True)&lt;br /&gt;    End Sub&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ผลการทดสอบครับ&lt;br /&gt;&lt;a href="http://3.bp.blogspot.com/_N-Phs4y2L44/SnJs8kxI5GI/AAAAAAAAACc/KXkyHyjJGcQ/s1600-h/jnithi09080101.JPG"&gt;&lt;img style="margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 400px; height: 296px;" src="http://3.bp.blogspot.com/_N-Phs4y2L44/SnJs8kxI5GI/AAAAAAAAACc/KXkyHyjJGcQ/s400/jnithi09080101.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5364469894069412962" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;สรุป&lt;br /&gt;1. ผมแยก LoadHandle Function ออกมาจาก $document.ready() เพื่อที่ว่าให้ function นี้เป็น Event Handler เมื่อทำการ Load ข้อมูลฝั่ง client เสร็จครับ &lt;br /&gt;2. ใน GridView_SelectedIndexChanged ผมทำการ register script เพื่อให้เรียก LoadHandle functionให้ทำงาน เมื่อ UpdatePanel ทำการ Update ข้อมูลเสร็จแล้ว&lt;br /&gt;3. ถ้าไม่ใช้ ScriptManager.RegisterStartupScript ซึ่งเป็นการสั่งที่ฝั่ง Server เราสามารถใช้ PageRequestManager (เขียน javascript) แทนได้ครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-3464169441739885430?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/3464169441739885430/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=3464169441739885430' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/3464169441739885430'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/3464169441739885430'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/07/jquery-hover-gridview.html' title='jQuery กับการกำหนด Mouse Hover ใน GridView'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_N-Phs4y2L44/SnJs8kxI5GI/AAAAAAAAACc/KXkyHyjJGcQ/s72-c/jnithi09080101.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-6996894455745506386</id><published>2009-07-28T22:22:00.002+07:00</published><updated>2010-10-29T11:21:50.502+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><category scheme='http://www.blogger.com/atom/ns#' term='LINQ'/><title type='text'>ลองสร้าง Extension Method ใน VB9</title><content type='html'>ถ้าพูดถึง Visual Studio 2008 แล้ว พระเอกที่มีคนกล่าวขวัญถึงมากที่สุดไม่น่าจะมี feature ไหนเกิน LINQ ไปได้ครับ หลายๆคนคงได้นำ LINQ ไปใช้ในงานจริงกันบ้างแล้ว ส่วนตัวผมเองตอนที่ VS2008 ยังอยู่ในช่วง Beta และ CTP ก็มีคนพูดถึง LINQ ให้ฟังหลายคน แต่ไม่ค่อยได้สนใจครับ แต่พอมาลองใช้งานดูแล้วต้องบอกว่าเยี่ยมมากครับ&lt;br /&gt;&lt;br /&gt;สำหรับเบื้องหลังของ LINQ นั้น เทคโนโลยีสำคัญอย่างหนึ่งก็คือ Extension Method นั่นเองครับ เพราะด้วย Extension Method ทำให้เราสามารถใช้ LINQ ได้ โดยที่ runtime ยังเป็น v2.0.50727 เหมือนเดิม ที่เป็นแบบนี้เพราะว่า Extension Method นั้นทำให้เราสามารถเพิ่ม behavior ให้กับ Type ของเราได้โดยไม่จำเป็นต้องมี source code เดิมครับ&lt;br /&gt;&lt;br /&gt;สมมติผมต้องการเพิ่มความสามารถให้กับ String Type สำหรับงาน Web Application ถ้าเป็นเมื่อก่อนก็อาจจะสร้าง sealed class ขึ้นมาโดยให้มี static method ที่ต้องการ (เพื่อที่ว่าจะได้ไม่ต้อง new object เวลาใช้งาน) ลองดูตัวอย่างครับ&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Public NotInheritable Class MyExtension&lt;br /&gt;    Public Shared Function ApplyStyle(ByVal s As String) As String&lt;br /&gt;        Return String.Format("&amp;lt;span style='FONT-WEIGHT: bold;font-family:Verdana;color:red;'&amp;gt;{0}&amp;lt;/span&amp;gt;", s)&lt;br /&gt;    End Function&lt;br /&gt;&lt;br /&gt;    Public Shared Function WriteLine(ByVal s As String) As String&lt;br /&gt;        Return String.Format("{0:#,##0}&lt;br /&gt;", s)&lt;br /&gt;    End Function&lt;br /&gt;End Class&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;แต่ถ้าเราสร้าง Extension Method ใน VB.NET นั้น เราจำเป็นต้องสร้างใน Module ครับ (C# จะสร้างใน Static Class ได้เลย แต่ VB.NET ไม่มี Static Class ครับ คือไม่สามารถใส่ "shared" keyword หน้า Class) และก็ใส่ Extension Attribute ด้วยครับ ลองมาดูโค้ดกัน&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Public Module MyExtension2&lt;br /&gt;    &amp;lt;System.Runtime.CompilerServices.Extension()&amp;gt; _&lt;br /&gt;    Public Function EXApplyStyle(ByVal s As String) As String&lt;br /&gt;        Return String.Format("&amp;lt;span style='color:Red;font-weight:bold;font-family:Verdana'&amp;gt;{0}&amp;lt;/span&amp;gt;", s)&lt;br /&gt;    End Function&lt;br /&gt;&lt;br /&gt;    &amp;lt;System.Runtime.CompilerServices.Extension()&amp;gt;&lt;br /&gt;    Public Function EXWriteLine(ByVal s As String) As String&lt;br /&gt;        Return String.Format("{0:#,##0} &amp;lt;br /&amp;gt;", s)&lt;br /&gt;    End Function&lt;br /&gt;End Module&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;คราวนี้มาดูเวลาการใช้งานกันครับ&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Partial Class ModuleDisbursement_DBItemTest&lt;br /&gt;    Inherits System.Web.UI.Page&lt;br /&gt;&lt;br /&gt;    Protected Sub form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles form1.Load&lt;br /&gt;        Dim str As String = "Hello world."&lt;br /&gt;        Response.Write(MyExtension.WriteLine(MyExtension.ApplyStyle(str)))&lt;br /&gt;        Response.Write(str.EXApplyStyle().EXWriteLine())&lt;br /&gt;    End Sub&lt;br /&gt;&lt;br /&gt;End Class&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Reponse.Write บรรทัดแรก ใช้ Static Method ส่วนบรรทัดที่สองใช้ Extension Method ครับ สังเกตุได้ว่าเมื่อเราใช้ Extension Method แล้วเราจะเห็นว่า String Type ของเรามี method เพิ่มขึ้นมาอีก 2 ตัวครับ สะดวกดีจริงๆเลย&lt;br /&gt;&lt;br /&gt;ปล. จากการ search ใน google พบว่า&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;มี developer คนหนึ่งต้องการเพิ่ม method ใน BitMap Type แต่ทำไม่ได้ เพราะเป็น Sealed Class ครับ บางคนแนะนำให้สร้าง BitMap Type ของตัวเอง (ซึ่งเสียเวลาและต้องตามไปแก้โค้ดอีกเยอะเลย) และก็มีคนแนะนำให้ใช้ Extension Method มาแก้ปัญหานี้ครับ&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Microsoft MVP ชาวต่างชาติคนหนึ่งเขียนใน blog ว่า Module นี่ไง Static Class ของ VB.NET &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Microsoft MVP อีกคนเขียนอีก blog ว่า อยากให้มี Static Class ใน VB10 เพราะว่า Module มันยังไม่ใช่ Static Class จริงๆ (เค้าอธิบายว่ามันไม่เหมือนกันเพราะ Reflection - อืมม ยังไม่ค่อยเข้าใจครับ)  &lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;เพิ่มเติมมีคุยกันเรื่องนี้ที่ greatfreinds ครับ&lt;br /&gt;&lt;a href="http://greatfriends.biz?110403"&gt;มีวิธีที่จะใช้ Extension method โดยไม่ผ่าน Module ไหมครับ&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-6996894455745506386?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/6996894455745506386/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=6996894455745506386' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6996894455745506386'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6996894455745506386'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/07/extension-method-vb9.html' title='ลองสร้าง Extension Method ใน VB9'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-1836794263809015590</id><published>2009-07-24T21:09:00.000+07:00</published><updated>2009-07-24T21:09:00.288+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>parseInt ทำงานผิดพลาด</title><content type='html'>วันนี้ user โทรมาแจ้งว่ามี bug เกิดขึ้น คือเมื่อเลือกวันที่จากปฏิทินแล้วกลายเป็นวันอื่นไป ลองไปไล่โค้ดดูปรากฏว่าผมไปกำหนด onblur event ของ textbox ให้ไปตรวจสอบวันที่และทำการจัด format เป็น dd/MM/yyyy&lt;br /&gt;&lt;br /&gt;ปรากฏว่าโค้ดส่วนที่มี bug คือคำสั่ง parseInt ครับ&lt;br /&gt;&lt;br /&gt;&lt;pre class="javascript" name="code"&gt;&lt;br /&gt;function DMY2Date(dmyStr) {&lt;br /&gt; var splitDateStr=dmyStr.split('/');&lt;br /&gt; return new Date(splitDateStr[2],String(parseInt(splitDateStr[1])-1),splitDateStr[0]);&lt;br /&gt;} //end function&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;คือผมสั่ง parseInt(splitDateStr[1]) เพื่อหาค่าเดือนเป็น Integer แต่ปรากฏว่า พอ split data string ออกมา มันได้ค่า "08" ซึ่งใน parseInt function มันมีเงื่อนไขว่าถ้าขึ้นต้นด้วย 0 แปลว่าเป็นเลขฐาน 8 ครับ พอลอง debug ดูปรากฏว่า parseInt("08") ได้ผลลัพธ์เป็น 0 เพราะเลขสูงสุดของเลขฐาน 8 คือ 7 ครับ  ถ้า parseInt("010") ถึงจะได้ค่า 8&lt;br /&gt;&lt;br /&gt;ลองดู Reference ที่ www.w3schools.com กันครับ&lt;br /&gt;&lt;br /&gt;Syntax&lt;br /&gt;parseInt(string, radix)&lt;br /&gt;&lt;br /&gt;If the radix parameter is omitted, JavaScript assumes the following:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;If the string begins with "0x", the radix is 16 (hexadecimal)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If the string begins with "0", the radix is 8 (octal). This feature is deprecated&lt;br /&gt;&lt;li&gt;If the string begins with any other value, the radix is 10 (decimal) &lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;ก็เลยไปแก้โดยกำหนด radix = 10 เข้าไปด้วย เพื่อบอกว่าเราต้องการ parseInt แบบเลขฐาน 10 ไม่ใช่เลขฐาน 8 นะเฟ้ย&lt;br /&gt;&lt;br /&gt;&lt;pre class="javascript" name="code"&gt;&lt;br /&gt;function DMY2Date(dmyStr) {&lt;br /&gt; var splitDateStr=dmyStr.split('/');&lt;br /&gt; return new Date(splitDateStr[2],String(parseInt(splitDateStr[1],10)-1),splitDateStr[0]);&lt;br /&gt;} //end function&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;เสร็จแล้วก็เลยต้องไปไล่นั่งตรวจสอบดูว่ามีการใช้ parseInt function ตรงไหนบ้าง เพื่อที่จะไปใส่ radix ให้หมดเพื่อป้องกันปัญหาครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-1836794263809015590?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/1836794263809015590/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=1836794263809015590' title='2 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/1836794263809015590'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/1836794263809015590'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/07/parseint.html' title='parseInt ทำงานผิดพลาด'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-1156725252254632608</id><published>2009-07-20T22:19:00.004+07:00</published><updated>2010-10-29T10:58:58.063+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>javascript กำหนด textbox ให้รับค่า time (hh:mm)</title><content type='html'>สมมติผมมี textbox id="txtTime" และต้องการให้ user สามารถคืย์ข้อมูลเฉพาะเวลา โดยให้ format = hh:mm (23:59) ครับ &lt;br /&gt;ก่อนอื่นก็ต้อง register event handler ให้กับ textbox ก่อน &lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:javascript"&gt;&lt;br /&gt;        $(function() {&lt;br /&gt;            $("#txtTime").keypress(function(e) {txtTime_OnKeyPress(this,e)});&lt;br /&gt;            $("#txtTime").blur(function() { ValidateTime(this) });&lt;br /&gt;        });&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;จะเห็นว่าผมสร้าง function สำหรับจัดการ keypress event กับ blur event ให้ textbox ครับ สำหรับ txtTime_OnKeyPress สำหรับกำหนดให้ user สามารถคีย์เฉพาะ d(2).d(2) คือตัวเลขสองตัว ตามด้วยจุดและตัวเลขอีกสองตัว ส่วน ValidateTime สำหรับตรวจสอบว่าค่าที่คีย์ตรงกับ format ที่ต้องการหรือเปล่า&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:javascript"&gt;&lt;br /&gt;        function txtTime_OnKeyPress(sender, e) {&lt;br /&gt;            var myTime = sender.value;&lt;br /&gt;            if(myTime.length&gt;4) {&lt;br /&gt;                event.keyCode = 0;&lt;br /&gt;                return false;&lt;br /&gt;            }&lt;br /&gt;            var charCode = (e.which) ? e.which : e.keyCode&lt;br /&gt;            switch (myTime.length) {&lt;br /&gt;                case 0:&lt;br /&gt;                    if (charCode &lt; 48 || charCode &gt; 50) event.keyCode = 0;&lt;br /&gt;                    break;&lt;br /&gt;                case 1:&lt;br /&gt;                    if (charCode&lt;48||(myTime == 2&amp;&amp;charCode &gt; 51)) event.keyCode = 0; &lt;br /&gt;                    break;&lt;br /&gt;                case 2:&lt;br /&gt;                    if (charCode != 46)  event.keyCode = 0; &lt;br /&gt;                    break;&lt;br /&gt;                case 3:&lt;br /&gt;                    if (charCode&lt;48||charCode &gt; 53) event.keyCode = 0; &lt;br /&gt;                    break;&lt;br /&gt;                default:&lt;br /&gt;                    if (charCode &lt; 48 || charCode &gt; 57) event.keyCode = 0;                 &lt;br /&gt;            }     &lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        function ValidateTime(sender) {&lt;br /&gt;            if (sender.value.length == 0) return false;&lt;br /&gt;            var regEx = /^(\d{2}).(\d{2})$/;&lt;br /&gt;            var arrMatch = sender.value.match(regEx);&lt;br /&gt;            if (arrMatch == null) {&lt;br /&gt;                alert("Invalid time.");&lt;br /&gt;                sender.value = "";&lt;br /&gt;                return false;&lt;br /&gt;            }&lt;br /&gt;            var hh = arrMatch[1];&lt;br /&gt;            var mm = arrMatch[2];&lt;br /&gt;            if (hh &gt;= 24 || mm &gt;= 60) {&lt;br /&gt;                alert("Invalid time.");&lt;br /&gt;                sender.value = "";&lt;br /&gt;                return false;&lt;br /&gt;            }&lt;br /&gt;            return true;&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ทดสอบกับ IE ได้ผล OK ครับ ใครจะนำไปใช้ก็ทดสอบกันก่อนนะ ว่างๆผมจะมาแก้ function ให้สามารถกำหนด format ของเวลาได้ เผื่อบางทีต้องใช้แบบ 11:59 A.M. แต่ตอนนี้ใช้แค่นี้ก่อนละกันครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-1156725252254632608?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/1156725252254632608/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=1156725252254632608' title='1 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/1156725252254632608'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/1156725252254632608'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/07/jquery-textbox-time-hhmm.html' title='javascript กำหนด textbox ให้รับค่า time (hh:mm)'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-5319242735999090933</id><published>2009-07-18T22:41:00.000+07:00</published><updated>2009-07-20T17:02:14.794+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><title type='text'>Visual Studio 2008 Samples</title><content type='html'>ผมลองคุยกับหลายๆคน ส่วนใหญ่ไม่ทราบว่าทาง Microsoft ได้ทำการเผยแพร่ "Visual Studio 2008 Samples" ให้ดาวน์โหลดมาลองกันครับ ผมเองได้ลองดาวน์โหลด Visual Studio 2008 RTM Samples มาลองดูตัวอย่าง (โดยเฉพาะ LINQ เปิดอ่านดูบ่อยพอสมควร)&lt;br /&gt;&lt;br /&gt;นอกจาก VS2008 Samples แล้วยังมี Code Samples อื่นๆเลือกให้ลองดาวน์โหลดมาศึกษาเยอะแยะมากครับ&lt;br /&gt;&lt;br /&gt;สนใจเข้าไปดาวน์โหลดได้ที่ &lt;a href="http://msdn.microsoft.com/en-us/vstudio/bb330936.aspx"&gt;Visual Studio 2008 Samples&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-5319242735999090933?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/5319242735999090933/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=5319242735999090933' title='2 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5319242735999090933'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5319242735999090933'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/07/visual-studio-2008-samples.html' title='Visual Studio 2008 Samples'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-8360867639252796050</id><published>2009-07-17T22:08:00.002+07:00</published><updated>2009-07-17T22:08:01.023+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VBScript'/><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>VBScript ดึงชื่อและอีเมลล์จาก Active Directory</title><content type='html'>เมื่อหลายวันก่อนผมต้องทำการดึง ชื่อพนักงาน และ email address ที่เก็บไว้ใน AD ออกมาเพื่อตรวจสอบ และ Import เข้า Database สำหรับไว้ใช้ในการทำ mailling list ใช้ภายในครับ ลอง search ดูใน google ก็พบว่ามีหลายวิธี และที่ปิ๊งที่สุดก็คือการเขียน VBScript เพื่อไปดึงข้อมูลจาก LDAP มาเขียนใส่ text file ครับ&lt;br /&gt;&lt;br /&gt;&lt;pre name=code class=vb&gt;&lt;br /&gt; Dim FileSystem&lt;br /&gt; 'Initialize global variables&lt;br /&gt; Set FileSystem = WScript.CreateObject("Scripting.FileSystemObject")&lt;br /&gt; Set OutPutFile = FileSystem.CreateTextFile("email.txt", True)&lt;br /&gt; Set oContainer=GetObject("LDAP://OU=Department Users,DC=MyDomainName,DC=com")&lt;br /&gt; 'Enumerate Container&lt;br /&gt; EnumerateUsers oContainer&lt;br /&gt; 'Clean up&lt;br /&gt; OutPutFile.Close&lt;br /&gt; Set FileSystem = Nothing&lt;br /&gt; Set oContainer = Nothing&lt;br /&gt; WScript.Echo "Finished"&lt;br /&gt; WScript.Quit(0)&lt;br /&gt;&lt;br /&gt; Sub EnumerateUsers(oCont)&lt;br /&gt;  Dim oUser&lt;br /&gt; For Each oUser In oCont&lt;br /&gt; Select Case LCase(oUser.Class)&lt;br /&gt;   Case "user"&lt;br /&gt;   OutPutFile.WriteLine oUser.givenname &amp; " " &amp; oUser.sn &amp; vbtab &amp; oUser.mail&lt;br /&gt;   Case "organizationalunit" , "container"&lt;br /&gt;    EnumerateUsers oUser&lt;br /&gt;   End Select&lt;br /&gt; Next&lt;br /&gt; End Sub&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ถ้าอยากรู้ว่า oUser (User Class) มี property อะไรบ้าง ดูตามนี้เลยครับ ไม่แน่ใจว่าครบหรือเปล่านะ&lt;br /&gt;&lt;br /&gt;&lt;pre name=code class=vb&gt;&lt;br /&gt;'// User Property&lt;br /&gt;'SamAccountName = oUser.samAccountName &lt;br /&gt;'Cn = oUser.CN &lt;br /&gt;'FirstName = oUser.GivenName &lt;br /&gt;'LastName = oUser.sn &lt;br /&gt;'initials = oUser.initials &lt;br /&gt;'Descrip = oUser.description &lt;br /&gt;'Office = oUser.physicalDeliveryOfficeName &lt;br /&gt;'Telephone = oUser.telephonenumber &lt;br /&gt;'EmailAddr = oUser.mail &lt;br /&gt;'WebPage = oUser.wwwHomePage &lt;br /&gt;'Addr1 = oUser.streetAddress &lt;br /&gt;'City = oUser.l &lt;br /&gt;'State = oUser.st &lt;br /&gt;'ZipCode = oUser.postalCode &lt;br /&gt;'Title = oUser.Title &lt;br /&gt;'Department = oUser.Department &lt;br /&gt;'Company = oUser.Company &lt;br /&gt;'Manager = oUser.Manager &lt;br /&gt;'Profile = oUser.profilePath &lt;br /&gt;'LoginScript = oUser.scriptpath &lt;br /&gt;'HomeDirectory = oUser.HomeDirectory &lt;br /&gt;'HomeDrive = oUser.homeDrive &lt;br /&gt;'AdsPath = oUser.Adspath &lt;br /&gt;'LastLogin = oUser.LastLogin &lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-8360867639252796050?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/8360867639252796050/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=8360867639252796050' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/8360867639252796050'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/8360867639252796050'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/07/vbscript-active-directory.html' title='VBScript ดึงชื่อและอีเมลล์จาก Active Directory'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-2617326224122381353</id><published>2009-07-15T22:02:00.002+07:00</published><updated>2010-10-29T11:00:13.799+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET AJAX'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>jQuery ทำ highlight ขัอมูลใน GridView</title><content type='html'>ผมเคยทำโปรแกรมค้นหาข้อมูลจากไฟล์เอกสารที่เก็บไว้ใน File Server โดย user สามารถใส่ key word ได้หลายตัว จากนั้นโปรแกรมจะไปค้นหาว่ามีไฟล์ไหนบ้างที่มีคำหรือประโยคตรงกับเงื่อนไขที่ต้องการ แล้วนำข้อมูลรวมถึง url มาใส่ใน GridView&lt;br /&gt;ทีนี้ user ก็ต้องการให้โปรแกรมสามารถ highlight คำหรือประโยคใน GridView ที่ตรงกับ key word ด้วย ซึ่งก่อนหน้านี้ผมก็เขียน javascript วนลูปเพื่อทำ highlight ซึ่งโค้ดก็ยาวพอสมควร วันนี้จะลองมา modify โดยใช้ jQuery มาช่วยครับ โดยผมจะเพิ่ม Custom Attribute ให้กับ Control ที่ต้องการจะไปทำ highlight ครับ ซึ่งในแต่ละแถวของ GridView จะให้ไป highlight ที่ label 2 ตัว&lt;br /&gt;&lt;pre class="brush:html"&gt;&lt;br /&gt;&amp;lt;asp:TemplateField HeaderText="Title" HeaderStyle-Width="400px" HeaderStyle-HorizontalAlign="Left"&amp;gt; &lt;br /&gt;     &amp;lt;ItemTemplate&amp;gt;&lt;br /&gt;          &amp;lt;asp:Label ID="lblDocTitle" runat="server"    ForeColor="Blue" style="font-size: 10pt" Highlight="true"  Font-Bold="False"&amp;gt;&amp;lt;%# eval("DocTitle") %&amp;gt;&amp;lt;/asp:Label&amp;gt;&lt;br /&gt;     &amp;lt;/ItemTemplate&amp;gt;&lt;br /&gt;&amp;lt;/asp:TemplateField&amp;gt;&lt;br /&gt;&amp;lt;asp:TemplateField HeaderText="Abstract" HeaderStyle-Width="300px" HeaderStyle-HorizontalAlign="Left"&amp;gt;&lt;br /&gt;     &amp;lt;ItemTemplate&amp;gt;&lt;br /&gt;          &amp;lt;asp:Label ID="lblAbstract" runat="server"  Highlight="true" &amp;gt;&amp;lt;%# eval("Abstract") %&amp;gt;&amp;lt;/asp:Label&amp;gt;&lt;br /&gt;     &amp;lt;/ItemTemplate&amp;gt;&lt;br /&gt;&amp;lt;/asp:TemplateField&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;เนื่องจากผมใช้ ScriptManager และ UpdatePanel ด้วย จากบทความเก่าผมใช้วิธีเขียนคำสั่ง jQuery ที่ code behind แล้วสั่ง ScriptManager ให้ Register Script ให้ ตามตำราเรียกวิธีนี้ว่าเป็น Server-centric แต่คราวนี้เราจะมาลองทำ Client-centric ดูบ้าง แทนที่เราจะใช้ UpdateProgress Control ผมก็จะมาใช้ PageRequestManager ในการแสดงรูปภาพแทน รวมถึงทำการ register event handler function ครับ&lt;br /&gt;&lt;pre class="brush:js"&gt;&lt;br /&gt;&amp;lt;asp:ScriptManager ID="ScriptManager1" runat="server"&amp;gt;&amp;lt;/asp:ScriptManager&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;script type="text/javascript" id="MSAjax"&amp;gt;&lt;br /&gt;     var prm = Sys.WebForms.PageRequestManager.getInstance();&lt;br /&gt;     prm.add_beginRequest(beginRequest);&lt;br /&gt;     prm.add_pageLoaded(pageLoaded);&lt;br /&gt;&lt;br /&gt;     function beginRequest(sender, args) {&lt;br /&gt;         $("#divProgress").show();&lt;br /&gt;         $("#MainGridView").html("");&lt;br /&gt;     }&lt;br /&gt;&lt;br /&gt;     function pageLoaded(sender, args) {&lt;br /&gt;         //Add elements'event handler.&lt;br /&gt;         $("txtFreeText").keypress(function(e) { txtFreeText_OnKeyPress(e); });&lt;br /&gt;         $("btnSearch").click(function() { ValidateSearch(); });&lt;br /&gt;         $("#divProgress").hide();&lt;br /&gt;                &lt;br /&gt;         HighlightSearch();&lt;br /&gt;    } &lt;br /&gt;&amp;lt;/script&amp;gt; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;คราวนี้มาลองดูโค้ดที่สำหรับทำ Highlight กันครับ&lt;br /&gt;&lt;pre class="brush:html"&gt;&lt;br /&gt;    &amp;lt;style type="text/css"&amp;gt;&lt;br /&gt;        .highlight{font-weight: bold; font-size: 16px; color: blue; background-color: papayawhip};&lt;br /&gt;    &amp;lt;/style&amp;gt;&lt;br /&gt;    &amp;lt;script type="text/javascript" src="Scripts/jquery-1.3.2-vsdoc.js"&amp;gt;&amp;lt;/script&amp;gt;  &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;  &lt;br /&gt;    &amp;lt;script type ="text/javascript" id="MainScript"&amp;gt;&lt;br /&gt;       &lt;br /&gt;        function trim(str) {&lt;br /&gt;            return str.replace(/^\s*|\s*$/g, "");&lt;br /&gt;        }  &lt;br /&gt;          &lt;br /&gt;        function HighlightSearch(){&lt;br /&gt;            var strSearch = $("#txtFreeText").val();&lt;br /&gt;            if (trim(strSearch)=="") return false;&lt;br /&gt;       &lt;br /&gt;            strSearch = strSearch.replace(/\"/gi,"");&lt;br /&gt;            strSearch = strSearch.replace(/\s*and\s*/gi, " ");&lt;br /&gt;            strSearch = strSearch.replace(/\s*or\s*/gi," ");&lt;br /&gt;            var arrSearch = strSearch.split(" ");&lt;br /&gt;&lt;br /&gt;            $("[Highlight=true]").each(function() {&lt;br /&gt;            var elm = $(this);&lt;br /&gt;            var strAbstarct = elm.html();&lt;br /&gt;            for (var i = 0; i &amp;lt; arrSearch.length; i++) {&lt;br /&gt;                var RE = new RegExp(arrSearch[i], "gi");&lt;br /&gt;                strAbstarct = strAbstarct.replace(RE, "&amp;lt;span class='highlight'&amp;gt;" + arrSearch[i] + "&amp;lt;/span&amp;gt;");&lt;br /&gt;            }&lt;br /&gt;            elm.html(strAbstarct) &lt;br /&gt;            });&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;    &amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;จากโค้ดด้านบน ผมใช้ Attribute Selector ในการเลือก element ที้ต้องการทำ Highlight มาจากนั้นมาวนลูปด้วย jQuery.each() ครับ แล้วก็ไปแก้ไข้ html ของ element ให้เพิ่ม span เพิ่อทำ highlight คำที่ต้องการ ลองรันดูได้ผลลัพธ์ตามต้องการครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-2617326224122381353?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/2617326224122381353/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=2617326224122381353' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/2617326224122381353'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/2617326224122381353'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/07/jquery-highlight-gridview.html' title='jQuery ทำ highlight ขัอมูลใน GridView'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-3462225700142656870</id><published>2009-07-12T22:37:00.002+07:00</published><updated>2010-10-29T11:22:39.189+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='LINQ'/><title type='text'>IComparer กับ IComparable ใช้งานอย่างไร</title><content type='html'>วันก่อนมีกระทู้ฮอตที่ greatfriends ครับ &lt;br /&gt;&lt;a href="http://greatfriends.biz?109524"&gt;BLOG - Generic Function - MAX(Of T As IComparable(Of T))&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;สรุปคือมีการสับสนระหว่าง IComparer กับ IComparable ครับ ซึ่งไม่ใช่เรื่องแปลกเพราะว่าในเวบต่างประเทศเอง ก็มีคำถามเรื่องนี้บ่อยๆ&lt;br /&gt;&lt;br /&gt;อาจารย์สุเทพสรุปดังนี้&lt;br /&gt;IComparer ใช้สำหรับ&lt;br /&gt;1. ไม่ต้องการ implement เข้าไปในคลาส เช่น มีรูปแบบการเปรียบเทียบไม่แน่นอน&lt;br /&gt;2. ไม่สามารถ implement เข้าไปในคลาสได้ เช่น ไม่มี source code&lt;br /&gt;&lt;br /&gt;ผมขอเพิ่มตามความเข้าใจนะครับ IComparable เป็น Interface สำหรับ Class ที่เราต้องการให้สามารถเปรียบเทียบได้ โดยมีลักษณะที่แน่นอน นั่นคือ Object ที่สร้างจาก class นี้ จะไปเปรียบเทียบกับ object อื่นที่สร้างจาก Class เดียวกัน&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Public Class Employee&lt;br /&gt;    Implements IComparable(Of Employee)&lt;br /&gt;    ...&lt;br /&gt;End Class&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ส่วน IComparer ชื่อก็ค่อนข้างสื่ออยู่แล้ว คือใช้สำหรับสร้าง Comparer Class หมายความว่าเราต้องการ Class สำหรับ Compare Object สรุปคือเป็น third party ที่ใช้เปรียบเทียบ object ที่1 และ object ที่2 ครับ&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Public Class EmployeeComparer&lt;br /&gt;    Implements IComparer(Of Employee)&lt;br /&gt;&lt;br /&gt;    Public Enum compareField&lt;br /&gt;        age&lt;br /&gt;        hiredDate&lt;br /&gt;        joinedDate&lt;br /&gt;    End Enum&lt;br /&gt;&lt;br /&gt;Private _compareField As compareField = compareField.age&lt;br /&gt;Private _sortDirection As System.ComponentModel.ListSortDirection = System.ComponentModel.ListSortDirection.Ascending&lt;br /&gt;&lt;br /&gt;Public Function Compare(ByVal x As [Class].Center.Employee, ByVal y As [Class].Center.Employee) As Integer Implements System.Collections.Generic.IComparer(Of [Class].Center.Employee).Compare&lt;br /&gt;&lt;br /&gt;     If _sortDirection = ComponentModel.ListSortDirection.Ascending Then&lt;br /&gt;        .....&lt;br /&gt;     Else&lt;br /&gt;        .....&lt;br /&gt;     End If&lt;br /&gt;&lt;br /&gt;End Function&lt;br /&gt;&lt;br /&gt;End Class&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;จากกระทู้มีการคุยกันถึงการใช้ Linq และการใช้ Lamda Expression ในการ Sort หรือเปรียบเทียบ Object ซึ่งจะสะดวกขึ้นมาก ลองเข้าไปอ่านกันดูครับ&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt; Dim employees As New List(Of BAS.Class.Center.Employee)&lt;br /&gt;employees.Add(New BAS.Class.Center.Employee With {.EmployeeID = "100", .NameEng = "tabd"})&lt;br /&gt;employees.Add(New BAS.Class.Center.Employee With {.EmployeeID = "200", .NameEng = "dss"})&lt;br /&gt;employees.Add(New BAS.Class.Center.Employee With {.EmployeeID = "300", .NameEng = "xxx"})&lt;br /&gt;employees.Add(New BAS.Class.Center.Employee With {.EmployeeID = "400", .NameEng = "yyy"})&lt;br /&gt; &lt;br /&gt;employees.Sort(Function(x, y) String.Compare(x.NameEng, y.NameEng)) '// Sort ascending &lt;br /&gt;employees.Sort(Function(x, y) String.Compare(y.NameEng, x.NameEng)) '// Sort descending&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-3462225700142656870?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/3462225700142656870/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=3462225700142656870' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/3462225700142656870'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/3462225700142656870'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/07/icomparer-icomparable.html' title='IComparer กับ IComparable ใช้งานอย่างไร'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-6358060862557258299</id><published>2009-07-09T21:00:00.003+07:00</published><updated>2010-10-29T11:31:49.692+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>การจัดการเอกสาร pdf ด้วย iTextSharp (2)</title><content type='html'>หลังจากที่ผมได้นำโปรแกรมไปให้ user ทดสอบ ก็เลยได้ requirement เพิ่มเติมมาอีกครับ คือ user บางกลุ่มจะไม่สามารถ print pdf ออกไปที่ printer ได้ และห้าม save ด้วย&lt;br /&gt;&lt;br /&gt;หลังจากเข้าไปอ่านเอกสารของ iTextSharp ก็พบว่ามันสามารถทำได้ครับ โดยตัว pdf เองนั้นเราสามารถทำการกำหนด permission ได้ และ iTextSharp มี class ชื่อ PdfWriter ซึ่งเราสามารถทำการ Set Encryption เพื่อกำหนด permission ของ pdf ได้ ดังนั้นจากโค้ดเดิมที่ทำไว้ ผมก็ไปเพิ่มคำสั่ง setEncryption ก่อนสั่ง document.open ครับ&lt;br /&gt;&lt;br /&gt;เนื่องจากโค้ดเดิมผมใช้ PdfCopy object สำหรับสร้างเอกสาร pdf ใหม่ แต่ว่า PdfCopy มันไม่มีคำสั่ง setEncryption ครับ พอลองไป view object browser ดูก็พบว่าตัว PdfCopy นั้นมัน inherit มาจาก PdfWriter อีกที ดังนั้นเราสามารถ Casting ไปเป็น PdfWriter ได้ครับ จากนั้นก็สั่ง SetEncryption เพื่อกำหนด permission ว่าจะให้ print ได้หรือไม่ และก็สั่งซ่อน menu bar และ toolbar เพื่อที่ว่า user จะได้สั่ง save ไม่ได้ครับ&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Dim writer As PdfWriter = TryCast(copy, PdfWriter)&lt;br /&gt;'writer.SetEncryption(PdfWriter.STANDARD_ENCRYPTION_128, Nothing, Nothing, PdfWriter.ALLOW_SCREENREADERS) '// CANNOT PRINT&lt;br /&gt;writer.SetEncryption(PdfWriter.STANDARD_ENCRYPTION_128, Nothing, Nothing, PdfWriter.ALLOW_PRINTING) '// CAN PRINT&lt;br /&gt;writer.ViewerPreferences = PdfWriter.HideMenubar + PdfWriter.HideToolbar + PdfWriter.HideWindowUI&lt;br /&gt;&lt;br /&gt;document.Open()&lt;br /&gt;For i As Integer = 1 To reader.NumberOfPages&lt;br /&gt;     Dim ipage As Integer = i&lt;br /&gt;     If intIgnorePages Is Nothing OrElse intIgnorePages.Find(Function(c) c = ipage) = 0 Then&lt;br /&gt;          copy.AddPage(copy.GetImportedPage(reader, ipage))&lt;br /&gt;     End If&lt;br /&gt;Next&lt;br /&gt;&lt;br /&gt;document.Close()&lt;br /&gt;reader.Close()&lt;br /&gt;copy.Close()&lt;br /&gt;fs.Close()&lt;br /&gt;fs.Dispose()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;เรียบร้อยครับ แหมมันช่างเยี่ยมยอดจริงๆ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-6358060862557258299?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/6358060862557258299/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=6358060862557258299' title='1 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6358060862557258299'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/6358060862557258299'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/07/pdf-itextsharp-2.html' title='การจัดการเอกสาร pdf ด้วย iTextSharp (2)'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-1294142431507431982</id><published>2009-07-08T22:31:00.000+07:00</published><updated>2009-07-20T17:03:04.788+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>เขียนโปรแกรมจัดการเอกสาร pdf ด้วย iTextSharp</title><content type='html'>เมื่อไม่นานไม่นี้ผมได้รับ requirement ให้โปรแกรมที่พัฒนาขึ้นสามารถค้นหาและแสดงเอกสาร pdf โดยมีฟังก์ชันพิเศษ นั่นคือให้สามารถตัดหน้าบางหน้าของเอกสาร pdf ออก สำหรับ user บางกลุ่ม เช่นตัดหน้าสุดท้ายออก ถ้า user เป็นพนักงานของแผนก xxx ทำให้ผมต้องไปพึ่งบริการของ google ซึ่งพบว่ามี free .net-Pdf library ตัวหนึ่งน่าสนใจมากนั่นคือ &lt;a href="http://sourceforge.net/projects/itextsharp/"&gt;iTextSharp&lt;/a&gt; ครับ&lt;br /&gt;&lt;br /&gt;หลังจาก download มาลงที่เครื่องเรียบร้อยก็ลองทดสอบกันเลย เริ่มจาก Add Reference ใน ASP.net โปรเจคก่อน และทำการ Import Namespace ให้เรียบร้อย ขั้นตอนการทำงานก็คือ ผมสร้าง Web Form ขึ้นมาโดยให้รับค่า Location ของ pdf file ที่จะทำการเปิดให้ user ผ่าน QueryString จากนั้นก็ไปตรวจสอบสิทธิ์ของ user ว่าจะให้ซ่อนหน้าไหนบ้าง แล้วจึงใช้ iTextSharp.PdfCopy ทำการสร้างเอกสาร pdf ขึ้นมาใหม่ และ copy หน้าที่ user มีสิทธิเห็นไปใส่ในเอกสารใหม่ลองดูโค้ดกันครับ&lt;br /&gt;&lt;br /&gt;&lt;pre name=code class=vb&gt;&lt;br /&gt;Public intIgnorePages As List(Of Integer)&lt;br /&gt;&lt;br /&gt;Protected Sub frmMain_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles frmMain.Load&lt;br /&gt;&lt;br /&gt;     Dim strLocation As String = Server.UrlDecode(Request("Location"))&lt;br /&gt;     Dim fileName As String = "OUTPUT/" &amp; _userCredential.EmployeeID &amp; "/" &amp; fileInfo.Name&lt;br /&gt;     Dim reader As New PdfReader(strLocation)&lt;br /&gt;     Dim document As New iTextSharp.text.Document(reader.GetPageSizeWithRotation(1))&lt;br /&gt;     Dim fs As New FileStream(Server.MapPath(fileName), FileMode.Create)&lt;br /&gt;     Dim page As PdfImportedPage = Nothing&lt;br /&gt;     Dim copy As New PdfCopy(document, fs)&lt;br /&gt;&lt;br /&gt;     If File.Exists(fileName) Then File.Delete(fileName)&lt;br /&gt;     intIgnorePages = ValidateUserPermission&lt;br /&gt;     document.Open()&lt;br /&gt;     For i As Integer = 1 To reader.NumberOfPages&lt;br /&gt;          Dim ipage As Integer = i&lt;br /&gt;          If intIgnorePages Is Nothing OrElse intIgnorePages.Find(Function(c) c = ipage) = 0 Then&lt;br /&gt;               copy.AddPage(copy.GetImportedPage(reader, ipage))&lt;br /&gt;          End If&lt;br /&gt;     Next&lt;br /&gt;     document.Close()&lt;br /&gt;     reader.Close()&lt;br /&gt;     copy.Close()&lt;br /&gt;     fs.Close()&lt;br /&gt;     fs.Dispose()&lt;br /&gt;     Response.Redirect(fileName)&lt;br /&gt;&lt;br /&gt;End Sub&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;สำหรับโค้ดด้านบนเป็นโค้ดที่ผมใช้ทดสอบโปรแกรมยังไม่ใช่โค้ดที่ใช้งานจริง แต่สำหรับผลลัพธ์เป็นที่น่าพอใจมาก สุดยอดจริงๆครับ &lt;br /&gt;&lt;br /&gt;Reference: &lt;br /&gt;&lt;a href="http://sourceforge.net/projects/itextsharp/"&gt;iTextSharpby blowagie, geraldhenson, psoares33 &lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-1294142431507431982?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/1294142431507431982/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=1294142431507431982' title='3 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/1294142431507431982'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/1294142431507431982'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/07/pdf-itextsharp.html' title='เขียนโปรแกรมจัดการเอกสาร pdf ด้วย iTextSharp'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-2986470019604339860</id><published>2009-07-07T22:42:00.002+07:00</published><updated>2010-10-29T11:01:10.331+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>jQuery  อ้างถึง Control ใน gridview</title><content type='html'>&lt;div&gt;วันก่อนเข้าไป &lt;a href="http://www.blogger.com/www.greatfriends.biz"&gt;http://www.blogger.com/www.greatfriends.biz&lt;/a&gt; พบคำถามนี้ในบอร์ดพอดีครับ เป็นคำถามที่น่าสนใจทีเดียว ผมตอบไว้แบบนี้ครับ&lt;br /&gt;ผมสมมติว่า เมื่อผมคลิ๊กที่ column ของ gridview ผมจะให้โปรแกรม alert ค่าทั้งหมดของแถวที่คลิ๊ก และแสดงค่าของ column ที่คลิ๊ก&lt;br /&gt;เนื่องจากผมใช้ MSAjax และเอา Gridview ไปใส่ใน UpdatePanel ดังนั้น แทนที่ผมจะใช้ $(document).ready(function(){}); ผมจะไป register click event ที่ gvDBItem_DataBound แทนครับ&lt;br /&gt;&lt;br /&gt;และก็ใน click(function(){}); แทนที่ผมจะเขียนโค้ดไปโดยตรง ผมเปลี่ยนให้ไปเรียก function ใช้งานแทน เพื่อที่ว่าตอน debug จะได้ง่ายครับ&lt;br /&gt;&lt;br /&gt;โค้ดส่วน Code Behind&lt;br /&gt;&lt;pre class="brush:vb"&gt;&lt;br /&gt;Protected Sub gvDBItem_DataBound(ByVal sender As Object, ByVal e As System.EventArgs) Handles gvDBItem.DataBound&lt;br /&gt;&lt;br /&gt;Dim sbScript As New Text.StringBuilder&lt;br /&gt;&lt;br /&gt;    sbScript.Append("$('#" &amp;amp; gvDBItem.ClientID &amp;amp; " tr').click(function(e){gv_RowClick(this,e);});")&lt;br /&gt;&lt;br /&gt;    ScriptManager.RegisterStartupScript(Page, Page.GetType, "GVDataBound", sbScript.ToString, True)&lt;br /&gt;&lt;br /&gt;End Sub&lt;br /&gt;&lt;br /&gt; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;โค้ดส่วน javascript&lt;br /&gt;&lt;pre class="brush:js"&gt;&lt;br /&gt;&amp;lt;script type="text/javascript" &amp;gt;&lt;br /&gt;&lt;br /&gt;    function gv_RowClick(sender, e) {&lt;br /&gt;&lt;br /&gt;        alert(sender.innerText);&lt;br /&gt;&lt;br /&gt;        alert(e.srcElement.innerHTML);&lt;br /&gt;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;br /&gt; &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ในตัวอย่างผมใช้ gridview id กับ tr เป็น selector แต่ว่าถ้าเราต้องการรู้ ID ของแถวที่กด หรือต้องการเอาค่าใน column ที่ต้องการเท่านั้น ก็สามารถเปลี่ยน selector ได้ครับ&lt;br /&gt;&lt;br /&gt;อีกวิธีที่เพิ่งนึกออก คือเราสามารถใช้ attribute เป็น selector ได้ ดังนั้นเราก็เพิ่ม custom attribute ให้ column ที่เราต้องการเช่น&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:html"&gt;&lt;br /&gt;&amp;lt;asp:TemplateField HeaderText="Item No"&amp;gt;&lt;br /&gt;&lt;br /&gt;     &amp;lt;ItemTemplate&amp;gt;&lt;br /&gt;&lt;br /&gt;          &amp;lt;asp:Label id="lblItemNo" runat="server" RowIndex="true" Text='&amp;lt;%# Eval("ORDERNO") %&amp;gt;' &amp;gt;&amp;lt;/asp:Label&amp;gt;&lt;br /&gt;&lt;br /&gt;     &amp;lt;/ItemTemplate&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/asp:TemplateField&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;แล้วใน jQuery เราก็กำหนด selector&lt;br /&gt;&lt;pre class="brush:js"&gt;&lt;br /&gt;&amp;lt;script type="text/javascript" &amp;gt;&lt;br /&gt;&lt;br /&gt;     $(document).ready(function() {&lt;br /&gt;&lt;br /&gt;     $("#gvDBItem [RowIndex='true']").click(function(e) { gv_RowIndexClick(this, e); });&lt;br /&gt;&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;     function gv_RowClick(sender, e) {&lt;br /&gt;&lt;br /&gt;          alert(sender.innerText);&lt;br /&gt;&lt;br /&gt;          alert(e.srcElement.innerHTML);&lt;br /&gt;&lt;br /&gt;     }&lt;br /&gt;&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;อันนี้เป็นตัวอย่างแบบไม่ได้ใช้ UpdatePanel ครับ ผมเลยใช้ $(document).ready()&lt;br /&gt;&lt;br /&gt;ลองอ่านกระทู้เต็มๆได้ที่นี่ครับ&lt;br /&gt;jQuery อ้างถึง Control ใน GridView อย่างไรครับ&lt;br /&gt;&lt;a href="http://greatfriends.biz/?109412"&gt;http://greatfriends.biz/?109412&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-2986470019604339860?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/2986470019604339860/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=2986470019604339860' title='1 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/2986470019604339860'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/2986470019604339860'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/07/jquery-control-gridview.html' title='jQuery  อ้างถึง Control ใน gridview'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-5118080863000763623</id><published>2009-07-06T22:57:00.004+07:00</published><updated>2010-10-29T11:01:34.016+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>jQuery กับการเรียก ASP.net Web Service</title><content type='html'>ต่อเนื่องจากบทความที่แล้ว เราได้ใช้ ScriptManager ในการ add Service Reference เพื่อที่จะให้ VS ทำการสร้าง Proxy สำหรับเรียกใช้ Web Service ซึ่งก็สะดวกดีครับ แต่ทีนี้ตัว jquery เองมีความสามารถของ ajax มาด้วย เราจะมาลองเขียน jQuery สำหรับเรียก Web Service โดยตรง ไม่ต้องผ่าน ScriptManager ดูครับ&lt;br /&gt;&lt;br /&gt;วิธีการก็ไม่ยาก แค่เราเรียกใช้ $.ajax(option) แค่นี้เอง ส่วน option นั้นเรากำหนดค่าในรูปแบบของ json ซึ่งมี member อะไรที่กำหนดได้บ้างนั้น ไปดูได้จาก document ของ jQuery ครับ ดังนั้นเราลอง comment โค้ดเดิมที่เรียก proxy ของ Web Service แล้วมาเขียนคำสั่งของ jQuery ดู &lt;br /&gt;&lt;pre class="brush:js"&gt;&lt;br /&gt;        function txtEmpName_OnKeyUp(e) {&lt;br /&gt;            if (e.keyCode != 13 || e.keyCode != 9) {&lt;br /&gt;                $("#txtEmpId").val("");&lt;br /&gt;                var empName = $("#txtEmpName").val();&lt;br /&gt;                if (empName.length &gt; 2) {&lt;br /&gt;                    //BAS.Web.Services.wsFiling.GetStaffsByKeyword(empName, GetStaffSuccess);&lt;br /&gt;                    $.ajax({&lt;br /&gt;                        type: "POST",&lt;br /&gt;                        url: "../wsFiling.asmx/GetStaffsByKeyword",&lt;br /&gt;                        data: "{keyWord:'" + empName + "'}",&lt;br /&gt;                        contentType: "application/json; charset=utf-8",&lt;br /&gt;                        dataType: "json",&lt;br /&gt;                        success: function(data) { GetStaffSuccess(data.d, "", "") }&lt;br /&gt;                    });&lt;br /&gt;                } else {&lt;br /&gt;                    $("#divStaff").hide();&lt;br /&gt;                }&lt;br /&gt;            }       &lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;ก็ไม่ยากอย่างที่คิดครับ ตรง success เราก็ไปเรียก GetStaffSuccess function ตัวเดิมนั่นแหละ ส่วน error ผมไม่ได้ใส่ไว้เหมือนเดิม และในการใช้งานจริงก็ควรสร้าง OnError function ไว้ด้วยครับ&lt;br /&gt;&lt;br /&gt;แต่โดยส่วนตัวขอใช้ผ่าน ScriptManager เหมือนเดิมครับ&lt;br /&gt;&lt;br /&gt;Reference: &lt;br /&gt;&lt;a href="http://prettycode.org/2009/04/07/using-jquery-with-aspnet-web-services-and-json/"&gt;Using jQuery with ASP.NET Web Services and JSON, www.prettycode.org&lt;/a&gt;&lt;br /&gt;&lt;a href="http://encosia.com/2008/03/27/using-jquery-to-consume-aspnet-json-web-services/"&gt;Using jQuery to Consume ASP.NET JSON Web Services, www.encosia.com&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-5118080863000763623?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/5118080863000763623/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=5118080863000763623' title='2 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5118080863000763623'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5118080863000763623'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/07/jquery-aspnet-web-service.html' title='jQuery กับการเรียก ASP.net Web Service'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-5493798238190896674</id><published>2009-07-03T15:55:00.010+07:00</published><updated>2010-10-29T11:02:08.838+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>jQuery กับการทำ Multi Column Autocomplete</title><content type='html'>พอดีได้รับ requirement ให้ทำ auto complete function ครับ สมมติว่า user คีย์ชื่อพนักงาน ให้โปรแกรมไปดึงรายชื่อพนักงานทั้งหมดที่ใกล้เคียงมาแสดง พอ user เลือกพนักงานที่ต้องการจากใน list ก็ให้ไปแสดงข้อมูลอื่นๆของพนักงานใน field อื่นๆด้วย เช่นรหัสพนักงาน ชื่อ นามสกุล แผนก วันเริ่มงาน เป็นต้นครับ&lt;br /&gt;ทีนี้ลอง search ดูพวก autocomplete จาก google พบว่าส่วนใหญ่มันเป็นแบบ single field คือใช้กับ textbox ตัวเดียวครับ เลยต้องเขียน javascript เพื่อไปดึงข้อมูลจาก Web Service มาให้ user เลือก แล้วพอเลือกแล้วก็เขียน javascript ให้ไปตัดข้อมูลลงใน element ต่างๆ&lt;br /&gt;รู้สึกว่าการเขียน javascript เองนี่มันยาวพอสมควรครับ พอมีเวลาว่างก็เลยมาลองใช้ jQuery ดูว่ามันจะง่ายและดีสมคำร่ำลือหรือเปล่า ผมเลยสร้าง web page สำหรับทดสอบดู เอาเป็นว่ามีแค่ 2 field คือ ID กับ ชื่อนามสกุลก็พอครับ &lt;br /&gt;ก่อนอื่นมาดูโค้ดหน้าเวบกันก่อน &lt;br /&gt;&lt;pre class="brush:html"&gt;&lt;br /&gt;&amp;lt;body&amp;gt;&lt;br /&gt;    &amp;lt;form id="form1" runat="server"&amp;gt;&lt;br /&gt;    &amp;lt;asp:ScriptManager ID="ScriptManager1" runat="server"&amp;gt;&lt;br /&gt;        &amp;lt;Services&amp;gt;&lt;br /&gt;            &amp;lt;asp:ServiceReference Path="~/Employee.asmx" /&amp;gt;&lt;br /&gt;        &amp;lt;/Services&amp;gt;&lt;br /&gt;    &amp;lt;/asp:ScriptManager&amp;gt;&lt;br /&gt;    &amp;lt;div&amp;gt;   &lt;br /&gt;        &amp;lt;asp:TextBox ID="txtEmpId" runat="server" style="width:100px" MaxLength="6"&amp;gt;&amp;lt;/asp:TextBox&amp;gt;&lt;br /&gt;        &amp;lt;asp:TextBox ID="txtEmpName" runat="server" style="width:250px"&amp;gt;&amp;lt;/asp:TextBox&amp;gt;&lt;br /&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;/form&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;จะเห็นว่ามี textbox แค่สองอัน และมีการใช้ ScriptManager เพื่อไปเรียก WebService ครับ&lt;br /&gt;โค้ดส่วน WebService ขอไม่เขียนนะครับ เพราะไม่ยาก โดยตัว WebService จะคืน Employee Object มาให้ในรูปของ JSON (ตาม default ครับ)&lt;br /&gt;ทีนี้มาดูโค้ดส่วน javascript กันบ้าง&lt;br /&gt;&lt;pre class="brush:js"&gt;&lt;br /&gt;    &amp;lt;script type="text/javascript" src="../Scripts/jquery-1.3.2-vsdoc.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;    &amp;lt;script type = "text/javascript"&amp;gt;&lt;br /&gt;        $(function() {&lt;br /&gt;            $("#txtEmpName").keyup(function(event) {txtEmpName_OnKeyUp(event)});&lt;br /&gt;        });&lt;br /&gt;&lt;br /&gt;        function txtEmpName_OnKeyUp(e) {&lt;br /&gt;            if (e.keyCode != 13 || e.keyCode != 9) {&lt;br /&gt;                $("#txtEmpId").val("");&lt;br /&gt;                var empName = $("#txtEmpName").val();&lt;br /&gt;                if (empName.length &amp;gt; 2) {&lt;br /&gt;                    jnithi.Employee.GetStaffsByKeyword(empName, GetStaffSuccess);&lt;br /&gt;                } else {&lt;br /&gt;                $("#divStaff").hide();&lt;br /&gt;                  &lt;br /&gt;                }&lt;br /&gt;            }       &lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        function GetStaffSuccess(result, methodName, context) {&lt;br /&gt;            if ($("#divStaff").length == 0) {&lt;br /&gt;                var txtEmpName = $("#txtEmpName");&lt;br /&gt;                var pos = txtEmpName.position();&lt;br /&gt;                var divStyle = {'position':'absolute','top': pos.top + txtEmpName.outerHeight() + 'px','left': pos.left + 'px','width':  txtEmpName.outerWidth() +'px','border':'1px solid #666666' };&lt;br /&gt;                $(document.createElement("div")).attr("id", "divStaff").css(divStyle).appendTo("body");                 &lt;br /&gt;            }&lt;br /&gt;            var divStaff=$("#divStaff");&lt;br /&gt;            divStaff.empty();&lt;br /&gt;            &lt;br /&gt;            if (typeof (result) != "undefinded" &amp;&amp; result.length &amp;gt; 0) {&lt;br /&gt;                $(document.createElement("dl")).width(divStaff.width()).appendTo("#divStaff");&lt;br /&gt;                var i;&lt;br /&gt;                for (i = 0; i &amp;lt; result.length; i++) {&lt;br /&gt;                    var employee = result[i];               &lt;br /&gt;                    var html = "&amp;lt;dt&amp;gt;&amp;lt;a class='search' href='#' onclick='dtStaff_OnClick(this);' &amp;gt;" + employee.NewEmployeeId + ":" + employee.NameEng + "&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;";&lt;br /&gt;                    $("dl").append(html);&lt;br /&gt;                }&lt;br /&gt;                if (i &amp;gt; 0) {&lt;br /&gt;                    divStaff.show();&lt;br /&gt;                }&lt;br /&gt;            } else {&lt;br /&gt;                divStaff.hide();&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        function dtStaff_OnClick(obj) {&lt;br /&gt;            result = obj.innerHTML.split(":");&lt;br /&gt;            $("#txtEmpId").val(result[0]);&lt;br /&gt;            $("#txtEmpName").val(result[1]);&lt;br /&gt;            $("#divStaff").hide();&lt;br /&gt;        } &lt;br /&gt;    &amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;เริ่มต้นก็ไปกำหนด Event Handler ให้กับ keyup event ของ txtEmpName ครับ &lt;br /&gt;$(function() {&lt;br /&gt;            $("#txtEmpName").keyup(function(event) {txtEmpName_OnKeyUp(event)});&lt;br /&gt;        });&lt;br /&gt;&lt;br /&gt;โดยปกติตัวอย่างของ jQuery จะเขียน function ไปเลย แต่ปรากฎว่า Visual Studio มัน debug ลำบาก ผมเลยแยกมาเขียน function ต่างหากครับ โดยมี window.event เป็น parameter หรือเราใช้ MSAjax อยู่แล้วอาจจะพิจารณาใช้ $addHandler() ของ MSAjax แทนก็ได้ครับ เมื่อเกิด keyup event มันก็จะไปเรียก txtEmpName_OnKeyUp ให้ทำงาน โดยจะไปเรียก WebService และกำหนด succeed callback function ไปที่ GetStaffSuccess ครับ (จริงๆควรจะกำหนด onFailed callback function ด้วยนะครับ)&lt;br /&gt;&lt;br /&gt;ใน GetStaffSuccess function ผมให้มันทำการสร้าง div ที่บรรจุ dl element ไว้ครับ เพื่อเป็น list ให้ user เลือก สังเกตุว่าใน function นี้ผมทดลองทำการ creat element ไว้ 2 แบบ แบบแรกคือใช้ $(document.createElement(element)).appendTo(element) กับอีกแบบคือ $(element).append(html) แล้วก็ต้องกำหนด onclick event ด้วย เพื่อที่ว่าเมื่อ user เลือกรายชือพนักงานแล้ว จะได้ไปทำ dtStaff_OnClick ซึ่งจะทำการตัดข้อมูลไปใส่ใน textbox ทั้ง 2 ตัวครับ&lt;br /&gt;&lt;br /&gt;ลองเอาโค้ดที่เขียน javascript เอง กับที่เขียนด้วย jQuery เห็นได้ว่าโค้ดสั้นลงประมาณ 30% เพราะใช้ประโยชน์ของ Chainability นั่นเองครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-5493798238190896674?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/5493798238190896674/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=5493798238190896674' title='1 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5493798238190896674'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/5493798238190896674'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/07/jquery-autocomplete-multi-fields.html' title='jQuery กับการทำ Multi Column Autocomplete'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-2745244560267731658</id><published>2009-07-03T12:16:00.006+07:00</published><updated>2009-07-20T17:03:04.789+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='jQuery'/><title type='text'>jQuery กับ Visual Studio 2008</title><content type='html'>ผมเคยลองเข้าไปด้อมๆมอง &lt;a href="http://www.jquery.com/"&gt;www.jquery.com&lt;/a&gt; มาตั้งนาน เพราะได้ยินกิตติศัพท์ว่าเป็นสุดยอด javascript framework ตัวหนึ่ง แต่เนื่องจากไม่ค่อยมีเวลาและขี้เกียจเลยไม่ได้สนใจมากนักครับ ลองอ่าน document ดูก็รู้สึกไม่ค่อยเข้าหัว แต่พอได้ข่าวจาก blognone ว่าตอนนี้ MS สนับสนุน jQuery แล้วเลยรู้สึกสนใจมากขึ้นครับ&lt;br /&gt;&lt;br /&gt;พอดีเมื่อ 3 วันก่อน มี requirement จาก user ให้เพิ่ม function ใหม่ ก็เลยเขียน javascript เพื่อไปติดต่อกับ Webservice รู้สึกว่าโค้ดยาวพอสมควร เลยนึกถึง jquery ขึ้นมาครับ ในที่สุดก็มีโอกาสได้ศึกษาอย่างจริงๆจังๆซะที เริ่มต้นก็เลยไป download jQuery เวอร์ชันล่าสุด (1.3.2) มาก่อนครับ และก็ไม่ลืมที่จะ download ไฟล์ jquery-1.3.2-vsdoc2.js สำหรับให้ Visual Studio เพิ่ม IntelliSense ของ jQuery เข้าไป จะได้สะดวกเวลาพัฒนาโปรแกรม&lt;br /&gt;&lt;br /&gt;ตอนแรกๆที่ลองเขียน jQuery ดูก็จะมี IntelliSense ขึ้นมาตามปกติครับ แต่พอใช้ไปประมาณ 1 ชั่วโมงตัว IntelliSense ของ jQuery ก็หายไป ผมนึกว่าเป็น bug ของ VS2008 (อีกแล้ว) ก็เลยลองปิดโปรแกรมแล้วเปิดใหม่ก็ไม่หาย ลอง restart Windows ก็ไม่หายครับ เลยไปลอง search ใน google ดู ก็พบว่า Scout Gu บอกให้ลง hot fix ตัวนี้ครับ “&lt;font color="#660000"&gt;VS90SP1-KB958502-x86.exe&lt;/font&gt;” (อ่านจากชื่อ ผมเข้าใจว่าเป็น Service Pack1 ของ VS2008 นะ) ลงแล้วก็ยังไม่หายครับ สุดท้ายไปเจอใน blog หนึงบอกว่าให้ลบเลข 2 ออกจากไฟล์ vsdoc แล้วลองใหม่ ก็เลยลองทำตามครับ แก้ไฟล์เป็น &lt;strong&gt;&lt;font color="#660000"&gt;jquery-1.3.2-vsdoc.js&lt;/font&gt;&lt;/strong&gt; ทีนี้ก็ใช้งานได้ตามปกติ IntelliSense มาแล้วครับ ลองเล่นมา 3 วันก็ยังใช้ได้ ถ้าใครเจอปัญหาเดียวกันก็ลองวิธีนี้ดูครับ&lt;br /&gt;&lt;br /&gt;Tips: ถ้าต้องการบังคับให้ Visual Studio 2008 ทำการ update JScript IntelliSense สามารถทำได้โดยกด Ctrl+Shift+j ครับ&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-2745244560267731658?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/2745244560267731658/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=2745244560267731658' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/2745244560267731658'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/2745244560267731658'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/07/jquery-visual-studio-2008.html' title='jQuery กับ Visual Studio 2008'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-9197413368106377199</id><published>2009-07-02T22:31:00.000+07:00</published><updated>2009-07-02T23:52:34.715+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tips'/><title type='text'>ทดสอบ Syntax Highlighter</title><content type='html'>&lt;span style="font-family:verdana;"&gt;ผมได้ลองเขียน blog เกี่ยวกับการเขียนโปรแกรมหลายๆที่ พบว่าปัญหาสำคัญอย่างหนึ่งคือการเขียน source code บน blog ซึ่งมันจัดยากมากครับ เพราะต้องเขียน html tag แล้วถ้าอยากให้มีสีสรรก็ต้องกำหนด style ให้มันอีกค่อนข้างเสียเวลาทีเดียวครับ หรือบางทีขี้เกียจก็ copy เป็นรูปมาแต่ว่ามันทำให้ copy source code ไม่ได้นี่สิ ทำให้รู้สึกไม่ค่อย work เลยหยุดเขียน blog เกี่ยวกับการเขียนโปรแกรมไปดื้อๆ &lt;br /&gt;&lt;br /&gt;คราวนี้ได้มา search เจอ Syntax Highlighter ซึ่งพัฒนาด้วย javascript ที่ code.google.com น่าสนใจมากครับ ก็เลยเอามาลองที่ blogger ซะเลย แต่ว่าลองใช้เวอร์ชัน 2.x.x แล้วยังไม่ได้ เลยมาลองใช้เวอร์ชันยอดฮิตคือ 1.5.1 ครับ&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;p style="font-family:verdana;"&gt;&lt;br /&gt;ก่อนอื่นก็ไปตั้งค่ารูปแบบ (template) ซะก่อน โดยไปที่ รูปแบบ --&gt; แก้ไข HTML และไปเพิ่ม tag ก่อน &amp;lt;/head&amp;gt;ดังนี้ครับ&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;pre class="html" name="code"&gt;&lt;br /&gt;&amp;lt;b:skin&amp;gt;&lt;br /&gt;&amp;lt;link href='http://alexgorbatchev.com/pub/sh/1.5.1/styles/SyntaxHighlighter.css' rel='stylesheet' type='text/css'/&amp;gt;&lt;br /&gt;&amp;lt;script class='javascript' src='http://alexgorbatchev.com/pub/sh/1.5.1/scripts/shCore.js' type='text/javascript'/&amp;gt;&lt;br /&gt;&amp;lt;script class='javascript' src='http://alexgorbatchev.com/pub/sh/1.5.1/scripts/shBrushCSharp.js' type='text/javascript'/&amp;gt;&lt;br /&gt;&amp;lt;script class='javascript' src='http://alexgorbatchev.com/pub/sh/1.5.1/scripts/shBrushJScript.js' type='text/javascript'/&amp;gt;&lt;br /&gt;&amp;lt;script class='javascript' src='http://alexgorbatchev.com/pub/sh/1.5.1/scripts/shBrushVb.js' type='text/javascript'/&amp;gt;&lt;br /&gt;&amp;lt;script class='javascript' src='http://alexgorbatchev.com/pub/sh/1.5.1/scripts/shBrushSql.js' type='text/javascript'/&amp;gt;&lt;br /&gt;&amp;lt;script class='javascript' src='http://alexgorbatchev.com/pub/sh/1.5.1/scripts/shBrushCss.js' type='text/javascript'/&amp;gt;&lt;br /&gt;&amp;lt;script class='javascript' src='http://alexgorbatchev.com/pub/sh/1.5.1/scripts/shBrushXml.js' type='text/javascript'/&amp;gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&lt;br /&gt;&amp;lt;body&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;และเพิ่ม script tag ก่อน &amp;lt;/body&amp;gt; ครับ&lt;/p&gt;&lt;br /&gt;&lt;pre class="html" name="code"&gt;&lt;br /&gt;&amp;lt;script class='javascript'&amp;gt;&lt;br /&gt;//&amp;lt;![CDATA[&lt;br /&gt;dp.SyntaxHighlighter.ClipboardSwf = 'http://alexgorbatchev.com/pub/sh/1.5.1/scripts/clipboard.swf';&lt;br /&gt;dp.SyntaxHighlighter.BloggerMode();&lt;br /&gt;dp.SyntaxHighlighter.HighlightAll('code');&lt;br /&gt;//]]&amp;gt;&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p style="font-family:verdana;"&gt;&lt;br /&gt;สังเกตุว่ามีการเรียกใช้ dp.SyntaxHighlighter.BloggerMode(); ซึ่งเป็น function สำหรับตัด &amp;lt;BR /&amp;gt; ของ Blogger โดยเฉพาะครับ ลอง comment บรรทัดนี้เพื่อดูความแตกต่างก็ได้ครับ&lt;br /&gt;&lt;br /&gt;วิธีการใช้งานก็คือ ตอนเราเขียน blog ให้ไปที่ แก้ไข Html แล้วไปสร้าง pre tag คลุมโค้ดที่ต้องการครับ&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;pre class="html" name="code"&gt;&lt;br /&gt;&amp;lt;pre class="html" name="code"&amp;gt;&lt;br /&gt;     เขียนโค้ดช่วงนี้ครับ&lt;br /&gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p style="font-family:verdana;"&gt;&lt;br /&gt;โดยใน pre tag ให้กำหนด name attribute = "code" ครับ ส่วน class นั้น กำหนดตามประเภทโค้ดที่เราต้องการแสดง ถ้าอยากรู้ว่าต้องใส่ class เป็นอะไร ไปดูได้ที่ &lt;a href="http://code.google.com/p/syntaxhighlighter/wiki/Languages"&gt;SyntaxHighlighter Wiki&lt;/a&gt; ครับ แต่ว่าเราต้องไปกำหนด &amp;lt;script src=...&amp;gt; ให้ครบนะครับ จากตัวอย่างด้านบนผมใช้แค่บางตัวเท่านั้นเอง &lt;br /&gt; &lt;br /&gt;คราวนี้ลองทดสอบ javascript มั่ง&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;pre class="javascript" name="code"&gt;&lt;br /&gt;&amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;     alert('Hello world');&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;แหม มันเยี่ยมจริงๆครับ&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-9197413368106377199?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/9197413368106377199/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=9197413368106377199' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/9197413368106377199'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/9197413368106377199'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2009/07/blog-post.html' title='ทดสอบ Syntax Highlighter'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-9014370714711979600</id><published>2007-10-25T13:06:00.000+07:00</published><updated>2009-07-20T17:05:54.760+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VB.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer'/><title type='text'>โค้ด Restore Database สำหรับ SQL Server</title><content type='html'>&lt;p&gt;จากบทความที่แล้วเราสามารถทำการ backup database ออกมาเป็น file ทีนี้ถ้าเราต้องการทำ restore ละ ก็ใช้ T-SQL เหมือนเดิม ลองดู syntax กันก่อนครับ&lt;/p&gt;&lt;p&gt;&lt;span style="color:#0000ff;"&gt;RESTORE DATABASE { &lt;i&gt;database_name&lt;/i&gt; @&lt;i&gt;database_name_var &lt;/i&gt;}&lt;br /&gt;[ FROM &lt;&gt; [ ,...&lt;i&gt;n &lt;/i&gt;] ]&lt;br /&gt;[ WITH&lt;br /&gt;[ RESTRICTED_USER ]&lt;br /&gt;[ [ , ] FILE = { &lt;i&gt;file_number&lt;/i&gt; @&lt;i&gt;file_number &lt;/i&gt;} ]&lt;br /&gt;[ [ , ] PASSWORD = { &lt;i&gt;password &lt;/i&gt;@&lt;i&gt;password_variable &lt;/i&gt;} ]&lt;br /&gt;[ [ , ] MEDIANAME = { &lt;i&gt;media_name &lt;/i&gt;@&lt;i&gt;media_name_variable &lt;/i&gt;} ]&lt;br /&gt;[ [ , ] MEDIAPASSWORD = { &lt;i&gt;mediapassword &lt;/i&gt;@&lt;i&gt;mediapassword_variable &lt;/i&gt;} ]&lt;br /&gt;[ [ , ] MOVE '&lt;i&gt;logical_file_name&lt;/i&gt;' TO '&lt;i&gt;operating_system_file_name&lt;/i&gt;' ]&lt;br /&gt;[ ,...&lt;i&gt;n &lt;/i&gt;]&lt;br /&gt;[ [ , ] KEEP_REPLICATION ]&lt;br /&gt;[ [ , ] { NORECOVERY RECOVERY STANDBY = &lt;i&gt;undo_file_name &lt;/i&gt;} ]&lt;br /&gt;[ [ , ] { NOREWIND REWIND } ]&lt;br /&gt;[ [ , ] { NOUNLOAD UNLOAD } ]&lt;br /&gt;[ [ , ] REPLACE ]&lt;br /&gt;[ [ , ] RESTART ]&lt;br /&gt;[ [ , ] STATS [ = &lt;i&gt;percentage &lt;/i&gt;] ]&lt;br /&gt;]&lt;/span&gt;&lt;/p&gt;&lt;p&gt;จะเห็นว่ามันมี option เยอะแยะเลย รายละเอียดของ option ไปดูใน Online book นะครับ&lt;/p&gt;&lt;p&gt;ในตัวอย่างผมจะทำการ Full Recovery &lt;span style="color:#ffffff;"&gt;ไป&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="color:#ffffff;"&gt;&lt;span style="color:#ff0000;"&gt;สำหรับการ Restore มันมีจุดสำคัญคือ ต้องไม่มี user ใช้งาน datbase ครับ&lt;br /&gt;ดังนั้นก่อนจะทำการ restore ให้บอก user ที่ใช้งานให้ออกไปก่อน (เราสามารถใช้ store procedure ดูรายชื่อคนที่ใช้งาน database อยู่ครับ แล้วจะให้ดีในโค้ดเราควรจะเตะ user ที่ใช้งานอยู่ออกไปด้วย&lt;/span&gt;&lt;/span&gt;&lt;span style="color:#ff0000;"&gt;เพื่อความปลอดภัย&lt;/span&gt;&lt;/p&gt;&lt;p&gt;ก่อนอื่นผมจะไปสร้าง table ใหม่ใน Northwind ก่อน สมมติชื่อ table1 จากนั้นก็ backup เป็นไฟล์ชื่อ mybackup.bak&lt;/p&gt;&lt;img id="BLOGGER_PHOTO_ID_5125136223431891346" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand; TEXT-ALIGN: center" alt="" src="http://1.bp.blogspot.com/_N-Phs4y2L44/RyAkPbclGZI/AAAAAAAAAAw/4xUHtvm7t1E/s400/sql3.jpg" border="0" /&gt; เมื่อ backup เสร็จแล้วก็ทำการ drop table1 ทิ้งไปครับ เดี๋ยวเราจะลอง restore database ถ้าผ่าน table1 ก็จะกลับมาหาเราอีกครั้ง&lt;img id="BLOGGER_PHOTO_ID_5125135708035815810" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand; TEXT-ALIGN: center" alt="" src="http://1.bp.blogspot.com/_N-Phs4y2L44/RyAjxbclGYI/AAAAAAAAAAo/4Dm3pRnvmxs/s400/sql.jpeg" border="0" /&gt; เอาละ หายไปเรียบร้อยแล้วครับ&lt;br /&gt;&lt;p&gt;คราวนี้มาดูโค้ดกันบ้าง จากบทความที่แล้วเราได้สร้างปุ่มเผื่อไว้แล้วชื่อ btnRestore&lt;/p&gt;&lt;span style="font-size:85%;"&gt;&lt;p&gt;&lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;Private&lt;/span&gt;&lt;span style="font-size:85%;"&gt; &lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;Sub&lt;/span&gt;&lt;span style="font-size:85%;"&gt; btnRestore_Click(&lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;ByVal&lt;/span&gt;&lt;span style="font-size:85%;"&gt; sender &lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;As&lt;/span&gt;&lt;span style="font-size:85%;"&gt; System.Object, &lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;ByVal&lt;/span&gt;&lt;span style="font-size:85%;"&gt; e &lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;As&lt;/span&gt;&lt;span style="font-size:85%;"&gt; System.EventArgs) &lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;Handles&lt;/span&gt;&lt;span style="font-size:85%;"&gt; btnRestore.Click&lt;/p&gt;&lt;p&gt;&lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;Dim&lt;/span&gt;&lt;span style="font-size:85%;"&gt; strSQL &lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;As&lt;/span&gt;&lt;span style="font-size:85%;"&gt; &lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;String&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;Dim&lt;/span&gt;&lt;span style="font-size:85%;"&gt; strCon &lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;As&lt;/span&gt;&lt;span style="font-size:85%;"&gt; &lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;String&lt;/p&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;p&gt;strCon = &lt;/span&gt;&lt;span style="font-size:85%;color:#800000;"&gt;"Data Source=NITHI;Initial Catalog=master;Integrated Security=True"&lt;/p&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;p&gt;&lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;Dim&lt;/span&gt;&lt;span style="font-size:85%;"&gt; cmdRestore &lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;As&lt;/span&gt;&lt;span style="font-size:85%;"&gt; SqlClient.SqlCommand = &lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;New&lt;/span&gt;&lt;span style="font-size:85%;"&gt; SqlClient.SqlCommand&lt;br /&gt;&lt;/p&gt;&lt;p&gt;sqlConnection1.ConnectionString = strCon&lt;br /&gt;SqlConnection1.Open()&lt;br /&gt;cmdRestore.Connection = SqlConnection1&lt;br /&gt;Cursor = Cursors.WaitCursor&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;Try&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;strSQL = &lt;/span&gt;&lt;span style="font-size:85%;color:#800000;"&gt;"ALTER DATABASE Northwind SET SINGLE_USER"&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;cmdRestore.CommandText = strSQL&lt;br /&gt;cmdRestore.ExecuteNonQuery()&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;strSQL = &lt;/span&gt;&lt;span style="font-size:85%;color:#800000;"&gt;"RESTORE DATABASE Northwind &lt;/span&gt;&lt;span style="font-size:85%;color:#800000;"&gt;FROM DISK = 'C:\mybackup.bak' "&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;cmdRestore.CommandText = strSQL&lt;br /&gt;cmdRestore.ExecuteNonQuery()&lt;br /&gt;MsgBox(&lt;/span&gt;&lt;span style="font-size:85%;color:#800000;"&gt;"finish"&lt;/span&gt;&lt;span style="font-size:85%;"&gt;)&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;Catch&lt;/span&gt;&lt;span style="font-size:85%;"&gt; ex &lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;As&lt;/span&gt;&lt;span style="font-size:85%;"&gt; Exception&lt;br /&gt;MsgBox(&lt;/span&gt;&lt;span style="font-size:85%;color:#800000;"&gt;"Error"&lt;/span&gt;&lt;span style="font-size:85%;"&gt;)&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;Finally&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;strSQL = &lt;/span&gt;&lt;span style="font-size:85%;color:#800000;"&gt;"ALTER DATABASE Northwind SET MULTI_USER"&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;cmdRestore.CommandText = strSQL&lt;br /&gt;c&lt;/span&gt;&lt;span style="font-size:85%;"&gt;mdRestore.ExecuteNonQuery()&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;End&lt;/span&gt;&lt;span style="font-size:85%;"&gt; &lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;Try&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color:#0000ff;"&gt;&lt;/span&gt;Cursor = Cursors.Arrow&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;SqlConnection1.Close()&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;cmdRestore.Dispose()&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;cmdRestore = &lt;span style="color:#0000ff;"&gt;Nothing&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color:#0000ff;"&gt;End&lt;/span&gt; &lt;span style="color:#0000ff;"&gt;Sub&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="color:#ff0000;"&gt;&lt;u&gt;จุดสังเกตุ&lt;br /&gt;&lt;/u&gt;&lt;/span&gt;&lt;span style="color:#ff0000;"&gt;1. ใน Connection String ผมกำหนด Initial Catalog เป็น master (คือจริงๆเป็น database ตัวไหนก็ได้ที่ไม่ใช่ตัวที่เราต้องการ restore) ถ้าเรากำหนด Initial Catalog เป็น Northwind ก็เท่ากับว่าเรากำลังล๊อก database ด้วยตัวเองครับ (ผมก็เป็น กว่าจะรู้ตัว error ไปแล้ว)&lt;br /&gt;&lt;/span&gt;&lt;span style="color:#ff0000;"&gt;2. ผมเลือกใช้คำสั่ง ALTER TABLE เพื่อ SET SINGLE_USER เพื่อกัน user อื่นออกจาก database&lt;br /&gt;&lt;/span&gt;&lt;span style="color:#ff0000;"&gt;3. รันคำสั่ง Restore เสร็จแล้วก็อย่าลืม SET กลับเป็น MULTI_USER นะครับ&lt;br /&gt;&lt;/span&gt;&lt;span style="color:#ff0000;"&gt;4. เนื่องจากบางครั้งกระบวนการ restore มันจะนานก็เลยสั่งให้เปลี่ยน cursor จะได้บอกให้ user รู้ว่ายังทำงานไม่เสร็จนะจ๊ะ&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;ลองรันโค้ดดูครับ&lt;/p&gt;&lt;img id="BLOGGER_PHOTO_ID_5125137580641556898" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand; TEXT-ALIGN: center" alt="" src="http://1.bp.blogspot.com/_N-Phs4y2L44/RyAlebclGaI/AAAAAAAAAA4/UNjiVky5zww/s400/form.jpg" border="0" /&gt;&lt;br /&gt;&lt;img id="BLOGGER_PHOTO_ID_5125137881289267634" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand; TEXT-ALIGN: center" alt="" src="http://3.bp.blogspot.com/_N-Phs4y2L44/RyAlv7clGbI/AAAAAAAAABA/DXIbj7bhdx4/s400/sql2.jpg" border="0" /&gt;&lt;br /&gt;&lt;p&gt;จะเห็นว่าระหว่างทำงาน Database จะเปลี่ยน mode เป็น Single User&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;img id="BLOGGER_PHOTO_ID_5125138177642011074" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand; TEXT-ALIGN: center" alt="" src="http://4.bp.blogspot.com/_N-Phs4y2L44/RyAmBLclGcI/AAAAAAAAABI/yYH5yIvh3yI/s400/finish.jpg" border="0" /&gt;&lt;br /&gt;&lt;p&gt;เสร็จการ restore &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;img id="BLOGGER_PHOTO_ID_5125136223431891346" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand; TEXT-ALIGN: center" alt="" src="http://1.bp.blogspot.com/_N-Phs4y2L44/RyAkPbclGZI/AAAAAAAAAAw/4xUHtvm7t1E/s400/sql3.jpg" border="0" /&gt; Table1 กลับมาแล้ว การ restore ประสบผลสำเร็จ&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="color:#ff0000;"&gt;Note: อยากให้ไปศึกษา option ต่างๆของการ restore เพิ่มนะครับ เพราะมันทำได้หลายอย่างมาก&lt;/span&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-9014370714711979600?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/9014370714711979600/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=9014370714711979600' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/9014370714711979600'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/9014370714711979600'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2007/10/restore-database-sql-server.html' title='โค้ด Restore Database สำหรับ SQL Server'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_N-Phs4y2L44/RyAkPbclGZI/AAAAAAAAAAw/4xUHtvm7t1E/s72-c/sql3.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-2592980086870280528</id><published>2007-10-24T17:20:00.000+07:00</published><updated>2009-07-20T17:05:54.761+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VB.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer'/><title type='text'>โค้ด Backup Database สำหรับ SQLServer</title><content type='html'>&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;br /&gt;โดยปกติแล้วการ Backup หรือ Restore รวมทั้งการ Maintenance RDBMS อย่าง SQLServer หรือ Oracle ควรให้ DBA ทำที่ตัว RDBMS เอง แต่ในบางกรณีเราอาจอยากให้ admin ของ application ที่เราพัฒนาขึ้นสามารถ backup/restore database จากหน้า form ที่เราสร้างขึ้น&lt;/span&gt; &lt;div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;/blockquote&gt;กรณีนี้เราสามารถใช้คำสั่ง T-SQL (Transact SQL) ได้ครับ เรามาดู syntax ของคำสั่งก่อนครับ&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;BACKUP DATABASE { database_name @database_name_var } &lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;TO &lt;&gt; [ ,...n ] &lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;[ WITH&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;[ BLOCKSIZE = { blocksize @blocksize_variable } ]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;[ [ , ] DESCRIPTION = { 'text' @text_variable } ] &lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;[ [ , ] DIFFERENTIAL ]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;[ [ , ] EXPIREDATE = { date @date_var }&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;RETAINDAYS = { days @days_var } ]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;[ [ , ] PASSWORD = { password @password_variable } ]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;[ [ , ] FORMAT NOFORMAT ]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;[ [ , ] { INIT NOINIT } ]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;[ [ , ] MEDIADESCRIPTION = { 'text' @text_variable } ]&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;[ [ , ] MEDIANAME = { media_name @media_name_variable } ]&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;[ [ , ] MEDIAPASSWORD = { mediapassword @mediapassword_variable } ] &lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;[ [ , ] NAME = { backup_set_name @backup_set_name_var } ]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;[ [ , ] { NOSKIP SKIP } ]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;[ [ , ] { NOREWIND REWIND } ]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;[ [ , ] { NOUNLOAD UNLOAD } ]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;[ [ , ] RESTART ]&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;[ [ , ] STATS [ = percentage ] ] ]&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;สำหรับรายละเอียดลองดูใน Online Book ของ SQLServer นะครับ&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;สมมติว่าเราต้องการ backup database ลงใน folder ที่ต้องการ คำสั่งจะประมาณนี้ครับ&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;&lt;em&gt;BACKUP DATABASE Northwind&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;&lt;em&gt;TO DISK 'C:\Northwind.bak'&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;color:#3333ff;"&gt;&lt;em&gt;WITH FORMAT, NAME = 'NorthwindBackup'&lt;/em&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;คราวนี้เราลองมาดูการเขียนโปรแกรมเลยครับ สมมติว่า ผมสร้าง Form มา 1 ฟอร์ม สร้าง ปุ่มชื่อ btnBackup ขึ้นมา 1 ปุ่ม&lt;br /&gt;แล้วก็สร้าง sqlConnection ชื่อ sqlConnection1 สำหรับ Form นี้ด้วยครับ&lt;/span&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;img id="BLOGGER_PHOTO_ID_5078743787028360306" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand; TEXT-ALIGN: center" alt="" src="http://2.bp.blogspot.com/_N-Phs4y2L44/RntSkOMMLHI/AAAAAAAAAAM/pmmxRHa1xDo/s320/form.jpg" border="0" /&gt;&lt;br /&gt;&lt;br /&gt;ทีนี้ก็มาดูโค้ด&lt;/span&gt;&lt;/div&gt;&lt;span style="font-family:verdana;font-size:85%;"&gt;&lt;div&gt;&lt;br /&gt;&lt;span style="color:#3333ff;"&gt;Private Sub btnBack_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnBackup.Click&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;span style="color:#3333ff;"&gt;Dim strSQL As String&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color:#3333ff;"&gt;Dim strCon As StringstrCon = &lt;span style="color:#990000;"&gt;"Data Source=NITHI;Initial Catalog=master;Integrated Security=True"&lt;/span&gt;&lt;br /&gt;Dim cmdBackup As SqlClient.SqlCommand = New sqlClient.SqlCommandSqlConnection1.ConnectionString = strConSqlConnection1.Open()&lt;br /&gt;Cursor = Cursors.WaitCursor&lt;br /&gt;Try&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color:#3333ff;"&gt;strSQL = &lt;/span&gt;&lt;span style="color:#990000;"&gt;"BACKUP DATABASE Northwind "&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color:#3333ff;"&gt;strSQL &amp;amp;= &lt;/span&gt;&lt;span style="color:#990000;"&gt;"TO DISK = 'C:\mybackup.bak' "&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color:#3333ff;"&gt;strSQL &amp;amp;= &lt;/span&gt;&lt;span style="color:#990000;"&gt;"WITH FORMAT, "&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color:#3333ff;"&gt;strSQL &amp;amp;= &lt;span style="color:#990000;"&gt;"NAME = 'myBackup'"&lt;br /&gt;&lt;/span&gt;cmdBackup.Connection = SqlConnection1&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color:#3333ff;"&gt;cmdBackup.CommandText = strSQL&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color:#3333ff;"&gt;cmdBackup.ExecuteNonQuery()&lt;br /&gt;MsgBox(&lt;span style="color:#990000;"&gt;"finish"&lt;/span&gt;)&lt;br /&gt;Catch ex As Exception&lt;br /&gt;MsgBox(&lt;span style="color:#660000;"&gt;"Error"&lt;/span&gt;)&lt;br /&gt;End Try&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color:#3333ff;"&gt;Cursor = Cursors.Arrow&lt;br /&gt;SqlConnection1.Close()cmdBackup.Dispose()cmdBackup = Nothing&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span style="color:#3333ff;"&gt;End Sub&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;ลองรันดูครับ&lt;br /&gt;เมื่อรันเสร็จ จะมี message box บอกว่า Finish แล้วไปดูที่ C: จะพบว่ามีไฟล์ mybackup.bak&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div&gt;&lt;img id="BLOGGER_PHOTO_ID_5078744908014824594" style="DISPLAY: block; MARGIN: 0px auto 10px; CURSOR: hand; TEXT-ALIGN: center" alt="" src="http://3.bp.blogspot.com/_N-Phs4y2L44/RntTleMMLJI/AAAAAAAAAAc/254rtFcKUm4/s400/explorer.jpg" border="0" /&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;div&gt;ก็เป็นอันเสร็จเรียบร้อยครับ&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;ถ้าเราตัองการเก็บไฟล์ backup เป็นหลายๆไฟล์ เราก็อาจจะเขียนโปรแกรมให้สร้างชื่อตามวันที่ก็ได้ครับ เช่น myBackup20070311.bak เป็นต้น&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-2592980086870280528?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/2592980086870280528/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=2592980086870280528' title='0 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/2592980086870280528'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/2592980086870280528'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2007/06/backup-database-sqlserver.html' title='โค้ด Backup Database สำหรับ SQLServer'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_N-Phs4y2L44/RntSkOMMLHI/AAAAAAAAAAM/pmmxRHa1xDo/s72-c/form.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5588108423363673584.post-8276852715673811497</id><published>2007-10-24T17:13:00.000+07:00</published><updated>2009-07-20T17:03:04.789+07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>ASP.Net Site Navigation</title><content type='html'>&lt;span style="font-family:arial;"&gt;เมื่อวันก่อนมีคนมาถามที่ http://www.greatfriends.biz เรื่อง Response.Redirect กับ&lt;br /&gt;Server.Transfer ว่าต่างกันอย่างไร ผมเห็นเรื่องนี้มีถามกันบ่อยพอสมควร แม้แต่ใน webboard&lt;br /&gt;ต่างประเทศก็มีถามกันบ่อยๆ เลยถือโอกาสนี้เอามาลงบล๊อกซะเลยครับ&lt;br /&gt;&lt;br /&gt;เรื่องนี้ถือว่าเป็นเรื่องสำคัญเรื่องหนึ่ง จนหนังสือ &lt;/span&gt;&lt;span style="font-family:arial;"&gt;&lt;span style="color:teal;"&gt;MCTS 70-528&lt;br /&gt;Microsoft .Net Framework 2.0 Web-based Client Development Book&lt;/span&gt; เขียนเป็นหัวข้อสำคัญบทหนึ่งทีเดียว นั่นคือเรื่อง Site Navigation ในหนังสือกล่าวว่า การทำ Navigate Pages หรือการเปลี่ยนหน้า&lt;br /&gt;page นั้นสามารถทำได้ 4 วิธีครับ ได้แก่&lt;br /&gt;&lt;br /&gt;1. สร้าง client-side code หรือ markup สำหรับ request page ใหม่&lt;br /&gt;2. ทำ Cross-page&lt;br /&gt;3. สั่ง client-side browser redirect&lt;br /&gt;4. สั่ง server-side transfer&lt;br /&gt;&lt;br /&gt;สำหรับวิธีที่1 ได้แก่การเขียนคำสั่ง javascript เช่น&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#990000;"&gt;&lt;span style="color:#3333ff;"&gt;&lt;&lt;/span&gt;script&lt;/span&gt; &lt;span style="color:#ff0000;"&gt;language&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;="javascript"&lt;/span&gt; &lt;span style="color:#ff0000;"&gt;type&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;="text/jscript"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#0000ff;"&gt;function&lt;/span&gt; Button1_OnClick(){&lt;br /&gt;document=&lt;span style="color:#990000;"&gt;"Page2.aspx"&lt;/span&gt;;&lt;br /&gt;}&lt;br /&gt;&lt;span style="color:#0000ff;"&gt;&lt;&lt;/span&gt;&lt;span style="color:#990000;"&gt;/script&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;&gt;&lt;/span&gt; &lt;/span&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-family:arial;"&gt;หรือจะกำหนดที่ tag ของ Control ก็ได้ เช่น&lt;br /&gt;&lt;span style="color:#0000ff;"&gt;&lt;&lt;/span&gt;&lt;span style="color:#990000;"&gt;asp&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;:&lt;/span&gt;&lt;span style="color:#990000;"&gt;HyperLink&lt;/span&gt; &lt;span style="color:#ff0000;"&gt;ID&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;="HyperLink1" &lt;/span&gt;&lt;span style="color:#ff0000;"&gt;runat&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;="server" &lt;/span&gt;&lt;span style="color:#ff0000;"&gt;NavigateUrl&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;="~/test.aspx"&gt;&lt;/span&gt;HyperLink&lt;span style="color:#0000ff;"&gt;&lt;&lt;span style="color:#990000;"&gt;asp&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;:&lt;/span&gt;&lt;span style="color:#990000;"&gt;HyperLink&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;&gt;&lt;/span&gt; &lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color:black;"&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;สำหรับวิธีที่2 การทำ Cross-paging หมายถึงการที่มีการใส่ข้อมูลที่&lt;br /&gt;page ที่หนึ่ง และทำการ process ข้อมูลที่ page ที่สอง ซึ่งในกรณีนี้ page ที่สองจะมี&lt;br /&gt;property ชื่อ ProviousPage สำหรับเก็บ object ของ page ที่หนึ่ง เพื่อที่ page&lt;br /&gt;ที่สองจะสามารถเข้าถึง property และ object ต่างๆใน page ที่หนึ่งได้ เช่นการไปเอาข้อมูลจาก&lt;br /&gt;web control เป็นต้น&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family:arial;"&gt;&lt;br /&gt;&lt;br /&gt;การทำ Cross-paging ได้แก่การกำหนด PostbackURL เพื่อไปเรียกใช้ page ปลายทางเช่น&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#0000ff;"&gt;&lt;&lt;/span&gt;&lt;span style="color:#800000;"&gt;asp&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;:&lt;/span&gt;&lt;span style="color:#800000;"&gt;LinkButton&lt;/span&gt; &lt;span style="color:#ff0000;"&gt;ID&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;="LinkButton1"&lt;/span&gt; &lt;span style="color:#ff0000;"&gt;runat&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;="server"&lt;/span&gt; &lt;span style="color:#ff0000;"&gt;PostBackUrl&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;="test.aspx"&gt; &lt;/span&gt;LinkButton &lt;/span&gt;&lt;span style="font-size:85%;color:#0000ff;"&gt;&lt;lt&gt;&lt;span style="font-family:arial;"&gt;&lt;span style="font-size:100%;"&gt;&lt;span style="color:#800000;"&gt;asp&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;:&lt;/span&gt;&lt;span style="color:#800000;"&gt;LinkButton&lt;/span&gt;&lt;span style="color:#0000ff;"&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt; &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:arial;font-size:100%;"&gt;&lt;span style="color:#000000;"&gt;สำหรับวิธีที่ 3 ก็คือการสั่ง Response.Redirect นั่นเอง ตามชื่อหัวข้อ&lt;br /&gt;client-side browser redirect ก็บอกอยู่แล้วครับ ว่าเป็นวิธีที่เกิดขึ้นที่ client-side&lt;br /&gt;หรือ browser นั่นเอง การทำงานวิธีนี้ก็คือ เมื่อเราเขียนโค้ด Response.Redirect ที่&lt;br /&gt;Server-side script ทาง Web Server ซึ่งก็คือ IIS จะทำการส่ง response กลับไปยัง browser&lt;br /&gt;ซึ่งจะระบุ location ของ page ใหม่ไปให้ เมื่อ browser ได้รับ response แล้ว browser&lt;br /&gt;ที่สนับสนุน Redirect (ซึ่งก็ทุกยี่ห้อนั่นแหละ) จะทำการส่ง Request กลับมาที่ Web Server&lt;br /&gt;เพื่อขอข้อมูลของ page ใหม่ที่ต้องการ ทาง Web Server ก็จะ process แล้วส่ง response&lt;br /&gt;ซึ่งก็คือ Page ใหม่ไปให้ ดังนั้นเราจะเห็นได้ว่าวิธีนี้จะเกิด round-trip คือจะมีการรับส่ง&lt;br /&gt;request-response ระหว่าง browser กับ Web Server หลายเที่ยว ทำให้กิน resource ของ network&lt;/span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;span style="font-family:Arial;color:#0000ff;"&gt;&lt;/span&gt;&lt;p&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;ทีนี้เราลองมาดูคำสั่ง Response.Redirect จะพบว่ามันมี 2 overload ครับ&lt;br /&gt;คือ&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;Response.Redirect(url As String) กับ&lt;br /&gt;&lt;br /&gt;Response.Redirect(url As String, endResponse as Boolean)&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;โดย default ถ้าเราไม่กำหนด endResponse มันจะมีค่าเป็น false ครับ หมายความว่าทาง&lt;br /&gt;Web Server จะต้องทำการ Process Response รวมถึงงานต่างๆให้เสร็จก่อน ถึงจะส่ง Reponse.Redirect&lt;br /&gt;กลับไปยัง Server ถ้าเรากำหนดเป็น True ทาง Web Server จะทำการยกเลิกงานอื่นๆทันที หมายความว่าโค้ดใดๆก็ตามที่คุณเขียนหลังบรรทัดนี้จะไม่ถูก&lt;br /&gt;process ครับ Web Server จะส่ง Response กลับไปที่ Browser ทันที&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;ทีนี้การใช้ Response.Redirect มีจุดหนึ่งที่ต้องระวังครับ คือ ปกติการส่ง&lt;br /&gt;Response กลับไปยัง browser นอกจากคำสั่ง redirect แล้วยังอาจจะมีข้อมูลอื่นๆส่งไปด้วย&lt;br /&gt;ดังนั้นถ้ามี data อื่นๆส่งไปแล้ว browser ทำการ process ก่อนที่จะทำการ redirect จะทำให้เกิด&lt;br /&gt;HttpException ทันที (แม้แต่ส่ง HTTP header ไปก็ไม่ได้ครับ) เพื่อป้องกันการเกิด Error&lt;br /&gt;เราจึงควรกำหนด Response.BufferOutput เป็น True ไว้ด้วยครับ&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;Response.BufferOutput = &lt;/span&gt;&lt;span style="font-family:arial;"&gt;&lt;span style="color:#0000ff;"&gt;True&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;Response.Redirect(&lt;span style="color:#800000;"&gt;"test2.aspx"&lt;/span&gt;) &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;&lt;span style="color:red;"&gt;สำหรับกรณีที่คุณใช้ MS Ajax เพื่อไม่ให้เกิด Exception คุณต้องกำหนด&lt;br /&gt;endResponse เป็น True ไปด้วยเสมอ เพื่อให้แน่ใจว่า WebServer ส่งแค่ Redirect อย่างเดียวไม่ส่ง&lt;br /&gt;data อย่างอื่นพ่วงไปด้วยครับ&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;Response.BufferOutput = &lt;/span&gt;&lt;span style="font-family:arial;"&gt;&lt;span style="color:#0000ff;"&gt;True&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;Response.Redirect(&lt;span style="color:#800000;"&gt;"test2.aspx"&lt;/span&gt;, &lt;span style="color:#0000ff;"&gt;True&lt;/span&gt;)&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;เมื่อ Response.Redirect ทำงานเสร็จสมบูรณ์ Web Server จะส่ง Response code&lt;br /&gt;302 ไปให้ browser พร้อมทั้ง URL ของ page ใหม่ ดังนั้นที่ browser ตรงช่อง address&lt;br /&gt;คุณจะเห็น URL ของ page เปลี่ยนไปเป็น page ปัจจุบัน&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;การที่คุณใช้ Response.Redirect คุณจะไม่สามารถใช้ Previous Page Property ได้ครับ&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;สำหรับวิธีที่ 4 ก็คือการสั่ง Server.Transfer&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:arial;"&gt;การใช้ Server.Transfer จะต่างจากการใช้ Response.Redirect เนื่องจาก&lt;br /&gt;Server.Transfer จะเป็นการทำงานที่ Web Server คือเมื่อมีการ Postback กลับมาที่ Web&lt;br /&gt;Server แล้ว ทาง Web Server จะทำการ process งานตามลำดับจนเมื่อเจอคำสั่ง Server.Transfer&lt;br /&gt;แล้ว Web Server จะทำการไปเรียกใช้งาน Page ใหม่ทันที ดังนั้นข้อดีก็คือจะไม่มี round-trip&lt;br /&gt;เกิดขึ้น เป็นการประหยัด network traffic ไปในตัวครับ &lt;/span&gt;&lt;span style="font-family:arial;color:red;"&gt;แต่มีข้อเสียคือ&lt;br /&gt;Server.Transfer สามารถสั่งเรียก page เฉพาะใน Server เท่านั้น ไม่สามารถ Transfer ไปที่&lt;br /&gt;Web Server ตัวอื่นได้ (ไม่เหมือน Response.Redirect คุณสามารถ redirect ไปที่ web ไหนก็ได้&lt;br /&gt;server ตัวไหนก็ได้ครับ)&lt;/span&gt;&lt;span style="font-family:arial;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:arial;"&gt;คำสั่ง Server.Transfer ก็มี 3 overload เหมือนกันครับ ลองไปศึกษาเพิ่มเอง&lt;br /&gt;แต่มี parameter ตัวหนึ่งน่าสนใจคือ preserveForm ถ้าคุณกำหนดเป็น True ค่าของ page แรกจะถูกเก็บไว้ใน PreviousPage Property ของ Page ปลายทางครับ นั่นคือมันจะทำ Cross-paging ให้ด้วยในตัว&lt;br /&gt;ดังนั้นที่ page ปลายทาง คุณสามารถสั่ง&lt;/span&gt;&lt;span style="font-family:arial;"&gt; PreviousPage.FindControl&lt;br /&gt;เพื่อไป get control ของ Page แรกได้ครับ&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5588108423363673584-8276852715673811497?l=jnithi.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://jnithi.blogspot.com/feeds/8276852715673811497/comments/default' title='ส่งความคิดเห็น'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5588108423363673584&amp;postID=8276852715673811497' title='1 ความคิดเห็น'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/8276852715673811497'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5588108423363673584/posts/default/8276852715673811497'/><link rel='alternate' type='text/html' href='http://jnithi.blogspot.com/2007/10/aspnet-site-navigation.html' title='ASP.Net Site Navigation'/><author><name>jnithi</name><uri>http://www.blogger.com/profile/17522897927391387266</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_N-Phs4y2L44/Sk2ZTSnf9LI/AAAAAAAAAB0/CD4SodIKkIY/S220/n1039559879_2799.jpg'/></author><thr:total>1</thr:total></entry></feed>
