[英]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.