Using RadGrid inside Sitefinity Form Widget
Hi!
I'm trying to embed a RadGrid in a Sitefinity Form Widget, so i can add a field to a form which allows users to create a collection of attendees that will attend a specific event. The process works fine, until I try to integrate the code into a FieldControl, then neither the insert or update command events will fire, meaning i can never add or update rows.
I've attached code, any ideas? I'm fresh out!
Attendees.ascx
<%@ Control %><%@ Register Assembly="Telerik.Sitefinity" TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI" %><asp:Label runat="server" ID="titleLabel" CssClass="sfTxtLbl" Text="title label" AssociatedControlID="TextBox1" /><div class="sfFieldWrp"> <asp:TextBox ID="TextBox1" CssClass="sfTxt" runat="server" /> <telerik:RadAjaxManager ID="RadAjaxManager1" runat="server"> <AjaxSettings> <telerik:AjaxSetting AjaxControlID="RadGrid1"> <UpdatedControls> <telerik:AjaxUpdatedControl ControlID="RadGrid1" LoadingPanelID="RadAjaxLoadingPanel1"> </telerik:AjaxUpdatedControl> </UpdatedControls> </telerik:AjaxSetting> </AjaxSettings> </telerik:RadAjaxManager> <telerik:RadGrid ID="attendees" runat="server" AutoGenerateColumns="False" AllowSorting="false" AllowFilteringByColumn="false"> <ItemStyle HorizontalAlign="Center"></ItemStyle> <AlternatingItemStyle HorizontalAlign="Center"></AlternatingItemStyle> <HeaderStyle HorizontalAlign="Center"></HeaderStyle> <MasterTableView CommandItemDisplay="Top" EditMode="InPlace" DataKeyNames="ID"> <Columns> <telerik:GridTemplateColumn UniqueName="FirstName" HeaderText="First Name" SortExpression="FirstName"> <ItemTemplate> <%# DataBinder.Eval(Container.DataItem, "FirstName") %> </ItemTemplate> <EditItemTemplate> <telerik:RadTextBox ID="firstName" runat="server" Skin="Web20" Width="100px" Text='<%# Bind("FirstName")%>'> </telerik:RadTextBox> </EditItemTemplate> </telerik:GridTemplateColumn> <telerik:GridTemplateColumn UniqueName="LastName" HeaderText="Last Name" SortExpression="LastName"> <ItemTemplate> <%# DataBinder.Eval(Container.DataItem, "LastName")%> </ItemTemplate> <EditItemTemplate> <telerik:RadTextBox ID="lastName" runat="server" Skin="Web20" Width="100px" Text='<%# Bind("LastName")%>'> </telerik:RadTextBox> </EditItemTemplate> </telerik:GridTemplateColumn> <telerik:GridTemplateColumn UniqueName="DateOfBirth" HeaderText="Date of Birth" SortExpression="DateOfBirth"> <ItemTemplate> <%# (DataBinder.Eval(Container.DataItem, "DateOfBirth")) %> </ItemTemplate> <EditItemTemplate> <telerik:RadDateInput ID="dateOfBirth" runat="server" Skin="Web20" Culture="(Default)" DbSelectedDate='<%# Bind("DateOfBirth")%>' DateFormat="dd/MM/yyyy" MinDate="01/01/1920" Width="120px"></telerik:RadDateInput> </EditItemTemplate> </telerik:GridTemplateColumn> <telerik:GridTemplateColumn UniqueName="Gender" HeaderText="Gender" SortExpression="Gender"> <ItemTemplate> <%# DataBinder.Eval(Container.DataItem, "Gender")%> </ItemTemplate> <EditItemTemplate> <telerik:RadMaskedTextBox ID="gender" Width="60px" runat="server" Skin="Web20" Text='<%# Bind("Gender")%>' Mask="<M|F|m|f>" PromptChar="_"></telerik:RadMaskedTextBox> </EditItemTemplate> </telerik:GridTemplateColumn> <telerik:GridEditCommandColumn ButtonType="ImageButton" UpdateText="Update" CancelText="Cancel" EditText="Edit"> </telerik:GridEditCommandColumn> </Columns> </MasterTableView> </telerik:RadGrid> <sf:SitefinityLabel runat="server" ID="descriptionLabel" WrapperTagName="div" CssClass="sfDescription"/> <sf:SitefinityLabel runat="server" ID="exampleLabel" WrapperTagName="div" CssClass="sfExample"/></div>using System;using System.Collections.Generic;using System.ComponentModel;using System.Linq;using System.Web.UI;using System.Web.UI.WebControls;using Telerik.Sitefinity.Data.Metadata;using Telerik.Sitefinity.Metadata.Model;using Telerik.Sitefinity.Model;using Telerik.Sitefinity.Modules.Forms.Web.UI.Fields;using Telerik.Sitefinity.Security;using Telerik.Sitefinity.Web.UI;using Telerik.Sitefinity.Web.UI.ControlDesign;using Telerik.Sitefinity.Web.UI.Fields;using Telerik.Sitefinity.Web.UI.Fields.Enums;using System.Data;using Telerik.Web.UI;using System.Collections;using System.Text;using System.Web;namespace SitefinityWebApp.Custom.Forms.Atendees /// <summary> /// Class used to create custom control for Form Builder /// </summary> /// <remarks> /// If this form widget is a part of a Sitefinity module, /// you can register it in the site's toolbox by adding this to the module's Install/Upgrade method(s): /// initializer.Installer /// .Toolbox(CommonToolbox.FormWidgets) /// .LoadOrAddSection(SectionName) // "TwoColumns" is Sitefinity's default /// .SetTitle(SectionTitle) // When creating a new section /// .SetDescription(SectionDescription) // When creating a new section /// .LoadOrAddWidget<Attendees>("Attendees") /// .SetTitle("Attendees") /// .SetDescription("Attendees") /// .LocalizeUsing<ModuleResourceClass>() // Optional /// .SetCssClass(WidgetCssClass) // You can use a css class to add an icon (this is optional) /// .Done() /// .Done() /// .Done(); /// </remarks> /// <see cref="http://www.sitefinity.com/documentation/gettingstarted/creating-custom-form-field-controls"/> [DatabaseMapping(UserFriendlyDataType.ShortText)] [PropertyEditorTitle("Attendees Properties"), Telerik.Sitefinity.Web.UI.ControlDesign.ControlDesigner(typeof(SitefinityWebApp.Custom.Forms.Atendees.Designer.AttendeesDesigner))] public class Attendees : FieldControl, IFormFieldControl #region Constructor /// <summary> /// Initializes a new instance of the Attendees class. /// </summary> public Attendees() this.Title = "Attendees"; #endregion #region Public properties (will show up in dialog) /// <summary> /// Example string /// </summary> public override string Example get; set; /// <summary> /// Title string /// </summary> public override string Title get; set; /// <summary> /// Description string /// </summary> public override string Description get; set; #endregion #region IFormFieldControl members /// <summary> /// Gets or sets MetaField property to persist data from control to the DB when form is submitted /// </summary> [TypeConverter(typeof(ExpandableObjectConverter))] public IMetaField MetaField get if (this.metaField == null) this.metaField = this.LoadDefaultMetaField(); return this.metaField; set this.metaField = value; #endregion #region Value method /// <summary> /// Get and set the value of the field. /// </summary> public override object Value get return this.TextBox1.Text; set this.TextBox1.Text = value.ToString(); #endregion #region Template /// <summary> /// Obsolete. Use LayoutTemplatePath instead. /// </summary> protected override string LayoutTemplateName get return string.Empty; /// <summary> /// Gets the layout template's relative or virtual path. /// </summary> public override string LayoutTemplatePath get if (string.IsNullOrEmpty(base.LayoutTemplatePath)) return Attendees.layoutTemplatePath; return base.LayoutTemplatePath; set base.LayoutTemplatePath = value; #endregion #region Labels on control template /// <summary> /// Gets reference to the TitleLabel /// </summary> protected internal virtual Label TitleLabel get return this.Container.GetControl<Label>("titleLabel", true); /// <summary> /// Gets reference to the DescriptionLabel /// </summary> protected internal virtual Label DescriptionLabel get return Container.GetControl<Label>("descriptionLabel", true); /// <summary> /// Gets reference to the ExampleLabel /// </summary> protected internal virtual Label ExampleLabel get return this.Container.GetControl<Label>("exampleLabel", this.DisplayMode == FieldDisplayMode.Write); /// <summary> /// Reference to the TitleControl /// </summary> protected override WebControl TitleControl get return this.TitleLabel; /// <summary> /// Reference to the DescriptionControl /// </summary> protected override WebControl DescriptionControl get return this.DescriptionLabel; /// <summary> /// Gets the reference to the control that represents the example of the field control. /// Return null if no such control exists in the template. /// </summary> /// <value></value> protected override WebControl ExampleControl get return this.ExampleLabel; #endregion #region Textbox on control /// <summary> /// Gets reference to the TextBox1 control /// </summary> protected virtual TextBox TextBox1 get return this.Container.GetControl<TextBox>("TextBox1", true); #endregion #region InitializeControls method /// <summary> /// Initializes the controls. /// </summary> /// <remarks> /// Initialize your controls in this method. Do not override CreateChildControls method. /// </remarks> protected override void InitializeControls(GenericContainer container) // Set the label values this.ExampleLabel.Text = this.Example; this.TitleLabel.Text = this.Title; this.DescriptionLabel.Text = this.Description; this.Grid.NeedDataSource += new GridNeedDataSourceEventHandler(Grid_NeedDataSource); this.Grid.UpdateCommand += new GridCommandEventHandler(Grid_UpdateCommand); this.Grid.InsertCommand += new GridCommandEventHandler(Grid_InsertCommand); this.TextBox1.Text = GetCurrentSitefinityUser(); #endregion #region Get Current User /// <summary> /// Get the full name for the currently logged-in Sitefinity user. /// </summary> /// <returns>String with full name of current user, or empty string if user not logged in.</returns> private static string GetCurrentSitefinityUser() Telerik.Sitefinity.Security.Web.UI.ProfileView pv = new Telerik.Sitefinity.Security.Web.UI.ProfileView(); Guid currentUserGuid = pv.CurrentUser.UserId; if (currentUserGuid != Guid.Empty) var userProfile = UserProfileManager.GetManager().GetUserProfiles(currentUserGuid).FirstOrDefault() as Telerik.Sitefinity.Security.Model.SitefinityProfile; if (userProfile != null) return userProfile.FirstName + " " + userProfile.LastName; return String.Empty; #endregion #region Script methods /// <summary> /// Get list of all scripts used by control /// </summary> /// <returns>List of all scripts used by control</returns> public override IEnumerable<ScriptDescriptor> GetScriptDescriptors() var descriptor = new ScriptControlDescriptor(typeof(Attendees).FullName, this.ClientID); descriptor.AddElementProperty("textbox", this.TextBox1.ClientID); descriptor.AddProperty("dataFieldName", this.MetaField.FieldName); //the field name of the corresponding widget return new[] descriptor ; /// <summary> /// Get reference to all scripts /// </summary> /// <returns>Reference to all scripts</returns> public override IEnumerable<System.Web.UI.ScriptReference> GetScriptReferences() var scripts = new List<ScriptReference>(base.GetScriptReferences()) new ScriptReference(Attendees.scriptReference), new ScriptReference("Telerik.Sitefinity.Web.UI.Fields.Scripts.FieldDisplayMode.js", "Telerik.Sitefinity"), ; return scripts; #endregion #region Private fields and constants private IMetaField metaField = null; public static readonly string layoutTemplatePath = "~/Custom/Forms/Atendees/Attendees.ascx"; public const string scriptReference = "~/Custom/Forms/Atendees/Attendees.js"; #endregion #region RadGrid private List<int> IDS get if (HttpContext.Current.Session["IDS" + Grid.ClientID] == null) HttpContext.Current.Session["IDS" + Grid.ClientID] = new List<int>(); return (List<int>)HttpContext.Current.Session["IDS" + Grid.ClientID]; /// <summary> /// Gets reference to the TextBox1 control /// </summary> protected virtual RadGrid Grid get return this.Container.GetControl<RadGrid>("attendees", true); private DataTable DataSource get if (HttpContext.Current.Session["RadGridCS" + Grid.ClientID] == null) DataTable data = new DataTable(); data.Columns.Add("ID", typeof(int)); data.Columns.Add("FirstName"); data.Columns.Add("LastName"); data.Columns.Add("DateOfBirth", typeof(DateTime)); data.Columns.Add("Gender"); data.Rows.Add(new object[] GenerateNewId(), "Mr.", "John Smith", new DateTime(1998, 01, 01), "M" ); data.Rows.Add(new object[] GenerateNewId(), "Mrs.", "Jane Brown", new DateTime(2003, 01, 01), "F" ); data.Rows.Add(new object[] GenerateNewId(), "Mr.", "Peter Johnson", new DateTime(2001, 01, 01), "M" ); data.Rows.Add(new object[] GenerateNewId(), "Ms.", "Diane Klive", new DateTime(1999, 01, 01), "F" ); data.Rows.Add(new object[] GenerateNewId(), "Mrs.", "Megan Hammer", new DateTime(1997, 01, 01), "F" ); HttpContext.Current.Session["RadGridCS" + Grid.ClientID] = data; return (DataTable)HttpContext.Current.Session["RadGridCS" + Grid.ClientID]; private int GenerateNewId() int id = 0; if (IDS.Count > 0) id = IDS.Max() + 1; IDS.Add(id); return id; protected void Grid_UpdateCommand(object source, GridCommandEventArgs e) GridEditableItem editedItem = e.Item as GridEditableItem; //Locate the changed row in the DataSource DataRow[] changedRows = DataSource.Select("ID = " + editedItem.OwnerTableView.DataKeyValues[e.Item.ItemIndex]["ID"]); Hashtable newValues = new Hashtable(); //The GridTableView will fill the values from all editable columns in the hash e.Item.OwnerTableView.ExtractValuesFromItem(newValues, editedItem); DataRow changedRow = changedRows[0]; changedRow.BeginEdit(); try foreach (DictionaryEntry entry in newValues) changedRow[(string)entry.Key] = entry.Value; changedRow.EndEdit(); catch (Exception ex) changedRow.CancelEdit(); Grid.Controls.Add(new LiteralControl("Unable to update the data. Reason: " + ex.Message)); e.Canceled = true; protected void Grid_NeedDataSource(object source, GridNeedDataSourceEventArgs e) Grid.DataSource = DataSource; protected void Grid_InsertCommand(object source, GridCommandEventArgs e) //Create new row in the DataSource DataRow newRow = this.DataSource.NewRow(); GridDataInsertItem item = (GridDataInsertItem)e.Item; //Insert new values Hashtable newValues = new Hashtable(); newValues["FirstName"] = ((RadTextBox)item["FirstName"].FindControl("firstName")).Text; newValues["LastName"] = ((RadTextBox)item["LastName"].FindControl("lastName")).Text; newValues["DateOfBirth"] = ((RadDateInput)item["DateOfBirth"].FindControl("dateOfBirth")).SelectedDate; newValues["Gender"] = ((RadMaskedTextBox)item["Gender"].FindControl("gender")).Text; //make sure that unique primary key value is generated for the inserted row newValues["ID"] = GenerateNewId(); try foreach (DictionaryEntry entry in newValues) newRow[(string)entry.Key] = entry.Value; this.DataSource.Rows.Add(newRow); this.DataSource.AcceptChanges(); catch (Exception ex) Grid.Controls.Add(new LiteralControl("Unable to update the data. Reason: " + ex.Message)); e.Canceled = true; private string DataSourceAsCSV() var result = new StringBuilder(); for (int i = 0; i < DataSource.Columns.Count; i++) result.Append(DataSource.Columns[i].ColumnName); result.Append(i == DataSource.Columns.Count - 1 ? "\n" : ","); foreach (DataRow row in DataSource.Rows) for (int i = 0; i < DataSource.Columns.Count; i++) result.Append(row[i].ToString()); result.Append(i == DataSource.Columns.Count - 1 ? "\n" : ","); string s = result.ToString(); s = s.TrimEnd(new char[] '\r', '\n' ); return s; #endregion Hello,
The code for handling grid events have to be integrated into Attendees.js which is the client side functionality for the form control and it will get executed when the form control renders in the frontend. To subscribe and handle grid events implement event handlers for the client side events of the grid in Attendees.js initializeBase function.
SitefinityWebApp.Custom.Forms.Atendees.Attendees.initializeBase(this, [element]);