簡體   English   中英

如何在循環中添加事件處理程序?

[英]How do I add event handlers in a loop?

我正在嘗試在C#/。NET中創建任務欄圖標,到目前為止,我的代碼可以正常工作:

        ....

        Icon i = new Icon("favicon.ico");
        ContextMenu cm = new ContextMenu();
        ni.Icon = i;            

        MenuItem delMi = new MenuItem("Delete stuff");
        MenuItem closeMi = new MenuItem("Close");
        MenuItem testMi = new MenuItem("Test");

        cm.MenuItems.Add(testMi);
        cm.MenuItems.Add(delMi);
        cm.MenuItems.Add(closeMi);

        testMi.Click += TestMi_Click;
        delMi.Click += DelMi_Click;
        closeMi.Click += CloseMi_Click;

        ni.ContextMenu = cm;
    }

    private void TestMi_Click(object sender, EventArgs e)
    {
        // Test event here
    }

    private void CloseMi_Click(object sender, EventArgs e)
    {
        // Close event here
    }

    private void DelMi_Click(object sender, EventArgs e)
    {
        // Delete event here
    }

但是我試圖通過具有返回MenuItem實例數組的函數,並具有將其添加到ContextMenu的循環來分離代碼,但是我不確定如何將click事件處理程序添加到ContextMenu項中的MenuItem實例。環:

        ....
        Icon i = new Icon("favicon.ico");
        ContextMenu cm = new ContextMenu();
        ni.Icon = i;            

        MenuItem[] miArray = getArrayMI();

        foreach(MenuItem mi in miArray)
        {
            cm.MenuItems.Add(mi);

            //Not sure what to do here
            mi.Click += mi
        }

        // How do I put this section into the loop instead 
        // of adding the event handlers one by one?  
        testMi.Click += TestMi_Click;
        delMi.Click += DelMi_Click;
        closeMi.Click += CloseMi_Click;

        ni.ContextMenu = cm;
    }

    private MenuItem[] getArrayMI( )
    {
        MenuItem[] miArray = { new MenuItem("Delete stuff"), new MenuItem("Close"), new MenuItem("Test") };
        return miArray;
    }

    private void TestMi_Click(object sender, EventArgs e)
    {
        // Test event here
    }

    private void CloseMi_Click(object sender, EventArgs e)
    {
        // Close event here
    }

    private void DelMi_Click(object sender, EventArgs e)
    {
        // Delete event here
    }

我唯一想到的就是要做這樣的事情:

    foreach(MenuItem mi in miArray)
    {
        cm.MenuItems.Add(mi);

        mi.Click += mi.ToString() + "_Click";
    }

我認為抽象您的原始代碼不是一個壞主意,但我建議您以另一種方式看待抽象。 我建議實現視圖與模型的某種分離-MVC,MVP,MVVM等。通過這種方式,將單擊發生時實際發生的代碼從視圖中抽象到另一層代碼中。

例如,考慮這樣的事情(在沒有IDE的情況下編寫,因此請原諒輸入錯誤):

public interface IContextAction
{
    string DisplayName { get; }
    Action Invoke { get; }
}


public class WindowViewModel
{
    public IEnumerable<IContextAction> ContextActions { get; private set; }
    /* ... */
}


    /* ... */
    ContextMenu cm = new ContextMenu();
    foreach (IContextAction action in viewModel.ContextActions)
    { 
        MenuItem item = new MenuItem(action.DisplayName);
        cm.MenuItems.Add(item);
        item.Click += (sender,args) => action.Invoke();
    }

我同意這樣的意見,即至少對於您發布的代碼示例,不需要“改進”代碼。 這已經是實現該特定邏輯的合理方法。 此外,我傾向於避免依賴命名約定將特定的代碼綁定到特定的運行時對象。 這樣做會導致實現方式脆弱(即容易損壞),並限制了您更改代碼名稱的能力(例如,解決了一些不相關的命名方面,否則將提供更易讀的代碼)。

就是說,如果您真的想這樣做,可以。 這是一個最小,完整且可驗證的代碼示例 ,該示例說明如何基於對象的名稱為事件處理程序創建委托實例,以及訂閱對象的事件:

class Program
{
    static void Main(string[] args)
    {
        Class[] classInstances =
        {
            new Class("A"),
            new Class("B"),
            new Class("C"),
        };

        foreach (Class c in classInstances)
        {
            string methodName = c.Name + "_Event";
            MethodInfo mi = typeof(Program).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static);
            EventHandler handler = (EventHandler)Delegate.CreateDelegate(typeof(EventHandler), mi);

            c.Event += handler;
        }

        foreach (Class c in classInstances)
        {
            c.RaiseEvent();
        }
    }

    static void A_Event(object sender, EventArgs e) { Console.WriteLine("A_Event handler"); }
    static void B_Event(object sender, EventArgs e) { Console.WriteLine("B_Event handler"); }
    static void C_Event(object sender, EventArgs e) { Console.WriteLine("C_Event handler"); }
}

class Class
{
    public string Name { get; }

    public Class(string name)
    {
        Name = name;
    }

    public event EventHandler Event;

    public void RaiseEvent()
    {
        Event?.Invoke(this, EventArgs.Empty);
    }
}

就個人而言,我更喜歡一種更明確的方法。 也就是說,如果確實需要以抽象的方式封裝處理程序對對象的分配,則可以將其放入顯式代碼中。 例如,提供一個事件處理程序方法來訂閱所有控件,然后按名稱將該方法分派到適當的方法:

static void Main(string[] args)
{
    Class[] classInstances =
    {
        new Class("A"),
        new Class("B"),
        new Class("C"),
    };

    foreach (Class c in classInstances)
    {
        c.Event += All_Event;
    }

    foreach (Class c in classInstances)
    {
        c.RaiseEvent();
    }
}

static void All_Event(object sender, EventArgs e)
{
    switch (((Class)sender).Name)
    {
        case "A":
            A_Event(sender, e);
            break;
        case "B":
            B_Event(sender, e);
            break;
        case "C":
            C_Event(sender, e);
            break;
    }
}

另外,您可以使用字典來表示從名稱到方法的映射:

static void Main(string[] args)
{
    Class[] classInstances =
    {
        new Class("A"),
        new Class("B"),
        new Class("C"),
    };

    Dictionary<string, EventHandler> nameToHandler = new Dictionary<string, EventHandler>()
    {
        { "A", A_Event },
        { "B", B_Event },
        { "C", C_Event },
    };

    foreach (Class c in classInstances)
    {
        c.Event += nameToHandler[c.Name];
    }

    foreach (Class c in classInstances)
    {
        c.RaiseEvent();
    }
}

在這兩個示例中,您都不需要保存任何類型(基於switch的方法特別冗長),但是它確實將對象與處理者的關系移到了自己的代碼區域,從而使維護起來更加容易無需處理事件訂閱本身。

如果您真的想要一種完全動態的,基於反射的方法,那么我會選擇一種更依賴方法名稱的更明確且更不易損壞的方法。 例如,您可以為事件處理程序方法創建一個自定義屬性,用於定義什么方法與哪個對象一起使用。 這提供了相當少的鍵入量,但是將方法名稱與映射斷開了連接,因此您可以繼續將代碼重構為您的內在內容,而不必擔心事件處理方面。

看起來像這樣:

class Program
{
    static void Main(string[] args)
    {
        Class[] classInstances =
        {
            new Class("A"),
            new Class("B"),
            new Class("C"),
        };

        Dictionary<string, EventHandler> nameToHandler =
                (from mi in typeof(Program).GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
                 let attribute = (Handler)mi.GetCustomAttribute(typeof(Handler))
                 where attribute != null
                 select new { attribute.Target, mi })
             .ToDictionary(x => x.Target, x => (EventHandler)Delegate.CreateDelegate(typeof(EventHandler), x.mi));

        foreach (Class c in classInstances)
        {
            c.Event += nameToHandler[c.Name];
        }

        foreach (Class c in classInstances)
        {
            c.RaiseEvent();
        }
    }

    [Handler("A")]
    static void A_Event(object sender, EventArgs e) { Console.WriteLine("A_Event handler"); }
    [Handler("B")]
    static void B_Event(object sender, EventArgs e) { Console.WriteLine("B_Event handler"); }
    [Handler("C")]
    static void C_Event(object sender, EventArgs e) { Console.WriteLine("C_Event handler"); }
}

class Handler : Attribute
{
    public string Target { get; }

    public Handler(string target)
    {
        Target = target;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM