views:

160

answers:

7

Is it possible to dynamically (and generically) clear the state of all of a user control's child controls? (e.g., all of its TextBoxes, DropDrownLists, RadioButtons, DataGrids, Repeaters, etc -- basically anything that has ViewState)

I'm trying to avoid doing something like this:

foreach (Control c in myUserControl.Controls)
{
    if (c is TextBox)
    {
        TextBox tb = (TextBox)c;
        tb.Text = "";
    }
    else if (c is DropDownList)
    {
        DropDownList ddl = (DropDownList)c;
        ddl.SelectedIndex = -1;
    }
    else if (c is DataGrid)
    {
        DataGrid dg = (DataGrid)c;
        dg.Controls.Clear();
    }

    // etc.

}

I'm looking for something like this:

foreach (Control c in myUserControl.Controls)
    c.Clear();

...but obviously that doesn't exist. Is there any easy way to accomplish this dynamically/generically?

A: 

You can do

foreach (Control c in myUserControl.Controls) {
    myUserControl.Controls.Remove(c);
}

Because Controls is just a list, you can call Remove() on it, passing it what you want to remove.

EDIT: Oh I'm sorry, I didn't read it correctly. I don't know of a way to do this, maybe someone here who is good with Reflection could make it where you could do like

foreach (Control c in myUserControl.Controls) {
    c = new c.Type.GetConstructor().Invoke();
}

or something, to turn it into a freshly made component.

Nilbert
I think Michael is looking to clear the values/state in each control. Not the controls themselves from the collection.
KP
KP: Correct. I don't want to *remove* the controls, merely clear them (e.g., clear a TextBox text value, clear the selected item in a DropDownList, clear the contents of a DataGrid/Repeater, etc.).
Michael
Yeah sorry, edited to reflect my bad reading.
Nilbert
Nilbert: No prob, thanks for your input regardless. I like where you're going with the Reflection concept; that's the type of thing I was wondering about.
Michael
A: 

I haven't tested it, but clearing viewstate for the usercontrol may work. You could expose a custom method on the user control as well:

usercontrol:

public void Clear()
{
    this.ViewState.Clear();
}

page:

myUserControlInstance.Clear();

Now again I haven't tested. It's possible this will only clear the StateBag for the UserControl container, and not its nested/child controls.. if the above doesn't work you could try using recursion to walk down the control tree to clear viewstate for all children:

usercontrol:

public void Clear()
{
    ClearViewState(this.Controls);
}

private void ClearViewState(ControlCollection cc)
{
    foreach(Control c in cc)
    {
        if(c.HasControls())
        {
            //clear the child controls first
            ClearViewState(c.Controls);
        }        

        //then clear the control itself
        c.ViewState.Clear();
    }
}

page:

myUserControlInstance.Clear();

Just an idea. I haven't tested it but I think in theory it could work. One implication would be to call Clear at the correct point in the page/controls lifecycle, otherwise it may not work.

Hope this helps!

KP
Hmmm.. tested this myself and doesn't seem to work.. I'll play around with it a little longer as I like the concept..
KP
I jumped the gun. `ViewState` is not a public property so you can't just clear it for controls. I like the recursion idea discussed in the other answer... I'll leave this answer up even though it isn't going to work in case others have the same thought...
KP
A: 

Why not do as you suggest:

foreach (Control c in myUserControl.Controls)
    c.Clear();

And then implement Clear:

public static class UserController
{
    public static void Clear( this Control c )
    {
        c.Controls.Clear();
    }

    public static void Clear( this TextBox c )
    {
        c.Text = String.Empty;
    }
}

That should do it.

Task
-1, Extension methods do not do dynamic dispatch.
sixlettervariables
So every call to Clear() in the foreach will call the static Control.Clear()? So to make it work you'd be back to the "if (c is TextBox)..." inside the Control.Clear()? Indeed, not a good solution then.
Task
Correct. `c` is statically typed as `Control`, hence extension method would look it up for `Control`.
sixlettervariables
A: 
myUserControl.Controls.ToList().ForEach(c => myUserControl.Controls.Remove(c));

However, be careful, because you modify the iterating list. This could lead to some strange behaviour.

Will Marcouiller
A: 

Setting EnableViewState="false" on the individual controls might save you the work, if it doesn't cause other problems for you in this instance.

tobybot
+1  A: 

I was going to suggest a solution similar to Task's except (as sixlettervariables points out) we need to implement it as 1 extension method and essentailly switch on the precise type of the control passed in (i.e. copy your logic that you posted in your question).

public static class ControlExtensions
{
    public static void Clear( this Control c )
    {
        if(c == null) {
            throw new ArgumentNullException("c");
        }
        if (c is TextBox)
        {
            TextBox tb = (TextBox)c;
            tb.Text = "";
        }
        else if (c is DropDownList)
        {
            DropDownList ddl = (DropDownList)c;
            ddl.SelectedIndex = -1;
        }
        else if (c is DataGrid)
        {
            DataGrid dg = (DataGrid)c;
            dg.Controls.Clear();
        }
        // etc....
    }
}

It is not particularly elegent looking method but your code in your page/control is now the more succinct

foreach (Control c in myUserControl.Controls) {
    c.Clear();
}

and you can of course now call control.Clear() anywhere else in you code.

Chris F
+1  A: 

What about the Control.ClearChildViewState method?

MSDN states

Deletes the view-state information for all the server control's child controls.

I have never used this though. So I am unsure if it will help you. Sounds good though, I think :)

scherand
It sounds like it should be the perfect solution, but unfortunately it doesn't work.
Michael