C#關於委託的一些事,開發日誌
----- 委託是什麼------
其實委託事件很好理解,就當成是c語言中的函式指標或者是回撥函式,或者說換種理解方式,訊號和槽?觸發器和接收器?總之就是一個地方呼叫了這個函式,那麼在另一個地方也會調起《同參數》《同類型》的這麼個函式。就目前我學到的,這個就比較接近於訊號和槽的關係。
另外由於C#相對自由的類外呼叫方式,一開始整個程式碼我看的雲裡霧裡的,但是這兩天嗯造看下來,已經大致明白了不少。
比如我們現在做的這個工具,在LBD.IEngTeachV53專案下的ClsTools.cs類中,我們做了兩個工具一個叫Intercom,師生對講,另一個叫Broadcast 視訊廣播。然後兩個工具是這樣的,都是先開啟教師端,然後再開啟學生端。向學生端啟動的時候傳入教師端的tcp ip 和 tcp port,然後將教師端再將需要的引數補給學生端即可。這只是說個前提,但是不管怎麼說,我們只用管一點就是如何在教師端和軟體之間互相傳遞引數。
反正是在本地,那就用WinMessage來傳遞訊號。
包含兩個部分:
1.教師端接收exe傳送的訊息
2.exe接收教師端傳送的訊息
詳解:
需要注意的是,這個ClsTools類中有一個專門用來處理本地WinMessage訊息的類物件,叫clsWinMessage,其中有很多方法:
在這個物件上我犯了很多錯誤,這裡只講解正確用法。
1.教師端接收exe傳送的訊息:
1>在啟動的時候,需要初始化這個物件,這個類中寫了一個方法叫InitWinMessage(),呼叫它就可以了。
2>需要改寫InitWinMessage方法,這個方法中需要包含以下幾個要素:1.new一下這個成員,否則可能為null 2.新增一個委託如下:
this.clsWinMessage.MessageReceive += new LBD.Frame.Base.Utils.MessageEventHandler(this.WinMessage_MessageReceive);
this.clsWinMessage.CopyDataMessageReceive += new LBD.Frame.Base.Utils.CopyDataMessageEventHandler(this.WinMessage_CopyDataMessageReceive);
這就是添加了一個委託,當clsWinMessage接到訊息MessageReceive和CopyDataMessageReceive,分別會觸發ClsTools.cs中的WinMessage_MessageReceive和WinMessage_CopyDataMessageReceive方法,並向其中傳值。
注意一點就是這個MessageReceive和CopyDataMessageReceive不同,具體哪裡不同我不知道,但是在我現在的這個Qt中的SendMessage的方法,傳送的其實是CopyData,和單純的Message好像又有些不一樣,具體的區別我不太瞭解,之後有機會看下MFC可能就懂了。
3>新增訊息接收。這個clsWinMessage類並不是什麼WinMessage訊息都可以接收,只能接受部分被宣告的訊息碼,不然整個程式需要相應的訊息碼太多了會導致卡頓。由上我們可以想到,Message和CopyData並不是一回事,所以新增的訊息也不是按照它原本的方式新增的。
this.clsWinMessage.AddMessage(WM_TAKECLASS);
//上為舊程式碼,下為新程式碼
this.clsWinMessage.AddCopyDataMessage(tINTERCOMSTART);
this.clsWinMessage.AddCopyDataMessage(tNEWSELECTED);
而教師端接收WinMessage訊息,也只能放在這個WinMessage_CopyDataMessageReceive的方法去接受處理,不在Message中去處理
private void WinMessage_CopyDataMessageReceive(System.Object sender, CopyDataMessage m)
{
try
{
System.String strData = System.String.Empty;
System.String strClientOpenIntercom = System.String.Empty;
if (m.Message == tINTERCOMSTART)
{
strData = m.Data;
System.Diagnostics.Trace.WriteLine("InterCome.StartMsg:" + strData);
blnIntercomState = true;
intIntercomHwnd = int.Parse(strData);
}else if(m.Message == tNEWSELECTED)
{
strData = m.Data;
string[] SelectedSeatArray = strData.Split('|');
strClientOpenIntercom = "PT_Frame_OpenIntercom";
foreach(string str in SelectedSeatArray)
{
//將訊息傳送給每一個Actived目錄中的成員即可
this.SendToClientMessage(this,0, strClientOpenIntercom);
System.Diagnostics.Trace.WriteLine("SelectedSeatArray:"+str);
}
}
}
catch (System.Exception err)
{
WriteErrorMessage("WinMessage_CopyDataMessageReceive:" + err.Message);
}
}
4>接收訊息的控制代碼
即使注意到上述的問題所在,但是仍有個問題,那就是傳入的控制代碼。需要注意的是這個clsWinMessage類是完全獨立的,也就是說它接收訊息碼其實是有一個自己的隱形窗體在進行,而不是通過整個軟體的母窗體來進行,這也是我們需要注意的一點,這個一開始我沒注意到
this.InitWinMessage();
if (this.clsWinMessage != null)
{
intHandle = this.clsWinMessage.WinHandle;
}
可以看上述程式碼,每次啟動這個軟體的時候就會Init一個 winmessage,當我們關閉的時候也應該要將其Dispose掉(暫時沒做,之後會考慮),也就是說每次啟動關閉軟體的時候,傳入的控制代碼都是可能不同的,和當前的母窗體並沒有什麼大關係。
2.exe接收框架端的訊息
到這裡了其實就簡單了,向軟體傳送訊息,其實就是直接呼叫WMUser的SendCopyDataMessage方法直接傳送就可以了,不需要別的,就是在這裡其實不需要額外宣告編碼格式,之前我宣告的是unicode,但是這樣傳過來的引數反而是亂碼了,是真的傻逼。
關於如何將座位更新
現在我們假設我們可以在主框架中將所有的學生座位資訊打包好,然後在軟體中也提供了根據使用者座位資訊重新整理的介面---
那麼這個時候問題來了,我們怎麼知道使用者的狀態更新?
這時候就要用到委託:
Seats.ValueChanged += new Seats.ValueChangedEventHandler(this.SeatValueChanged);
在這個軟體啟動的時候新增這個委託,當Seats中的委託ValueChanged觸發的時候,觸發當前類下的函式SeatValueChanged,並在這個函式中組裝所有的座次資訊,將其傳送給exe,注:控制代碼是由軟體啟動後自己傳送給教師端的,所以只有當教師端接收到exe傳送過來的訊息之後才能感知到框架下學生座位資訊變化的效果(不過時間很短就是了)。
關於教師端如何向學生端傳送訊息
這個和之前的 有點不一樣,區別在於要知道委託是從哪來傳來的,最開始我以為就和WinMessage和 座位的ValueChanged這樣的委託訊息一樣,宣告一下就好了,這還是源自對委託的認識不足。而事實上,WinMessage來自WMUser,而ValueChanged來自Seats,但是我自己定義的這個SendToClientMessage呢?並不是某個類延伸出來的方法,只是單獨的聲明瞭,這就不對了,這樣只能讓外部來呼叫這個ClsTools的委託,而不是從某個類中延伸出來的委託,這就是問題所在。
所以我根據一個向所有學生群發的方法出發,向上去尋找:
這時候才發現,其實我們應該呼叫的方法,原來是ClsEnTeaching中的Tool_SendMessageToClient方法,而這個所謂的SendToClientMessage方法,其實也就是呼叫了CallMethod
這樣真相大白,其實我們需要的就是clsTools中的SendMessageToClient其實能委託ClsEnTeaching中的Tool_SendMessageToClient方法,並通過其呼叫NetInterop類,來向學生端傳送訊號,而不是從某個物件中衍生出一個方法或者物件。
所以我在初始化整個工具類InitTools()處添加了一行
this.clsTools.SendToClientMessage += new ClsTools.SendToClientMessageEventHandler(this.Tool_SendMessageToClient);
這樣就可以讓我們在clsTools中呼叫方法的訊號發到ClsEnTeaching中,並通過NetInterop傳送給學生端,而不是訊號無端消失。