如何处理泛型类型事件的订阅?

时间:2009-06-22 15:36:43

标签: c# generics events

我正在开发一个WinForms应用程序。在很多情况下,我需要在小型查看区域中显示新的屏幕,因此我使用的是Panels。基本上,我从面板继承,从面板中显示我需要的信息的任何属性,任何需要在面板中显示信息的事件都发生在它自己的代码背后。这些面板将始终完全停靠在一个父控件(主窗体)上。

我创建了一个显示这些面板的通用方法:

private static T ShowPanel<T>(Control parent, params object[] parameters) where T: Panel
{
    T panelToShow = (T)Activator.CreateInstance(typeof(T), parameters);

    parent.Controls.Add(panelToShow);

    panelToShow.Dock = DockStyle.Fill;
    panelToShow.BringToFront();
    panelToShow.Show();

    return panelToShow;
}

我正在使用它,但我知道必须有一种更好的方法来处理事件订阅。

private void ShellButton_Click(object sender, EventArgs e)
{
    if (CurrentSelectedSite == null)
    {
        AlertSelectSite();
        return;
    }

    SystemViewPanel panel = ShowPanel<SystemViewPanel>(this, CurrentSelectedSite.Systems);

    panel.SystemsListbox.DoubleClick += new EventHandler(ShellAccessSystemSelected);
}

有许多按钮可以执行不同的操作。如果站点具有多个系统,则会显示SystemViewPanel以选择要对其执行操作的系统。按照我现在的方式,我必须订阅一个不同的命名事件处理程序来指定我想要执行的操作,因此我的主窗体代码变得混乱(即ShellAccessSystemSelected,DownloadFileSystemSelected,ViewSystemSystemSelected等)。

修改

我认为事情可以推广,因为我将展示一个允许用户为我的应用程序中的大多数工具选择系统的面板。但是,对于每个不同的工具,将根据启动SystemViewPanel创建的工具执行不同的操作。

2 个答案:

答案 0 :(得分:0)

您可能希望通过推入自己的基类来概括Panel的行为,该基类公开您通常期望从面板中发生的那些事件。这可能是一个开始。 您还可以直接在构造函数中提供某些事件的处理程序......

SystemViewPanel panel = ShowPanel<SystemViewPanel>(
  this, 
  panel => {
    //Do the stuff you would do on click event, panel impl ensures this 
    // gets called at the right moment
  },
  CurrentSelectedSite.Systems);

因评论而更新:

您需要知道lambda需要什么参数。如果您对标准事件处理程序的签名感到满意,则可以使用“EventHandler”作为参数类型。然后你的lambda看起来像

(sender, args) => //Do stuff

除此之外,如果你的被调用函数只需要面板,你可以使用一个 Action&lt; T [,T2等]&gt; 委托,它们允许你用参数表达方法但没有返回值。

如果您想从被调用的lmbda返回一些值,然后由调用者(您的面板)处理,您可以使用任何 Func&lt; T,[T1等]&gt; < / strong>代表

答案 1 :(得分:0)

这个SystemViewPanel非常微不足道。它所做的只是提供一种选择系统名称的方法,这就是为什么我不想仅仅为了选择系统名称而拥有10种不同的事件订阅方法。这就是我想出的:

private void ShellButton_Click(object sender, EventArgs e)
{
    if (CurrentSelectedSite == null)
    {
        AlertSelectSite();
        return;
    }

    SystemViewPanel systemSelectPanel = ShowPanel<SystemViewPanel>(this, CurrentSelectedSite.Systems);

    /*
    I decided that this since this panel is used quickly, I'd rather 
    keep the flow of what's happening in the same place. The line
    above shows the panel, a double click later, and I'm back to doing
    what the ShellButton does. 

    I've exposed a SystemSelected event, which just wraps the panel's 
    SystemsListBox.DoubleClick event.*/


    systemSelectPanel.SystemSelected += delegate(object s, EventArgs eArgs)
    {
        ListBox p = (ListBox)s;
        System system = (System)p.SelectedItem;

        if (system != null)
        {
            Process shell = new Process();
            shell.StartInfo = new ProcessStartInfo("cmd.exe",
            String.Format(@"/K psexec.exe \\{0} -u {1} -p {2} cmd.exe", system.IpAddress, CurrentSelectedSite.RemoteAccess.UserName, CurrentSelectedSite.RemoteAccess.DecryptedPassword));
            shell.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
            shell.StartInfo.UseShellExecute = true;


            shell.Start();
        }

        this.Controls.Remove(p.Parent);
        p.Parent.Dispose();
        this.SearchPanel.BringToFront();
    };
}