23

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.

2
  • 1
    Should you put the button in a separate form? Commented Nov 19, 2012 at 0: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. Commented Nov 19, 2012 at 0:28

6 Answers 6

36

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); } } 
Sign up to request clarification or add additional context in comments.

9 Comments

Why would you include the ref keyword there?
I wouldn't. I was on the phone while typing it and for some reason it just came out
Ha. Well done. I like how you made the methods not know or care about any specific controls. Newbies overlook things like that.
@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.
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...
|
15

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.

3 Comments

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.
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.
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?
11

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 

1 Comment

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

Comments

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 { } } } 

Comments

0

you can do this with a oneliner! foreach(Control con in this.Controls) con.Enabled = false; button3.Enabled = true;

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.