Search code examples
c#asp.netdynamiccontrolsasp.net-controls

Retrieve SelectedValues of a dynamically generated control


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();
        }
    }
}

Solution

  • 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().