18

My form has hundreds of controls: menus, panels, splitters, labels, text boxes, you name it.

Is there a way to disable every control except for a single button?

The reason why the button is significant is because I can't use a method that disables the window or something because one control still needs to be usable.

Ryan Peschel
  • 11,087
  • 19
  • 74
  • 136
  • 1
    Should you put the button in a separate form? – SLaks Nov 19 '12 at 00:28
  • Can't you just loop through all of the controls on the form, setting the Enabled property on each? In your loop, ignore the button by using its ID/name. Or, go ahead and disable everything in the loop, then immediately after that enable the button. – Bob Horn Nov 19 '12 at 00:28

5 Answers5

32

You can do a recursive call to disable all of the controls involved. Then you have to enable your button and any parent containers.

 private void Form1_Load(object sender, EventArgs e) {
        DisableControls(this);
        EnableControls(Button1);
    }

    private void DisableControls(Control con) {
        foreach (Control c in con.Controls) {
            DisableControls(c);
        }
        con.Enabled = false;
    }

    private void EnableControls(Control con) {
        if (con != null) {
            con.Enabled = true;
            EnableControls(con.Parent);
        }
    }
pinkfloydx33
  • 11,863
  • 3
  • 46
  • 63
  • 1
    Why would you include the ref keyword there? – Bob Horn Nov 19 '12 at 00:43
  • 1
    I wouldn't. I was on the phone while typing it and for some reason it just came out – pinkfloydx33 Nov 19 '12 at 00:53
  • Ha. Well done. I like how you made the methods not know or care about any specific controls. Newbies overlook things like that. – Bob Horn Nov 19 '12 at 01:00
  • @BobHorn - good point but the code doesn't not account for all control properties. It would break if used on a custom control that doesn't support `Enabled` – Jeremy Thompson Nov 19 '12 at 02:05
  • Why do call the function recursively? I just tried almost the same function where I disable a controls in the «foreach» instead of recursive call there, and all a controls was disabled successfully. – Hi-Angel Nov 18 '14 at 14:08
  • 4
    @Hi-Angel: the recursive calls are necessary because the parent/child relationships in Winforms are recursive. In many forms, there is only one level of descendants and so recursion wouldn't be necessary, but a good, general-purpose solution needs to be so that it will work in all cases, not just the one you tested. – Peter Duniho May 03 '15 at 17:34
  • Great, I used the same logic and just checked type of the controls I wished to exclude. General and easy to understand, thank you – Rice Jun 26 '17 at 19:34
  • 1
    This doesn't work if you want to re-enable the form. If you call DisableControls(this); then call EnableControls(this) it will only enable the form and not any of the child controls... – John Grabanski Feb 19 '18 at 18:30
  • This answer was a good start. Another answer takes it a little further: https://stackoverflow.com/questions/3419159/how-to-get-all-child-controls-of-a-windows-forms-form-of-a-specific-type-button – IdusOrtus Apr 19 '19 at 16:57
14

Based on @pinkfloydx33's answer and the edit I made on it, I created an extension method that makes it even easier, just create a public static class like this:

public static class GuiExtensionMethods
{
        public static void Enable(this Control con, bool enable)
        {
            if (con != null)
            {
                foreach (Control c in con.Controls)
                {
                    c.Enable(enable);
                }

                try
                {
                    con.Invoke((MethodInvoker)(() => con.Enabled = enable));
                }
                catch
                {
                }
            }
        }
}

Now, to enable or disable a control, form, menus, subcontrols, etc. Just do:

this.Enable(true); //Will enable all the controls and sub controls for this form
this.Enable(false);//Will disable all the controls and sub controls for this form

Button1.Enable(true); //Will enable only the Button1

So, what I would do, similar as @pinkfloydx33's answer:

private void Form1_Load(object sender, EventArgs e) 
{
        this.Enable(false);
        Button1.Enable(true);
}

I like Extension methods because they are static and you can use it everywhere without creating instances (manually), and it's much clearer at least for me.

coloboxp
  • 494
  • 8
  • 15
  • This doesn't work. If you call this.Enable(false); then call Button1.Enable(true), then this(the form) and any other parent controls of Button1 are still disabled. – John Grabanski Feb 19 '18 at 18:24
  • Probably I don't understand you, but I think that was the point. Try enabling the parent of Button1 if that's what you require, it should enable the parent and all it's children controls. – coloboxp Feb 21 '18 at 09:38
  • `this.Enable(false);` prevents the user from closing the form as well (the x on the top right corner). Is there a way to allow a user to close the form with this? – slayernoah Mar 23 '22 at 04:32
9

For a better, more elegant solution, which would be easy to maintain - you probably need to reconsider your design, such as put your button aside from other controls. Then assuming other controls are in a panel or a groupbox, just do Panel.Enabled = False.

If you really want to keep your current design, you can Linearise ControlCollection tree into array of Control to avoid recursion and then do the following:

Array.ForEach(Me.Controls.GetAllControlsOfType(Of Control), Sub(x As Control) x.Enabled = False)
yourButton.Enabled = True
Victor Zakharov
  • 25,801
  • 18
  • 85
  • 151
  • +1 for `Panel.Enabled = false|true`. Redesign your interface to create a group of controls and then disable the entire group is surely a better way to design the UI. – sentenza Apr 15 '16 at 09:10
2

When you have many panels or tableLayoutPanels nested the situation becomes tricky. Trying to disable all controls in a panel disabling the parent panel and then enabling the child control does not enable the control at all, because the parent (or the parent of the parent) is not enabled. In order to keep enabled the desired child control I saw the form layout as a tree with the form itself as the root, any container or panel as branches and child controls (buttons, textBoxes, labels, etc.) as leaf nodes. So, the main goal is to disable all nodes within the same level as the desired control, climbing up the control tree all the way to the form level, stablishing a path of enabled controls so the child one can work:

public static void DeshabilitarControles(Control control)
{
    if (control.Parent != null)
    {
        Control padre = control.Parent;
        DeshabilitarControles(control, padre);
    }
}

private static void DeshabilitarControles(Control control, Control padre)
{
    foreach (Control c in padre.Controls)
    {
        c.Enabled = c == control;
    }
    if (padre.Parent != null)
    {
        control = control.Parent;
        padre = padre.Parent;
        DeshabilitarControles(control, padre);
    }
}

public static void HabilitarControles(Control control)
{
    if (control != null)
    {
        control.Enabled = true;
        foreach (Control c in control.Controls)
        {
            HabilitarControles(c);
        }
    }
}
Yayo Bruno
  • 21
  • 2
0

I have corrected @coloboxp answer, first you must enable all parents:

    public static void Enable(this Control con, bool enable)
    {
        if (con != null)
        {
            if (enable)
            {
                Control original = con;

                List<Control> parents = new List<Control>();
                do
                {
                    parents.Add(con);

                    if (con.Parent != null)
                        con = con.Parent;
                } while (con.Parent != null && con.Parent.Enabled == false);

                if (con.Enabled == false)
                    parents.Add(con); // added last control without parent

                for (int x = parents.Count - 1; x >= 0; x--)
                {
                    parents[x].Enabled = enable;
                }

                con = original;
                parents = null;
            }

            foreach (Control c in con.Controls)
            {
                c.Enable(enable);
            }

            try
            {
                con.Invoke((MethodInvoker)(() => con.Enabled = enable));
            }
            catch
            {
            }
        }
    }
ag93
  • 343
  • 4
  • 17
Andreas_k
  • 103
  • 8