Custom form control with multiple fields

Pam Reid asked on April 14, 2016 18:45

I've created a custom form control for use with a document type. It contains two fields: a dropdown of available meetings, and a checkboxlist that populates with a list of sessions when a meeting is selected.

Everything is working fine as far as the display and writing back to both fields in the database when a user submits a form. The one thing I'm having trouble with is having the fields populate with the previously saved information when a user returns to the form. The dropdown populates correctly, but not the checkbox list.

.ascx code:

<cms:CMSUpdatePanel ID="pnlUpdate" runat="server" ChildrenAsTriggers="true">
<ContentTemplate>
    <asp:DropDownList ID="ddMtg" runat="server" OnSelectedIndexChanged="ddMtg_SelectedIndexChanged" AutoPostBack="true" CssClass="form-control"></asp:DropDownList>

    <asp:CheckBoxList ID="cblFunctions" runat="server" CssClass="checkbox checkbox-list-vertical"></asp:CheckBoxList>
</ContentTemplate>

<Triggers>
    <asp:AsyncPostbackTrigger ControlID="ddMtg" EventName="SelectedIndexChanged" />
</Triggers>
</cms:CMSUpdatePanel>

.cs code:

protected string connectionString = ConfigurationManager.ConnectionStrings["DataSource.iMIS.Connection"].ConnectionString;

/// <summary>
/// Gets or sets the value entered into the field
/// </summary>
public override object Value
{
    get
    {
        return ddMtg.SelectedValue;
    }
    set
    {
        // Selects the matching value in the drop-down
        EnsureItems();
        ddMtg.SelectedValue = System.Convert.ToString(value);
    }
}


/// <summary>
/// Returns an array of values of any other fields returned by the control.
/// </summary>
/// <returns>It returns an array where the first dimension is the attribute name and the second is its value.</returns>
public override object[,] GetOtherValues()
{
    object[,] array = new object[1, 2];
    //array[0, 0] = "mtgCode";
    //array[0, 1] = ddMtg.SelectedItem.Value;
    array[0, 0] = "listTktFunctions";

    List<string> listFunctions = new List<string>();

    foreach (ListItem thisItem in cblFunctions.Items)
    {
        if (thisItem.Selected)
        {
            listFunctions.Add(thisItem.Value);
        }
    }

    array[0, 1] = string.Join("|", listFunctions);

    return array;
}

/// <summary>
/// Returns true if a meeting is selected. Otherwise, it returns false and displays an error message.
/// </summary>
public override bool IsValid()
{
    if ((string)Value != "")
    {
        return true;
    }
    else
    {
        // Sets the form control validation error message
        this.ValidationError = "Please select the iMIS meeting code.";
        return false;
    }
}


/// <summary>
/// Sets up the internal DropDownList control.
/// </summary>
protected void EnsureItems()
{
    // Generates the options in the drop-down list
    if (ddMtg.Items.Count == 0)
    {
        DataSet data = new DataSet();

        string qs = @"
        SELECT MEETING, TITLE
        FROM [APSA_Test].[dbo].[Meet_Master]
        ORDER BY MEETING";

        using (SqlConnection connection = new SqlConnection(connectionString))
        {
            SqlDataAdapter adapter = new SqlDataAdapter(qs, connection);
            adapter.Fill(data);
        }

        if (data.Tables[0].Rows.Count > 0)
        {
            foreach (DataRow row in data.Tables[0].Rows)
            {
                ddMtg.Items.Add(new ListItem(row["MEETING"].ToString() + " - " + row["TITLE"].ToString(), row["MEETING"].ToString()));
            }                
        }
    }
}


protected void populateFunctions(string mtgCode)
{
    DataSet data = new DataSet();

    string qs = @"
    SELECT PRODUCT_CODE, PRODUCT_CODE
    FROM [APSA_Test].[dbo].[Product_Function] pf
    WHERE PRODUCT_CODE LIKE @mtgCode + '/%'
    ORDER BY pf.PRODUCT_CODE";

    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        SqlDataAdapter adapter = new SqlDataAdapter(qs, connection);

        adapter.SelectCommand.Parameters.Add("@mtgCode", SqlDbType.NVarChar, 10);
        adapter.SelectCommand.Parameters["@mtgCode"].Value = mtgCode;

        adapter.Fill(data);
    }

    if (data.Tables[0].Rows.Count > 0)
    {
        foreach (DataRow row in data.Tables[0].Rows)
        {
            cblFunctions.Items.Add(new ListItem(row["PRODUCT_CODE"].ToString(), row["PRODUCT_CODE"].ToString()));
        }
    }
}


protected void ddMtg_SelectedIndexChanged(object sender, System.EventArgs e)
{
    cblFunctions.Items.Clear();
    populateFunctions(ddMtg.SelectedValue);
}


/// <summary>
/// Handler for the Load event of the control.
/// </summary>
protected void Page_Load(object sender, EventArgs e)
{
    // Ensure drop-down list options
    EnsureItems();
}

Any insight is much appreciated.

Thanks, Pam

Recent Answers


Roman Hutnyk answered on April 14, 2016 19:25

Pam,

There is a code that sets a selected value to the drop down list:

ddMtg.SelectedValue = System.Convert.ToString(value);

But I can't see any code that would setup values (checked or not) for your check box list. So you need to implement some code, that will retrieve those values and set them up.

1 votesVote for this answer Mark as a Correct answer

Pam Reid answered on April 14, 2016 20:22

Hi Roman,

That's where I was getting hung up - I wasn't sure how to reference those values. In the GetOtherValues() method, I'm using an array to reference the second checkboxlist field - I modeled this after an example I found online. That method appears to be used when writing back to the database, but I wasn't sure if I should also be using that to pull info back out.

I see how the Value property is used to populate the saved info for the dropdown, but I'm unclear on how to use a similar property for the second field.

Thanks, Pam

0 votesVote for this answer Mark as a Correct answer

Roman Hutnyk answered on April 14, 2016 21:08

Kentico will not automatically populate that field. You may try to get it from the current document:

DocumentContex.CurrentDocument.GetValue("fieldname");

To add more flexibility I'd add another property to your form control to specify field name you need to read data from.

2 votesVote for this answer Mark as a Correct answer

Pam Reid answered on April 14, 2016 23:03

Hi Roman,

I couldn't get the document context to be recognized from the form tab, but that did point me in the right direction. I was eventually able to get things working by adding this to the Page_Load function:

        List<string> listFunctions = Form.GetFieldValue("listTktFunctions").ToString().Split('|').ToList();

        foreach (ListItem thisItem in cblFunctions.Items)
        {
            if (listFunctions.Contains(thisItem.Value))
            {
                thisItem.Selected = true;
            }
        }

Thanks very much, Pam

0 votesVote for this answer Mark as a Correct answer

   Please, sign in to be able to submit a new answer.