diff --git a/components/editorControls/MultipleTextstring/MultipleTextstringControl.cs b/components/editorControls/MultipleTextstring/MultipleTextstringControl.cs
new file mode 100644
index 0000000000..28083636d0
--- /dev/null
+++ b/components/editorControls/MultipleTextstring/MultipleTextstringControl.cs
@@ -0,0 +1,187 @@
+using System;
+using System.Collections.Generic;
+using System.Web.UI;
+using System.Web.UI.WebControls;
+using umbraco;
+using umbraco.cms.businesslogic.datatype;
+
+[assembly: WebResource("umbraco.editorControls.MultipleTextstring.MultipleTextstring.css", "text/css")]
+[assembly: WebResource("umbraco.editorControls.MultipleTextstring.MultipleTextstring.js", "application/x-javascript")]
+
+namespace umbraco.editorControls.MultipleTextstring
+{
+ ///
+ /// The MultipleTextstring control sets a character limit on a TextBox.
+ ///
+ [ValidationProperty("IsValid")]
+ public class MultipleTextstringControl : PlaceHolder
+ {
+ ///
+ /// Field for the list of values.
+ ///
+ private List values;
+
+ ///
+ /// The HiddenField to store the selected values.
+ ///
+ private HiddenField SelectedValues = new HiddenField();
+
+ ///
+ /// Gets or sets the options.
+ ///
+ /// The options.
+ public MultipleTextstringOptions Options { get; set; }
+
+ ///
+ /// Gets the value of IsValid.
+ ///
+ /// Returns 'Valid' if valid, otherwise an empty string.
+ public string IsValid
+ {
+ get
+ {
+ if (!string.IsNullOrEmpty(this.Values))
+ {
+ return "Valid";
+ }
+
+ return string.Empty;
+ }
+ }
+
+ ///
+ /// Gets or sets the values.
+ ///
+ /// The values.
+ public string Values
+ {
+ get
+ {
+ return this.SelectedValues.Value;
+ }
+
+ set
+ {
+ this.SelectedValues.Value = value;
+ }
+ }
+
+ ///
+ /// Initialize the control, make sure children are created
+ ///
+ /// An object that contains the event data.
+ protected override void OnInit(EventArgs e)
+ {
+ base.OnInit(e);
+
+ this.EnsureChildControls();
+ }
+
+ ///
+ /// Add the resources (sytles/scripts)
+ ///
+ /// The object that contains the event data.
+ protected override void OnLoad(EventArgs e)
+ {
+ base.OnLoad(e);
+
+ // Adds the client dependencies.
+ this.AddResourceToClientDependency("umbraco.editorControls.MultipleTextstring.MultipleTextstring.css", ClientDependencyType.Css);
+ this.AddResourceToClientDependency("umbraco.editorControls.MultipleTextstring.MultipleTextstring.js", ClientDependencyType.Javascript);
+ }
+
+ ///
+ /// Raises the event.
+ ///
+ /// An object that contains the event data.
+ protected override void OnPreRender(EventArgs e)
+ {
+ base.OnPreRender(e);
+
+ // initalise the string array/list.
+ this.values = new List();
+
+ // load the values into a string array/list.
+ if (!string.IsNullOrEmpty(this.Values))
+ {
+ this.values.AddRange(this.Values.Split(new[] { Environment.NewLine }, StringSplitOptions.None));
+ }
+
+ // check the minimum number allowed, add extra fields.
+ if (this.values.Count < this.Options.Minimum && this.Options.Minimum > 1)
+ {
+ this.values.AddRange(new string(',', this.Options.Minimum - 1).Split(new[] { ',' }, StringSplitOptions.None));
+ }
+
+ // check the maxmimum number allowed, remove the excess.
+ if (this.values.Count > this.Options.Maximum && this.Options.Maximum > 0)
+ {
+ this.values.RemoveRange(this.Options.Maximum, this.values.Count - this.Options.Maximum);
+ }
+
+ // if there are no selected values...
+ if (this.values.Count == 0)
+ {
+ // ... then add an empty string to display a single textstring box.
+ this.values.Add(string.Empty);
+ }
+ }
+
+ ///
+ /// Called by the ASP.NET page framework to notify server controls that use composition-based implementation to create any child controls they contain in preparation for posting back or rendering.
+ ///
+ protected override void CreateChildControls()
+ {
+ base.CreateChildControls();
+
+ this.EnsureChildControls();
+
+ // populate the control's attributes.
+ this.SelectedValues.ID = this.SelectedValues.ClientID;
+
+ // add the controls.
+ this.Controls.Add(this.SelectedValues);
+ }
+
+ ///
+ /// Sends server control content to a provided object, which writes the content to be rendered on the client.
+ ///
+ /// The object that receives the server control content.
+ protected override void Render(HtmlTextWriter writer)
+ {
+ writer.AddAttribute(HtmlTextWriterAttribute.Class, "MultipleTextstring");
+ writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID);
+ writer.RenderBeginTag(HtmlTextWriterTag.Div);
+
+ // loop through each value
+ foreach (string value in this.values)
+ {
+ writer.AddAttribute(HtmlTextWriterAttribute.Class, "textstring-row");
+ writer.RenderBeginTag(HtmlTextWriterTag.Div);
+
+ // input tag
+ writer.AddAttribute(HtmlTextWriterAttribute.Class, "textstring-row-field");
+ writer.RenderBeginTag(HtmlTextWriterTag.Div);
+ writer.WriteLine("", value.Replace("'", "'"));
+
+ // append the add/remove buttons
+ writer.WriteLine(" ", GlobalSettings.Path);
+ writer.WriteLine(" ", GlobalSettings.Path);
+ writer.RenderEndTag(); // .textstring-row-field
+
+ writer.WriteLine("", GlobalSettings.Path);
+
+ writer.RenderEndTag(); // .textstring-row
+ }
+
+ this.SelectedValues.RenderControl(writer);
+
+ writer.RenderEndTag(); // .MultipleTextstring
+
+ // add jquery window load event
+ var javascriptMethod = string.Format("jQuery('#{0}').MultipleTextstring('#{1}', {2}, {3});", this.ClientID, this.SelectedValues.ClientID, this.Options.Minimum, this.Options.Maximum);
+ var javascript = string.Concat("");
+ writer.WriteLine(javascript);
+ }
+ }
+}
\ No newline at end of file
diff --git a/components/editorControls/MultipleTextstring/MultipleTextstringDataType.cs b/components/editorControls/MultipleTextstring/MultipleTextstringDataType.cs
new file mode 100644
index 0000000000..75ea3a9d53
--- /dev/null
+++ b/components/editorControls/MultipleTextstring/MultipleTextstringDataType.cs
@@ -0,0 +1,135 @@
+using System;
+using umbraco.cms.businesslogic.datatype;
+using umbraco.interfaces;
+
+namespace umbraco.editorControls.MultipleTextstring
+{
+ ///
+ /// Data Editor for the Multiple Textstring data type.
+ ///
+ public class MultipleTextstringDataType : AbstractDataEditor
+ {
+ ///
+ /// The control for the Multiple Textstring data-editor.
+ ///
+ private MultipleTextstringControl m_Control = new MultipleTextstringControl();
+
+ ///
+ /// The Data object for the data-type.
+ ///
+ private IData m_Data;
+
+ ///
+ /// The PreValue Editor for the data-type.
+ ///
+ private IDataPrevalue m_PreValueEditor;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MultipleTextstringDataType()
+ {
+ // set the render control as the placeholder
+ this.RenderControl = this.m_Control;
+
+ // assign the initialise event for the control
+ this.m_Control.Init += new EventHandler(this.m_Control_Init);
+
+ // assign the save event for the data-type/editor
+ this.DataEditorControl.OnSave += new AbstractDataEditorControl.SaveEventHandler(this.DataEditorControl_OnSave);
+ }
+
+ ///
+ /// Gets the id of the data-type.
+ ///
+ /// The id of the data-type.
+ public override Guid Id
+ {
+ get
+ {
+ return new Guid(DataTypeGuids.MultipleTextstringId);
+ }
+ }
+
+ ///
+ /// Gets the name of the data type.
+ ///
+ /// The name of the data type.
+ public override string DataTypeName
+ {
+ get
+ {
+ return "Multiple Textstring";
+ }
+ }
+
+ ///
+ /// Gets the data for the data-type.
+ ///
+ /// The data for the data-type.
+ public override IData Data
+ {
+ get
+ {
+ if (this.m_Data == null)
+ {
+ this.m_Data = new CsvToXmlData(this, "values", "value", new[] { Environment.NewLine });
+ }
+
+ return this.m_Data;
+ }
+ }
+
+ ///
+ /// Gets the prevalue editor.
+ ///
+ /// The prevalue editor.
+ public override IDataPrevalue PrevalueEditor
+ {
+ get
+ {
+ if (this.m_PreValueEditor == null)
+ {
+ this.m_PreValueEditor = new MultipleTextstringPrevalueEditor(this);
+ }
+
+ return this.m_PreValueEditor;
+ }
+ }
+
+ ///
+ /// Handles the Init event of the control.
+ ///
+ /// The source of the event.
+ /// The instance containing the event data.
+ private void m_Control_Init(object sender, EventArgs e)
+ {
+ var options = ((MultipleTextstringPrevalueEditor)this.PrevalueEditor).GetPreValueOptions();
+
+ if (options == null)
+ {
+ // load defaults
+ options = new MultipleTextstringOptions(true);
+ }
+
+ // check if the data value is available...
+ if (this.Data.Value != null)
+ {
+ // set the value of the control
+ this.m_Control.Values = this.Data.Value.ToString();
+ }
+
+ // set the controls options
+ this.m_Control.Options = options;
+ }
+
+ ///
+ /// Saves the data for the editor control.
+ ///
+ /// The instance containing the event data.
+ private void DataEditorControl_OnSave(EventArgs e)
+ {
+ this.Data.Value = this.m_Control.Values;
+ }
+ }
+}
diff --git a/components/editorControls/MultipleTextstring/MultipleTextstringOptions.cs b/components/editorControls/MultipleTextstring/MultipleTextstringOptions.cs
new file mode 100644
index 0000000000..170950f8b0
--- /dev/null
+++ b/components/editorControls/MultipleTextstring/MultipleTextstringOptions.cs
@@ -0,0 +1,41 @@
+using System.ComponentModel;
+using umbraco.cms.businesslogic.datatype;
+
+namespace umbraco.editorControls.MultipleTextstring
+{
+ ///
+ /// The options for the Multiple Textstring data-type.
+ ///
+ public class MultipleTextstringOptions : AbstractOptions
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MultipleTextstringOptions()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// if set to true [load defaults].
+ public MultipleTextstringOptions(bool loadDefaults)
+ : base(loadDefaults)
+ {
+ }
+
+ ///
+ /// Gets or sets the maximum.
+ ///
+ /// The maximum.
+ [DefaultValue(-1)]
+ public int Maximum { get; set; }
+
+ ///
+ /// Gets or sets the minimum.
+ ///
+ /// The minimum.
+ [DefaultValue(1)]
+ public int Minimum { get; set; }
+ }
+}
diff --git a/components/editorControls/MultipleTextstring/MultipleTextstringPrevalueEditor.cs b/components/editorControls/MultipleTextstring/MultipleTextstringPrevalueEditor.cs
new file mode 100644
index 0000000000..1912601e2a
--- /dev/null
+++ b/components/editorControls/MultipleTextstring/MultipleTextstringPrevalueEditor.cs
@@ -0,0 +1,118 @@
+using System;
+using System.Web.UI;
+using System.Web.UI.WebControls;
+// using uComponents.DataTypes.Shared.Extensions;
+// using uComponents.DataTypes.Shared.PrevalueEditors;
+using umbraco.cms.businesslogic.datatype;
+
+namespace umbraco.editorControls.MultipleTextstring
+{
+ ///
+ /// The PreValue Editor for the Multiple Textstring data-type.
+ ///
+ public class MultipleTextstringPrevalueEditor : AbstractJsonPrevalueEditor
+ {
+ ///
+ /// The TextBox control for the maximum value of the control.
+ ///
+ private TextBox TextBoxMaximum;
+
+ ///
+ /// The TextBox control for the minimum value of the control.
+ ///
+ private TextBox TextBoxMinimum;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Type of the data.
+ public MultipleTextstringPrevalueEditor(umbraco.cms.businesslogic.datatype.BaseDataType dataType)
+ : base(dataType, umbraco.cms.businesslogic.datatype.DBTypes.Ntext)
+ {
+ }
+
+ ///
+ /// Saves this instance.
+ ///
+ public override void Save()
+ {
+ // set the options
+ var options = new MultipleTextstringOptions(true);
+
+ // parse the maximum
+ int maximum;
+ if (int.TryParse(this.TextBoxMaximum.Text, out maximum))
+ {
+ if (maximum == 0)
+ {
+ maximum = -1;
+ }
+
+ options.Maximum = maximum;
+ }
+
+ // parse the minimum
+ int minimum;
+ if (int.TryParse(this.TextBoxMinimum.Text, out minimum))
+ {
+ if (minimum == 0)
+ {
+ minimum = -1;
+ }
+
+ options.Minimum = minimum;
+ }
+
+ // save the options as JSON
+ this.SaveAsJson(options);
+ }
+
+ ///
+ /// Called by the ASP.NET page framework to notify server controls that use composition-based implementation to create any child controls they contain in preparation for posting back or rendering.
+ ///
+ protected override void CreateChildControls()
+ {
+ base.CreateChildControls();
+
+ // set-up child controls
+ this.TextBoxMaximum = new TextBox() { ID = "Maximum", CssClass = "guiInputText" };
+ this.TextBoxMinimum = new TextBox() { ID = "Minimum", CssClass = "guiInputText" };
+
+ // add the child controls
+ this.Controls.AddPrevalueControls(this.TextBoxMaximum, this.TextBoxMinimum);
+ }
+
+ ///
+ /// Raises the event.
+ ///
+ /// The object that contains the event data.
+ protected override void OnLoad(EventArgs e)
+ {
+ base.OnLoad(e);
+
+ // get PreValues, load them into the controls.
+ var options = this.GetPreValueOptions();
+
+ // no options? use the default ones.
+ if (options == null)
+ {
+ options = new MultipleTextstringOptions(true);
+ }
+
+ // set the values
+ this.TextBoxMaximum.Text = options.Maximum.ToString();
+ this.TextBoxMinimum.Text = options.Minimum.ToString();
+ }
+
+ ///
+ /// Renders the contents of the control to the specified writer. This method is used primarily by control developers.
+ ///
+ /// A that represents the output stream to render HTML content on the client.
+ protected override void RenderContents(HtmlTextWriter writer)
+ {
+ // add property fields
+ writer.AddPrevalueRow("Minimum:", "Minimum number of rows to display.", this.TextBoxMinimum);
+ writer.AddPrevalueRow("Maximum:", "Maximum number of rows to display.", this.TextBoxMaximum);
+ }
+ }
+}