views:

136

answers:

2

I am working on a WinForms app. There are many instances where I need to display a new screen in the small viewing area, so I am using Panels. Basically, I inherit from panel, expose any properties for the information I need from the panel, anything that needs to happen to display information in the panel happens in it's own code behind. These panels will always be docked full on one parent control (the main form).

I created a generic method to display these 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;
}

I am using it like this, but I know there has to be a better way to handler the event subscription.

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

There are a bunch of buttons that do different things. If a Site has multiple systems, the SystemViewPanel is shown to select which system to perform the action on. With the way I have it now, I have to subscribe to a different named event handler to specify which action I want to perform, so my main form's code is getting cluttered (i.e. ShellAccessSystemSelected, DownloadFileSystemSelected, ViewSystemSystemSelected, etc).

Edit

I think things can be generalized in the fact that I will be showing a panel that allows a user to select a system for most of the tools in my application. However, for each different tool, a different action will be required based on the tool that initiated the SystemViewPanel creation.

A: 

You may want to generalize the Panel's behaviour by pushing in your own base class that exposes those events that you commonly expect from your panel. This could be a start. You could also provide handlers of certain happenings directly in the constructor...

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);

Update due to comment:

You need to know what parameters your lambda requires. If you are happy with the signature of the standard event handler, you can just use "EventHandler" as parameter type. Then your lambda would looks like

(sender, args) => //Do stuff

Apart from that, if your called function just requires the panel, you would use one of the Action<T[,T2,etc.]> delegates that allows you to express methods with parameters but no return values.

If you would like to return some value from your called lmbda that would then get processed by the caller (your panel), you would use any of the Func<T,[T1,etc.]> delegates

flq
Ok, I see where you're going. What kind of argument would I pass to that (I'm still not great on the Func<something, SystemViewPanel> type argument).
scottm
@Frank, I posted my own answer, but your input gave me the idea. Feel free to let the SO democracy determine if it's an acceptable approach.
scottm
A: 

This SystemViewPanel is pretty insignificant. All it does is provide a way to select a system name, that's why I didn't want to have 10 different event subscription methods just to pick a system name. Here's what I came up with:

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();
    };
}
scottm