Thursday, August 22, 2013

Deploy Application ด้วย clickonce ใช้ Crystal Report

คลิกไฟล์ .rpt ใน Solution Explorer
แล้วดู property Build Action
 
ถ้าใช้ Crystal Report for .NET โดยปกติจะมีค่าเป็น Embeded Resource
นั่นคือไฟล์ .rpt จะถูกแนบไปกับไฟล์ .Exe หรือ .Dll ด้วย
ในกรณีนี้ ไม่ต้องนำไฟล์รายงานไปลงในเครื่องของผู้ใช้ต่างหาก
 
แต่ถ้าใช้ Crystal Report ในลักษณะ stand-alone
(ใช้เมธอด ReportDocument.Load("Filename.rpt") ในการโหลดรายงาน)
ให้ดูให้แน่ใจว่า กำหนด Build Action เป็น Content
และดูใน Project Properties > tab Publish > Application Files ว่าได้ Include ไปด้วย

Friday, August 16, 2013

วงจรชีวิตของ ASP.NET Page

เนื่องจากมีคำถามเข้ามาเกี่ยวกับลำดับเหตุการณ์ที่เกิดขึ้นใน Page ซึ่งประกอบด้วย Master Page และ User Control ว่าเหตุการณ์ใดเกิดขึ้นก่อนหรือหลัง ผมจึงขออนุญาตอธิบายไว้ในบทความนี้ โดยจะอธิบายเหตุการณ์ต่างๆ ที่เกิดขึ้นในวงจรชีวิตของ Page และจะตอบคำถามในช่วงท้ายของบทความครับ
สำหรับนักพัฒนาเว็บไซต์ด้วย ASP.NET นั้นมักจะคุ้นเคยกับการเขียนชุดคำสั่งลงในเหตุการณ์ Load ของ Page กันดีอยู่แล้ว แต่ในการเรียกใช้ ASP.NET Page แต่ละครั้งนั้นจะมีลำดับขั้นของการประมวลผล ซึ่งจะเกิดเหตุการณ์ต่างๆ ประกอบกันเป็นวงจรชีวิตของ Page จึงเป็นเรื่องสำคัญที่นักพัฒนาต้องเข้าใจวงจรชีวิตของ Page ซึ่งจะทำให้สามารถเขียนชุดคำสั่งได้ถูกต้องและเหมาะสมกับเหตุการณ์ที่เกิดขึ้น
ตารางต่อไปนี้แสดงรายชื่อเหตุการณ์ที่เกิดขึ้นในวงจรชีวิตของ Page ซึ่งจะใช้กันค่อนข้างบ่อย
 เหตุการณ์ของ Page การนำไปใช้
PreInit
เกิดขึ้นหลังจาก Page ถูกสร้างแล้วและก่อนการกำหนดค่าเริ่มต้นของ Control ใช้เหตุการณ์นี้สำหรับทำสิ่งต่อไปนี้ได้
  • ตรวจสอบคุณสมบัติ IsPostBack เพื่อตัดสินใจว่า Page ถูกประมวลผลครั้งแรก คุณสมบัติ IsCallBack และ IsCrossPagePostBack ถูกกำหนดค่าในเวลาเดียวกันด้วย
  • สร้าง Control แบบ Dynamic
  • กำหนด Master Page แบบ Dynamic
  • กำหนดคุณสมบัติ Theme แบบ Dynamic
  • อ่านหรือกำหนดค่าคุณสมบัติ Profile
ข้อสังเกต
ถ้า Request เป็น PostBack ค่าของ Control จะยังไม่อ่านจาก View State ถ้ากำหนดคุณสมบัติของ Control ที่เหตุการณ์นี้ ค่าของ Control จะถูกเขียนทับในเหตุการณ์ถัดไป
Init
เกิดขึ้นหลังจาก Control ทั้งหมดได้ถูกสร้างแล้วและ Skin ที่จัดเตรียมถูกนำไปใช้ เหตุการณ์ Init ของแต่ละ Control เกิดขึ้นก่อนเหตุการณ์ Init ของ Page
ใช้เหตุการณ์นี้อ่านหรือกำหนดคุณสมบัติเริ่มต้นของ Control
InitComplete
เกิดขึ้นที่ตอนจบของการกำหนดค่าเริ่มต้นของ Page
ใช้เหตุการณ์นี้เพื่อเปลี่ยนค่า ViewState
PreLoad
เกิดขึ้นหลังจาก Page ดึงค่า View State สำหรับ Page และ Control ทั้งหมด
Load
เหตุการณ์ Load ของแต่ละ Control เกิดขึ้นหลังจากเหตุการณ์ Load ของ Page
ใช้เหตุการณ์ Load เพื่อกำหนดค่าให้กับคุณสมบัติต่างๆ ใน Control และสถาปนาการเชื่อมต่อฐานข้อมูล 
Control Events
ใช้เหตุการณ์เหล่านี้เพื่อเขียนชุดคำสั่งในเหตุการณ์ของ Control เช่น เหตุการณ์ Click ของปุ่มหรือเหตุการณ์ TextChanged ของกล่องข้อความ
ข้อสังเกต
ใน Request ที่เป็น PostBack ถ้า Page มี Validator Control ให้ตรวจสอบคุณสมบัติ IsValid ของ Page ก่อนการประมวลผลใดๆ 
LoadComplete
เกิดขึ้นหลังจากเหตุการณ์ของ Control ถูกประมวลผลแล้ว
ใช้เหตุการณ์นี้สำหรับงานที่ต้องการให้ Control อื่นทั้งหมดใน Page ผ่านเหตุการณ์ Load แล้ว
PreRender
เกิดขึ้นหลังจาก Page ได้สร้าง Control ทั้งหมดแล้ว
เหตุการณ์ PreRender ของแต่ละ Control เกิดขึ้นหลังเหตุการณ์ PreRender ของ Page
ใช้เหตุการณ์นี้เพื่อทำการเปลี่ยนแปลงครั้งสุดท้ายให้กับเนื้อหาของ Page หรือ Control ก่อนเริ่มต้นการ Render
PreRenderComplete
เกิดขึ้นหลังจาก Data Bound Control ซึ่งถูกกำหนดค่าให้กับคุณสมบัติ DataSourceID เรียกเมธอด DataBind 
SaveStateComplete
เกิดขึ้นหลังจาก View State และ Control State ถูกบันทึกเรียบร้อยแล้ว ความเปลี่ยนแปลงใดๆ ที่เกิดขึ้นกับ Page และ Control ที่จุดนี้จะมีผลต่อการ Render แต่ความเปลี่ยนแปลงจะไม่ถูกเรียกคืนมาใน PostBack ถัดไป
Render
ขั้นตอนนี้ไม่ใช่เหตุการณ์ แต่ที่ขั้นตอนนี้ของการประมวลผล Page จะเรียกเมธอดนี้ในแต่ละ Control โดยที่ ASP.NET Web ServerControl ทั้งหมดมีเมธอด Render ซึ่งเขียน Markup ของ Control ส่งออกให้กับ Browser
Unload
เกิดขึ้นกับแต่ละ Control ก่อนแล้วจึงเกิดขึ้นกับ Page
ใน Control ใช้เหตุการณ์นี้ทำการปล่อยทรัพยากรของระบบ เช่น การปิดการติดต่อกับฐานข้อมูลของ Control
สำหรับ Page ใช้เหตุการณ์นี้ทำการปล่อยทรัพยากรของระบบ เช่น การปิดไฟล์ที่เปิดไว้และการติดต่อฐานข้อมูล
ข้อสังเกต
ระหว่างเหตุการณ์ Unload ได้ผ่านขั้นตอนการ Render แล้ว ดังนั้นจึงไม่สามารถเปลี่ยนแปลง Response Stream ได้อีก ถ้ามีการเรียกเมธอด เช่น Response.Write จะมีความผิดพลาดเกิดขึ้น
สำหรับคำถามของเหตุการณ์ที่เกิดขึ้นใน Page ซึ่งประกอบด้วย Master Page และ User Control ว่าเหตุการณ์ใดเกิดขึ้นก่อนหรือหลัง เช่น เหตุการณ์ Load ของ Master Page และ User Control โดยลำดับที่เกิดขึ้นจะเป็นดังนี้ (เรียงลำดับตามเหตุการณ์ที่เกิดขึ้นก่อน-หลัง)
  • Page_PreInit 
  • UserControl_Init
  • MasterPage_Init
  • Page_Init
  • Page_InitComplete
  • Page_PreLoad
  • Page_Load
  • MasterPage_Load
  • UserControl_Load
  • Button_Click (Control Events)
  • Page_LoadComplete
  • Page_PreRender
  • MasterPage_PreRender
  • UserControl_PreRender
  • Page_PreRenderComplete
  • Page_SaveStateComplete
  • UserControl_Unload
  • MasterPage_Unload
  • Page_Unload 
รูปภาพข้างล่างนี้แสดงลำดับการทำงานของเมธอดและเหตุการณ์ที่เกิดขึ้น
ASP.NET Page Life Cycle Diagram
อ้างอิงจาก http://msdn.microsoft.com

การทำ precompilation ใน asp.net 2.0 v2

ASP.NET 2.0 โมเดลใหม่ที่เปลี่ยนไป
ผมไม่ได้ใช้ ASP.NET เป็นหลัก จึงต้องขออภัยที่ตอบช้ามากๆ
เพิ่งจะถูกกระตุ้นด้วยคำถามทำนองนี้เข้ามาอีก 
จนกระทั่งรู้สึกว่าต้องไปหาคำตอบมาให้ แต่บางที 
ตอนนี้คุณชายสี่หมี่เกี๊ยวอาจจะหาคำตอบได้เองแล้ว

1. พฤติกรรมปกติของ ASP.NET 2.0 เป็นอย่างไร?
ปกติแล้ว ASP.NET 2.0 จะ compile เว็บเป็น .Dll ทำนองเดียวกับ
ที่เคยทำใน ASP.NET 1.x โดยจะไปวางไว้ในโฟลเดอร์นี้
<Windows directory>\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files


ไฟล์ .Dll นี้จะถูกสร้างอัตโนมัติเมื่อมีการเข้าถึงไฟล์ .aspx, .asmx, .ascx ครั้งแรก
ดังนั้น การใช้เว็บ "ครั้งแรก" จะช้ากว่าปกติสักหน่อย แต่หลังจากนั้น 
เร็วฉลุย เพราะใช้โค้ดที่ compile ไว้แล้วแทน
(จนกว่า ไฟล์ ต้นฉบับ หรือ code-behind มีการเปลี่ยนแปลง)

หมายเหตุสำหรับคนใช้ Express Edition
* ผู้ใช้ Visual Web Developer (VWD) จะมีสิทธิ์ได้เฉพาะโมเดลแบบนี้
(คอมไพล์ .Dll ในการเรียกครั้งแรก ใส่ให้ใน Temporary Folder เท่านั้น
ทำ precompile ไม่ได้ - แต่ความจริงก็ทำได้แหล่ะครับ
ถ้าใช้ command-line utility "aspnet_compiler" เอาเอง)


ผมเรียกว่า "ลูกกั๊ก" ของไมโครซอฟต์ก็แล้วกัน อย่างที่ทราบแล้วไงครับ
ว่า VWD ใช้สำหรับ students และ hobbiest เท่านั้น ก็ฟรีนี่ครับ
ใช้ในแบบของ commercial software ไม่ได้ครับ 

2. ถ้าต้องการ Deploy เว็บไซต์ upload เฉพาะไฟล์ .Dll
ไม่เอา source code ขึ้นไปด้วย ได้มั้ย?

ตอบว่า ทำได้ครับ และทำได้ดีกว่าเดิมอีกด้วยสิครับ

ASP.NET 1.x
ใน ASP.NET 1.x เว็บเพจหนึ่งหน้า จะประกอบด้วย
ไฟล์ .aspx และ .aspx.vb (หรือ .aspx.cs) ซึ่งเป็น code-behind
ซึ่งจะสามารถเอา .vb หรือ .cs นี้ คอมไพล์ก่อนให้เป็น .Dll
แล้วเอาขึ้นเว็บโฮสต์ได้ กลไกโดยละเอียด เป็นดังนี้ครับ

ขอไม่อธิบายรายละเอียดของภาพล่ะ เดี๋ยวจะยาวเกินไป.
สรุปเลยครับว่า ASP.NET 1.x สามารถ compile ไฟล์ .vb, .cs ให้เป็น .Dll
แล้วเอาขึ้นเฉพาะไฟล์ .Dll ได้ (source code ไม่ต้อง) แต่ไฟล์ .aspx ที่คู่กัน
ต้องเอาขึ้นไปด้วย ดังนั้นแบบที่ปลอดภัยที่สุดที่จะทำได้คือ

สรุปได้ว่า
deploying ASP.NET 1.x => ได้ไฟล์ .aspx + .dll



ASP.NET 2.0
asp.net เวอร์ชั่นนี้ แปลกไป ทำให้ผู้ที่เคยใช้ asp.net 1.x มาก่อน งงไปกันหมด
ว่า .Dll มันหายไปไหน ผมจะสรุปให้ครับ สุดท้ายแล้ว ก็ไม่วุ่นวายซับซ้อนอะไร
ก่อนอื่นขอบอกก่อนว่า มี command-line utility ชื่อว่า "aspnet_compiler" 
ที่ทำหน้าที่อยู่เบื้องหลัง แต่ผมขออธิบายวิธีการใช้แบบผ่าน Visual Studio ที่ง่ายกว่า
และครอบคลุมงานหลักๆ ที่ต้องการแล้วครับ

1. รู้จักเมนูใหม่ Build --> Publish
Visual Studio จะไม่สร้าง .Dll ให้ถ้าไม่ขอให้ทำ 
เราสั่งได้ที่เมนู Publish ครับ 

เมนูนี้ จะทำการ Pre-compile เว็บโปรเจ็กต์ให้ 
(หมายถึงทำการ compile ไว้ก่อนเลย ไม่ใช่รอให้มี
การเรียกใช้เว็บครั้งแรก แล้วค่อย compile อัตโนมัติ)
โดยผลของการ Compile จะถูกกำหนดใน Target Location
ในหน้าจอ Publish Web Site นี้ครับ


ในตัวอย่างผมก็ใส่พาธ C:\MyWeb ให้ดูเป็นตัวอย่าง
เมื่อคลิกปุ่ม OK ผลลัพธ์ของการ compile จะไปสร้างทั้ง Web Project เลยครับ
รวมถึงโฟลเดอร์ Bin ที่คิดถึงด้วย :-) ดูภาพตัวอย่างครับ

และในโฟลเดอร์ Bin

ไฟล์ .Dll อยู่นี่เองครับ

เราสามารถนำผลลัพธ์จาก Target Location นี้ทั้งโฟลเดอร์
upload ขึ้น web hosting เพื่อ deploy ได้เลย. ไม่ต้องมี source code.


2. ตั้งค่า Publish Web Site 
เมื่อให้เห็นภาพรวมแล้ว ผมขอย้อนกลับมาให้พวกเราดูของใหม่
น่าสนใจใน ASP.NET 2.0 นี้ ให้เข้าใจ แบบใช้ไม่ยากเลย 
จากข้อที่แล้ว เมนู Build -> Publish จะพบหน้าจอนี้

ยังมี ตัวเลือกที่อยากให้สนใจอยู่ 2 ตัว คือ

[x] Allow this precompiled site to be updatable
เลือกตัวเลือกนี้
 --> จะทำให้การ precompile คอมไพล์เฉพาะ code-behind (.vb, .cs) เท่านั้น
และไม่คอมไพล์ส่วน .aspx ซึ่งจะได้ผลลัพธ์คล้ายโมเดลของ ASP.NET 1.x
วิธีการนี้ เรายังสามารถแก้ไขส่วน .aspx เช่น layout ต่างๆ ของเว็บเพจ ได้โดยไม่ต้อง compile โปรเจ็กต์ใหม่

ไม่เลือกตัวเลือกนี้ --> จะทำให้การ precompile คอมไพล์ทั้งไฟล์ code-behind (.vb, .cs)
และส่วนไฟล์ .aspx ไปด้วยกัน ลงในไฟล์ .Dll เดียวกันนั่นเลย ซึ่งจะทำให้การแก้ไข
เว็บเพจใดๆ ต้องมีการคอมไพล์กันใหม่ทั้งโปรเจ็กต์เลย. วิธีการนี้ ไฟล์ .aspx จะถูกสร้างใน Target Location ด้วย
แต่ภายในไฟล์จะไม่มีข้อมูลของไฟล์ .aspx ต้นฉบับเลย แต่มีข้อความ marker แทน ดังนี้


[x] Use fixed naming and single page assemblies
เลือกตัวเลือกนี้ --> ทำให้ชื่อไฟล์ .Dll ที่จะได้คงที่ตลอด ไม่เปลี่ยนแปลง
ไม่เลือกตัวเลือกนี้ --> ชื่อไฟล์จะถูกสร้างใหม่เรื่อยๆ ถ้า upload ขึ้นเว็บโฮสต์ หลายๆ ทีเข้า
ก็อาจจะมีไฟล์ที่ไม่ได้ใช้จำนวนมาก.

สรุปได้ว่า
deploying ASP.NET 2.0 (แบบ updatable) => ได้ไฟล์ .aspx + .dll 

หรือเลือก
deploying ASP.NET 2.0 (แบบ not updatable) => ได้ เฉพาะไฟล์ .dll

การทำ precompilation ใน asp.net 2.0

ไม่ได้อัพหลายวันมีเรื่องหลายเรื่องที่เกิดขึ้น บางครั้งถ้าเราแยกความรู้สึกได้ในคนคนเดียวก็คงจะดี งานก็เยอะมากจริงๆ จนไม่มีเวลาทำอย่างอื่นเลย เราเกิดมาเพื่อสิ่งนี้จริงๆ
บ่นเสร็จเรียบร้อยแล้ววันนี้จะมาพูดถึงการทำ pre compilation ในเวบ 2.0 สืบเนื่องจากประทับใจเป็นการส่วนตัวที่ความสามารถมากกว่า เวบ 1.1 เสียอีก ในการทำ pre compilation นั้นก็หมายถึง เมื่อเวลาที่เราสร้าง project หนึ่ง project ออกมาแล้วเมื่อเราต้องการนำเวบ project นั้นขึ้นไปไว้บน server เราต้องทำการ deployment project ให้สามาถรันได้เหมือนที่อยู่ในฝั่ง local แต่ถ้าใครได้ใช้ Web Developer 2005 Express จะไม่มี feature ในการ deploy project ซึ่งความจริงแล้วเราสามารถนำ file ทั้งหมดขึ้นไปใว้บน server รวมทั้ง source code ทั้งหมดที่เรามี ซึ่งไม่ใช่เรื่องดีเลยที่เราจะทำเช่นนั้น เพราะเวลาที่มีการเรียกใช้หน้าเวบจะมีการ compile ใหม่ทุกครั้งทำให้ประสิทธิภาพไม่ดีเท่าที่ควร ยิ่งถ้าเป็น hosting ที่เราไปเช่าไว้ก็เสี่ยงต่อการนำ source code ไปทิ้งไว้ เป้าหมายในการทำ pre compile นั้นก็คือ "no source code"
การทำ precompile สามารถทำได้หลายวิธีแต่วิธีที่จะพูดถึงในวันนี้ก็คือก็ใช้ความสามารถของ aspnet_compiler.exe ซึ่งมาพร้อมกับ Framework 2.0 เหมาะกับการนำไปใช้ในเวบ Web Developer 2005 Express เป็นอย่างยิ่งaspnet_compiler นั้นสามารถ compile project ที่เป็น visual directory ซึ่งต้องอยู่ภายใต้ server ที่เป็น iis aspnet_compiler จะไปทำการตรวจสอบในmeta dataของiis ว่ามีvisaul directoryอยู่จริงหรือไม่แล้วcompileออกมาตามtarget pathที่เรากำหนดไว้ หรือ เราจะcompile project folderภายในต้องมีfileนามสกุล.aspx,.ascx.vb,.csซึ่งในเวบ2.0นั้นจะมีการสร้างfolderมาให้ก็คือApp_Code มีไว้เก็บ code ที่เป็น .vb หรือ .cs App_Dataมีไว้เก็บfile databaseขั้นตอนในการทำpre compileมีดังต่อไปนี้
1.เข้าถึงfile aspnet_compiler.exeรูปด้านล่าง

2. พิมพ์
aspnet_compiler -p"c:\mywebsite\" -v /c:\deploy3. เข้าไปดู folder target ที่เรากำหนดไว้ คือ c:\deploy\bin ปรากฏดังรูปด้านล่าง


จะเห็นว่าการทำ pre compile นั้นเราจะได้ target file ที่เป็น file assemplies (dll file) ภายใน folder deploy จะมีเฉพาะ fileaspx ,html ,config file ,image ... file อื่นๆ ที่เป็น .vb .cs จะโดน compile ให้อยู่ในรูป
aspx --> .compiled ภายในเป็น xml file บอกถึง ชื่อ file ที่อ้างอิงกันในแต่ละfile
App_Code -->assemplies (dll file) และ .compiled
.vb หรือ.cs ที่เป็น file separateกับaspx -->ขึ้นต้นด้วย App_Webซึ่งเป็น assemplies (dll file)

ถ้าเปิด file aspx ดู จะไม่มี source code html .ให้เห็น แต่จะมี ข้อความ

This is a marker file generated by the precompilation tool, and should not be deleted!
เค้าบอกว่า ห้ามลบ file นี้เด็ดขาด
เอาล่ะครับเราก็ได้ file ทั้งและพร้อมที่จะส่งขึ้นไปยัง server ตาม concept การทำ pre compile ก็คือ .. "no source code"
ปล.
- fixednames ใช้เมื่อต้องการ update file assemply เดิม
-d ใช้เมื่อต้องการ compile พร้อมทำการ debuging บอก line number ที่ compile
-f ใช้เมื่อต้องการ re-write folder targetตัวเดิม


Thursday, August 1, 2013

A Imagebutton on a FormView / repeater isn't working in IE10 on Windows 8

If it's the same problem we ran across, the issue is that ImageButtons in IE10 submit the x and y coordinates of the click location as decimals, when ASP.NET 4.0 only understands integers.
The fix we did, which requires no server change or framework change, was to cheat a little bit, and replace all of our ImageButtons with a custom control which inherits from ImageButton and strips off the decimal parts.
Here's the class:
[DefaultProperty("Text")]
    [ToolboxData("<{0}:ImageButtonFixed runat=server></{0}:ImageButtonFixed>")]
    public class ImageButtonFixed : ImageButton
    {
        protected override bool LoadPostData(string postDataKey, NameValueCollection postCollection)
        {
            // Control coordinates are sent in decimal by IE10
            // Recreating the collection with corrected values
            NameValueCollection modifiedPostCollection = new NameValueCollection();
            for (int i = 0; i < postCollection.Count; i++)
            {
                string actualKey = postCollection.GetKey(i);
                string[] actualValueTab = postCollection.GetValues(i);

                if (actualKey != null)
                {
                    if (actualKey.EndsWith(".x") || actualKey.EndsWith(".y"))
                    {
                        string value = actualValueTab[0];
                        decimal dec;
                        Decimal.TryParse(value, out dec);
                        modifiedPostCollection.Add(actualKey, ((int)Math.Round(dec)).ToString());
                    }
                    else
                    {
                        foreach (string actualValue in actualValueTab)
                        {
                            modifiedPostCollection.Add(actualKey, actualValue);
                        }
                    }
                }
            }
            return base.LoadPostData(postDataKey, modifiedPostCollection);
        }
    }
And to use it, all you'd need to do is add this control to your page in place of ImageButton:

<%@ Register assembly="App_Code" namespace="XExcept.Controls" tagprefix="cc1" %>
<cc1:ImageButtonFixed runat="server" CommandName="Whatever" ImageUrl="whatever.png" />

Not a great permanent solution, but it works.

source : http://forums.asp.net/t/1905046.aspx/2/10?A+button+on+a+FormView+isn+t+working+in+IE10+on+Windows+8