locked
Html content input with validation RRS feed

  • Question

  • User-1254308814 posted
    <FORM id=form1 runat="server">

    About time I got it to work.  

    1. Install FreeTextBox by putting the dll in the bin and the FreeTexBox foler in the root of the web application. Add the AjaxControlToolKit dll to your application bin. No other configuration is required for FreeText for this scenario.

     

    2. Next add the following usercontrol to your project.

     

    FreeEdit.ascx

     

    <%@ Control Language="C#" AutoEventWireup="true" CodeFile="FreeEdit.ascx.cs" Inherits="DynamicData_Controls_FreeEdit" %>

    <%@ Register Assembly="FreeTextBox" Namespace="FreeTextBoxControls" TagPrefix="FTB" %>

    <%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="ajax" %>

    <br />

    <asp:Panel ID="EditPanel1" BackColor="White" BorderWidth="4" BorderColor="ActiveBorder"

        runat="server" Style="padding: 5px;">

        <FTB:FreeTextBox ID="FreeTextBox1" SupportFolder="~/FreeTextBox" JavaScriptLocation="ExternalFile"

            ToolbarImagesLocation="ExternalFile" ButtonImagesLocation="ExternalFile" ClientSideTextChanged="client_textChanged"

            runat="server" OnLoad="FreeTextBox1_Load">

        </FTB:FreeTextBox>

        <div style="clear: both">

            <asp:Button ID="btnSave" runat="server" Text="Ok" />

        </div>

    </asp:Panel>

    <ajax:ModalPopupExtender ID="modalFck1" OkControlID="btnSave" Drag="true" BackgroundCssClass="modalBackground"

        runat="server" PopupControlID="EditPanel1" DropShadow="false" TargetControlID="txtEditor">

    </ajax:ModalPopupExtender>

    <asp:LinkButton ID="txtEditor" runat="server" Text="Edit"></asp:LinkButton>

     

    Notice that there is a ClientSideTextChanged event with the "client_textChanged" event attached. The FreeTextBox instance is passed to the associated javascript function which will be implemented in a moment.

     

    FreeEdit.ascx.cx

     

    using System;

    using System.Data;

    using System.Configuration;

    using System.Collections;

    using System.Linq;

    using System.Web;

    using System.Web.Security;

    using System.Web.UI;

    using System.Web.UI.WebControls;

    using System.Web.UI.WebControls.WebParts;

    using System.Web.UI.HtmlControls;

    using System.Xml.Linq;

    using System.Web.DynamicData;

    using System.ComponentModel;

     

     

    public partial class DynamicData_Controls_FreeEdit : System.Web.UI.UserControl

    {

        protected FreeTextBoxControls.FreeTextBox freeTextBox1 = null;

     

        protected void Page_Load(object sender, EventArgs e)

        {

            String script1 = @"   

            function client_textChanged(objA) {

                var ctrlID = $get('hdn').value;

                $get(ctrlID).value = escapeHTMLEncode(obj.GetHtml());

            }

            function escapeHTMLEncode(str) {

                var div = document.createElement('div');

                var text = document.createTextNode(str);

                div.appendChild(text);

                return div.innerHTML;

            } ";

     

            Page.ClientScript.RegisterClientScriptBlock(Page.GetType(), "textChangedScript1", script1, true);

     

            // Add the reference to the Details view.

            this.DataBind();

     

            TextBox txtField = DetailsView1.FindFieldTemplate(FieldName).Controls[0] as TextBox;

            Page.ClientScript.RegisterHiddenField("hdn", txtField.ClientID);

            txtField.PreRender += new EventHandler(txtField_PreRender);

        }

     

        protected void FreeTextBox1_Load(object sender, EventArgs e)

        {

            freeTextBox1 = sender as FreeTextBoxControls.FreeTextBox;

        }

     

        void txtField_PreRender(object sender, EventArgs e)

        {

            TextBox tb = sender as TextBox;

            freeTextBox1.Text = HttpUtility.HtmlDecode(tb.Text);

        }

     

     

        public DetailsView DetailsView1 { get; set; }

        public String FieldName { get; set; }

    }

     

     

    The scripts that are registered when the control loads will take care of updating the textbox as the user types. They also html encode the content so that the validation doesn't break down with a dangerous request exception. The this.Databind(); binds the user control which makes the DetailsView1 reference available to code in this class.

     

    The textbox(TextBox1) instance is the first control in the input field in the following  If you customize the template you may have to change which control is referenced shown as TextBox1 in the example code above. code. We set the EventHandler so that we can pass the text from the Textbox to the FreeTextBox when it has finally gotten populated from the database. As you can see a reference to the FreeTextBox1 instance is kept from the FreeTextBox1_Load event.

     

    The hdn is a hidden field that is used to hold the value of the clientid of the Textbox that you will be sending htmlEncoded text to.

     

    Finally, the two properties are used to help find the correct field for text inserting/updating.

     

     

    3. Add the template field to the desired insert or update custom page as shown below in the custom page.  The FreeText.ascx control requires two parameters. First, the name of the DataField in the control in the template as shown below and next, a databinding that acts as a reference to the DetailsView1 unless you changed the name.

     

     

    <%@ Page Language="C#" MasterPageFile="~/Site.master" CodeFile="Insert.aspx.cs" Inherits="Insert" %>

    <%@ Register src="~/DynamicData/Controls/FreeEdit.ascx" tagname="Editor" tagprefix="bosi" %>

    <%@ Register Assembly="FreeTextBox" Namespace="FreeTextBoxControls" TagPrefix="FTB" %>

    <%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="ajax" %>

    <%@ Register Assembly="System.Web.Entity, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" Namespace="System.Web.UI.WebControls" tagprefix="asp" %>

    <asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">

        <asp:DynamicDataManager ID="DynamicDataManager1" runat="server" AutoLoadForeignKeys="true" />

     

        <h2>Add new entry to table <%= table.DisplayName %></h2>

     

        <asp:ScriptManagerProxy runat="server" ID="ScriptManagerProxy1" />

     

        <asp:UpdatePanel ID="UpdatePanel1" runat="server">

            <ContentTemplate>

                <asp:ValidationSummary ID="ValidationSummary1" runat="server" EnableClientScript="true"

                    HeaderText="List of validation errors" />

                <asp:DynamicValidator runat="server" ID="DetailsViewValidator" ControlToValidate="DetailsView1" Display="None" />

     

                <asp:DetailsView ID="DetailsView1" runat="server"

                    DataSourceID="DetailsDataSource" DefaultMode="Insert"

                    AutoGenerateRows="False"

                    OnItemCommand="DetailsView1_ItemCommand"

                    CssClass="detailstable" FieldHeaderStyle-CssClass="bold">

                    <FieldHeaderStyle CssClass="bold" />

                    <Fields>

                        <asp:DynamicField DataField="ID" /> 

                        <asp:TemplateField ConvertEmptyStringToNull="False" HeaderText="Description"

                            SortExpression="Bio">

                            <EditItemTemplate>

                                <asp:DynamicControl ID="DynamicControlDescription" runat="server" DataField="Description" Mode="Edit" />

                                <br />

                                <bosi:Editor ID="editor1" FieldName="Description"  DetailsView1='<%# DetailsView1 %>' runat="server" />

                            </EditItemTemplate>

                        </asp:TemplateField>

                        <asp:DynamicField DataField="Keyword"  />

                        <asp:DynamicField DataField="Sequence" />

                        <asp:DynamicField DataField="IsActive" />

                        <asp:DynamicField DataField="IsDefault" />

                    </Fields>   

                </asp:DetailsView>

                <asp:EntityDataSource ID="DetailsDataSource" runat="server" EnableInsert="true">

                </asp:EntityDataSource>

            </ContentTemplate>

        </asp:UpdatePanel>

    </asp:Content>

     

    I most definitely has to be changed if you want to have multiple instances on the same page but it would be a simple modification in a couple places. Maybe passing in a id to the hidden control for each instance and hooking it up would work.

     

    You might also want to go into the Site.css and add this style so that the FreeTextBox formatting doesn't get all messed up. 

    table.detailstable td td

    {

    border:0px;

    padding:0px;

    }

    and if you want to hide the stuff behind while you are editing.
     
    .modalBackground 
    {
    	background-color:#eee
    }
     

     

    </FORM>
    Saturday, October 25, 2008 3:42 AM

All replies

  • User-330204900 posted

    <STRIKE>

    FreeTextBox did the trick.
    </STRIKE>

    <STRIKE>Which FreeTextBox is it and is it free?</STRIKE>

    Got it from your other post [:D]

    Saturday, October 25, 2008 12:17 PM
  • User-1254308814 posted

    A couple of notes that might make things work a little better -- things that I missed. Changing the causesvalidation="false" will stop all the validators from popping up everytime you use the editor.

    <asp:Button ID="btnSave" runat="server" Text="Ok" CausesValidation="false" />

    The following change to the display:none will make it so the editor doesn't flash when you navigate to the insert/edit pages.

    <asp:Panel ID="EditPanel1" BackColor="White" BorderWidth="4" BorderColor="ActiveBorder"

        runat="server" Style="padding: 5px; display:none;">

    <!--EndFragment-->

    The following is very important because the insert or edit will fail if there is leftover content in the editor when you click the respective button. It causes an annoying 500 error which is not straight forward for all of us to seek out. By the way, you can break into script debugging mode and examine the this object and find that the Validation found dangerous code and couldn't make the submission. The ftb variable is a document level variable that is left available from the instantiation of the FreeTextBox instance. The is probably a more reliable way to find that insert button but I haven't found it yet.

        Int32 rowIndex = DetailsView1.Rows.Count - 1;

        LinkButton insertButton = (LinkButton)DetailsView1.Rows[rowIndex].Cells[0].Controls[0];

        insertButton.OnClientClick = "ftb.SetHtml('');";

     

    I think that is it. But please ask if you notices anything different.  

     

    Saturday, October 25, 2008 12:24 PM
  • User-1254308814 posted

     

    As an addition bonus I wanted to see if there was an easy way to make this available as an attribute so I remapped this good article(Dynamic Data - Hiding Columns in selected PageTemplates) into my requirements. Thanks for that! By adding the following classes I was able to get it working as I had intended.

    ExtensionMethods.cs

    using System;

    using System.Web.DynamicData;

    using System.Linq;

     

    public static partial class ExtensionMethods

    {

        public static Boolean HasHtmlEditor(this MetaColumn table, PageTemplateType currentPage, String fieldName)

        {

            var hasHtmlEditorIn = table.Attributes.OfType<HtmlTextEditorAttribute>().

                DefaultIfEmpty(new HtmlTextEditorAttribute(fieldName)).First() as HtmlTextEditorAttribute;

            return hasHtmlEditorIn.PageTemplates.Contains(currentPage);

        }

    }

    FreeTextBoxManager.cs

    using System.Web.DynamicData;

    using System.Collections.Generic;

    using System.Web.UI;

    using System.Collections;

    using System.Web.UI.WebControls;

    using FreeTextBoxControls;

     

    /// <summary>

    /// Summary description for FreeTextBoxManager

    /// </summary>

    public class FreeTextBoxManager

    {

        private MetaTable m_Table = null;

        private PageTemplateType m_CurrentPage;

        private Page Context = null;

     

        public FreeTextBoxManager(Page page, MetaTable table, PageTemplateType currentPage, DetailsView view)

        {

            Context = page;

            m_Table = table;

            m_CurrentPage = currentPage;

            SetupHtmlEditor(view);

        }

     

        public void SetupHtmlEditor(DetailsView view)

        {

     

            foreach (var column in m_Table.Columns)

            {

                if (column.Scaffold && column.HasHtmlEditor(m_CurrentPage, column.Name))

                {

                    Control freeEdit = Context.LoadControl("~/DynamicData/Controls/FreeEdit.ascx");

                    IFreeEdit iFreeEdit = freeEdit as IFreeEdit;

                    if (null != iFreeEdit)

                    {  

                        iFreeEdit.FieldName = column.Name;

                        iFreeEdit.DetailsView1 = view;

     

                        view.FindFieldTemplate(column.Name).Controls.AddAt(1, freeEdit);

                    }

                }

    <!--EndFragment-->

            }

        }

    }

    <!--EndFragment--><!--EndFragment-->

    HtmlTextEditorAttribute.cs

    using System;

     

    [AttributeUsage(AttributeTargets.Property)]

    public class HtmlTextEditorAttribute : Attribute

    {

        public PageTemplateType[] PageTemplates { get; private set; }

        public String FieldName { get; set; }

        public HtmlTextEditorAttribute(String fieldName)

        {

            this.FieldName = FieldName;

            PageTemplates = new PageTemplateType[0];

        }

     

        public HtmlTextEditorAttribute(String fieldName, params PageTemplateType[] lookupTable)

        {

            this.FieldName = fieldName;

            PageTemplates = lookupTable;

        }

    }

    <!--EndFragment--><!--EndFragment-->

    IFreeEdit.cs

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Web;

    using System.Web.UI.WebControls;

     

    public interface IFreeEdit

    {

        String FieldName { get; set; }

        DetailsView DetailsView1 { get; set; }

    }

     

     

    PageTemplateType.cs

     

    public enum PageTemplateType

    {

        Edit,

        Insert

        // add any custom page templates here

    }

    <!--EndFragment--> 


     

    After this is all said and done you have to add one more line of code to the insert or edit code-files to get the attributes working. This should happen in the Page_Load event handler in the Insert.aspx and  in the Page_Event in the Edit.aspx template pages.

    Insert.aspx

    protected override void OnLoadComplete(EventArgs e)
    {
         base.OnLoadComplete(e);
         new FreeTextBoxManager(this.Page, table, PageTemplateType.Insert, DetailsView1);
    }
     

    Edit.aspx
    protected
    override void OnLoadComplete(EventArgs e)
    {
         base.OnLoadComplete(e);
         new FreeTextBoxManager(this.Page, table, PageTemplateType.Edit, DetailsView1);
    }

    <!--EndFragment--> Then you can just start decorate the field in the metadata class to get the editor to show up.

            [HtmlTextEditor("Description", PageTemplateType.Insert)]

            public Object Description { get; set; }

    <!--EndFragment-->

     Hopefully, this works for someone else too [:)]

    If it doesn't work for you it is probably because I left something out. But here is a link to the files.

    <!--EndFragment-->
    <!--EndFragment-->
    Sunday, October 26, 2008 4:44 AM
  • User-1254308814 posted

     

    I guess I was doing things the hard way. And the winner is........ drum roll. The way it is supposed to work. I just didn't know where to find an easy example of how to use the UIHintAttribute and my mind told me that is was possible do to the other way. This has been a great lesson in the dynamic data framwork for me at any rate.  

    1. Put FreeTextBox dll in the bin and put the FreeTextBox folder in the root.

    2. Add a reference to the ajaxtoolkitdll as well.

    3. Put the following user control in the FieldTemplates folder with the filenames as shown.

     

    FreeTextBox_Edit.ascx.cs 

    <%@ Control Language="C#" AutoEventWireup="true" CodeFile="FreeTextBox_Edit.ascx.cs" Inherits="<yournamespace>.FreeTextBoxEditor" %>

    <%@ Register Assembly="FreeTextBox" Namespace="FreeTextBoxControls" TagPrefix="FTB" %>

    <%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="ajax" %>

    <asp:TextBox ID="TextBox1" runat="server" Height="120px" TextMode="MultiLine"

        Width="515px"></asp:TextBox>

    <br />

    <asp:Panel ID="EditPanel1" BackColor="White" BorderWidth="4" BorderColor="ActiveBorder"

        runat="server" Style="padding: 5px; display:none;">

        <FTB:FreeTextBox ID="FreeTextBox1" SupportFolder="~/FreeTextBox" JavaScriptLocation="ExternalFile"

            ToolbarImagesLocation="ExternalFile" ButtonImagesLocation="ExternalFile" ClientSideTextChanged="client_textChanged"

            runat="server" AllowHtmlMode="false" OnLoad="FreeTextBox1_Load">

        </FTB:FreeTextBox>

        <div style="clear: both">

            <asp:Button ID="btnSave" runat="server" Text="Ok" CausesValidation="false" />

        </div>

    </asp:Panel>

    <ajax:ModalPopupExtender ID="modalFck1" OkControlID="btnSave" Drag="true" BackgroundCssClass="modalBackground"

        runat="server" PopupControlID="EditPanel1" DropShadow="false" TargetControlID="txtEditor">

    </ajax:ModalPopupExtender>

    <asp:LinkButton ID="txtEditor" runat="server" Text="Edit"></asp:LinkButton>

    <!--EndFragment-->

     FreeTextBox_Edit.ascx

    using System;

    using System.Data;

    using System.Configuration;

    using System.Collections;

    using System.Collections.Generic;

    using System.Linq;

    using System.Web;

    using System.Web.Security;

    using System.Web.UI;

    using System.Web.UI.WebControls;

    using System.Web.UI.WebControls.WebParts;

    using System.Web.UI.HtmlControls;

    using System.Xml.Linq;

    using System.Web.DynamicData;

    using System.ComponentModel;

     

    namespace <yournamespace>

    {

     

        public partial class FreeTextBoxEditor : FieldTemplateUserControl

        {

            protected FreeTextBoxControls.FreeTextBox freeTextBox1 = null;

     

            protected void Page_Load(object sender, EventArgs e)

            {

                String script1 = @"   

            function client_textChanged(obj) {

                var ctrlID = $get('hdn').value;

                $get(ctrlID).value = escapeHTMLEncode(obj.GetHtml());

            }

            function escapeHTMLEncode(str) {

                var div = document.createElement('div');

                var text = document.createTextNode(str);

                div.appendChild(text);

                return div.innerHTML;

            } ";

     

     

                Page.ClientScript.RegisterClientScriptBlock(Page.GetType(), "textChangedScript1", script1, true);

            }

     

            protected void Page_PreRender(object sender, EventArgs e)

            {

                DetailsView1 = this.NamingContainer as DetailsView;

     

                Int32 rowIndex = DetailsView1.Rows.Count - 1;

                LinkButton insertButton = (LinkButton)DetailsView1.Rows[rowIndex].Cells[0].Controls[0];

                insertButton.OnClientClick = "ftb.SetHtml('');";

     

                TextBox txtField = DetailsView1.FindFieldTemplate(this.Column.Name).Controls[0] as TextBox;

                txtField.CausesValidation = false;

                Page.ClientScript.RegisterHiddenField("hdn", txtField.ClientID);

                txtField.PreRender += new EventHandler(txtField_PreRender);

            }

     

            public void txtField_PreRender(object sender, EventArgs e)

            {

                TextBox tb = sender as TextBox;

                freeTextBox1.Text = HttpUtility.HtmlDecode(tb.Text);

            }

     

            public void FreeTextBox1_Load(object sender, EventArgs e)

            {

                freeTextBox1 = sender as FreeTextBoxControls.FreeTextBox;

            }

     

            public String FieldName { get; set; }

            public DetailsView DetailsView1 { get; set; }

        }

    }

     

     

    4. Create a MetaClass for your entity. ex:

     

    Entity and Metadata Class

     

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Web;

    using System.ComponentModel.DataAnnotations;

    using System.Web.UI.WebControls;

    using System.Web.DynamicData;

     

    namespace <yournamespace>

    {

        /// <summary>

        /// Summary description for Class1

        /// </summary>

     

        [MetadataType(typeof(TaskMetaData))]

        public partial class Task

        {

     

        }

     

        public class TaskMetaData

        {

            [UIHint("FreeTextBox", "Insert,Edit")]

            public Object Description { get; set; }

        }

    }

    <!--EndFragment--> 

     

    5. Add the following attribute to your properties as required. 

     

            [UIHint("FreeTextBox", "Edit,Insert")]

            public Object Description { get; set; }<!--EndFragment-->

     

    6. Fix up the formatting in the editor. 

    table.detailstable td td

    {

    border:0px;padding:0px;

    }

    7.  and if you want to hide the stuff behind while you are editing.
    .modalBackground 
    {
    	background-color:#eee
    }

    8. (Optional) Modify the code to allow multiple instances on the same form.

     

    Have fun and Good Luck!

     

    9. Give a round of applause for the people who designed this dynamic data architecture!

       "Clapping! WOoooooh! WHISTLE from the background!... goes on for a minute."

     

    Monday, October 27, 2008 12:43 AM