1. 程式人生 > 其它 >C#關於委託的一些事,開發日誌

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傳送給學生端,而不是訊號無端消失。