tags:

views:

1600

answers:

6

I do remember seeing someone ask something along these lines a while ago but I did a search and couldn't find anything.

I'm trying to come up with the cleanest way to clear all the controls on a form back to there defaults. eg Clear textboxs, uncheck checkboxes.

How would you go about this?

+9  A: 

What I have come up with so far is something like this:

public static class extenstions
{
    private static Dictionary<Type, Action<Control>> controldefaults = new Dictionary<Type, Action<Control>>() { 
            {typeof(TextBox), c => ((TextBox)c).Clear()},
            {typeof(CheckBox), c => ((CheckBox)c).Checked = false},
            {typeof(ListBox), c => ((ListBox)c).Items.Clear()},
            {typeof(RadioButton), c => ((RadioButton)c).Checked = false},
            {typeof(GroupBox), c => ((GroupBox)c).Controls.ClearControls()},
            {typeof(Panel), c => ((Panel)c).Controls.ClearControls()}
    };

    private static void FindAndInvoke(Type type, Control control) 
    {
        if (controldefaults.ContainsKey(type)) {
            controldefaults[type].Invoke(control);
        }
    }

    public static void ClearControls(this Control.ControlCollection controls)
    {
        foreach (Control control in controls)
        {
             FindAndInvoke(control.GetType(), control);
        }
    }

    public static void ClearControls<T>(this Control.ControlCollection controls) where T : class 
    {
        if (!controldefaults.ContainsKey(typeof(T))) return;

        foreach (Control control in controls)
        {
           if (control.GetType().Equals(typeof(T)))
           {
               FindAndInvoke(typeof(T), control);
           }
        }    

    }

}

Now you can just call the extension method ClearControls like this:

 private void button1_Click(object sender, EventArgs e)
    {
        this.Controls.ClearControls();
    }

EDIT: I have just added a generic ClearControls method that will clear all the controls of that type, which can be called like this:

this.Controls.ClearControls<TextBox>();

At the moment it will only handle top level controls and won't dig down through groupboxes and panels.

Nathan W
a fascinating example ! thanks.
BillW
A: 

You can loop for control

foreach (Control ctrl in this)
{
 if(ctrl is TextBox)
  (ctrl as TextBox).Clear();
}
Daok
I was doing this type of approach but I wanted to find something a bit less if..then..else base.
Nathan W
+2  A: 

I voted for Nathan's solution, but wanted to add a bit more than a comment can handle.

His is actually very good, but I think the best solution would involve sub-classing each of the control types you might be adding before adding them to the GUI. Have them all implement an interface "Clearable" or something like that (I'm a java programmer, but the concept should be there), then iterate over it as a collection of "Clearable" objects, calling the only method .clear() on each

This is how GUIs really should be done in an OO system. This will make your code easy to extend in the future--almost too easy, you'll be shocked.

Edit: (per Nathan's comment about not changing existing controls)

Perhaps you could create "Container" classes that reference your control (one for each type of control). In a loop like the one you set up in your answer, you could instantiate the correct container, place the real control inside the container and store the container in a collection.

That way you are back to iterating over a collection.

This would be a good simple solution that isn't much more complex than the one you suggested, but infinitely more expandable.

Bill K
Yes +million. Always, always, always subclass every control before adding it to your project. It lets you change things so much easier later on.
Cade Roux
I thought about the whole sub class thing but I was just refactoring an old project and I didn't want to have to redo the UI. In future projects I will sub class everything.
Nathan W
@Bill I'll take your suggestions into consideration.
Nathan W
A: 

Below are methods I use to clear text from a type of control that implements ITextBox.

I noticed in the example default boolean values are set. I'm sure you can modify it to set default values of boolean components.

Pass the Clear method a control type (TextBox, Label... etc) and a control collection, and it will clear all text from controls that implement ITextBox.

Something like this:

//Clears the textboxes
WebControlUtilities.ClearControls<TextBox>(myPanel.Controls);

The Clear method is meant for a Page or Masterpage. The control collection type may vary. ie. Form, ContentPlaceHolder.. etc

        /// <summary>
    /// Clears Text from Controls...ie TextBox, Label, anything that implements ITextBox
    /// </summary>
    /// <typeparam name="T">Collection Type, ie. ContentPlaceHolder..</typeparam>
    /// <typeparam name="C">ie TextBox, Label, anything that implements ITextBox</typeparam>
    /// <param name="controls"></param>
    public static void Clear<T, C>(ControlCollection controls)
        where C : ITextControl
        where T : Control
    {
        IEnumerable<T> placeHolders = controls.OfType<T>();
        List<T> holders = placeHolders.ToList();

        foreach (T holder in holders)
        {
            IEnumerable<C> enumBoxes = holder.Controls.OfType<C>();
            List<C> boxes = enumBoxes.ToList();

            foreach (C box in boxes)
            {
                box.Text = string.Empty;
            }
        }
    }

    /// <summary>
    /// Clears the text from control.
    /// </summary>
    /// <typeparam name="C"></typeparam>
    /// <param name="controls">The controls.</param>
    public static void ClearControls<C>(ControlCollection controls) where C : ITextControl
    {
        IEnumerable<C> enumBoxes = controls.OfType<C>();
        List<C> boxes = enumBoxes.ToList();

        foreach (C box in boxes)
        {
            box.Text = string.Empty;
        }
    }
Chuck Conway
+1  A: 

The above solutions seem to ignore nested controls.

A recursive function may be required such as:

public void ClearControl(Control control)
{
  TextBox tb = control as TextBox;
  if (tb != null)
  {
    tb.Text = String.Empty;
  }
  // repeat for combobox, listbox, checkbox and any other controls you want to clear
  if (control.HasChildren)
  {
    foreach(Control child in control.Controls)
    {
      ClearControl(child)
    }
  }
}

You don't want to just clear the Text property without checking the controls type.

Implementing an interface, such as IClearable (as suggested by Bill K), on a set of derived controls would cut down the length of this function, but require more work on each control.

benPearce
The solution that I presented will include nested controls.
Nathan W
Nathan, that is an interesting solution. The nested aspect of the code was not immediately evident from looking at the code, so I did not spot that.
benPearce
Yeah it is a little but hidden but hey it works well
Nathan W
+2  A: 

Here is the same thing that I proposed in my first answer but in VB, until we get VB10 this is the best we can do in VB because it doesn't support non returning functions in lambdas:

VB Solution:

Public Module Extension
    Private Sub ClearTextBox(ByVal T As TextBox)
        T.Clear()
    End Sub

    Private Sub ClearCheckBox(ByVal T As CheckBox)
        T.Checked = False
    End Sub

    Private Sub ClearListBox(ByVal T As ListBox)
        T.Items.Clear()
    End Sub

    Private Sub ClearGroupbox(ByVal T As GroupBox)
        T.Controls.ClearControls()
    End Sub

    <Runtime.CompilerServices.Extension()> _
    Public Sub ClearControls(ByVal Controls As ControlCollection)
        For Each Control In Controls
            If ControlDefaults.ContainsKey(Control.GetType()) Then
                ControlDefaults(Control.GetType()).Invoke(Control)
            End If
        Next
    End Sub

    Private _ControlDefaults As Dictionary(Of Type, Action(Of Control))
    Private ReadOnly Property ControlDefaults() As Dictionary(Of Type, Action(Of Control))
        Get
            If (_ControlDefaults Is Nothing) Then
                _ControlDefaults = New Dictionary(Of Type, Action(Of Control))
                _ControlDefaults.Add(GetType(TextBox), AddressOf ClearTextBox)
                _ControlDefaults.Add(GetType(CheckBox), AddressOf ClearCheckBox)
                _ControlDefaults.Add(GetType(ListBox), AddressOf ClearListBox)
                _ControlDefaults.Add(GetType(GroupBox), AddressOf ClearGroupbox)
            End If
            Return _ControlDefaults
        End Get
    End Property

End Module

Calling:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Me.Controls.ClearControls()
    End Sub

I'm just posting this here so that people can see how to do the same thing in VB.

Nathan W