2016-11-13 9 views
0

私はNETのC#/にトレイアイコンを作成しようとしていますし、これまでのところ、私は働くこのコードを持っている:。ループ内にイベントハンドラを追加するにはどうすればよいですか?

 .... 

     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に追加するループを持つが、私はループ内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"; 
    } 
+2

これはoversimplifyingの一例です。あなたのコードをスリム化しようとすると、あなたはそれを過度に複雑にしています。元のコードは問題ありませんでした。 – Abion47

+0

私は自分のコードをスリム化しようとしているとは言いませんでした。私は単にこれがどのように達成されるのか不思議です。 – glen4096

答えて

1

投稿したコード例では、少なくともコードを改善する必要はないという意見に同意します。この特定のロジックを実装するのは合理的な方法です。さらに、特定のコードを特定の実行時オブジェクトに結びつけるために命名規則に頼るのを避けることが私の好みです。そうしないと、実装が壊れやすくなり、コードの名前を変更する権限が制限されます。

あなたが本当にこれをしたい場合は、あなたがすることができます。ここでは、オブジェクトの名前に基づいて、イベントハンドラのデリゲートのインスタンスを作成し、オブジェクトのイベントをサブスクライブする方法を示しMinimal, Complete, and Verifiable code example次のとおりです。個人的に

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; 
    } 
} 
1

を私はそれが抽象元のコードに悪いアイデアだとは思わないが、私は別の方法で抽象化を見てお勧めしたいです。 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(); 
    } 
関連する問題