C#中動態訂閱控制項中任意事件的方法

來源:互聯網
上載者:User

這個題目想了半天,不太好用一句話描述。這樣,舉個簡單的應用情境:在用Windows Forms製作嚮導程式的時候,通常會有“上一步”、“下一步”這樣的按鈕。假設現在需要做一個通用的“嚮導製作架構”,那麼我們就需要在這個“嚮導製作架構”中,對“上一步”、“下一步”這些按鈕是否可用(是否Enabled)進行控制。而控制條件是由開發人員在實際使用“嚮導製作架構”進行開發時確定的。比如:只有在當前嚮導頁面的某個文字框裡被輸入了字串以後,“下一步”才可用;或者只有在某個按鈕被按下的時候,“下一步”才可用。於是,我們的“嚮導製作架構”要能夠允許開發人員來確定,當觸發什麼事情的時候,“下一步”才可用。

 

我們先從特例入手,假設嚮導頁面上只有一個按鈕叫btn,只有點擊btn以後,“下一步”才能夠被點擊。編程上很容易實現這個效果:直接在表單上訂閱btn的Click事件,在Click事件裡,寫下“btnNext.Enabled = true;"這樣一句話。

 

現在問題來了:我們需要為開發人員提供一個“嚮導製作架構”,也就是說,這個架構根本無法預測開發人員需要訂閱哪些控制項的哪些事件,只能留出一個介面,讓開發人員自己調用這個介面實現事件的動態註冊。Windows Forms提供的控制項類型多種多樣,而且不同的事件有著不同的函數簽名(也就是委託,比如Click事件和MouseDown事件就是用的兩個不同的委託),如何讓我們的架構能夠支援任意的控制項,並在任意控制項的任意事件發生時,調用“btnNext.Enabled = true;”這條語句,使得“下一步”按鈕可用呢?

 

要實現這樣的功能,我們需要用到反射。首先,定義一個泛型方法,在這個方法裡,我們直接對btnNext進行設定,如下:

 

protected void DoTrigger<T>(object sender, T eventArgs)    where T : System.EventArgs{    this.btnNext.Enabled = true;}

然後,根據使用者給定的控制項執行個體和事件名稱,獲得EventInfo對象。這個EventInfo裡有個重要的屬性,就是EventHandlerType,它就是定義event所使用的委託類型。為了使開發人員指定的事件能夠綁定到上面的DoTrigger函數上,我們需要知道那個EventArgs的具體類型,下面的代碼可以將某個委託類型的所有參數類型全部讀取出來:

 

 

private Type[] GetDelegateParameterTypes(Type d){    if (d.BaseType != typeof(MulticastDelegate))    {        throw new InvalidOperationException("Not a delegate.");    }    MethodInfo invoke = d.GetMethod("Invoke");    if (invoke == null)    {        throw new InvalidOperationException("Not a delegate.");    }    ParameterInfo[] parameters = invoke.GetParameters();    Type[] typeParameters = new Type[parameters.Length];    for (int i = 0; i < parameters.Length; i++)    {        typeParameters[i] = parameters[i].ParameterType;    }    return typeParameters;}

 

注意:如果是標準的事件委託,一般情況下都是第一個參數為object類型,第二個參數為EventArgs綁定類型,無傳回值的簽名格式。換句話說,一般情況下,上面的這段代碼返回的數組包含兩個對象:object和一個繼承於EventArgs(或者是EventArgs本身)的類型。在這裡,我們取數組裡的第二個成員。

好了,現在通過反射,獲得DoTrigger方法的MethodInfo,並通過MethodInfo.MakeGenericMethod方法,將上一步獲得的EventArgs類型綁定到DoTrigger方法上,並使用Delegate.CreateDelegate產生Event Handler:

private Delegate GetEventHandler(Control control, EventInfo eventInfo){    try    {        if (eventInfo == null)            throw new Exception(string.Format("Unable to find an event named '{0}' on the control '{1}'.",                eventInfo.Name, control));        Type[] delegateParameters = this.GetDelegateParameterTypes(eventInfo.EventHandlerType);        if (delegateParameters == null ||            delegateParameters.Length != 2)            throw new InvalidOperationException(string.Format("Event '{0}' is not valid.", eventInfo.Name));        Type eventArgsType = delegateParameters[1];        MethodInfo doEventMethod = this.GetType().GetMethod("DoTrigger",             BindingFlags.NonPublic | BindingFlags.Instance);        if (doEventMethod == null)            throw new Exception("DoTrigger method doesn't exist.");        if (!doEventMethod.IsGenericMethod)            throw new Exception("DoTrigger method is not a generic method.");        MethodInfo concreteDoEventMethod = doEventMethod.MakeGenericMethod(eventArgsType);        Delegate d = Delegate.CreateDelegate(eventInfo.EventHandlerType, this, concreteDoEventMethod);        return d;    }    catch    {        throw;    }}

通過向上面的方法傳入一個任意控制項和該控制項中任意事件的Method Info,即可獲得處理該事件的Event Handler,也就是由DoTrigger泛型方法來處理該指定的事件:

public void RegisterTrigger(Control control, string eventName){    try    {        EventInfo eventInfo = control.GetType().GetEvent(eventName,             BindingFlags.Public | BindingFlags.Instance);        Delegate d = this.GetEventHandler(control, eventInfo);        eventInfo.AddEventHandler(control, d);    }    catch    {        throw;    }}

最後,在使用的時候,代碼就簡單啦:

private void Form_Load (object sender, System.EventArgs e){    this.RegisterTrigger (btn, "Click");    this.RegisterTrigger (textBox1, "TextChanged");    this.RegisterTrigger (textBox2, "MouseDown");}

現在,不管是btn被單擊,還是textBox1裡的文字被更改,還是在textBox2滑鼠按鍵被按下,都會直接觸發DoTrigger函數,進而使得“下一步”按鈕變得可用(Enabled為true)。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.