2009年2月26日 星期四

Web Application輸入頁面按下Enter鍵時,如何模擬Click功能?續1

問題說明:
當使用者在網頁頁面習慣性按下Enter鍵時,我們在第一篇文章已經說明了,如何模擬滑鼠Click的功能。但是問題來了,如果網頁頁面上包含了TEXTAREA(ASP.NET的TextBox MultiLine)時,當使用者在此元件中輸入資料時,按下了Enter鍵,系統也啟動滑鼠Click的功能,應該會讓使用者嚇一跳;因為此時使用者應該是希望換下一行,而不是將資料送出,所以面對有TEXTAREA輸入元件的網頁時,就必須調整按鍵處理機制的程式碼!

我的做法如下:

//啟動網頁攔截按鍵機制
window.onload =function(){
document.onkeydown=KeyDownProcess;
};

function KeyDownProcess() {
var kv = 0;
var rv = false;
var isTextarea = false;
if (document.all && typeof (document.all) == "object") {
kv = event.keyCode;
isTextarea = (event.srcElement.tagName == "TEXTAREA");
}
else {
kv = evt.keyCode;
isTextarea = (evt.target == "TEXTAREA");
}
switch (kv) {
case 13:
if (isTextarea ==true) {
rv = isTextarea;
}
else {
var cts = document.all && typeof (document.all) == "object" ? document.all.tags("input") : document.getElementsByTagName('input');
var cid = "";
var queryBu = null;
for (ii = 0; ii < cts.length; ii++) {
if (cts[ii].type == "submit" && cts[ii].id.indexOf("loginBU") > -1) {
queryBu = cts[ii];
break;
}
}
if (null != queryBu) {
doClick(queryBu);
rv = false;
}
}
break;
default:
rv = true;
break;
}
return rv;
}
//實做模擬Click的功能
function doClick(o) {
if (document.all && typeof (document.all) == "object") //IE
o.click();
else {
var e = document.createEvent('mouseEvent');
e.initEvent('click', true, true);
o.dispatchEvent(e);
}
}
*以上程式碼,適用於使用Input Buttom Submit type的資料後送模式;如果使用Input Button Image type,則需要修改判斷的type 為image。

後記:
如有任何建議或改善意見,歡迎隨時來函指正或討論。Thanks.

2009年2月25日 星期三

Web Application輸入頁面按下Enter鍵時,如何模擬Click功能?

問題說明:
在網頁上如果包含資料輸入元件和『送出』的按鈕時,當使用者輸入完資料後,會習慣性的按下Enter鍵,想將資料送出存檔,而一般網頁基本設定是需要按下『送出』這個按鈕,才開始檢驗資料或後送資料;因此,如果想讓使用者按下Enter鍵就可以達到『送出』的功能,需要透過Java Script達成來模擬object Click的功能。
如何達到上面的需求呢?個人試以登入頁面為例,將相關的Java Script程式碼說明如下:

有關登入頁面的元件部署和程式不在這裡說明,一般說來登入頁面應該會有一顆按鈕是執行資料後送的功能,這個元件的ID就假設為:loginBU。
假設上面的條件式成立的,下面的程式碼就可以完成:使用者按下Enter鍵時,自動模擬 loginBU Click的功能:

//啟動網頁攔截按鍵機制
document.onkeydown = KeyDownProcess;

//使用者如果按下Enter鍵時,模擬Click功能
function KeyDownProcess(){
var kv = 0;
if (document.all&& typeof (document.all) == "object") //IE
kv = event.keyCode;
else
kv = evt.keyCode;
if (kv == 13) {
doClick('logonBU');
return false;
}
}

//實做模擬Click的功能
function doClick(linkId) {
var o = document.getElementById(linkId);
if (document.all && typeof (document.all) == "object") //IE
o.click();
else {
var e = document.createEvent('mouseEvent');
e.initEvent('click', false, false);
o.dispatchEvent(e);
}
}

在網頁頁面的JavaScrip程式區,加入上面的程式碼後,當使用者輸入資料後,習慣性按下Enter鍵時,就會執行和按下loginBU Click相同的事件功能。
*當輸入的資料項複雜時,就必須改寫上面的程式碼,才能正常運作。
*更複雜的資料輸入頁面的處理原則,留待下篇文章說明與討論。

後記:
如有任何建議或改善意見,歡迎隨時來函指正或討論。Thanks.

2009年2月20日 星期五

在Windows平台使用JSP Solution時, 如何引用Com元件,增加系統功能?

問題說明:
最近開發一套Windows平台Web Application 的單一簽入系統,須要讓JSP引用已開發好的單一簽入開發端元件;免得重寫開發端元件,也有利於系統的維護。因此,尋找相關的解決方案。
在ASP Solution中有Server.CreateObject("元件名稱")可以引用COM元件。
在PHP Solution中有new COM("元件名稱")可以引用Com元件。
JSP Solution引用Com元件的解決方案如下:
1.須取得JACOB套件,安裝於JAVA 2 SDK中。
2.使用JACOB套件的new ActiveXComponent("元件名稱")或new Dispatch("元件名稱")功能。
由於此元件的搜尋與安裝有些複雜,因此記錄於此,隨時提供備忘索引;也分享給有此需求的網友。

JACOB簡介:
JACOB is a JAVA-COM Bridge that allows you to call COM Automation components from Java.
官方網站:http://danadler.com/jacob/

JACOB下載:
下載JACOB1.7版
下載JACOB1.14.3版

JACOB安裝:
1.在j2sdk所在資料夾,建立一次目錄:com;再將下載檔案解縮後的jacob.dll複製至此目錄。
2.將下載檔案解縮後的jacob.jar複製到j2sdk所在資料夾的lib次目錄。
3.CLASSPATH環境變數加入,jacob.jar所在路徑。
4.PATH環境變數加入,jacob.dll所在路徑。
注意1:設定上面環境變數時,需要保留原來的設定值;也就是說是附加不是取代。如果取代了上面的環境變數,可能會引起系統運作不正常。
注意2:JACOB如果安裝1.14.3版,web server使用Resin時,須升級至:3.2以上版本。
下面環境變數設定是以JDK1.6.0-12為例:
CLASSPATH=.;C:\Program Files\Java\jdk1.6.0_12\lib\tools.jar;C:\Program Files\Java\jdk1.6.0_12\lib\dt.jar
Path=C:\Program Files\Java\jdk1.6.0_12;C:\Program Files\Java\jdk1.6.0_12\bin;C:\Program Files\Java\jdk1.6.0_12\com
JAVA_HOME=C:\Program Files\Java\jdk1.6.0_12

接著就可以開始撰寫JSP程式囉:
引用ActiveXCom元件時,使用:new ActiveXComponent("元件名稱.Class名稱")
引用一般Com元件時,使用:new Dispatch("元件名稱.Class名稱")
JOCA官方網站,範例程式碼如下:
ActiveXComponent xl = new ActiveXComponent("Excel.Application");
Object xlo = xl.getObject();
try {
System.out.println("version="+xl.getProperty("Version"));
System.out.println("version="+Dispatch.get(xlo, "Version"));
xl.setProperty("Visible", new Variant(true));
Object workbooks = xl.getProperty("Workbooks").toDispatch();
Object workbook = Dispatch.get(workbooks,"Add").toDispatch();
Object sheet = Dispatch.get(workbook,"ActiveSheet").toDispatch();
Object a1 = Dispatch.invoke(sheet, "Range", Dispatch.Get,
new Object[] {"A1"},
new int[1]).toDispatch();
Object a2 = Dispatch.invoke(sheet, "Range", Dispatch.Get,
new Object[] {"A2"},
new int[1]).toDispatch();
Dispatch.put(a1, "Value", "123.456");
Dispatch.put(a2, "Formula", "=A1*2");
System.out.println("a1 from excel:"+Dispatch.get(a1, "Value"));
System.out.println("a2 from excel:"+Dispatch.get(a2, "Value"));
Variant f = new Variant(false);
Dispatch.call(workbook, "Close", f);
} catch (Exception e) {
e.printStackTrace();
} finally {
xl.invoke("Quit", new Variant[] {});
}

個人自行開發元件引用程式碼如下:
Dispatch ctsso= new Dispatch("SSOUtilNoneAspx.CTSSOUtil");
Variant cookieDomain=Dispatch.call(ctsso,"GetCookieDomain",new Variant(true));
Variant cookieName=Dispatch.call(ctsso,"GetCookieName",new Variant(true));
Variant result= Dispatch.call(ctsso,"IsAuthenticated", new Variant(kValue),new Variant(""));

2009年2月7日 星期六

ADSI技術文章-使用LDAP Provider取得User的Primary Group

DirectoryEntry domain=new DirectoryEntry(ladpPath, accountName, pwd);
search.SearchScope = SearchScope.Subtree;
search.Filter = "(objectclass=User)";
SearchResult result = search.FindOne();
object primaryGroupID=result.Properties["primaryGroupID"][0];
DirectoryEntry gEntry = new DirectoryEntry(ldapRootPath,account,password);
DirectorySearcher search = new DirectorySearcher(gEntry);
search.SearchScope = SearchScope.Subtree;
search.Filter = "(objectclass=group)";
string rv = string.Empty;
try
{
SearchResultCollection result = search.FindAll();
string sid;
foreach (SearchResult sr in (IEnumerable)result)
{
sid = ObjectIdToString((byte[])sr.Properties["objectsid"][0]);
if (sid.EndsWith(primaryGroupID.ToString()) == true)
{
rv = sr.Properties["cn"][0].ToString();
break;
}
}
}
catch{
}


public string ObjectIdToString(byte[] oSid)
{
SecurityIdentifier sid = new SecurityIdentifier(oSid, 0);
return sid.ToString();
}

//重點提示:group objectSid 的尾碼==PrimaryGroupID

ADSI技術文章-使用LDAPProvider取得User的Groups

//方法一(ldap path 需包含使用者名稱)
DirectoryEntry domain=new DirectoryEntry(ladpPath, uName, pwd);
search.SearchScope = SearchScope.Subtree;
search.Filter = "(objectclass=User)";
SearchResult result = search.FindOne();
IADsUser iu = (IADsUser)result.GetDirectoryEntry().NativeObject;
List rv = new List();
foreach (IADsGroup item in (IEnumerable)iu.Groups())
rv.Add(item.Name.Split('=')[1]);

//方法二(ldap path 不包含使用者名稱)
DirectoryEntry domain=new DirectoryEntry(ladpPath, uName, pwd);
search.SearchScope = SearchScope.Subtree;
search.Filter = string.Format("(&(objectclass=User)(CN={0})",uName);
SearchResult result = search.FindOne();
IADsUser iu = (IADsUser)result.GetDirectoryEntry().NativeObject;
List rv = new List();
foreach (IADsGroup item in (IEnumerable)iu.Groups())
rv.Add(item.Name.Split('=')[1]);

-------------------------------------
使用IADsUser的Groups取得User的Groups
注意:此方法取得的Groups並不包含PrimaryGroup

2008年12月11日 星期四

使用XmlSerializer時出現無法產生暫存類別的錯誤!

在Asp.Net使用XmlSerializer時,如果網站應用程式的身分識別,使用Network Service時,預設情況會產生下列錯誤:
無法產生暫存類別 (result=1)。
error CS2001: 找不到原始程式檔 'C:\WINDOWS\TEMP\uacaqkr_.0.cs'
error CS2008: 未指定輸入
產生錯誤原因:因為.net framework需要使用到,Windows\Temp產生暫存類別,但預設權限不足產生的錯誤。
解決方法:將網站應用程式的身分識別提升為『本機系統』或是調整Windows\Temp的Network service權限為允許寫入。

2008年7月27日 星期日

ASP.NET無接縫傳值的撰寫模式

問題說明:
所謂ASP.NET的『無接縫傳值』,是指在撰寫網頁間的傳值時,不使用queryString或Session來傳值,而是利用元件的PostBackUrl和Page的PreviousPage來完成網頁間的傳值。一般說來,queryString的方式容易使得網頁的運作參數暴露在瀏覽器的網址列,而遭有心人士利用;而session傳值的方式,容易在使用者同時開啟多個網頁時,產生資料錯置的嚴重錯誤。所以說網頁間的傳值最少必須是使用『無接縫傳值』模式,網站系統的安全性才能獲得基本的保障。

『無接縫傳值』簡易撰寫模式:
一般Asp.Net的程式設計師,大都可以輕易的完成。
首先,在父頁上部署一個具有PostBackUrl Method的元件,並設定此元件的PostBackUrl值。
例如:ImageButton1.PostBackUrl="Page2.aspx";
然後,在Page2.aspx的Page_Load event中接收傳值,語法如下:
protected void Page_Load(object sender, EventArgs e)
{
string postedValue=string.Empty();
if (Page.PreviousPage != null && Page.PreviousPage.IsCrossPagePostBack == true)
postedValue=((TextBox)Page.PreviousPage.FindControl("TextBox1")).Text;
.
.
.
}
如果你使用了MasterPage的機制,請將FindControl的方法,修改如下:
((TextBox)PreviousPage.Master.FindControl("ContentPlaceHolder1").FindControl("TextBox1")).Text;//TextBox1是主頁的控制項ID,也是傳值的控制項

『無接縫傳值』進階整合撰寫模式:
整合AjaxToolKit的HoverMenuExtender控制項,由GridView的子選單中,傳出選取資料列的Key值,並將
PostBackUrl的頁面開啟在新的window。也就是說,當使用者點選選單上的選項後,會開啟新視窗,而新視窗必須接到傳值才能顯示詳細資料,不接受QueryString或Session的方式傳值;當新視窗無法獲得Key時會自動於顯示錯誤說明後關閉視窗。
這個做法包含下列兩個問題:
1.要如何讀取GridView中HoverMenuExtender控制項LinkButton的CommandArgument
2.要如何讓PostBackUrl的網頁在新視窗開啟
如果解決了這兩個問題,再配合基本撰寫模式,網頁間的無縫傳值就可以正確運作了。

我的做法:
1.宣告LinkButton的相關屬性,例如:
<asp:LinkButton ID="lnBtnEdit" runat="server" OnCommand="lnBtnEdit_Click" PostBackUrl="~/Admin/eArticleEdit.aspx" OnClientClick="var scriptName=document.forms[0].action;window.document.forms[0].target='_blank';setTimeout(function(){window.document.forms[0].target='';document.forms[0].action=scriptName;}, 500);" CommandArgument='<%# Bind("articleId") %>'>《編修文章》</asp:LinkButton>

 有關OnClientClick和CommandArgument也可以在GridView1_RowDataBound(object sender, GridViewRowEventArgs e)中完成設定。

2.完成lnBtnEdit_Click的事件程式碼:
protected void lnBtnEdit_Click(Object sender, CommandEventArgs e)
{
HiddenField hf = (HiddenField)Page.Master.FindControl("postbackHF");
hf.Value = e.CommandArgument.ToString();
}


3.在Page2.aspx的Page_Load event中接收傳值。

以上作法就可以完成,ASP.NET無接縫傳值,讓編修資料的頁面,在新的視窗中開起。
接著問題來了,當編修資料頁面完成存檔後,可能需要讓父頁重新Binding資料,該如何做呢~?
請看下回分解!