Как унифицировать / упростить этот код - обработка / делегирование событий? - PullRequest
0 голосов
/ 09 ноября 2009

У меня есть объекты процессов, которые отслеживаются из двух разных представлений. Windows.Forms.ListView (на самом деле производный класс) и средство просмотра графиков (на основе автоматического графического макета Microsoft Research). У каждого есть контекстное меню, в котором могут быть активированы похожие события. В то время как представление списка может иметь несколько вариантов выбора, я не разрешаю его в представлении графика.

Это то, что у меня сейчас есть:

    private void ctxgrphAddBreakpoint_Click(object sender, EventArgs e)
    {
        Process p = GetProcess(viewer);
        if (p != null)
        {
            p.AddBreakpoint();
            BeginRefresh(false, false);
        }
    }

    private void ctxgrphRemoveBreakpoint_Click(object sender, EventArgs e)
    {
        Process p = GetProcess(viewer);
        if (p != null)
        {
            p.RemoveBreakpoint();
            BeginRefresh(false, false);
        }
    }

    private void ctxlistAddBreakpoint_Click(object sender, EventArgs e)
    {
        foreach (Process p in lvwProcessList.SelectedProcesses())
        {
            p.AddBreakpoint();
        }
        BeginRefresh(false, false);
    }

    private void ctxlistRemoveBreakpoint_Click(object sender, EventArgs e)
    {
        foreach (Process p in lvwProcessList.SelectedProcesses())
        {
            p.RemoveBreakpoint();
        }
        BeginRefresh(false, false);
    }

Я хотел бы объединить два контекстных меню в одно, а обработку событий - в одно из следующих:

    private void ctxlistAction_Click(object sender, EventArgs e)
    {
        // I can unify the Viewer and ListView and implement some common interface,
        // so I'm pretty sure I can handle this part
        foreach (Process p in UIView.SelectedProcesses())
        {
            p.Action(); // What is the best way to handle this?
        }
        BeginRefresh(false, false);
    }

Как мне туда добраться?

Ответы [ 4 ]

3 голосов
/ 09 ноября 2009

Шаблон посетителя?

2 голосов
/ 09 ноября 2009

Найдите назначение события (... += new System.EventHandler(ctxlistAction_Click);) в файле * .Designer.cs и заставьте их указывать на одну и ту же функцию.

0 голосов
/ 09 ноября 2009

Во-первых, попробуйте рефакторинг общей функциональности и использование делегатов. (пожалуйста, извините за мой плохой выбор имен, таких как "DoListAction"):

readonly Action<Process> removeBreakpoint = p => p.RemoveBreakpoint();
readonly Action<Process> addBreakpoint = p => p.AddBreakpoint(); 

void DoSingleAction(Action<Process> action)
{
     var p = GetProcess(viewer);
     if(p != null)
     {
         action(p); //invokes the action
         BeginRefresh(null,null);
     }
}

void DoListAction(Action<Process> action)
{
    lvwProcessList.ForEach(action);
    BeginRefresh(false, false);
}



private void ctxgrphAddBreakpoint_Click(object sender, EventArgs e)
{
    DoSingleAction(addBreakpoint);
}

private void ctxgrphRemoveBreakpoint_Click(object sender, EventArgs e)
{
    DoSingleAction(removeBreakpoint);
}

private void ctxlistAddBreakpoint_Click(object sender, EventArgs e)
{
    DoListAction(addBreakpoint);
}

private void ctxlistRemoveBreakpoint_Click(object sender, EventArgs e)
{
    DoListAction(removeBreakpoint);
}

тогда вы можете объединить DoProcessAction и DoListAction:

void DoAction(object sender, Action<Process>)
{
     if(sender is ListView)
     {
         lvwProcessList.ForEach(action);
         BeginRefresh(false, false);
     }
     else if (sender is GraphView)
     {
         var p = GetProcess(viewer);
         if(p != null)
         {
             action(p); //invokes the action
             BeginRefresh(null,null);
         }
     }
     else {throw new Exception("sender is not ListView or GraphView");}
}

//and update all the handlers to use this, and delete DoSingleAction and DoListAction:
private void ctxgrphAddBreakpoint_Click(object sender, EventArgs e)
{
    DoAction(sender, addBreakpoint);
}
//etc.

Независимо от того, что в каждом обработчике событий, я думаю, вам нужно будет указать, какое действие предпринять. Я не уверен, что методы наследования или расширения действительно будут вашим другом в этом случае, но вот как это будет выглядеть, используя методы расширения:

//these two are in a separate static class
public static InvokeAction(this ListView listView, Action<Process> action)
{ ... }
public static InvokeAction(this GraphView listView, Action<Process> action)
{ ... }

private void handler(object sender, Action<Process> action)
{ 
    var lv = sender as ListView;
    var gv = sender as GraphVeiw;    
    lv.InvokeAction(action); //(need to check for null first)
    gv.InvokeAction(action);
    if(lv == null && gv == null) {throw new Exception("sender is not ListView or GraphView");}
}

//then update all the handlers:
private void ctxgrphAddBreakpoint_Click(object sender, EventArgs e)
{
    handler(sender, addBreakpoint);
}


C # 2 / .NET 2.0

Чтобы сделать это в C # 2 (VS2005), первый способ использования делегатов все еще будет работать, вы просто не можете иметь лямбды. (2.0 имеет делегата Action<T>). Просто измените лямбда-функции на функции. Все остальное будет работать правильно.

void removeBreakpoint(Process p) { p.RemoveBreakpoint(); }
void addBreakpoint(Process p) { p.AddBreakpoint(); }

//you could also do this, but it's overkill:
readonly Action<Process> removeBreakpoint = delegate(Process p) { p.RemoveBreakpoint(); };
readonly Action<Process> addBreakpoint = delegate(Process p) { p.AddBreakpoint(); };
0 голосов
/ 09 ноября 2009

Разве вы не можете просто привести отправителя к своему интерфейсу?

 private void ctxlistAction_Click(object sender, EventArgs e)
 {
      UIView view = sender as UIView;

      if (view != null)
      {

         // I can unify the Viewer and ListView and implement some common interface,
         // so I'm pretty sure I can handle this part
         foreach (Process p in view.SelectedProcesses())
         {
             p.Action(); // What is the best way to handle this?
         }
         BeginRefresh(false, false);
      }
  }
...