0

I'm trying to get my head around generating dynamic controls. I have a Button1_Clickevent that creates a CheckBoxList (id=cblAnswers) or a RadioButtonList (id=rblAnswers) depending on the value of a field in a database.

On Button3_Click event I want to be able to retrieve the selected values from the generated control and compare them with a List listOfCorrectAnswerIDs. If the selected values exactly match those in listOfCorrectAnswerIDs, I'd like to set a variable.

I can create the controls but how do I retrieve the selected values in Button3_Click and compare them with List listOfCorrectAnswerIDs?

protected void Button1_Click(object sender, EventArgs e)
{
    string connStr = ConfigurationManager.ConnectionStrings["myConnectionString"].ConnectionString;
    MySqlConnection conn = new MySqlConnection(connStr);
    MySqlDataReader reader;

    List<string> listOfAnswerIDs = new List<string>();
    List<string> listOfAnswers = new List<string>();
    List<string> listOfCorrectAnswerIDs = new List<string>();

    try
    {
        conn.Open();
        string cmdText = "SELECT * FROM questions_m WHERE question_id=1";
        MySqlCommand cmd = new MySqlCommand(cmdText, conn);

        reader = cmd.ExecuteReader();

        if (reader.Read())
        {
            lblQuestion.Text = reader["question"].ToString();

            if (reader["type"].ToString().Equals("C"))
            {
                CheckBoxList cblAnswers = new CheckBoxList();
                cblAnswers.ID = "cblAnswers";
                Page.Form.Controls.Add(cblAnswers);
            }
            else if (reader["type"].ToString().Equals("R"))
            {
                 RadioButtonList rblAnswers = new RadioButtonList();
                 rblAnswers.ID = "rblAnswers";
                 Page.Form.Controls.Add(rblAnswers);
            }

            ViewState["QuestionID"] = reader["question_id"].ToString();
            reader.Close();

            string cmdText2 = "SELECT * FROM answers_m WHERE question_id=1";
            MySqlCommand cmdAnswers = new MySqlCommand(cmdText2, conn);
            reader = cmdAnswers.ExecuteReader();

            while (reader.Read())
            {
                listOfAnswerIDs.Add(reader["answer_id"].ToString());
                listOfAnswers.Add(reader["answer"].ToString());
                if (reader["correct"].ToString().Equals("Y"))
                {
                    listOfCorrectAnswerIDs.Add(reader["answer_id"].ToString());
                }
            }

            reader.Close();
            populateAnswers(listOfAnswers, listOfAnswerIDs);

        }
        else
        {
            reader.Close();
            lblError.Text = "(no questions found)";
        }
        ViewState["listOfCorrectAnswerIDs"] = listOfCorrectAnswerIDs;

    }
    catch
    {
        lblError.Text = "Database connection error - failed to read records.";
    }
    finally
    {
        conn.Close();
    }
}

    protected void Button3_Click(object sender, EventArgs e)
{
    if (((CheckBoxList)this.FindControl("cblAnswers")) != null)
    {
        List<string> selectedValues = ((CheckBoxList)this.FindControl("cblAnswers")).Items.Cast<ListItem>()
    .Where(li => li.Selected)
    .Select(li => li.Value)
    .ToList();
        for (int i = 0; i < selectedValues.Count; i++)
        {
            lblSelected.Text += selectedValues[i].ToString();
        }
    }
}
Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
Bhav
  • 1,957
  • 7
  • 33
  • 66

2 Answers2

1

When you load the question, store the name of the control that you populate with the answers in the ViewState, and in the button click event, retrieve the control name and pass it to FindControl(). Cast the result of that to the common base type of CheckBoxList and RadioButtonList, which is ListControl. The button handler might look like this:

protected void Button3_Click(object sender, EventArgs e)
{
    string controlName = ViewState["answerControlName"];
    ListControl answersControl = (ListControl)this.FindControl(controlName);
    if (answersControl != null)
    {
        List<string> selectedValues = answersControl.Items.Cast<ListItem>()
            .Where(li => li.Selected)
            .Select(li => li.Value)
            .ToList();

        List<string> listOfCorrectAnswerIDs = (List<string>)ViewState["listOfCorrectAnswerIDs"];

        // check for equivalency
        string selectedString = string.Join(",", selectedValues.OrderBy(val => val));
        string correctString = string.Join(",", listOfCorrectAnswerIDs.OrderBy(val => val));

        bool allAnswersCorrect = selectedString == correctString;
    }
}

You didn't specify how you wanted to compare the sets of answers, so here I provided a simple way to check whether or not the sets are equivalent (sorting the values, converting to comma-delimited list, and using string equality). If you want something like % correct, you would be better off looping through the correct answers and checking each one to see if it is contained in the selectedValues list, or using the LINQ set operation functions like .Intersect().

Matt Miller
  • 443
  • 4
  • 10
0

This is because of the way you are dynamically creating those controls. You are creating them after the page has initialized all of the controls, so you lose them on each PostBack. I recommend reading up on the ASP.NET Page Life Cycle.

You have a couple options for accomplishing your goal. You can either override the CreateChildControls method, or override the page's OnInit method. Either will accomplish the same thing, but remember to call the base Init method if you use the latter.

protected override void CreateChildControls()
{
    CreateDynamicControls();
}

protected override void OnInit(EventArgs e)
{
    CreateDynamicControls();
    base.OnInit(e);
}
j.f.
  • 3,908
  • 2
  • 29
  • 42