在組件編程中對事件的理解是十分重要的,c# 中的“事件”是當對象發生某些有趣的事情時,類向該類的客戶提供通知的一種方法。與事件聯繫最爲緊密的,個人認爲是委託.委託可以將方法引用封裝在委託對象內。爲了弄清組件-事件-委託三者的關係,本人用實際的例子來談 談小弟的理解。
首先創建一個windows控件項目,添加如下控件樣板。 當事件觸發時,會傳遞一個eventargs類型的參數給事件處理方法,爲了能傳遞自定義的信息,我們可以創建一個繼承於eventargs的事件參數 類,其定義如下: public class eventloginargs:system.eventargs { public string struserid; public string strusername; public string struserpwd; public bool bvaild; public eventloginargs(string userid,string username,string userpwd) { struserid = userid; strusername = username; struserpwd = userpwd; } 再聲明兩個委託,它們是對eventloginargs和eventargs對象中的信息的封裝,如下: public delegate void userlogineventhandler(object sender,eventloginargs e); public delegate void canceleventhandler(object sender,eventargs e); 在組件中爲了能讓用戶自定義某事件的處理方法,所以組件必需提供事件接口.如果只是繼承於單個已有的windows控件,可以重載已知的方 法進行添加自己的處理,也可以聲明自定義的事件接口.而若組件中包含多個控件,應該根據實際需要聲明事件接口,此處本人就兩個按鈕的 使用而聲明兩個自定義的事件接口,如下: public event userlogineventhandler submitlogin; public event canceleventhandler cancel; protected virtual void onsubmitlogin(eventloginargs e) { if(this.submitlogin!=null) { submitlogin(this,e); } } protected virtual void oncancel(eventargs e) { if(this.cancel!=null) { cancel(this,e); } 其實submitlogin 是userlogineventhandler委託的實例,令人費解的是此事件的觸發,傳遞,處理過程如何呢? 在本例中是通過確定按鈕來觸發submitlogin事件的: private void btnok_click(object sender, system.eventargs e) { if(txtid.text != ""&&txtname.text !=""&&txtpwd.text !="") { intlogintime++; onsubmitlogin(new eventloginargs(txtid.text,txtname.text,txtpwd.text)); blogin = testuserindb(new eventloginargs(txtid.text,txtname.text,txtpwd.text)); messagebox.show("this is the btnok_click function!","in control",messageboxbuttons.ok); if(!blogin) messagebox.show("login in failed!","login error",messageboxbuttons.ok); } else { messagebox.show("your must input all the items!","login info",messageboxbuttons.ok); } } 注意本例中的對話框是爲了幫助瞭解事件的過程,真正有用的是第二個例子。 在btnok_click事件響應中,先對進行簡單的有效性檢查,建議實際工作應作加強完善.intlogintime變量是嘗試登錄的次數.testuserindb是 通過已知信息在數據庫中搜索出有關記錄進行判斷用戶是否合法. 因爲組件的測試是通過客戶程序的,所以應該創建一個最簡單明瞭的客戶 程序.這是一個windows應用程序,將編譯好的組件添加到用戶控件欄中,拖出到工作區中,添加submitlogin事件的響應程序,如下: private void usercontrol1_submitlogin(object sender, userlogin.eventloginargs e) { messagebox.show("this is in test form!"+ usercontrol1.blogin +"/ns login times is "+usercontrol1.intlogintime +"/nes struserid="+e.struserid,"test",messageboxbuttons.ok); } 此時運行客戶程序可得以下結果: this is in test form! this is the process in db this is the btnok_click function! 結果表明單擊btnok按鈕時執行組件中的onsubmitlogin(new eventloginargs(txtid.text,txtname.text,txtpwd.text)),此方法又調用 submitlogin(this,e),從而激發submitlogin事件,usercontrol1_submitlogin就進行響應,故打印第一行。 跟着是執行testuserindb,它打印出第二行。 最後是返回到btnok_click中輸出最後一行。 注意若btnok_click中的onsubmitlogin和testuserindb所在的行調換位置,其結果是不同的.第二個例子中,二者的位置調換,先進行數據庫 查詢判斷,再在submitlogin的事件響應usercontrol1_submitlogin中處理結果,下面的是例子二的主要代碼: public delegate void userlogineventhandler(object sender,eventloginargs e); public delegate void canceleventhandler(object sender,eventargs e); public event userlogineventhandler submitlogin; public event canceleventhandler cancel; protected virtual void onsubmitlogin(eventloginargs e) { if(this.submitlogin!=null) { submitlogin(this,e); } } protected virtual void oncancel(eventargs e) { if(this.cancel!=null) cancel(this,e); } public string server { } public string database { } public string tableset { } public string userfordb { } public string pwdfordb { } public bool testuserindb(eventloginargs e) { //messagebox.show("this is the process for db!","testuserindb",messageboxbuttons.ok); bool bok = false; if(this.strdatabase!=null && this.strserver!=null && this.struserfordb!=null) { if(this.strpwdfordb==null) this.strpwdfordb = ""; string strconnection = "server="+this.strserver +";database="+this.strdatabase +";uid="+this.struserfordb +";pwd="+this.strpwdfordb; string strsql = "select userid,username,userpwd from "+this.strtableset+" where userid="+e.struserid+" and username="+e.strusername +" and userpwd="+e.struserpwd+""; sqlconnection conn = new sqlconnection(strconnection); try { conn.open(); } catch(sqlexception ex) { messagebox.show("數據庫不能打開!請檢查有關參數.","error",messageboxbuttons.ok); return false; } sqldataadapter da = new sqldataadapter(strsql,conn); dataset ds = new dataset(); try { da.fill(ds,this.strtableset); } catch(sqlexception ex) { ...... } foreach(datarow row in ds.tables[this.strtableset].rows) { if(row != null) { bok = true; } } ....... } else { bok = false; } return bok; } private void btnok_click(object sender, system.eventargs e) { if(txtid.text != ""&&txtname.text !=""&&txtpwd.text !="") { intlogintime++; blogin = testuserindb(new eventloginargs(txtid.text,txtname.text,txtpwd.text)); if(!blogin) messagebox.show("login in failed!","login error",messageboxbuttons.ok); else onsubmitlogin(new eventloginargs(txtid.text,txtname.text,txtpwd.text)); } else { messagebox.show("your must input all the items!","login info",messageboxbuttons.ok); } } private void btncancel_click(object sender, system.eventargs e) { oncancel(e); } private void usercontrol_load(object sender, system.eventargs e) { intlogintime = 0; } } public class eventloginargs:system.eventargs { public string struserid; public string strusername; public string struserpwd; public bool bvaild; public eventloginargs(string userid,string username,string userpwd) { struserid = userid; strusername = username; struserpwd = userpwd; } } 它的客戶程序主要如下: private void usercontrol1_submitlogin(object sender, userlogin.eventloginargs e) { messagebox.show("this result is blogin="+ usercontrol1.blogin +" at "+usercontrol1.intlogintime +" times /n userid="+e.struserid+"/n username="+e.strusername,"testresult",messageboxbuttons.ok); } private void form1_load(object sender, system.eventargs e) { usercontrol1.server = "localhost"; usercontrol1.database="weiwen"; usercontrol1.tableset = "testuser"; usercontrol1.userfordb="sa"; usercontrol1.pwdfordb = "sa"; } 這兩個例子的完整代碼可以點擊這裏下載. 讀者可以參考學習,也可直接使用此組件,但使用時應當以microsoft sql server 作爲後臺數據庫,所用到的用戶表格應有 userid,username,userpwd三列,同時在客戶程序中應對有關參數初始化,submitlogin事件返回值是嘗試次數intlogintime和驗證是否成功blogin,可參考擴展例子二。 |