Introduction
This article demonstrate that how can you create a Rich Editor, which supports
HTML formatting, Server Side Control in VB .NET. This is my very first control
I wrote in .NET. The purpose to write this article is to validate the technique
I used and to know about any other better idea on writing controls in .NET.
So let's begin.
Understanding JavaScript Editor
First of all, I am going to demonstrate that how we can create an Editor using
HTML and JavaScript. Later we will convert the same to .NET server side control.
The core technique of creating the Editor is the use of execCommand method
(Java Script). Let's see a simple example.
<HTML>
<Head>
<Title>Simple Demo of HTML Area </Title>
<Script Language="JavaScript">
function doBold()
{
frameEdit.document.execCommand('bold', false, null);
}
function doItalic()
{
frameEdit.document.execCommand('italic', false, null);
}
</Script>
</Head>
<Body onLoad="frameEdit.document.designMode='on';">
<form id="frmEdit">
<IFRAME id="frameEdit" style="width:600px; height:300px" align=center>
</IFRAME>
<br>
<P align=center>
<Input type="Button" value="Bold" onClick="doBold()">
<Input type="Button" value="Italic" onClick="doItalic()">
</P>
</form>
</Body>
</HTML>
Just copy and paste above code to some editor and save as HTML. Run in browser,
you will see that IFrame is behaving like a TextArea HTML control. OK, type
something there, select and click on Bold Button. You will see that the text
you typed is converted to Bold, and same with the Italic. It means it worked.
Let's analyze the code we have written above.
We are starting from Body tag. The Script portion inside Head tag will be discussed
later. On loading of body, we said that browser should start the design
mode of our Inline Frame (IFrame, named frameEdit). This property will
convert the IFrame to an Editable textbox. Ok, then on the Click events of the
buttons, we invoked the JavaScript functions, doBold and doItalic.
The functions were defined in the Head portion of our HTML document. There is
only one statement in each function (doBold and doItalic). That is, the execCommand
method of frameEdit's document property. The execCommand has
three parameters, the Command, UserInterface, and the UserValue. Let's see them
in some detail
Parameters of execCommand Method
Let's take a look at parameters of execCommand. As I told you before, this
method is the heart of our HTML Area. Just understand the parameters of this
method, and the rest is really more than easy.
Command (First Parameter)
The First parameter of execCommand is UserCommand. It is a string value which
defines the work to do. In our above example, we passed bold
and italic respectively. There are a number of commands which
could be passed to this function. A complete list of these commands can be found
in MSDN. Just look for Command Identifiers or execCommand
in Index of your MSDN library. I am writing some commands here which were used
in my HTML area.
- BackColor To set the Back Color of the Text
- Bold To set the Text to Bold
- Copy To Copy some text to ClipBoard. (Copy will be made
in HTML Format)
- CreateLink Select some text and execute this command. This
will make your text a Hyper Link
- Cut Cuts text.
- FontName To Change the Font of the Selected Text
- FontSize To Change the Size of the Font.
- ForeColor To Change the Fore Color of the Text.
- Indent Increase the Indent of the Text/Paragraph.
- InsertImage To Insert Image in your Editor area.
- Italic To set the Text to Italic
- JustifyCenter Center Justifies the Text
- JustifyLeft Left Justifies the Text
- JustifyRight Right Justifies the Text
- Outdent Decrease the Indent.
- Redo Redo your last action (inverse of Undo)
- SaveAs Opens the Dialogue Box to save your work.
- StrikeThrough Set the Text to Strike Through.
- Subscript Moves the Selected text to slightly down than
a normal text.
- Supercript Moves the Selected text to slightly up than
a normal text.
- UnderLine To create Underlined text
- Undo Undo the last action.
- Unlink Removes hyperlink, if any
There are many more commands which could be used to format your HTML document.
You can experience for more commands from MSDN. But for the moment, this is
enough.
User Interface (Second Parameter)
The Second parameter asks you that either the execCommand should show some
user interface for the Command you have just passed in first parameter or not?
It is either true or false. If you set it to true, then Explorer will provide
appropriate user interface for that command, if no, then you manually have to
pass the values (if any required) to the function in it's third parameter (third
parameter, Value is discussed later). In the above example, we just passed false
for this parameter because, no User Interface was required for Bold or Italic
Commands. Now let's see a simple example which will use this parameter to show
any user interface.
<HTML>
<Head>
<Title>Simple Demo of HTML Area </Title>
<Script Language="JavaScript">
function doLink1()
{
frameEdit.document.execCommand('createlink', true, null);
}
function doLink2()
{
frameEdit.document.execCommand('createlink', false, 'http://msdn.microsoft.com');
}
</Script>
</Head>
<Body onLoad="frameEdit.document.designMode='on';">
<form id="frmEdit">
<IFRAME id="frameEdit" style="width:600px; height:300px" align=center>
</IFRAME>
<br>
<P align=center>
<Input type="Button" value="Link 1 User Interface" onClick="doLink1()">
<Input type="Button" value="Link 2 No User Interface" onClick="doLink2()">
</P>
</form>
</Body>
</HTML>
When you will run this example, you will see something like this
This interface was provided to you when you passed true as second parameter
of execCommand. This is the Explorer's own user interface. Note that not all
commands support user interfaces, only those which needs it. Like, if you pass
true for user interface with bold command, then no user interface will be provided.
Actually, this parameter will be ignored because doing bold, italic or underlined
do not require any user interface.
When you will click on the second button (Link 2 No User Interface) then no
user interface will be provided but still the Link will be created. If you see
at the code, then you will note one thing that we have passed third parameter
to the link location. Let's discuss the third parameter.
Value (Third Parameter)
Value parameter is passed to send some value to execCommand method in
only those cases if the execCommand needs some value and user interface parameter
was set to false. As in our above example, we used second button (Link
2 No User Interface). If you see the code working behind this button, then you
will see that we passed command createlink, same as first buttons
command, User Interface to false, and value to the URL of the MSDN site. Since
we said the execCommand is to create some link, it must be aware of the location
or URL which should be used to create link. Now we had two options, either prompt
from user or hard code it (manual). So, if we want to manually set the value,
then we can pass false for the user interface and some value (URL) as the value
parameter.
So, that was all about the execCommand method. If you have understand what
I said above then there should be no hurdle to make your own HTML Area using
HTML and JavaScript. Just create buttons and execute the appropriate commands
against each button. I have attached the HTML version of my TextArea too with
this article. Now let's move to our real task, the HTML Text area Server Side
Control.
Moving to Server Side Control for ASP .NET
Now let's see that how can we create the Server Side control for our text area. The Server Side controls of ASP .NET are very easy to build and require no permission or attention of user to be downloaded. Since the Server Side controls of .NET generates equivalent HTML code which is sent to the requested user’s browser.
To create our Server Side Control, just start your VS .NET and select new project and from project type, select Web Control Library from the Project templates of Visual Basic. In the name portion of project, set the name to RTFBox. When new project will be created, it will have one module with some code which is needed for the Server Side Control. For the moment, delete all the code and only leave the following.
Imports System.ComponentModel
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Xml
Imports System.Drawing
Imports System.Windows.Forms.Design
Imports System
Imports System.IO
Imports System.Web
Imports System.Drawing.Design
Namespace HangamaHouse
Public Class RTFBox
Inherits System.Web.UI.WebControls.WebControl
End Class
End Namespace
OK, Now Create Some private methods which will return us the HTML code of our
control. Your methods will be look like this.
Private Function GenerateCSSCode() As String
Dim mCSS As String
mCSS = vbCrLf
mCSS = "<STYLE> " & vbCrLf
mCSS += ".EditControl { " & vbCrLf
mCSS += "width:" & Me.mWidth & "px; " & vbCrLf
mCSS += "height:300px; } " & vbCrLf
mCSS += ".tblTable { " & vbCrLf
mCSS += "width : " & Me.mWidth & "px; " & vbCrLf
mCSS += "height: 30px; " & vbCrLf
mCSS += "border:0; " & vbCrLf
mCSS += "cellspacing:0; " & vbCrLf
mCSS += "cellpadding:0; " & vbCrLf
mCSS += "background-color: #D6D3CE ; " & vbCrLf
mCSS += " } " & vbCrLf
mCSS += ".butClass { " & vbCrLf
mCSS += "width:22; " & vbCrLf
mCSS += "height:22; " & vbCrLf
mCSS += "border: 0px solid; " & vbCrLf
mCSS += "border-color: #D6D3CE ; " & vbCrLf
mCSS += "background-color: #D6D3CE ; " & vbCrLf
mCSS += " } " & vbCrLf
mCSS += ".tdClass { " & vbCrLf
mCSS += "padding-left: 0px; " & vbCrLf
mCSS += "padding-top:0px; " & vbCrLf
mCSS += "background-color: #D6D3CE ; " & vbCrLf
mCSS += "</STYLE>" & vbCrLf
Return (mCSS & vbCrLf)
End Function
This function will return us the Style Sheet for our text area. In the same
way, we will create other functions which will return us the HTML code of creating
toolbars and IFrame, Java Script which will handle the formatting of HTML in
IFrame (using execCommand) and others. I am not writing the complete code here,
just telling that how design the project. You can see the sample attached with
this article.
When you finish writing all functions which returns the strings to send to
client in order to create an HTML document looking like the picture at the top,
there is the time to write code which will be responsible for sending code to
client.
If you are writing a Server Side control which may send some output to User's
browser, then in that control normally you write code in Overrides function
Render. But in our case, we also have to send the JavaScript to the
client browser, so first we will register our JavaScript to the browser and
then we will send our normal HTML using the Render overridden function. So, to
register your script you will probably write the code in OnPreRender
Orverrides functions. Your code may look like this
Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
'Register Style Sheet for our HTML area
Page.RegisterClientScriptBlock("myStyleSheetScript", Me.GenerateCSSCode())
'Register main Function which will deal with execCommand
Page.RegisterClientScriptBlock("myCommandScript", Me.GenerateCommandScript())
'Register Script which will handle with the code when button(s) is down
Page.RegisterClientScriptBlock("mySelUpDownScript", Me.GenerateSelDown_UpScript())
'Register Script which will handle with the code when mouse is away from the button(s)
Page.RegisterClientScriptBlock("myselOFFScript", Me.GenerateSelOFFScript())
'Register Script which will handle with the code when mouse is on from the button(s)
Page.RegisterClientScriptBlock("myselONScript", Me.GenerateSelONScript())
End Sub
RegisterClientScriptBlock method of Page object takes two
arguments, as in the above code. One the unique key of the script, and other
is the script itself. Key is used to check that either the script is already
registered or not, if it is registered already, then it is not sent to the client.
The good way is that we first check either the script is registered or not.
This method will also send all the script to the client browser. Note that we
have to completely describe our script including <Script> tag.
When our script is sent to client, then we need to send actual HTML code which
will produce interface of our HTML area. This code will be sent using OnRender
overrides function.
Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
writer.Write(Me.GenerateHTMLAreaBody)
'Convert the Simple IFrame to Editable TextBox
writer.RenderBeginTag("Script")
writer.Write("RTFEdit_" & Me.UniqueID & ".document.designMode='on';")
'writer.Write("RTFEdit_" & Me.UniqueID & ".document.body.innerText = '" & _
Text & "'")
writer.RenderEndTag()
'End Converting IFrame to Editable Textbox
End Sub
As we saw in our first example that we need to say to our browser that it should
on the design Mode of IFrame. So, we the code for this work is sent to client
here. RenderBeginTag method of HtmlTextWrite class takes responsibility
to write some tag to the client output stream. We just passed the name of the
tag, script in this case. Then we used the write method to
write the contents of the tag, and then we called RenderEndTag.
RenderEndTag will automatically close the last opened tag (script in this case).
Note that we sent this code as the last output of our control. Because, when
this code will be sent to client, it will look for the object we mentioned here.
If we send this code before sending HTML of out document, then the client browser
will generate error by saying something like object not found etc.
Don't confuse for Me.UniqueID, this will be discussed in the last section
Managing View State.
Up to here our server side control is complete. Just compile it and it will
create a DLL of the same name as your project name was. Now is the time to use
our control in any ASP .NET page. Let's see the output of this control and then
we will be back to our control to discuss some more things about this control.
Create Test Page for out Control
Open the note pad and write the following code there.
<%@ Page Language="VB" %>
<%@ Register TagPrefix="mArea" Assembly="RTFBox" namespace="HangamaHouse" %>
<html><Head>
<Title>Testing HTML Area Server Side Control</Title>
</Head>
<Bod>
<Form id="Form1" Runat="server">
<h1 align=center>HTML Text Area Demo</h1>
<mArea:RTFBox Runat="server" />
</Form>
</Body>
</html>
Save this code naming it test.aspx and save to some folder. Copy your compiled
DLL file of Server Side Control in the folder named Bin. Note that the Bin Folder
should be inside the folder of that where you saved your test.aspx. Make this
folder a virtual folder. And type the URL in the browser. You will see the following
output there.
Hey, the text you are seeing in the HTML area is written by me manually. Don't confuse if you do not see this in your browser. (Ok ok, I know you are very intelligent. just kidding). Anyway, we have seen our Server Side Control in work. Now let's move back to discuss something more which was used in out HTML Area Sever Control.
Color Management of Server Side Control
This control has the properties which can be used to set the color of HTML
Area according to the colors of your site. As you can see in the picture at
the start of this article and in the picture just above. Actually, the Colors
are set in the Style Sheet we generated using GenerateCSSCode
function (private function, user created). I simply created a property which
is used to set or get the color of different portions of the HTML area, like
Table Background. The type of the Color property is Color. But we know that
HTML does not understand the Color object type. So, Microsoft was intelligent
and he provided a method to convert Color Object to HTML Color string and vice
versa. Well, I used ColorTranslator.FromHtml("#D6D3CE") to Convert
HTML Color #D6D3CE to the equivalent color for Color object and in the same
way, ColorTranslator.ToHtml(Color.Red) to convert Color Object value to HTML
equivalent color string.
Managing View State of HTML Area Control
The final topic of this article is to demonstrate the View State handling for
our Server Side Control. View state keeps the value of the form objects even
when they are posted to the server. The major problem in implementing view state
for our control is that MSDN clearly defines that only specific HTML controls
can have view state which includes CheckBox, CheckBoxList, DropDownList, HTMLInputCheckBox,
HTMLInputFile, HTMLInputHidden, HTMLInputImage, HTMLInputRadioButton, HTMLInputText,
HTMLInputSelect, HTMLInputTextArea, ImageButton, ListBox, RadioButtonList and
TextBox. IFrame is not included in these elements and it can not support View
State. So what I did, that I got a hidden field and moved all text from IFrame
to that hidden field. Then I implemented View state on that hidden field. It
worked. So, let's see first that how I did that.
When User interface for HTML Area Server Side control is written to client,
then with IFrame I also sent the code for a hidden field. That looks like this
in the HTML generation code.
' Code before this for HTML Interface
mStr += " <IFrame name=RTFEdit_" & Me.UniqueID & " ID=RTFEdit_" & _
Me.UniqueID & " class=""EditControl"" ></IFrame>" & vbCrLf
mStr += " <Input type=""hidden"" name=" & Me.UniqueID & " ID=" & _
Me.UniqueID & " value='" & Text & "' >" & vbCrLf
'Code after this for HTML Interface
Note that the Value property of input type=hidden is Text.
Text is the public property we created above and will be discussed soon. When
this code is sent to the client, it will generate an IFrame and a Hidden field.
I also transferred text from IFrame to this hidden field. The code for this
is in the GeneratePostBackScript function. You should also
note that the name of this hidden field is the Me.UniqueID. UniqueID is the
property of every server control which returns the Unique ID for that control.
If you hard code some name here, then it may conflict with other instance of
the same control on the client. UniqueID makes it possible to have different
IDs of each control on the client side. And it is also used for the View State
Management. Now let's discuss something about the View State.
To implement the View State, we must have to implement the IPostBackDataHandler
Interface. To implement the View State, I just implemented this Interface and
also wrote some code for two functions of this Interface. But first of all,
let we create a property named Text which should return us the Text (HTML) of
our Control.
Public Property Text() As String
Get
Return CType(Me.ViewState("Text"), String)
End Get
Set(ByVal Value As String)
Value = Replace(Value, "\", "\")
'\ is a Special Character, Replace with \\
Value = Replace(Value, "'", "’")
' ' is cause of Error, replace with \'
Value = Replace(Value, vbCrLf, " ")
Me.ViewState("Text") = Value
End Set
End Property
Text Property retuns some String from the View State's collection. And when
we need to set the value, it also set that in the ViewState collection. But
before setting it to view state collection, it format's the value passed to
it. It converts \ to equivalent HTML code \ since we are using
JavaScript and in JavaScript \ indicates that we are going to write some special
character. In the same way, single quote (') and new line (carriage return)
is also converted to fit into JavaScript.
Now come to the implementation of IPostBackDataHandler interface.
Here is the code.
Public Event TextChanged As EventHandler
Public Function LoadPostData(ByVal postDataKey As String,
ByVal postCollection As
System.Collections.Specialized.NameValueCollection)
As Boolean
Implements System.Web.UI.IPostBackDataHandler.LoadPostData
Dim currentValue As String = Text
Dim postedValue As String = postCollection(postDataKey)
If currentValue Is Nothing Or Not postedValue.Equals(currentValue) Then
Text = postedValue
Return True
End If
Return False
End Function
Public Sub RaisePostDataChangedEvent()
Implements System.Web.UI.IPostBackDataHandler.RaisePostDataChangedEvent
OnTextChanged(EventArgs.Empty)
End Sub
Protected Overridable Sub OnTextChanged(ByVal e As EventArgs)
RaiseEvent TextChanged(Me, e)
End Sub
We first declared an Event. Then we implemented the Interface's Functions/Methods.
First Function is LoadPostData which is responsible for, as the name says, loading
Posted data. It has two arguments, key and a data collection. We received the
value from that collection to a variable postedValue and then
checked if that is not equal to our previous value or nothing (null), then pass
it to our Text property. Else wise just return false and exit.
The Second Method RaisePostDataChangedEvent, is used to raise
events for our custom control, if we want to. We simply called the OnTextChanged
overrideable method and in that method, we raised our event. We didn't raise
our event in the RaisePostDataChangedEvent method, but in OnTextChanged
method, so that if someone would like to override this method, so he can.
This was all about the creation of HTML area as a Server Side Control. There
are some functions to be provided in it like the Special Characters. If you
feel that some improvement should be made in this control, then let me know.
If in any case, you are facing some problem in understanding code or else, then
you can write me at theangrycoder@yahoo.com.
Your comments and suggestions will be welcomed.