Method with generic and multiple parameters

Viewed 156

I want to enable/disable controls in a Windows Forms application according to the user privileges.

Initially I thought of writing a method in each form class that would check the user credentials and then enable/disable its controls. But then I realized I could (maybe) create a static class method which would take the form as a parameter and do the job.

So I started writing it, presuming that sometimes I would like to enable the controls of just one or two panels, instead of the whole form. So, I need the parameters to be:

  • a varying number of panels and/or
  • a form class.

My difficulties with this task is that I'm getting an error trying to make the panels argument varying, and I have no idea how to set a parameter that could take any form class. All my form classes obviously inherits from Form generic class, but I don't know how to apply this.

Here's what I got:

public static void Enable(TableLayoutPanel[] containers = null)
    {
        if (MyOF.isEnabled)
        {
            return;
        }
        else
        {
            try
            {
                foreach (TableLayoutPanel table in containers)
                {
                    foreach (Control control in table.Controls)
                    {
                        control.Enabled = false;
                    }
                }
            }
            catch (NullReferenceException)
            {
            }
        }
    }
3 Answers

If we remember that the Form class derives from Control (indirectly, by deriving from ContainerControl which derives from ScrollableControl, which derives from Control), and the Enabled property belongs to the Control class, we can write a method that will enable any control's children (including the Form or TableLayoutPanel controls), since the Controls collection also belongs to the Control class:

public static void EnableChildren(Control control, bool enabled = true)
{
    foreach (Control child in control.Controls)
    {
        child.Enabled = enabled;
    }
}

And then if we also want to be able to use this with a collection of controls (as in your example), we can write an overload that takes a collection:

public static void EnableChildren(IEnumerable<Control> controls = null, 
    bool enabled = true)
{
    if (controls == null) return;

    foreach (var control in controls)
    {
        EnableChildren(control, enabled);
    }
}

Now we can use this with a Form or a collection of TableLayoutPanel controls (or any control that has controls in it's Controls collection).

Examples of usage:

var myForm = new Form1();

EnableChildren(this);     // 'this' is the current form
EnableChildren(myForm);   // a separate instance of a form control
EnableChildren(tableLayoutPanel1, false);  // A single TableLayoutPanel control

var tableLayoutPanels = new [] {tableLayoutPanel1, tableLayoutPanel2, tableLayoutPanel3};
EnableChildren(tableLayoutPanels);  // An array of tableLayoutPanel controls

One of the simple ways I can think about what you are trying to do, is this. Let me get away for a sec here. I worked on projects where all form controls were built from Metadata. And meta came with licensing info. So, when control was placed where it should, it also was disabled or set read-only based on Metadata but the whole feature would be hidden if licensing info was restricting access to it. Coming back to your approach, this is not a bad approach and I see that this is can be done. And it can be done in 2 ways, (quickly from my head).

  1. Use user controls as surface for the components you want to enable/disable. Create an interface
public interface IDisableableControl // make your fine name, no methods needed - marker interface
 . .  . . . 
public class MyFineUserControl : UserControl, IDisableableControl 

And in your static method that you're going to write pass the form, and find all controls that implement this interface and work them the way you want.

2. Similarly, you can use property Tag, which is available on each control. With that, you can actually set your complex security object that can come from DB-stored metadata and then you evaluate this object stored in Tag to apply your configuration

Your method needs to be recursive

internal static void SetAllControls(Control parent)
{
    // Do something with control, for example parent.Enabled = false
    if (parent is IDisableableControl)
    {
       // here you use your logic, evaluate your parent you're dialing with and
       // enable/disable correspondingly 
       parent.Enabled = false;
       return;
    }
    foreach(var c in parent.Controls)
        SetAllControls(c);
} 

In real life, your TOP parent will be a form and will not need to be disabled, but it's certain children will. In fact, most of the time, once you found a UserControl which implements IDisableableControl that should be end of line, means, you don't need to go into children controls as they all sit on this parent and all will be disabled

I manage to accomplish what I was trying to do with the code below, which is pretty much a blend of all the helpful answers I got:

public static void EnableContainer(params Control[] containers)
    {
        if(containers.Count() == 0) { return; }
        if (MyOF.isEnabled)
        {
            return;
        }
        else
        {
            try
            {
                foreach (var container in containers)
                {
                    foreach (Control control in container.Controls)
                    {
                        control.Enabled = false;
                    }
                }
            }
            catch (NullReferenceException)
            {
            }
        }
    }
    public static void EnableForm<form>(form f) where form : Form
    {
        if (MyOF.isEnabled)
        {
            return;
        }
        else
        {
            foreach(Control control in f.Controls)
            {
                control.Enabled = false;
            }
        }
    }

The community is welcome to suggest improvements as I am far from being a professional programmer. Thanks everyone once again.

Related