views:

90

answers:

5

I have seen many others with similar problems but I cannot find the flaw in my logic here. Any help would be greatly appreciated.

I have a Panel which I have added numerous label and textbox controls to, ie:

myPanel.Controls.Add(txtBox);

These controls are created and added in a method called previous to the iteration method.

I want to iterate through each textbox and use its Text property as a parameter in another method but I am not having any luck. Here is my attempt to iterate:

public void updateQuestions()
{
    try
    {
        foreach (Control c in editQuestionsPanel.Controls)
        {
            if (c is TextBox)
            {
                TextBox questionTextBox = (TextBox)c;

                string question = questionTextBox.Text;

                writeNewQuestionToTblQuestions(question);
            }
        }
    }
    catch (Exception err)
    {
        Console.WriteLine(err.Message);
    }
}

The problem I am having is that the controls are not in the Panel when I arrive at this updateQuestions() method. Here is the process involved:

A commandButton is clicked and the questions are read from a DB, for each question a method is called which adds 2 labels and a textbox to editQuestionsPanel.Controls. This panel is inside a PlaceHolder which is then made visible.

When a button inside the PlaceHolder is clicked, the updateQuestions() method is called and the editQuestionsPanel.Controls.Count = 1. As there are approx 12 questions in the DB it should be around 36. The one control inside the Panel is of type:

System.Web.UI.LiteralControl  

It contains no controls.

I am sure that somwhere in the lifecycle the Panel's controls are being cleared but I do not know how to step thru the life cycle. I have a Page_load method which is called as soon as a button is clicked but once the button which calls updateQuestions() is clicked the editQuestionsPanel.Controls.Count is already back to 1 so it must be cleared before this but I do not know how to correct this...

Any help you can give to help me solve this would be greatly appreciated - its killing me!

A: 

You can do something like this instead:

var questions = from tb in editQuestionsPanel.Controls.OfType<TextBox>()
                select tb.Text;

foreach(var question in questions)
{
    writeNewQuestionToTblQuestions(question);
}
Richard Hein
A: 

If the other answers don't help, try doing your code but add recursivity. Your code would not work if it's editQuestionsPanel => Panel => Textbox

Dinah
A: 

A frequently made mistake: Container.Controls only contains the first level child controls in this container. That is: TextBox1 in PanelA, PanelA in PanelB, you can't get TextBox1 in PanelB.Controls.

My solution is to write an extension method:

public static IEnumerable<Control> AllControls(this Control ctl)
{
   List<Control> collection = new List<Control>();
   if (ctl.HasControls())
   {
       foreach (Control c in ctl.Controls)
       {
             collection.Add(c);
             collection = collection.Concat(c.AllControls()).ToList();
       }
   }
   return collection;
}

Now TextBox1 is in PanelB.AllControls(). To filter all controls with type, using PanelB.AllControls().OfType<TextBox>()

Danny Chen
You're right but using LINQ you can do the same much easier. See my post
abatishchev
+1  A: 

This selects from collection controls only that which are of type TextBox.

(the same as control is TextBox or (control as TextBox) != null)

If controls are contained in editQuestionsPanel.Controls:

using System.Linq;

IEnumerable<TextBox> textBoxes = editQuestionsPanel.Controls.OfType<TextBox>();    
foreach (TextBox textBox in textBoxes)
{
    // do stuff
}

To select all child controls use next extension method:

public static IEnumerable<T> GetChildControls<T>(this Control control) where T : Control
{
    var children = control.Controls.OfType<T>();
    return children.SelectMany(c => GetChildControls<T>(c)).Concat(children);
}

Using:

IEnumerable<TextBox> textBoxes = editQuestionsPanel.GetChildControls<TextBox>();
abatishchev
Thank you for this solution - it is definately a better approach to reading the controls. However, this is of no use to me as the controls within editQuestionPanel.Controls seem to be getting cleared before it reaches my code. I have stepped through PreInit and Init using override methods. When the controls are all in place in the panel, the Save button is clicked - At the PreInit stage the editQuestionsControls.Count = null and at the Init stage it is 1. It should be 37. How do I stop the controls from being cleared?
Frank
@Frank: I think you need to investigate/debug your code more to find a place where panel drops its child control. I'm sure it happens somewhere programmatically
abatishchev
@Frank: One more complex solution - replace panel with your one where you will override type of collection holding child controls, just to debug `Collection.Controls.Add/Remove/Clear`
abatishchev
I just stepped through the page, testing editQuestionPanel.Controls.Count at each meaningful step. The controls are added and I get a value of 37 right up to the end of my override OnUnload() method. Then once I click the Save button to start iterating thru the controls, on the OnInit() method I get a value of null. So the controls must be being cleared after the Unload stage. I'm sorry but I did not full understand ur solution in the above comment - will it help me?
Frank
@Frank: Oops, sorry, some moment I thought that you're talking about WinForms controls. So when you're using ASP.NET it's the normal behavior of UpdatePanel to drop its child controls, I guess
abatishchev
I am not using an UpdatePanel - simply an ordinary Panel. If that makes a difference..
Frank
+1 nice answer using SelectMany.
Danny Chen
@Danny: Thanks! :)
abatishchev
+2  A: 

When you add controls dynamically, you need to do that on every request - asp.net doesn't do that for you!

Add the controls in the Init or Load phase, then they will get populated with the postback values.

Hans Kesting