2009年2月28日 星期六

在ASP.NET的Web使用者控制項中,如何利用delegate傳出資料?

問題說明:
一般程式開發過程Reuse的技術是必要的,透過Reuse的做法可精簡系統程式碼;更可以減少程式維護的複雜度。ASP.NET的Web使用者控制項就是基於Reuse的觀念而建立的機制。Web使用者控制項的Reuse可以分為兩種類型:一是不同頁面直接重覆引用、二是透過Property的設定更改控制項元件頁面重覆使用。這兩種類型都有可能需要將處理完成的資料外送給父頁。本文將說明作者如何透過delegate的機制,建立Web使用者控制項專用的事件,傳出選取或處理完成的資料;讓資料選取或處理頁面和後續處理機制分離。如此可以大大的提高此使用者控制項的Reuse功能,傳出的資料也可以有不同的處理彈性。

本文將以資料選取的Web使用者控制項為Reuse元件,在該控制項中包含了一個GridView元件和一個Button元件,GridView中除了資料欄位外,外加一個CheckBox,做為使用者選取該筆資料與否的標誌。
使用者點選資料後,假設需要外送的資料有兩個屬性~:
1.資料來源:string SourceName
2.選取的資料清單:Dictionary SelectedInfo

我的做法如下:

1.宣告一個delegate 命名為:ButtonSelectedClick
2.建立一個EventArgs 命名為:ClickEvenArgsDictionary
程式碼如下:
public delegate void ButtonSelectedClick(object sender, ClickEvenArgsDictionary e);
public class ClickEvenArgsDictionary : EventArgs
{
public Dictionary SelectedInfo { get; set; }
public string SourceName { get; set; }
public ClickEvenArgsDictionary(Dictionary selectedInfo,string sourceName)
{
this.SelectedInfo = selectedInfo;
this.SourceName = sourceName;
}

}

3.在資料處理的頁面加入ButtonSelectedClick的event 命名為 ucSelectedClick
程式碼如下:
public event ButtonSelectedClick ucSelectedClick;

4.在資料處理頁面的Button Click事件中處理deletegate的外傳事件
程式碼如下:
string sourceName="source name";
Dictionary selected = new Dictionary();
...
...
選取資料的處理程式碼
...
ClickEvenArgsDictionary ce = new ClickEvenArgsDictionary(selected,sourceName);
if (ucSelectedClick != null)
this.buttonClick(this, ce);

5.在引用此控制項頁面中Page_Load()中設定資料處理頁面的事件,假設此資料處理頁面的ID:UserDataBrows1
程式碼如下:
UserDataBrows1.ucSelectedClick +=new ButtonSelectedClick(UserDataBrows1_ucSelectedClick);

6.在事件捕捉區完成資料的處理或存檔
程式碼如下:
void UserDataBrows1_ucSelectedClick(object sender, ClickEvenArgsDictionary e)
{
  //在此事件捕捉區中可以取得:e.SourceName 和 e.SelectedInfo
}

完成上面六個處理步驟,就可以輕鬆的捕捉到使用者控制項傳出來的資料,讓控制項和資料處理頁面完全分離。

後記:
1.這是delegate功能的另一種用法,希望能幫助有此需求的系統開發者。
2.Delegates in C# are like functions pointers in C/C++.
3.如有任何建議或改進意見,歡迎隨時來函指正或討論。Thanks.

2009年2月27日 星期五

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

問題說明:
如果我們想使用LDAP Provider,透過群組名稱取得所有的成員,應該如何做呢?
下面範例會傳回一組帳號和FullName的Dictionary資料:

public static Dictionary GetLDApGroupUsers(string ldapPath,string groupName,string uName,string pwd)
{
Dictionary rv=new Dictionary();
DirectoryEntry domain = new DirectoryEntry(ldapPath , uName, pwd);
DirectorySearcher search = new DirectorySearcher(domain);
search.SearchScope = SearchScope.Subtree;
//搜尋條件
search.Filter = string.Format("(&(objectclass=Group)(cn={0}))",groupName);
SearchResult result = search.FindOne();
if (result != null)
{
DirectoryEntry gEntry = result.GetDirectoryEntry();
DirectoryEntry mEntry;
IADsUser iu;
DataRow dr;
//叫用 Group 的 Members
object members = gEntry.Invoke("Members", null);
foreach (object mb in (IEnumerable)members)
{
mEntry = new DirectoryEntry(mb);
if (mEntry.SchemaClassName == "user")
{
iu = (IADsUser)mEntry.NativeObject;
rv.Add(iu.Name.Split('=')[1],iu.FullName));
}
}
}
return rv;
}
*使用IADsUser的COM介面,可以輕鬆取得User的屬性。

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