Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Disabling and Re-enabling Control Collections

0.00/5 (No votes)
3 Jan 2011 1  
Disabling a control collection, and returning all sub controls to their respective Enabled state when re-enabling the collection

Introduction

This is not a complex article or topic, and attempts to solve an issue I just came across and thought others might be coming by as well. A quick search on everyone's favourite search engine yielded no results, so here I am typing up my own solution.

The issue is quite straight forward - say you're in a scenario where you need to disable a dialog or form because you're waiting for something to happen on another thread, and do not want the user interacting with anything in the meantime.

A good example of this would be a login dialog that asks for credentials to an SQL Server database, and upon clicking on "Ok" the login is attempted. If, for example, the server name specified was wrong (and you're using the default 30 second connection timeout), the user would have to wait for 30 seconds until an error message is displayed.

While the connection is being established, you want to make sure that the user does not continue interacting with your screen and that all controls are effectively disabled during this time. The easy way to do this would be to simply set the form's Enabled flag to false, but that would be nasty.

The thing is - if your application is properly multi threaded, and the connection attempt is happening behind the scenes, your dialog will remain unlocked during this time and the user should be able to move it around. If you disable the entire screen, you can no longer move the form around, which leads the user to believe things are frozen.

The Solution

The solution to this would be quite simple. Just disable all controls on the screen, one by one, and then you're set. Right? Well, almost. You would also have to consider that some controls might already be disabled on the screen, and upon disabling and subsequently re-enabling the screen, you'd want those controls to remain disabled. Going back to the login sample, disabled controls might be the user name and password fields if you're using NT authentication.

Then there's also the issue of some controls possibly containing others (such as group boxes, panels, etc.), and their child controls would have to be considered as well.

To achieve this, a dynamic pair of methods are needed. One, to catalog all controls on your screen (storing their names and Enabled states) and disable them, and another, which uses the catalog built by the first method to put everything back the way it was.

The UIHelper class has these two methods, and they are quite simple.

The GetControlStatesAndDisable() method takes as a single parameter the Controls collection you want to disable (such as Form.Controls). It catalogs and returns the names and Enabled states of all controls in it (and recursively calls itself in order to go down the tree of any sub Controls lists such as for group boxes or panels), and then disables all the controls in this list.

The RestoreControlStates() method is called after your work is complete, and takes as parameters the same Controls collection used earlier as well as the catalog created by the first method.

For example, assuming you're currently within a Form class:

SortedList<string, bool> controlStates = 
	UIHelper.GetControlStatesAndDisable(this.Controls); 
// do your lengthy operations here
UIHelper.RestoreControlStates(mForm1.Controls, controlStates); 

Using the Sample Code

The sample code attached shows how this helper class works. It is comprised of two forms - one containing of several test controls and container controls (with their own child controls), and next to each control is a button letting you enable or disable it.

The second form is used to enable or disable all of form1 by using the helper class and methods. Note that no matter how you enable or disable individual controls on form1, enabling or disabling them through the buttons on form2 (and therefore the helper class) always returns them to their original Enabled states.

The two methods are as follows (and can be found in UIHelper.cs in the attachment to this article):

/// <summary>
/// Get a list of all the controls in the ControlCollection 
/// along with their Enabled state. Disables all those controls after generating
/// a list of their current Enabled state.
/// </summary>
/// <param name="controlCollection">The ControlCollection to scan for controls and 
/// Enabled flags.</param>
/// <returns>List of control names and Enabled states.</returns>
public static SortedList<string, bool> GetControlStatesAndDisable
			(Control.ControlCollection controlCollection)
{
    SortedList<string, bool> controlStates = new SortedList<string, bool>();
 
    // Loop through the controls under the current ControlCollection
    for (int ctr = 0; ctr < controlCollection.Count; ctr++)
    {
        // Store the current control name and its Enabled state
        controlStates.Add(controlCollection[ctr].Name, controlCollection[ctr].Enabled);
 
        // If the current control also has its own ControlCollection 
        // (such as for a group box, panel, etc), loop through
        // its own child controls and extract the same info.
        foreach (KeyValuePair<string, bool> pair in GetControlStatesAndDisable
					(controlCollection[ctr].Controls))
        {
            controlStates.Add(pair.Key, pair.Value);
        }
 
        // Set the current control's Enabled state to false
        controlCollection[ctr].Enabled = false;
    }
    return controlStates;
}
 
/// <summary>
/// Restore each control in the ControlCollection to the original 
/// Enabled state that existed before calling GetControlStatesAndDisable().
/// </summary>
/// <param name="controlCollection">The ControlCollection to scan for controls 
/// and restore their Enabled states.</param>
/// <param name="controlStates">The original Enabled states as retrieved 
/// from a call to GetControlStatesAndDisable().</param>
public static void RestoreControlStates
  (Control.ControlCollection controlCollection, SortedList<string, bool> controlStates)
{
    // Loop through the controls under the current ControlCollection
    for (int ctr = 0; ctr < controlCollection.Count; ctr++)
    {
        if (controlStates.ContainsKey(controlCollection[ctr].Name))
        {
            // Set the current control's Enabled state to what it was 
            // before the call to GetControlStatesAndDisable().
            controlCollection[ctr].Enabled = controlStates[controlCollection[ctr].Name];
        }
 
        // If the current control has its own ControlCollection 
        // (e.g. for a group box, panel, etc) perform the same job
        // on its own child controls.
        RestoreControlStates(controlCollection[ctr].Controls, controlStates);
    }
} 

Points of Interest

I did notice, however, that disabling and re-enabling a container control does in fact retain all the individual Enabled states of its child controls, so there may be no need to traverse the control collections and store (or restore) their individual Enabled states. This also works when enabling or disabling the entire form, but that then freezes the form and makes it look like the app is frozen or has crashed. I have left the recursive code in the sample anyway, just in case it comes in handy for another scenario I have not thought of.

I hope this little tidbit comes in handy to someone else, and as always, comments and suggestions are welcome.

History

  • Jan 01 2010 - Initial publication
  • Jan 02 2010 - Added the UIHelper code into the article

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here