Creating Multi-Language Custom Control
 
Published: 02 Jul 2008
Abstract
Suppose that we are dealing with a multi-language website: English, French, Chinese, etc. and in some forms we need to enter a username in three languages, each language in a separate textbox. Abdulla's custom control allows developers to add these three textboxs inside a custom multilang control that will reform these textboxes inside a sliding div that displays one textbox at a time for the selected language. This custom control includes two icons for each language: one in "on" case, and one for "off" case, so the end user can click in the language image that he wants to enter and the proper textbox will appear in the sliding animation using JavaScript code. He provides a comprehensive explanation of each step involved in developing the control with source codes and screenshots in addition to some of the tips and tricks for advanced functionality.
by Abdulla Hussein AbdelHaq
Feedback
Average Rating: 
Views (Total / Last 10 Days): 33794/ 102

Introduction

Sometimes, when you are browsing on a global website that has multi-languages, multi-interfaces and a lot of fields for each culture that change direction and language of the page depending on some culture, you have to enter some text in multi-language. For example, you want to create an account in a registration form and they recommend you enter your name in English, French and Arabic. In that case we will have three Textboxes under each other - Textbox for each language.

For now, three textboxes seem fine to the layout of the page and to the end-user, but suppose that there are more fields in the page that need to be entered in three languages! What if there are four more fields? In that case we will have 15 textboxes in the page, so the layout will not be developed in a professional way and also will not be user-friendly.

In this article, I am going to illustrate how to create a custom multi-language control that collapses the three textboxes into one control that will display one textbox every time, and this will result in using four Textboxes instead of 15 Textboxes. Also, this control will include an image for each language that enables the end-user to select the language he wants, with a great sliding JavaScript animation, and to show all languages with JavaScript sliding animation too.

Throughout this article we are going to customize three languages: English, Arabic and French.

How flexible is it?

Generally, custom controls should be flexible to be more reusable, and it should allow the page developer to customize the control depending on some cases. Because of that, this custom control will contain a lot of properties that allow the page developer to change and hide such images and texts, determine the type of control to be displayed (textbox or textarea) and control which language to be displayed.

Also, page developer can set a CssClass for the labels and textboxes.

I will talk more about these properties later in this article.

Note: I supposed that you have a background about creating custom controls, so I will not go into detail about creating custom controls.

Control Preparations

You need to add the following references to build it successfully.

1.    System.Design

2.    System.Drawing

3.    System.Web

4.    System.Web.Extensions

5.    System.Web.Extensions.Design

As you see in Listing 1, our custom control will inherit the CompositeControl class and it has an “asp” prefix tag to make our custom control supporting Intellisense in the HTML view source.

Keep in mind that if you are using Visual Studio 2005, you have to install at least Visual Studio 2005 SP1 to fix the Intellisense bug.

Listing 1

Namespace CustomsControls
< _
    AspNetHostingPermission(SecurityAction.Demand, _
    Level
   = AspNetHostingPermissionLevel.Minimal), _
     AspNetHostingPermission(SecurityAction.InheritanceDemand, _
     Level
   = AspNetHostingPermissionLevel.Minimal), _
     Designer(GetType(MultiLangDesginTime)), _
     ToolboxData("<{0}:CustomMultiLang runat=""server"" />") _
     > _
     Public Class CustomMultiLang
  Inherits CompositeControl
 
End Class
 
End Namespace
Embedding JavaScript Files and Blocks Rendering

If you download the source code, you will find two embedded JavaScript files that support client-side functionality for the control.

Using embedded JavaScript files has many benefits; this allows the client browser to cache the script which should lead to faster page loads especially if the script is large. By embedding the JavaScript file as a resource rather than putting it in the control’s code, the overall code base is also much easier to maintain.

Let us overview the JavaScript file "animatedcollapse.js." This is the part that is responsible for sliding contents. If you take a look through the JavaScript code, you will find "slidedown" and "slideup" functions, these two JavaScript functions are responsible for sliding down the content to hide it and sliding up the content to show it.

We will render a JavaScript block at render time using ScriptManager.RegisterStartupScript method for each Textbox- for slide it up and slide it down. By using ScriptManager rather than writer.write(the script block), the custom control will be worked under Ajax environment. So if you are trying to render the script block using writer.write, it will not work under Ajax environment.

Sliding mechanism occurs by placing each textbox control inside a separate html div, and these html div's will appear/disappear using the two JavaScript functions that I talked about.

As I mentioned, we have three languages (English, Arabic, and French), so we have to put the English textbox control inside html div, the Arabic textbox control should be inside another html div, and the same thing with the French textbox control.

Take a look at the code below, registering startup script at render time for English language. This code will be repeated three times for English, Arabic and French with a little change for each language to make it unique.

Listing 2

'//Ajax Enabled
ScriptManager.RegisterStartupScript(Page, Me.GetType, Me.ID & "_EnBlock", _
"var " & Me.ClientID & "_collapseEn = new animatedcollapse('" & _
Me.ClientID & "_dvEn', 400, false"IIf(DefaultLanguage = _
ControlLanguages.English, ",'block'", "") & ");"True)

As you see in listing 2, we are registering startup script at render time for English content. Note that we are declaring a variable from the animatedcollapse JavaScript Class. Assume that our custom control ClientID is MutliLang1 so the variable will be like this (MultiLang1_collapseEn). This will be a unique object for our custom control, so it does not matter now how many times you drag/drop or how many times you created an instance from this custom control in the page, each custom control will be worked separately with its own JavaScript functions by using its own JavaScript object. Take this advice; if you want to create your own custom control, use the previous technique when you are dealing with JavaScript, which makes your custom control more reusable.

To collapse the English content, the MultiLang1_collapseEn.slideup() function will take care of that, and to expand it again, use MultiLang1_collapseEn.slidedown().  Use the same thing in the left languages, but remember that each language has its own JavaScript object.

Back again to the downloaded source code, open the JavaScript file "MultiLangJS.js.” It contains two JavaScript functions: "MultiLangClass" function which is the main function, and "HideShowLang" function which is responsible for calling Slideup/Slidedown functions. "MultiLangClass" function receives many arguments; each argument will be assigned to a JavaScript variable. These variables will be a global through the JavaScript file.

As you saw in figure 1, we are passing the ClientID's for controls to be assigned in the JavaScript file.

Using the same technique which we have used, we will register another JavaScript block at render time for the whole control. This script block will be the JavaScript controller for the whole control.

Figure 1

In the above code, we declare a JavaScript variable during render time, which is an instance from the MultiLangClass JavaScript class. This variable will be the key for calling all JavaScript functions from the embedded "MultiLangJS.js" file. Again, by using this technique (i.e. creating instance variable from JavaScript file), it allows us to drag and drop my custom control many times in the page without worrying about confliction between these instances. Assume that my control ID is MultiLang1 so the JavaScript variable will be like this: (ObjJS_MultiLang1). I will use this JavaScript object in the onclick event, when the end-user clicks on the image of the language he wants to display. I will give more examples about how to call this variable later in this article.

Embedding the JavaScript files will be done by using the WebResource attribute typically over the namespace declaration in the class file for your custom control as you see in the code below.

Listing 3

<Assembly: TagPrefix("MyCustomsControls.CustomsControls""asp")> 
<Assembly: WebResource("MyCustomsControls.MultiLangJS.js", "text/javascript")>

And we have to tell the Page that our custom control has javascript files needed to be registered when the final HTML is generated for that page. So we need to write a bit of code in the OnInit sub as in Listing 4. 

Listing 4

Protected Overrides Sub OnInit(ByVal e As EventArgs)
    MyBase.OnInit(e)
    Me.Page.ClientScript.RegisterClientScriptInclude(Me.GetType(), "MultiLangJS", _
      Page.ClientScript.GetWebResourceUrl(Me.GetType(), _
      "MyCustomsControls.MultiLangJS.js"))
 
    Me.Page.ClientScript.RegisterClientScriptInclude(Me.GetType(), _
      "animatedcollapse", Page.ClientScript.GetWebResourceUrl(Me.GetType(), _
      "MyCustomsControls.animatedcollapse.js"))
 
End Sub
Variables and Properties

As I mentioned, Multi-Lang custom control contains a lot of properties that allows page developer to set and customize the control as you want.

In listing 5, we declared for each language three Textboxes, three labels and three images. These images will appear at the bottom of the control as you can see in Figure 2. The _imagExpand image control is the Expand image that will be responsible for (Show All) languages; it will arrange the three textboxes under each other using the JavaScript functions that we have talked about.

Listing 5

Private _txtEnglish As TextBox
Private _txtArabic As TextBox
Private _txtFrench As TextBox
 
Private _lblEnglish As Label
Private _lblArabic As Label
Private _lblFrench As Label
 
Private _imgEnglish As Image
Private _imgArabic As Image
Private _imgFrench As Image
Private _imgExpand As Image
 
Public Enum ControlToDisplay
     TextBox
     TextArea
End Enum
Public Enum ControlLanguages
      English
      Arabic
      French
End Enum
Public Enum LabelLanguages
      Yes
      No
End Enum

Figure 2

Also, we have three Enumerations:

·         ControlToDisplay: Used to specify the type of the control that will be displayed, the Textbox or the TextArea.

·         ControlLanguages: Used to specify the default language for the control. It contains three members (English,Arabic,French). If we select the French member, the French Textbox will be the first control that appears when the page is loaded.

·         LabelLanguages: Used to specify whether to show labels above each Textbox or to hide them.

If you have a look at the CustomMultiLang.vb file, you will find a lot of properties with an XML Doc comments to understand the purpose from that property. You can set a CssClass for each label, and you can set/get the text of the labels as you can see in Listing 6.

Note the Localizable(true) attribute, it allows the property to be localizable, so our custom control will support localization.

Page developer can set/get the Tool tip for the images, and it sets to be localizable too. The code sample in Listing 6 will be repeated for the left languages. There are more properties that you can find in CustomMultiLang.vb class.

Listing 6

<Category("English Tab"), _
   Description("English CssClass.") _
    > _
    Public Property EnglishLabelCssClass() As String
            Get
                EnsureChildControls()
                Return _lblEnglish.CssClass
            End Get
            Set(ByVal value As String)
                EnsureChildControls()
                _lblEnglish.CssClass = value
            End Set
    End Property
 
   < Category("English Tab"), _
     Localizable(True), _
     Description("English Label.") _
      > _
       Public Property EnglishLabel() As String
            Get
                EnsureChildControls()
                Return _lblEnglish.Text
            End Get
            Set(ByVal value As String)
                EnsureChildControls()
                _lblEnglish.Text = value
            End Set
        End Property
 
< _
      Category("English Tab"), _
      Localizable(True), _
      Description("English Image ToolTip."), _
        DefaultValue("") _
                      > _
          Public Property EnglishImageToolTip() As String
            Get
                EnsureChildControls()
                Return _imgEnglish.ToolTip
            End Get
            Set(ByVal value As String)
                EnsureChildControls()
                _imgEnglish.ToolTip = value
            End Set
        End Property
 
< _
    Category("English Tab"), _
    DefaultValue(""), _
    Description("English Image On."), _
    Browsable(True), _
    Editor(GetType(System.Web.UI.Design.UrlEditor), _
    GetType(System.Drawing.Design.UITypeEditor)) _
    > _
         Public Property EnglishImageOn() As String
            Get
                _CheckObj = ViewState("EnglishImageOn")
                If _CheckObj Is Nothing Then
                    Return String.Empty
                Else
                    Return DirectCast(ViewState("EnglishImageOn"), String)
                End If
            End Get
            Set(ByVal value As String)
                ViewState("EnglishImageOn"= ResolveUrl(value)
            End Set
        End Property
 
        < _
            Category("English Tab"), _
            DefaultValue(""), _
            Description("English Image Off."), _
            Browsable(True), _
            Editor(GetType(System.Web.UI.Design.UrlEditor), _
            GetType(System.Drawing.Design.UITypeEditor)) _
          > _
       Public Property EnglishImageOff() As String
           Get
              _CheckObj = ViewState("EnglishImageOff")
              If _CheckObj Is Nothing Then
                    Return String.Empty
                Else
                    Return DirectCast(ViewState("EnglishImageOff"), String)
                End If
            End Get
            Set(ByVal value As String)
                ViewState("EnglishImageOff"= ResolveUrl(value)
            End Set
        End Property

Now, each image-language will have two statuses- active and inactive. The image-language will be activated when the user clicks on it. When this happens the source of the image will be set into ImageOn. When the user clicks out on another image, the status of the image will be inactive and the source of the image will be ImageOff.

As you saw in listing 6, we have EnglishImageOn/EnlgishImageOff properties. This allows the page developer to customize the active image and the inactive image for each language.

If the developer set these properties to empty value, embedded images will be assigned for these properties by default. Look at Figure3; you can see the embedded images that will be assigned for each language.

Figure 3

You would want to display some text above each textbox, and you can do that by simply setting the ShowLabels() property into True. Then you can enter the text you want for each label by its own property, as you see in Figure 4. “In French” appears above the French Textbox.

Figure 4

As in listing 6, the EnglishLabel property allows you to enter the Text you wish to display above the English Textbox. If you download the source code, you will find a sample website that shows you how to use ShowLabels property.

I suggest downloading the source code; it contains the source code for the custom control and also a sample website.

Listing 7

< _
    Category("Language Tab"), _
    DefaultValue(""), _
    Description("Default Language that will display on runtime.") _
    > _
    Public Property DefaultLanguage() As ControlLanguages
  Get
  _CheckObj = ViewState("DefaultLanguage")
  If _CheckObj Is Nothing Then
    If ShowEnglish = True Then
      Return ControlLanguages.English
    ElseIf ShowArabic = True Then
      Return ControlLanguages.Arabic
    ElseIf ShowFrench = True Then
      Return ControlLanguages.French
    Else
      Return ControlLanguages.English
    End If
  Else
    Return DirectCast(ViewState("DefaultLanguage"), ControlLanguages)
  End If
  End Get
  Set(ByVal Value As ControlLanguages)
  ViewState("DefaultLanguage"= Value
  End Set
End Property

In the code above, the DefaultLangauge property allows page developer to set the default language for the control. If he selects Arabic language, the Arabic Textbox will appear first after the page is loaded.

The return value from that property is the ControlLanguages Enumeration, which will be a dropdownlist at design time as you see in Figure 5.

Figure 5

Also, there are three properties (ShowEnglish, ShowArabic and ShowFrench) that enable page developer to disappear the language he wants from the control. For example, if he sets ShowArabic property into False, the Arabic part will not appear in the control.

Render Time

Here is the control kitchen, render time. At render time we will add the attributes, render the controls and register startup scripts.

Listing 8

'//English Tab
If ShowEnglish Then
  '<div> English
  writer.AddAttribute("id"Me.ClientID & "_dvEn")
  writer.RenderBeginTag(HtmlTextWriterTag.Div)
 
  Select Case ShowLabels
    Case LabelLanguages.Yes
      _lblEnglish.RenderControl(writer)
      writer.WriteBreak()
      _lblStatus = "yes"
    Case LabelLanguages.No
      _lblStatus = "no"
 
  End Select
 
  Select Case TypeOfControl
    Case ControlToDisplay.TextBox
      _txtEnglish.RenderControl(writer)
 
    Case ControlToDisplay.TextArea
      _txtEnglish.TextMode = TextBoxMode.MultiLine
      _txtEnglish.RenderControl(writer)
  End Select
  ' writer.Write("</div")
  writer.RenderEndTag() ' </div>
End If

At first, we are checking if the page developer wants to show the English language in the control. If it assigned to True, then as I mentioned, we have to put the English textbox inside HTML div.

Listing 9

'//English Img
If ShowEnglish Then
  writer.AddStyleAttribute("align", "absmiddle")
  writer.AddStyleAttribute("cursor", "pointer")
 
  If DefaultLanguage = ControlLanguages.English Then
    If EnglishImageOn = String.Empty Then
      _imgEnglish.ImageUrl = ViewState("Default_En_On")
    Else
      _imgEnglish.ImageUrl = EnglishImageOn.Trim
    End If
  Else
    If EnglishImageOff = String.Empty Then
      _imgEnglish.ImageUrl = ViewState("Default_En_Off")
    Else
      _imgEnglish.ImageUrl = EnglishImageOff.Trim
    End If
  End If
  _imgEnglish.ImageAlign = ImageAlign.AbsMiddle
  writer.AddAttribute("onclick", "ObjJs_" & Me.ID & ".HideShowLang(this.id)")
  _imgEnglish.RenderControl(writer)
  writer.Write("&nbsp;")
 
  '//Ajax Enabled
  ScriptManager.RegisterStartupScript(Page, Me.GetType, Me.ID & "_EnBlock", _
  "var " & Me.ClientID & "_collapseEn = new animatedcollapse('" & _
Me.ClientID & "_dvEn', 400, false"IIf(DefaultLanguage = _
  ControlLanguages.English, ",'block'", "") & ");"True)
End If

After rendering the English Textbox, we need to render the English image too, which will be the key for sliding up/sliding down the English Div. As you see in Listing 9, we are registering startup script for the English tab. If the English language is set to be the default language, the ( ‘block’ ) parameter will be passed to the animatedcollapse function to expand the English tab after the page is loaded, otherwise the English div will be collapsed. As you see in Listing 9, we have added an onclick attribute for the English image that will call the JavaScript function “HideShowLang().” Note the name of the JavaScript variable “objJS_”; this is the object instance from JavaScript file “MultiLangJS.js” that will be registered as startup script at render time as seen in Figure 1.

Listing 8 and Listing 9 will be repeated for the left languages.

Supporting HTML Design-Time

Listing 10

Public Class MultiLangDesginTime Inherits ControlDesigner
  Public Overloads Overrides Function GetDesignTimeHtml() As String
  Return "<span style='font-family:Verdana;font-size:10pt'>[ MultiLang Design-Time
  ]</span>"
  End Function
End Class

Multi-language custom control has as Design-Time class as you see in Figure 6. So nothing will be visible at design-time, which makes designing the page much easier. As you saw in Listing 1, we are assigning the Design-Time class by Designer(GetType(Class Name)) attribute typically over the CustomMultiLang class.

Figure 6

Tips and Tricks

You may ask what if I want to do some thing with the Textbox that is not supported as a property, like settings the English Textbox as a Default focus. Fortunately, if you look at listing 11, there is a read-only property that gives me the ID for the English Textbox, so you can search about the Textbox on the page using that property and you can add more styles and more attributes to the Textbox.

Note the Browsable(False) attribute placed above the property, it means that the property will not appear in the properties window or in the html design time, you can access it from code behind only.

Listing 11

< _
    Category("Misc"), _
    Browsable(False), _
    Description("ID of English TextBox.") _
    > _
    Public ReadOnly Property EnglishTextBoxID() As String
  Get
  EnsureChildControls()
  Return _txtEnglish.ID
  End Get
End Property

As you can see in Listing 12, I set the English Textbox as Default Focus. You can reform the Textbox as you want; I have changed the back color for the Arabic Textbox into Blue.

Listing 12

'//Set English Textbox as DefaultFocus
Page.Form.DefaultFocus = _
MultiLang1.FindControl(MultiLang1.EnglishTextBoxID).ClientID
DirectCast(MultiLang1.FindControl(MultiLang1.ArabicTextBoxID), TextBox).BackColor = _
Drawing.Color.Blue

What about client-side validation.  Suppose that the French Textbox was a required field. In that case, we have to write a JavaScript function, and call it in the button event OnClientClick.

If you back again to the downloaded source code, open the JavaScript file “MultiLangJS.js”; you will see a lot of JavaScript variables that be assigned to the passed arguments.

 One of these variables is “this.imgFrId.”   It is the ClientID for the French Image and we have passed it via the script block during render time.

Also, we have the JavaScript variable “this.txtFrObj.” It is the object from the French Textbox. As you can see in listing 13, we are passing the ClientID for the French Image to the “HideShowLang()” function, which is the responsible for sliding the contents.

The “HideShowLang()” function receive the Image ClientID for the content you want to Slide it down. So, for sliding down the French div, we should pass the ClientID for the French Image, so the French div will slide down and the other languages div’s will slide up.

Listing 13

//JavaScript Funciton
function PageValidation()
  {
   if(ObjJs_MultiLang1.txtFrObj.value =='')
   {
     alert('Please Enter Text in French');
     ObjJs_MultiLang1.HideShowLang(ObjJs_MultiLang1.imgFrId);
     ObjJs_MultiLang1.txtFrObj.focus();
     return false;
   }
   return true;
 }
Conclusion

In this article I have demonstrated how to create a custom Multi-Language control, and I tried to illustrate how to initialize an instance JavaScript object from a JavaScript file. I also illustrated how to create a simple Design-Time class for custom controls.

I hope you liked the idea.

[Download Source]



User Comments

Title: Good Article   
Name: Vagueraz
Date: 7/4/2008 5:03:31 AM
Comment:
Very Good and Strong Article , and it's not only useful for devlopers also for end-users ....
Title: Outstanding Work   
Name: Jean-Pierre Jamous
Date: 7/2/2008 10:01:11 PM
Comment:
Extremely nice. It comes handy for developers. You've done a tremendous job at it. Keep up the great work.
Title: Very good article   
Name: Anas al-qudah
Date: 7/2/2008 4:03:52 AM
Comment:
It's realy Very good article and help me.
Thanks thanks alot.

Product Spotlight
Product Spotlight 





Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2014 ASPAlliance.com  |  Page Processed at 9/15/2014 10:56:41 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search