![]() |
Web Development »
Ajax and Atlas »
Samples
Intermediate
License: The GNU Lesser General Public License
How to Create an HTML Editor for ASP.NET AJAXBy Eric Williams (winthusiasm.com)This article discusses how to create an HTML editor server control specifically for the Microsoft AJAX environment. |
C#, HTML, ASP.NET, Ajax, Dev
|
|
Advanced Search Add to IE Search |
|
|
||||||||||||||||||
Most blog, forum, and Wiki applications use an HTML editor as the primary authoring tool for site content. With this type of a control, an online user can create and edit an HTML document. The user is able to modify the text — including its format, fonts, and colors — as well as add links and images. Often, the user can also view and/or edit the HTML source.
Microsoft AJAX (ASP.NET AJAX Extensions) introduces a new implementation model for server controls with related scripts. This article discusses how to create an HTML editor server control specifically for the Microsoft AJAX environment. The reader can also download the source code, including a sample web page, and view an online demo.
Most modern browsers now support the "editing" of displayed HTML content by the user. When the document designMode property is set to true, the rendered HTML on the page can be edited by the user. While in designMode, the document's execCommand
method supports additional commands that enable "programmatic"
modification of document content. For example, passing the command
string bold as the first parameter to execCommand causes the selected text to appear bold by adding appropriate HTML tags and/or attributes.
The resources listed at the end of this article discuss designMode and execCommand
in more detail, and describe how to implement a basic HTML editor.
Also, the source code available with this article can be examined for
implementation examples.
As part of ASP.NET AJAX, Microsoft introduced a new "model" for extending the capabilities of a server control with client-side script. That model is described in an ASP.NET AJAX tutorial that should be read along with this article. In general, we create two related controls:
IScriptControl interface Sys.UI.Control (part of the client-side AJAX library) In order for ScriptManager to know how to create and initialize the related client control, we implement the new IScriptControl interface, adding two callback methods. Then, we add a few lines of code to OnPreRender and Render
to trigger those callbacks at appropriate times in the page life cycle.
The specifics are described in the tutorial, and again in the
discussion of HtmlEditor.cs below.
We also encapsulate our client-side behavior in a JavaScript class, implemented as a Microsoft AJAX client control. This permits the client control object to be created and initialized in a standard way by the client-side AJAX code. The specifics are described in the tutorial, and again in the discussion of HtmlEditor.js below.
Click the appropriate link to download the source code, including a sample web page.
Component appearance:

Click here for an online demo.
Div container for the control
Div container for each Toolbar
Select elements for dropdown lists Img elements for buttons Div container for the Design editor
IFrame element for the Design mode document Div container for the HTML editor
IFrame element for the HTML mode document
Textarea for HTML mode editing Div container for the Tabbar
Div element for each tab
Img element for the tab icon Span element for the tab text public property methods for configuration properties (colors, etc.) IScriptControl methods get and set property methods for properties passed from the server control on initialization IFrame documents designMode to true for the Design mode document Toolbar and Tabbar mouse events designMode browser to a standards-based equivalent designMode browsers The component itself contains many different HTML elements, so the server control class is derived from CompositeControl. In addition, the class must implement the IScriptControl methods:
public class HtmlEditor : CompositeControl, IScriptControl
In CompositeControl, child controls are added in the CreateChildControls method:
protected override void CreateChildControls()
{
...
CreateToolbars(...);
this.Controls.Add(CreateHtmlArea());
this.Controls.Add(CreateDesignArea());
this.Controls.Add(CreateTabbar());
this.Controls.Add(CreateUpdateArea());
base.CreateChildControls();
}
To implement the IScriptControl interface, two callback methods are required. The first, GetScriptReferences, tells ScriptManager what related script file(s) to load and from where. For this server control, we have chosen to embed the HtmlEditor.js file in our assembly resources. We tell ScriptManager the full resource path and assembly name so that it can load it from there, simplifying deployment:
protected virtual IEnumerable<ScriptReference>
GetScriptReferences()
{
ScriptReference htmlEditorReference =
new ScriptReference(
"Winthusiasm.HtmlEditor.Scripts.HtmlEditor.js",
"Winthusiasm.HtmlEditor");
...
return new ScriptReference[] { htmlEditorReference, ... };
}
The second callback method, GetScriptDescriptors, "maps" properties in the client control(s) to properties in the server control. ScriptManager uses this information to set the appropriate values in the client control as part of its client-side creation:
protected virtual IEnumerable<ScriptDescriptor>
GetScriptDescriptors()
{
ScriptControlDescriptor descriptor =
new ScriptControlDescriptor("Winthusiasm.HtmlEditor",
this.ClientID);
descriptor.AddProperty("htmlencodedTextID",
this.HtmlEncodedTextID);
...
return new ScriptDescriptor[] { descriptor };
}
Although we have now implemented the IScriptControl methods, there are two remaining modifications to make so that the IScriptControl callbacks get called. First, OnPreRender must be modified to call RegisterScriptControl, as follows:
protected override void OnPreRender(EventArgs e)
{
...
if (!this.DesignMode)
{
// Test for ScriptManager and register if it exists.
sm = ScriptManager.GetCurrent(Page);
if (sm == null)
throw new HttpException(
"A ScriptManager control must exist on the page.");
sm.RegisterScriptControl(this);
...
}
base.OnPreRender(e);
}
Then, Render must be modified to call RegisterScriptDescriptors:
protected override void Render(HtmlTextWriter writer)
{
if (!this.DesignMode)
sm.RegisterScriptDescriptors(this);
base.Render(writer);
}
Because the client-side behaviors for an HTML editor are fairly
extensive, there is a fairly extensive amount of JavaScript required in
the client control. Most of this is typical designMode
client-side programming. More from the point of this article, the
entire JavaScript code structure has been formatted to follow the
client-side coding model recommended by Microsoft for all client
controls built upon the AJAX client-side libraries. This includes:
get and set methods for each property passed from the server control initialize method dispose method Descriptor method Namespace registration:
Type.registerNamespace("Winthusiasm");
Constructor:
Winthusiasm.HtmlEditor = function(element)
{
Winthusiasm.HtmlEditor.initializeBase(this, [element]);
this._htmlencodedTextID = "";
...
}
Prototype block with methods comma-separated:
Winthusiasm.HtmlEditor.prototype =
{
method1: function()
{
...
},
method2: function()
{
...
},
...
}
Prototype get and set methods for each property passed from the server control:
get_htmlencodedTextID: function()
{
return this._htmlencodedTextID;
},
set_htmlencodedTextID: function(value)
{
this._htmlencodedTextID = value;
},
...
Prototype initialize method:
initialize: function()
{
Winthusiasm.HtmlEditor.callBaseMethod(this, 'initialize');
...
},
Prototype dispose method:
dispose: function()
{
...
Winthusiasm.HtmlEditor.callBaseMethod(this, 'dispose');
},
Descriptor method:
Winthusiasm.HtmlEditor.descriptor =
{
properties: [ {name: 'htmlencodedTextID', type: String },
... ]
}
Class registration:
Winthusiasm.HtmlEditor.registerClass("Winthusiasm.HtmlEditor",
Sys.UI.Control);
Download the appropriate zip file and unzip it to a new directory. It includes:
HtmlEditor control Double-click the solution file to start Visual Studio, and then select Build/Rebuild Solution from the menu. This will build the project and copy the project DLL to the Bin folder of the sample website. Set Demo.aspx as the Start Page and press F5.
Register statement to the page Text property to set the editor HTML Text property to get the "saved" HTML Example Register statement:
<%@ Register TagPrefix="cc"
Namespace="Winthusiasm.HtmlEditor"
Assembly="Winthusiasm.HtmlEditor" %>
Example custom control tag:
<cc:HtmlEditor ID="Editor"
runat="server"
Height="400px"
Width="600px" />
Example set Text:
Editor.Text = initialText;
The editor's "client-side" Save method instructs the editor to store the current HTML (converting to XHTML, if appropriate), and clears the modified flag. When the editor property AutoSave is set to true (the default), the client-side Save method is called automatically as part of the client-side ASP.NET validation process before the form is submitted. All controls with a CausesValidation property set to true (the default) trigger the behavior. If the AutoSave implementation is not appropriate or sufficient, the client script to trigger the client-side Save can be attached through the optional SaveButtons property, or manually.
In the server-side event handler, the Text property is used to retrieve the "saved" text:
DataStore.StoreHtml(Editor.Text);
To simplify deployment, the HtmlEditor.js file and the image files for the toolbar buttons are embedded as resources within the HtmlEditor.dll assembly.
Storing un-encoded HTML in a form control, such as a text area or a hidden input element, is problematic when submit behavior is triggered on the client:

This implementation uses HiddenField to store the edited HTML, and therefore always stores the text in an HtmlEncoded state.
The HiddenField used to store the HTML text is created within UpdatePanel. If the Text property is set during an "asynchronous" postback, the server control responds by calling Update on UpdatePanel and registers DataItem with ScriptManager. Because the client control uses an endRequest handler to monitor all PageRequestManager updates, it detects that DataItem has been registered, and automatically updates the HTML in the editor.
Both Internet Explorer and Firefox output "HTML" when in designMode.
To convert to "XHTML", this implementation reads the "client-side" DOM
tree and outputs the elements and attributes in XHTML format. This
"client-side" conversion of HTML to its XHTML equivalent effectively
"hides" the underlying HTML implementation. When the user switches from
Design to HTML mode, the output displayed is XHTML. In addition, the
server-side Text property retrieves XHTML. Note that the editor configuration property OutputXHTML is defaulted to true. If set to false, no XHTML conversion takes place and the output is browser-generated HTML.
Internet Explorer and Firefox both output and "expect to modify" deprecated syntax while operating in designMode.
Consequently, the implementation of this editor converts the deprecated
syntax into a standards-based equivalent when converting to XHTML. It
"restores" the deprecated syntax when converting back to designMode HTML. Note that the editor configuration property ConvertDeprecatedSyntax is defaulted to true. If set to false, no conversion takes place and the output includes the deprecated syntax.
Internet Explorer outputs and "expects to modify" paragraph elements while operating in designMode. If the editor configuration property ConvertParagraphs is set to true, the implementation of this editor "modifies" the displayed style of paragraph elements while in designMode. It converts the paragraph elements into appropriate break and/or div elements when converting to XHTML, and "restores" the paragraph elements when converting back to designMode HTML. Note that this configuration property applies only to Internet Explorer and is defaulted to false. Unless set to true, no conversion takes place, and the output includes the paragraph elements.
A web page containing an HTML editor most likely stores the HTML created by the user. Later, it probably displays that HTML in another web page, perhaps for a different user. This presents a classic exposure to Script Injection, and perhaps SQL Injection as well. The client of the HTML editor should take appropriate steps to control these exposures.
The control discussed in this article was tested on Firefox 2+, IE 6, IE 7, and Opera 9+.
Using the tutorial from Microsoft, as well as the designMode and execCommand resources
listed below, the HTML editor server control has been implemented to
work in a Microsoft AJAX environment. Possible enhancements include:
ViewState size by 72%Validator support Validators pre tag conversion issues XHTML conversion issue with Internet Explorer duplicate DOM elements valign in the default AllowedAttributes valign attribute to standards-based equivalent GetInitialHtmlEditorOuterHTML missing textarea end tag Link dialog reads the href attribute Image dialog reads the src attribute descriptor ModalPopupExtender issue Save issue with an internal timer DesignModeEmulateIE7 property designMode scrollbars issue CLSCompliant(true) assembly attribute SetMode onload validator issue ToLower Culture issue GetVersion method sub and sup to default allowed tags DesignModeCss absolute path issue AutoSaveValidationGroups property ConvertParagraphs horizontal rule issues AutoSave property Modified server-side property ValidationProperty attribute GetText client-side method SaveButtons property ColorScheme property CreateColorSchemeInfo server-side event property ToolstripBackgroundImage property ToolstripBackgroundImageCustomPath property NoToolstripBackgroundImage property EditorInnerBorderColor property SelectedTabTextColor property DialogSelectedTabTextColor property ScriptReference issue DialogFloatingBehavior property CreateDialogInfo event property alt and title in allowed attributes Toolbars property ToolbarButtonsTop property ToolbarButtonsBottom property ToolbarSelectLists property ToggleMode property ToolbarDocked property ToolbarClass property EditorBorderSize property EditorBorderColor property SelectedTabBackColor property ModifiedChanged client-side event handler property ContextChanged client-side event handler property Save server-side event handler property ConvertParagraphs issues ToolbarButtonsTop property ToolbarButtonsBottom property ToolbarSelectLists property CreateToolbarInfo event property TextDirection property dl in allowed tags ConvertParagraphs property ReplaceNoBreakSpace property $find startup issue EditorBackColor property EditorForeColor property DesignModeCss property NoScriptAttributes property InitialMode DesignModeEditable HtmlModeEditable Text issue u, blockquote, and align
General
News
Question
Answer
Joke
Rant
Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads.
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 4 Jul 2009 Editor: Deeksha Shenoy |
Copyright 2008 by Eric Williams (winthusiasm.com) Everything else Copyright © CodeProject, 1999-2010 Web18 | Advertise on the Code Project |