แสดงบทความที่มีป้ายกำกับ javascript แสดงบทความทั้งหมด
แสดงบทความที่มีป้ายกำกับ javascript แสดงบทความทั้งหมด

วันพฤหัสบดีที่ ๓ ธันวาคม พ.ศ. ๒๕๕๒

ASP.NET สั่งรัน Win App บนเครื่อง Client

ผมเคยสร้าง Web Application ตัวหนึ่ง เพื่อทำเป็น Application Portal คือถ้า user sign in เข้ามาใน Web นี้ จะมองเห็น link ของโปรแกรมทั้ง Win App และ Web App ที่มีสิทธิ์เปิดใช้งาน รวมถึงทำ Single Sign On ด้วย นั่นคือจะส่ง User Name + Password เพื่อไป log in ที่ app นั้นๆทันทีครับ แต่ใน App นั้นๆก็ต้องรองรับการส่ง Parameter ๆไปด้วยนะ

ทีนี้ถ้าเป็น Web App ด้วยกันก็ไม่มีปัญหาครับ แค่สร้าง link เท่านั้นเองและก็ส่ง parameter ที่เข้ารหัสไปด้วย แต่ถ้าเป็น Win App นี่แหละที่เป็นเรื่องใหญ่ เพราะว่า ASP.NET นั้นทำงานเป็น Server side script นั่นคือคำสั่งพวก Shell จะทำให้เกิดการรันโปรแกรมที่ฝั่ง Server ครับ เหมือนกรณีเราเขียนคำสั่ง MessageBox.Show นั่นแหละ มันจะขึ้น message box ที่ server เสมอ เราต้องใช้ Client side script แทน ถ้าเป็น javascript ก็คือ alert() นั่นเอง

ทีนี้ Win App ที่เราต้องการเรียกนั้น ถ้าเราพัฒนาด้วย .NET 2.0 ขึ้นไป สามารถใช้ ClickOnce ในการ Deploy ได้ ถ้าเป็นแบบนี้ก็ง่ายขึ้นครับ เพราะเราจะมี URI ให้เลือกใช้ดังนี้
  • http://ClickOnceServer/TestApplication ตัวนี้จะลิงค์ไปหน้าหลักของโปรแกรม ซึ่งจะมีปุ่ม Install และ ปุ่ม Install Prerequisites

  • http://ClickOnceServer/TestApplication/TestApplication.Application ตัวนี้จะไปรัน Application ทันทีครับ

ดังนั้นเราก็เขียน javascript เรียก .Application เพื่อสั่งรันโปรแกรมได้เลย



function btnAttn_OnClick() {
window.open("http://ClickOnceServer/TestApplication/TestApplication.Application", "Application");
intervalId = setInterval(UpdateInfo, 10000);
event.returnValue = false;
}

ในโค้ดผมมีการใช้ setInverval ด้วย เพราะต้องการไปเรียก UpdateInfo function ทุก 10 วินาที function ตัวนี้จะไปเรียก WebService เพื่อทำการ update สถานะบน label ทุก 10 วินาทีครับ

ตัว ClickOnce เราสามารถส่ง Commandline argument (parameter) ไปได้ด้วยครับ แต่ขอข้ามไปก่อนละกัน

ทีนี้ถ้าเป็น Win App ที่ไม่ใช่ ClickOnce ก่อนอื่นเลยจะมีข้อจำกัดดังนี้

  1. Win App นั้นต้อง Install ที่เครื่อง Client แล้ว และเราต้องรู้ Path ที่แน่นอน
  2. ต้องกำหนด Security Option ของ IE ในเครื่อง Client ด้วย ที่ผมทำคือไปกำหนด Trust Site ครับ เพราะเป็น Intranet อยู่แล้วก็ไม่มีปัญหา เพราะเราต้องใช้ WScript ในการสั่ง Shell application ที่ต้องการนั่นเอง

ดังนั้นวิธีนี้เหมาะกับเครื่องภายในเท่านั้นครับ

โค้ดส่วน HTML ซึ่งเก็บ hidden field ไว้สองตัวนั่นคือ User name กับ password อย่าลืมเข้ารหัสด้วยนะครับ



<body>
<form id="form1">
<div>
<input type=hidden id='hdnExeCommand' name='hdnExeCommand' value='<%=strExeCommand%>'>
<input type=hidden id='hdnWinAppLocation' name='hdnWinAppLocation' value='<%=strWinAppLocation %>'>
</div>
</form>
</body>

จะเห็นว่ามีการเอาค่าตัวแปรมาใส่ใน hidden field ด้วย ค่าตัวแปรที่ได้นี้มาจากคำสั่งถอดรหัสในฝั่ง Code Behind ครับ ทีนี้มาดู vbscript กัน (จริงๆจะใช้ javascript ก็ได้และดีกว่าด้วย แต่ผมอยากลอง vbscript ครับ)



<script language="vbscript">
dim WshShell, strExe, fso, strCommand
set WshShell = CreateObject("WScript.Shell")
set fso = CreateObject("Scripting.FileSystemObject")
strExe = document.all("hdnWinAppLocation").value
strCommand = document.all("hdnExeCommand").value
if fso.FileExists(strexe) then
WshShell.Run strCommand ,1, false
else
msgbox "File not found.",vbOKOnly+vbInformation,"TG Application Portal"
end if
set fso = nothing
set WshShell = nothing
window.close()
<script>


ส่วนเรื่องการส่ง command line argument กับ Encrypt&Decrpyt ถ้ามีเวลาจะกลับมาเขียนเพิ่มครับ

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

Javascript - สร้าง TextBox เพื่อ filter รายการใน DropDownList

ผมได้รับ requirement ใหม่จาก user เจ้ากรรมอีกแล้ว นั่นคือมี DropDownList ตัวหนึ่งใน ASP.NET ที่มีรายการมากกว่า 100 รายการ (อันนี้กรองมาแล้วนะครับ) ซึ่ง option มันเยอะมาก ดังนั้น user ต้องการให้โปรแกรมสามารถ filter รายการใน DropDownList นี้ได้ครับ ทีนี้จะเขียนโปรแกรมที่ฝั่ง server ก็สามารถทำได้ แต่มันจะทำให้มีการ postback ไปหา ซึ่งไม่ค่อยดีเท่าไหร่ ดังนั้นผมเลยใช้ javascript สำหรับ filter แทนครับ

วิธีก็คือผมสร้าง Textbox ขึ้นมาหนึ่งตัว สำหรับให้ user พิมพ์ข้อความที่ต้องการกรอง และสร้าง drop-down list อีกหนึ่งตัวโดยให้ visibility=hidden คือซ่อนไม่ให้ user เห็น สำหรับเก็บ option ทั้งหมดที่ไม่ได้ทำการกรองครับ ลองมาดูโค้ดกัน

เริ่มจาก WebControl ก่อน มี DropDownList 2 ตัว และ TextBox ครับ


<asp:DropDownList ID="ddlWorkCode" runat="server" CssClass="txtRequired" Width="540px" AutoPostBack="True" style="height:24px"></asp:DropDownList"> 
<asp:TextBox ID="txtSearchWorkCode" runat="server" Width="421px" style="color: #000099; background-color: #afeeee;""></asp:TextBox">
<asp:DropDownList ID="ddlCache" runat="server" Width="0px" style="visibility:hidden" "></asp:DropDownList"> 


ในหน้า Code Behind ก็ทำการ Add Attribute ให้ txtSearchWorkCode

txtSearchWorkCode.Attributes.Add("onkeyup", "txtSearchWorkCode_OnKeyUp(this);")
txtSearchWorkCode.Attributes.Add("onblur", "txtSearchWorkCode_OnBlur();")



คราวนี้มาถึง javascript บ้าง คราวนี้เขียนสดๆไม่ได้ใช้ jQuery ครับ

function txtSearchWorkCode_OnKeyUp(obj) {
if (event.keyCode != 13 || event.keyCode != 9) {
var str = obj.value.toUpperCase();
var ddlWorkCode = $get("ddlWorkCode");
var ddlCache = $get("ddlCache");
var listWorkCode = ddlCache.getElementsByTagName("OPTION");

// ถ้าไม่มี OPTION ใน cache ให้ออกจาก function
if(listWorkCode.length==0) return false;

//clear option ใน DropDownList
ddlWorkCode.length = 0;
for (i = 0; i < listWorkCode.length; i++) {
if (listWorkCode[i].text.toUpperCase().indexOf(str) > -1) {
var o = listWorkCode[i].cloneNode(true);
ddlWorkCode.appendChild(o);
}
}
ddlWorkCode.selectedIndex = 0;
}
}

function txtSearchWorkCode_OnBlur() {
if ($get("txtSearchWorkCode").value == "") {
// ถ้าไม่มีคำสั่งกรอง ให้ไปดึง OPTION ทั้งหมดจาก DropDownList2 มาใส่คืน
var ddlWorkCode = $get("ddlWorkCode");
var ddlCache = $get("ddlCache");

ddlWorkCode.lenght = 0;
for (i = 0; i < ddlWorkCode2.options.length; i++) {
var o = ddlWorkCode2.options[i].cloneNode(true);
ddlWorkCode.appendChild(o);
}
ddlWorkCode.selectedIndex = 0;
}
}


ในส่วนคำสั่ง javascript มีสองจุดที่น่าสนใจครับ นั่นคือ
1. ผมใช้ ddlWorkCode.length=0; ในการเคลียร์ option ของ drop-down list ทิ้งครับ ถือว่าง่ายและสะดวกมาก ทีแรกผมใช้ for วนลูปเพื่อ remove element ออก ซึ่งแน่นอนว่าช้ากว่าครับ

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 ซะอีก

ตอนนี้ลองทดสอบโปรแกรมดู ก็ใช้งานได้ดียังไม่เจอ bug แต่เพิ่งทดสอบได้นิดเดียวคงต้องลองเล่นดูอีกสักพักครับ

ปล. สำหรับ $get() เป็น shortcut ของ ASP.NET AJAX ครับ มีค่าเท่ากับ document.getElementById()

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

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

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

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

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

Public employeeName(5) As String

Protected Function GetEmployees() As String

Dim sb As New StringBuilder

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

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

End Function


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

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

End Sub



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

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

</script>



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

วันพุธที่ ๑๙ สิงหาคม พ.ศ. ๒๕๕๒

สร้าง DIV เพื่อบัง WebPage ขณะใช้งาน AJAX

ปกติเวลาพัฒนา Web Application ด้วย ASP.NET ผมมักจะใช้ ASP.NET AJAX ร่วมด้วยเสมอ และเมื่อมีการ PostBack หรือเรียกใช้ WebServices ก็จำเป็นต้องเขียน javascript เพื่อโชว์รูปว่ากำลัง Update และ disable controls ต่างๆ

พอดีได้เข้าไปดูเวบไหนจำไม่ได้แล้วครับ เค้าน่าจะสร้าง Div ที่กำหนด OPACITY ไว้เพื่อให้เป็นกรอบจางๆสำหรับบังหน้าเวบขณะ process งาน ก็สะดวกดีนะครับ เพราะว่าถ้าในหน้ามี control หลายๆตัว จะไปนั่งไล่ disable ทีละตัวก็จะถึกไปหน่อย ยิ่งถ้าใช้พวก asp:menu หรือ Master Page ด้วยแล้ว เสียเวลาน่าดูชม สร้าง Div มาบังมันซะเลย เร็วและได้ผลลัพท์ไม่แตกต่างกัน (อาจจะดีกว่าด้วยครับ) เลยมาลองทำดูบ้างครับ


<div id="divProgress">
<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"></div>
<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">
<img alt="Processing" src="../Images/loading.gif" /><asp:label ID="lblLoading" CssClass ="textNormal" Text="Preparing reports..." runat="server"></asp:label>
</div>
<div>


โดยปกติก็จะกำหนด visibility เป็น hidden ไว้ก่อน พอมีการเรียกใช้ AJAX ก็จะเปลี่ยนเป็น visible มาดูโค้ดกันครับ


<script type="text/javascript" id="MSAJAX">
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_beginRequest(onBeginRequest);
prm.add_endRequest(onEndRequest);

function onBeginRequest(sender, e) {
ShowProgress(true);
}

function onEndRequest(sender, e) {
ShowProgress(false);
}
function ShowProgress(isShow) {
if(isShow == true){
$get("divProgress").style.visibility = "visible";
var dv = $get("divBackground")
dv.style.width = "1200px";
dv.style.height = "1000px";
}else{
$get("divProgress").style.visibility = "hidden";
}
}
</script>


เนื่องจากผมใช้ PageRequestManager ของ ASP.NET AJAX เป็นตัวสั่ง ShowProgress ดังนั้น Script นี้ต้องวางไว้ข้างล่าง ScriptManager control นะครับ

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

parseInt ทำงานผิดพลาด

วันนี้ user โทรมาแจ้งว่ามี bug เกิดขึ้น คือเมื่อเลือกวันที่จากปฏิทินแล้วกลายเป็นวันอื่นไป ลองไปไล่โค้ดดูปรากฏว่าผมไปกำหนด onblur event ของ textbox ให้ไปตรวจสอบวันที่และทำการจัด format เป็น dd/MM/yyyy

ปรากฏว่าโค้ดส่วนที่มี bug คือคำสั่ง parseInt ครับ


function DMY2Date(dmyStr) {
var splitDateStr=dmyStr.split('/');
return new Date(splitDateStr[2],String(parseInt(splitDateStr[1])-1),splitDateStr[0]);
} //end function


คือผมสั่ง parseInt(splitDateStr[1]) เพื่อหาค่าเดือนเป็น Integer แต่ปรากฏว่า พอ split data string ออกมา มันได้ค่า "08" ซึ่งใน parseInt function มันมีเงื่อนไขว่าถ้าขึ้นต้นด้วย 0 แปลว่าเป็นเลขฐาน 8 ครับ พอลอง debug ดูปรากฏว่า parseInt("08") ได้ผลลัพธ์เป็น 0 เพราะเลขสูงสุดของเลขฐาน 8 คือ 7 ครับ ถ้า parseInt("010") ถึงจะได้ค่า 8

ลองดู Reference ที่ www.w3schools.com กันครับ

Syntax
parseInt(string, radix)

If the radix parameter is omitted, JavaScript assumes the following:
  • If the string begins with "0x", the radix is 16 (hexadecimal)

  • If the string begins with "0", the radix is 8 (octal). This feature is deprecated
  • If the string begins with any other value, the radix is 10 (decimal)



ก็เลยไปแก้โดยกำหนด radix = 10 เข้าไปด้วย เพื่อบอกว่าเราต้องการ parseInt แบบเลขฐาน 10 ไม่ใช่เลขฐาน 8 นะเฟ้ย


function DMY2Date(dmyStr) {
var splitDateStr=dmyStr.split('/');
return new Date(splitDateStr[2],String(parseInt(splitDateStr[1],10)-1),splitDateStr[0]);
} //end function


เสร็จแล้วก็เลยต้องไปไล่นั่งตรวจสอบดูว่ามีการใช้ parseInt function ตรงไหนบ้าง เพื่อที่จะไปใส่ radix ให้หมดเพื่อป้องกันปัญหาครับ

วันจันทร์ที่ ๒๐ กรกฎาคม พ.ศ. ๒๕๕๒

javascript กำหนด textbox ให้รับค่า time (hh:mm)

สมมติผมมี textbox id="txtTime" และต้องการให้ user สามารถคืย์ข้อมูลเฉพาะเวลา โดยให้ format = hh:mm (23:59) ครับ
ก่อนอื่นก็ต้อง register event handler ให้กับ textbox ก่อน


$(function() {
$("#txtTime").keypress(function(e) {txtTime_OnKeyPress(this,e)});
$("#txtTime").blur(function() { ValidateTime(this) });
});


จะเห็นว่าผมสร้าง function สำหรับจัดการ keypress event กับ blur event ให้ textbox ครับ สำหรับ txtTime_OnKeyPress สำหรับกำหนดให้ user สามารถคีย์เฉพาะ d(2).d(2) คือตัวเลขสองตัว ตามด้วยจุดและตัวเลขอีกสองตัว ส่วน ValidateTime สำหรับตรวจสอบว่าค่าที่คีย์ตรงกับ format ที่ต้องการหรือเปล่า


function txtTime_OnKeyPress(sender, e) {
var myTime = sender.value;
if(myTime.length>4) {
event.keyCode = 0;
return false;
}
var charCode = (e.which) ? e.which : e.keyCode
switch (myTime.length) {
case 0:
if (charCode < 48 || charCode > 50) event.keyCode = 0;
break;
case 1:
if (charCode<48||(myTime == 2&&charCode > 51)) event.keyCode = 0;
break;
case 2:
if (charCode != 46) event.keyCode = 0;
break;
case 3:
if (charCode<48||charCode > 53) event.keyCode = 0;
break;
default:
if (charCode < 48 || charCode > 57) event.keyCode = 0;
}
}

function ValidateTime(sender) {
if (sender.value.length == 0) return false;
var regEx = /^(\d{2}).(\d{2})$/;
var arrMatch = sender.value.match(regEx);
if (arrMatch == null) {
alert("Invalid time.");
sender.value = "";
return false;
}
var hh = arrMatch[1];
var mm = arrMatch[2];
if (hh >= 24 || mm >= 60) {
alert("Invalid time.");
sender.value = "";
return false;
}
return true;
}


ทดสอบกับ IE ได้ผล OK ครับ ใครจะนำไปใช้ก็ทดสอบกันก่อนนะ ว่างๆผมจะมาแก้ function ให้สามารถกำหนด format ของเวลาได้ เผื่อบางทีต้องใช้แบบ 11:59 A.M. แต่ตอนนี้ใช้แค่นี้ก่อนละกันครับ