+1-888-365-2779
Try Now
More in this section

Forums / General Discussions / Dynamically displaying Form Responses with Field Titles

Dynamically displaying Form Responses with Field Titles

15 posts, 0 answered
  1. Tony
    Tony avatar
    5 posts
    Registered:
    09 Sep 2004
    06 Dec 2011
    Link to this post
    Hi there,

    I don't have a question -- I just wanted to post my solution for a problem that I'm sure others struggled with and I could not find help anywhere in the documentation or forums for (I only found a few blog posts and forum posts that got you half way).

    I wanted to build a widget that lets our client display their form responses formatted on a page for review and printing, which means I needed to get both form responses and the field titles, and I didn't want to manually set the form field names, I wanted one dynamic control for displaying the results of any form.

    Below is how I solved it (if you see anywhere where it can be improved please tell me, I'm new to the sitefinity API):

    You'll need the following references:

    using Telerik.Sitefinity;
    using Telerik.Sitefinity.Modules.Forms;
    using Telerik.Sitefinity.Forms.Model;
    using Telerik.Sitefinity.Model;
    using System.ComponentModel;


    The first thing was to get the form and its entries. I'm just hard-coding the form name here.

    // get form
    FormsManager formsManager = FormsManager.GetManager();
    var form = formsManager.GetFormByName("sf_testform");
    // if you want all the entries
    var entries = formsManager.GetFormEntries(form.EntriesTypeName);
    // get one entry (i.e. form response) by guid ID
    var entry = formsManager.GetFormEntries(form.EntriesTypeName).Single(fe => fe.Id == yourFormResponseGuidId);


    The next thing to do is determine the field names and titles of the fields on the form. For that I used a LINQ query, and data-shaped it into a small class. Here's the class:
    private class ControlForDisplay
    {
        public string fieldName { get; set; }
        public string fieldTitle { get; set; }
        public Guid siblingId { get; set; }
        public Guid Id { get; set; }
        public string controlType { get; set; }
    }



    and here's the LINQ queries. It grabs the field name, title, id, and sibling id (which you need for sorting), and makes sure to only grab 'metafield' controls (i.e. the controls you actually care about).

    // build a fieldname/title dictionary
    var fields = (from ctrl in form.Controls.Where(c => c.Properties.Count(c2 => c2.Name.Equals("MetaField")) > 0).Cast<FormControl>()
                  select new ControlForDisplay
                       {
                           fieldName = ctrl.Properties.FirstOrDefault(p => p.Name.Equals("MetaField")).ChildProperties.FirstOrDefault(cp => cp.Name == "FieldName").Value,
                           fieldTitle = ctrl.Properties.FirstOrDefault(p => p.Name.Equals("Title")).Value,
                            siblingId = ctrl.SiblingId,
                           Id = ctrl.Id,
                                            controlType = "formField"
                       }).ToList<ControlForDisplay>();

    Two more queries add instructional text and section headers into the collection.

    // get section headers
    var sectionHeaders = (from ctrl in form.Controls.Where(c => c.Properties.Count(c2 => c2.Name.Equals("Title")) > 0 && c.Properties.Count(c2 => c2.Name.Equals("MetaField")) == 0).Cast<FormControl>()
                                      select new ControlForDisplay
                                      {
                                          fieldName = string.Empty,
                                          fieldTitle = ctrl.Properties.FirstOrDefault(p => p.Name.Equals("Title")).Value,
                                          siblingId = ctrl.SiblingId,
                                          Id = ctrl.Id,
                                          controlType = "sectionheader"
                                      }).ToList<ControlForDisplay>();
     
    // get instructional fields
    var instructionalText = (from ctrl in form.Controls.Where(c => c.Properties.Count(c2 => c2.Name.Equals("Html")) > 0).Cast<FormControl>()
                              select new ControlForDisplay
                                   {
                                       fieldName = string.Empty,
                                       fieldTitle = ctrl.Properties.FirstOrDefault(p => p.Name.Equals("Html")).Value,
                                       siblingId = ctrl.SiblingId,
                                       Id = ctrl.Id,
                                       controlType = "instructionaltext"
                                   }).ToList<ControlForDisplay>();
     
     
    fields.AddRange(sectionHeaders);
    fields.AddRange(instructionalText);


    Now you sort the fields in the order they appear on your form. I couldn't find an extension method to do this, so I did it manually. I'm sure this can be improved, I just wanted to it work first. Sitefinity marks the control with a 'sibling id', i.e. the id of the control above it. An odd way to sort, but it makes sense for their drag and drop interface. (note: the first item in the list has an empty Guid of zeroes for sibling id)

    // sort fields (first place the first item)
    List<ControlForDisplay> sortedFields = new List<ControlForDisplay>();
    sortedFields.Add(fields.Single(f => f.siblingId == Guid.Empty));
    fields.Remove(sortedFields[0]);
    Guid currentSiblingId = sortedFields[0].Id;
     
    foreach (ControlForDisplay field in fields)
    {
        ControlForDisplay nextField = fields.Single(f => f.siblingId == currentSiblingId);
        sortedFields.Add(nextField);
        currentSiblingId = nextField.Id;
    }


    Finally I iterate across the entries, grab the response for each form field and display the results. I'm just dropping a bit of HTML into a Literal on the ASCX page right now, but you could use the final results however you like.

    // sort fields (first place the first item)
    List<ControlForDisplay> sortedFields = new List<ControlForDisplay>();
    sortedFields.Add(fields.Single(f => f.siblingId == Guid.Empty));
    fields.Remove(sortedFields[0]);
    Guid currentSiblingId = sortedFields[0].Id;
     
                foreach (ControlForDisplay field in fields)
                {
                    ControlForDisplay nextField = fields.Single(f => f.siblingId == currentSiblingId);
                    sortedFields.Add(nextField);
                    currentSiblingId = nextField.Id;
                }
     
                // iterate across field names in dictionary getting responses
                var formResponse = from fld in sortedFields
                                    select new
                                    {
                                        Title = fld.fieldTitle,
                                        Response = fld.fieldName == string.Empty ? "" : entry.GetValue(fld.fieldName),
                                        ControlType = fld.controlType
                                    };
     
    // display the responses
    litFormTitle.Text = form.Title;
    rptFormResponses.DataSource = formResponse;
    rptFormResponses.DataBind();



    And that's it! Took awhile, but it works. Not sure if there are any potential problems here, but I thought I'd post it.

    -Tony
  2. Saad
    Saad avatar
    98 posts
    Registered:
    10 Oct 2008
    06 Dec 2011
    Link to this post
    Nice job Tony, much appreciated.
  3. Eric
    Eric avatar
    60 posts
    Registered:
    24 Feb 2011
    21 Dec 2011
    Link to this post
    Thank you for that! I guess I have a general understanding of what you did, but I don't understand this line:
    Response = fld.fieldName == string.Empty ? "" : entry.GetValue(fld.fieldName),

    Can you please explain it to me? Also, how/where do you define "entry"? It doesn't exist in my context. Thanks!
  4. Tony
    Tony avatar
    5 posts
    Registered:
    09 Sep 2004
    21 Dec 2011
    Link to this post
    Hi,
    Sorry about that. I just realized I didn't drop a line of code. But where I was declaring 'var entries' to grab all form responses, I had a single var to grab a single response:
    // get one entry (i.e. form response) by guid ID
     var entry = formsManager.GetFormEntries(form.EntriesTypeName).Single(fe => fe.Id == yourFormResponseGuidId);

    I've updated the post to add this line of code. It's just a reference to one particular form response that you want to print. So if you use 'entries' to list your form responses, you can select one via  and get it via the formsManager by GuidID.

    This line:
    Response = fld.fieldName == string.Empty ? "" : entry.GetValue(fld.fieldName),
    is a ternary operator (see here) which is just if-statement shorthand.

    It sets the Response only if the fieldname is not empty. So if I have a fieldname, then get the value the user entered. If I don't have a fieldname (section headers, for example don't have fieldnames), then just set it to empty.

    This is because sitefinity forms contain not only form fields, but also section headers and separators. You only want to GetValue() for a form field.

    Thanks!
    Tony
  5. Eric
    Eric avatar
    60 posts
    Registered:
    24 Feb 2011
    21 Dec 2011
    Link to this post
    Thanks! That helped :) but now I have another question. I inherited the Forms control and made a custom one because I want the form to redirect to a custom coded page and I want to pass the submitted response to it.
    void CustomFormsControl_BeforeFormAction(object sender, System.ComponentModel.CancelEventArgs e)
            {
                //set the form action to redirect
                this.FormData.SubmitAction = Telerik.Sitefinity.Forms.Model.SubmitAction.PageRedirect;
                //set redirect url here you can pass the this.FormId (id of the form) as query string parameter
                this.FormData.RedirectPageUrl = "~/Custom/Pages/RedirHere.aspx?id=" + this.XXXXXXXX.Id.ToString() + "&FormName=" + this.FormData.Name.ToString();
                    
            }

    Have you any idea what I would pass here in place of the XXXXXXX to get the id (GUID) to a page with your code on it?
  6. Eric
    Eric avatar
    60 posts
    Registered:
    24 Feb 2011
    22 Dec 2011
    Link to this post
    Ok, thanks to Telerik support, I got my answer and turned it into this :)

     void CustomFormsControl_BeforeFormAction(object sender, System.ComponentModel.CancelEventArgs e)
            {

                FormsManager formsManager = FormsManager.GetManager();
                var form = formsManager.GetFormByName(this.FormData.Name.ToString());
                           
                //set the form action to redirect
                this.FormData.SubmitAction = Telerik.Sitefinity.Forms.Model.SubmitAction.PageRedirect;
                           
                //set redirect url here you can pass the this.FormId (id of the form) as query string parameter
                var manager = FormsManager.GetManager();
                var entry = manager.GetFormEntries(this.FormData).Where(fE => fE.ReferralCode == this.FormData.FormEntriesSeed.ToString()).FirstOrDefault();
                Guid entryId;
                string strGuid = null;
                if (entry != null)
                {
                    entryId = entry.Id;
                    strGuid = entryId.ToString();
                }
                
                //set redirect url here you can pass the this.FormId (id of the form) as query string parameter
                this.FormData.RedirectPageUrl = "~/Custom/Pages/RedirHere.aspx?Guid=" + strGuid + "&FormName=" + this.FormData.Name.ToString();
       

            }

    But I have a couple more questions...

    I'm not familiar with Repeaters (thats what you used to show the data, right?) so I used a Datagrid, but it doesn't show the response column. I also don't know Linq but from what I understood, the formResponse is an anonymous type of (String, Object, String) with Object being the Response. Do you know if that might be what's causing the response not to show up?

    One more question, in the line I was asking about with "entry", how would you use "entries" to get all of the responses to the form? Ok, thanks for your time!

    Eric

  7. Lubomir Velkov
    Lubomir Velkov avatar
    688 posts
    Registered:
    03 Nov 2014
    26 Dec 2011
    Link to this post
    Hi Eric,

    To get all the entries you need to remove the call to .FirstOrDefault() as it will return a single FormEntry result. You can also replace it with ToList(). Then you can cycle through the entries with

    foreach (var e in entry)
    {
    }

    where e will be of type FormEntry (and not an anonymous type). You can use the different FormEntry properties when binding to either a Repeater or Grid.

    Greetings,
    Lubomir Velkov
    the Telerik team
    Do you want to have your say in the Sitefinity development roadmap? Do you want to know when a feature you requested is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  8. OC
    OC avatar
    129 posts
    Registered:
    17 Nov 2004
    26 Jan 2012
    Link to this post
    Great job. Thanks a lot!
  9. Eric
    Eric avatar
    60 posts
    Registered:
    24 Feb 2011
    03 Feb 2012
    Link to this post
    Hi,
    Do you have any idea what to do with the code if below my submit button, I have a container in which I place a few text boxes that I hide with css. I use them to code values into the the form that I can read or set, something like "NiceFormName" or an email address to send the results to. My question is that when I use your code to spin through the elements, the textboxes inside the container all have a giud of 000000000000, and there's no way to sort them. Yet they are still in order on the form itself. What am I missing to get the sort order out of such a form? Thanks!

    Eric
  10. Lubomir Velkov
    Lubomir Velkov avatar
    688 posts
    Registered:
    03 Nov 2014
    06 Feb 2012
    Link to this post
    Hi Eric,

    I'm not sure what do you mean with GUID of 0000000 exactly. Are these text fields normal form field controls only with applied CSS class that hides them? How do you hide them - is it with display:none? I think when you hide form elements with such style they aren't actually posted to the server after a form submission.

    Kind regards,
    Lubomir Velkov
    the Telerik team
    Do you want to have your say in the Sitefinity development roadmap? Do you want to know when a feature you requested is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  11. Eric
    Eric avatar
    60 posts
    Registered:
    24 Feb 2011
    06 Feb 2012
    Link to this post
    I'm sorry, I meant the SiblingID Guid, they are all 0's. I hope the screenshot helps.
    I followed this post:
    http://blog.falafel.com/blogs/noelrice/11-06-06/Dynamically_Retrieving_Sitefinity_Form_Entries.aspx 

    and I have my fields hidden with: visibility:hidden !important;
    They pass the data I need well.

    I can still get their data programatically, but because I know what they are called. I would like to spin through the fields in order somehow, not knowing what they are.
  12. Tony
    Tony avatar
    5 posts
    Registered:
    09 Sep 2004
    06 Feb 2012
    Link to this post
    One possibility: In getting this to work I found that 'siblingId' only applies to form elements within the same container.

    The very first form element of a container gets a siblingId of an all-zero GUID (essentially an empty GUID). So your data suggests that you have form elements in different containers. So you'd need to place all your fields in the same container, and use a different technique to CSS hide them (say giving them all a certain class, or something like that).
  13. Eric
    Eric avatar
    60 posts
    Registered:
    24 Feb 2011
    06 Feb 2012
    Link to this post
    Yes, I have them in a different container, because even though I hide them, they still take up space at the bottom of the page. Because of this, I put them in as small text boxes, into a container with 5 columns, so they are as compact as possible. If I use display: none, they won't be rendered at all, will they? I'll have to think about it some more, thank you!

    Eric
  14. Nicolas
    Nicolas avatar
    9 posts
    Registered:
    12 Apr 2012
    20 Apr 2012
    Link to this post
    I'm brand new to Sitefinity and have been tasked to find a solution to have a user print form results. This sounds almost exactly like what we need, however I'm not sure how to go about implementing it. Can someone please point me in the right direction so we can try to use Tony's solution?

    Thanks!
    -Nic
  15. Mike
    Mike avatar
    3 posts
    Registered:
    10 Mar 2014
    06 Oct
    Link to this post

    Tony's solution is good and appreciated, but it is incomplete. It only works if grid layouts are NOT used in the form.

    What is the correct solution to find the first (sorted) control in the form (instead of the Guid.Empy only approach)?

15 posts, 0 answered