Dynamically displaying Form Responses with Field Titles
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;// get formFormsManager formsManager = FormsManager.GetManager();var form = formsManager.GetFormByName("sf_testform");
// if you want all the entriesvar 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);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; // build a fieldname/title dictionaryvar 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>();// get section headersvar 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 fieldsvar 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);// 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;// 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 responseslitFormTitle.Text = form.Title;rptFormResponses.DataSource = formResponse;rptFormResponses.DataBind();Nice job Tony, much appreciated.
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),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);Response = fld.fieldName == string.Empty ? "" : entry.GetValue(fld.fieldName),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(); Ok, thanks to Telerik support, I got my answer and turned it into this :)
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.
Great job. Thanks a lot!
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!
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,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
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).
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!
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
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)?