Click here to Skip to main content
15,879,535 members
Articles / Web Development / ASP.NET
Article

ASP.NET Server Control - Design Time Support

Rate me:
Please Sign up or sign in to vote.
4.41/5 (12 votes)
9 Feb 2005CPOL4 min read 126.5K   56   17
A tutorial on adding design time support to ASP.NET custom server control.

Sample Image - DesignTimeSupport.jpg

Updates - see at the article's end

Introduction

If you already have created a custom server control, you would have probably noticed that the intelisense in the HTML view is absent.

This article will show you how to get it back.

Softomatix published an article about how to create server control: ASP.NET Server Control - Updown Control 1. That's the basics. I'll go on to other topics.

My code example is ClickOnce a control that becomes disabled, until postback returns.

IteliSense support

Add your code Identity to the control TagPrefix.

C#
[assembly: System.Web.UI.TagPrefix("Gootvilig.Controls","Gootvilig")]

Now when you drop your control, we will have a friendly name in the TagPrefix attribute and not something like TagPrefix="cc1".

ASP.NET
<%@ Register TagPrefix="gootvilig" Namespace="Gootvilig.Controls" 
                       Assembly="Gootvilig.Controls" %> 

The Intelisense for web controls is in a file named "asp.xsd", that can be found in the path "Program Files\Microsoft Visual Studio .NET 2003\Common7\Packages\schemas\xml". This is the schema for any controls prefixed with "asp:", and their attributes.

Furthermore, the ability to display data in the Properties Window is also dependent on this schema.

Image 2

So how can do we add our schema?

  • Add your own XSD file.
  • Copy the file to the "Asp.xsd" folder.
  • Add namespace to the page BODY tag:
HTML
<body MS_POSITIONING="GridLayout" xmlns:Gootvilig=
                  "urn:http://www.Gootvilig.com/schemas">

Now we will get intlisense support (And also Properties Window support).

Image 3

The next code snippet is the base of the schema for my control.

XML
<?xml version="1.0" encoding="utf-8" ?>
<xsd:schema targetNamespace="urn:http://www.Gootvilig.com/schemas" 
elementFormDefault="qualified"
xmlns="urn:http://www.Gootvilig.com/schemas" 
xmlns:xsd="<A href="http://www.w3.org/2001/XMLSchema" target=_blank>http://www.w3.org/2001/XMLSchema</A>" 
xmlns:vs="<A href="http://schemas.microsoft.com/Visual-Studio-Intellisense" target=_blank>http://schemas.microsoft.com/Visual-Studio-Intellisense</A>" 
vs:friendlyname="Gootvilig Control Schema" 
vs:ishtmlschema="false" 
vs:iscasesensitive="false" 
vs:requireattributequotes="true">
 
<xsd:annotation> 
<xsd:documentation>Gootvilig Control schema.</xsd:documentation> 
</xsd:annotation> 

<xsd:element name="ClickOnce" type="ClickOnceDef"></xsd:element> 
<xsd:complexType name="ClickOnceDef" vs:noambientcontentmodel="true"> 
<xsd:attributeGroup ref="<CODE>ButtonDef</CODE>" /> 
</xsd:complexType> 
</xsd:schema> 

Now we have a problem. The reference to "ButtonDef" is located in "asp.xsd" file with a different namespace from the one I chose for my control. So we have to copy some lines from "asp.xsd" file. The whole schema is in the code download.

Now there is complete intelisense for the control.

Full Intelisense

Support for the Toolbox

Adding your component to the toolbar is simple. Just right click on the desired toolbox tab, choose "Add/Remove Items…", and browse to your component's assembly DLL. The component name will appear there with the following default icon: Image 5.

To add your Icon to the toolbox, add this attribute to your class:

C#
ToolboxBitmap(typeof(ClickOnce),"Gootvilig.Controls.ClickOnce.bmp")
// or
ToolboxBitmap(typeof(Bottun)) 

As you can see, there are two choices. The first choice uses an embedded BMP file and associates it to the class. The file must be 16 X 16 pixels. "Build Action" of the file must be "Embedded Resource".

The second choice associates your class with a built in class icon.

Image 6

Now when you drag and drop ClickOnce to our page, lines will be added to our page: (First we have to add a reference to the assembly)

ASP.NET
<%@ Register TagPrefix="gootvilig" Namespace="Gootvilig.Controls"
                                       Assembly="Gootvilig.Controls" %>

<Gootvilig:ClickOnce id="ClickOnce1" runat="server" 
                               Text="Button"></Gootvilig:ClickOnce>

The ClickOnce control

The control is designed to support long term round trip actions. In that case we want to avoid any possibility of re-clicking on a button.

To achieve this action we need to add some JavaScript code, for the client to execute it before the long time server callback code will take place.

Be sure not to replace the "onclick" event, but add your code to the event code chain.

Here is the code for implementing this:

C#
protected override void OnPreRender(EventArgs e)
{
      base.OnPreRender(e);

      string currentOnClick = Attributes["onclick"];
 
      // Add javascript onclick handler
      Attributes["onclick"] 
        += string.Format("document.getElementById(
                     '{0}').disabled = true;",ClientID);
}

Now we have an additional problem: Because the control was set to "disabled = true", the server callback method doesn’t fire.

So I added a call to JavaScript method that is generally added to most ASP.NET pages - __doPostBack()

C#
Attributes["onclick"] 
   += string.Format("document.getElementById('{0}').disabled 
          = true;__doPostBack('{0}','');",ClientID);

Here is the code that the Page added to support postback operations:

HTML
<input type="hidden" name="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" value="" /> 
JavaScript
<script language="javascript" type="text/javascript">
<!--

function __doPostBack(eventTarget, eventArgument) {
var theform;            

if (window.navigator.appName.toLowerCase().indexOf("microsoft") > -1) {
theform = document.Form1;           }           
else {                  
    theform = document.forms["Form1"];        }    
    theform.__EVENTTARGET.value = eventTarget.split("$").join(":"); 
    theform.__EVENTARGUMENT.value = eventArgument;        
    theform.submit(); }// -->
</script>

But the page doesn't always add these snippet code. Only if one of the following controls is embedded in the page, it will call the page internal RegisterPostBackScript() method to accomplish that. The controls that do that are: HtmlAnchor, HtmlButton, HtmlInputButton, HtmlImage, Calendar, CheckBox, LinkButton, ListButton, ListControl and TextBox. Those controls call RegisterPostBackScript() depend on some conditions.

Here is the code of Page.RegisterPostBackScript(). The flag _fRequirePostBackScript is for inserting the script later on.

C#
internal void RegisterPostBackScript() 
{ 
    if (this._fPostBackScriptRendered);
    { 
        return; 
    } 
     if (!this._fRequirePostBackScript) 
    { 
         this.RegisterHiddenField("__EVENTTARGET", ""); 

         this.RegisterHiddenField("__EVENTARGUMENT", "");    
    }
   this._fRequirePostBackScript = true; 
} 

Here is the code to enforce the page to call RegisterPostBackScript(), using reflection:

C#
//Call the internal method Page.RegisterPostBackScript()
MethodInfo  methodInfo = 
typeof(Page).GetMethod("RegisterPostBackScript",
          BindingFlags.Instance|BindingFlags.NonPublic);

if(methodInfo != null)
{
      methodInfo.Invoke(Page,new object[]{});
}

The ClientID, as the name indicates, is good only for client script code. When calling the __doPostBack(), in case our button is located on User Control, we need UniqueIDWithDollars name, which is also an internal method. Our good old friend, reflection, will help here as well:

C#
//Code from Control 
internal string UniqueIDWithDollars{
      get
      {
         string text1 = this.UniqueID;
         if (text1 == null)
         {
             return null;
         }
         if (text1.IndexOf(':') >= 0)
         {
            return text1.Replace(':', '$');          
         }
         return text1;
     }
 }

Here is the code to call UniqueIDWithDollars by reflection:

C#
//Get the Control UniqueIDWithDollars for the call to __doPostBack()
PropertyInfo  propertyInfo = typeof(Control).GetProperty(
         "UniqueIDWithDollars",BindingFlags.Instance|BindingFlags.NonPublic);
string uniqueIDWithDollars = ClientID;
if(propertyInfo != null)
{
    uniqueIDWithDollars = (string)propertyInfo.GetValue(this,new object[]{});
}

Wait! How will the ClickOnce control behave now? It should be enabled after the long server run:

So we set Enabled to true in the OnInit method.

C#
protected override void OnInit(EventArgs e)
{
      base.OnInit(e);      
      
      //Enable upon PostBack
      Enabled = true;
}

After OnInit, ViewState will take place, so users of our ClickOnce Button, can change it's state to disabled. The demo code illustrates that.

See also Eric Plowe's ClickOnce Button Server Control.

Conclusions

What did we have here :

  • Adding design time ability to our custom web control.
  • Adding Toolbox Icon support.
  • How to enforce a button to have a ClickOnce behavior.

Updates

  • 09-02-2005

InteliSence in User Control

We saw that, we need to copy the XSD that support our control schema to the "Asp.xsd" folder, then add our namespace to the page BODY tag.

But what if we are working with a User Control, where we have no BODY tag?

We can do that by enclosing all the HTML code in a tag and add the namespace to that tag.For example:

ASP.NET
<DIV xmlns:Gootvilig="urn:http://www.Gootvilig.com/schemas">
<!-- Here is all the User Control code -->
</DIV>

Or better in a way that will not rendered to the actual HTML of the client side:

ASP.NET
<% if(false) { %>
<DIV xmlns:Gootvilig="urn:http://www.Gootvilig.com/schemas">
<%}%>
 
<!-- Here is all the User Control code -->
<% if(false) { %>
</DIV>
<%}%>

No need to copy the XSD file

If we have the XSD in the solution which we are working on, we don't have to copy that file to the "Asp.xsd" folder!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Matrix-IT
Israel Israel
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionserver time Pin
Ravi Punjwani27-Apr-09 0:33
Ravi Punjwani27-Apr-09 0:33 
can anyone tell me how to get server time easilyConfused | :confused:
GeneralCreation of our own server control Pin
AnuMaria5-Jan-09 23:09
AnuMaria5-Jan-09 23:09 
JokeWell Done Pin
Muhammad Albanna23-Sep-08 12:28
Muhammad Albanna23-Sep-08 12:28 
QuestionHow to get the columns list return by a store procedure in design time Pin
spidervn10-Jul-08 23:20
spidervn10-Jul-08 23:20 
Questionhow to invoke a OpenFile and SaveFile Dialog Box in asp.net Pin
pandeybrijendra14-Sep-07 21:56
pandeybrijendra14-Sep-07 21:56 
GeneralUse Page.GetPostBackEventReference to insert __doPostBack Pin
Michael Freidgeim6-Nov-05 11:58
Michael Freidgeim6-Nov-05 11:58 
GeneralThank you for info about UniqueIDWithDollars Pin
Michael Freidgeim3-Nov-05 20:45
Michael Freidgeim3-Nov-05 20:45 
Questionhow to add a dropdownlist dynamically using dhtml Pin
surajsm7-Oct-05 21:40
surajsm7-Oct-05 21:40 
Questionhow to access clientside(javascript) value from serversideC# Pin
mohd rafi16-Sep-05 19:48
mohd rafi16-Sep-05 19:48 
GeneralInteliSence in User Control Pin
weezowazo9-Apr-05 2:51
weezowazo9-Apr-05 2:51 
Generalopen source utility to generate the XSD for your control Pin
Ashley van Gerven27-Mar-05 3:35
Ashley van Gerven27-Mar-05 3:35 
GeneralRe: open source utility to generate the XSD for your control Pin
weezowazo9-Apr-05 2:20
weezowazo9-Apr-05 2:20 
QuestionHow to drag into HTML code? Pin
Ashley van Gerven27-Mar-05 3:16
Ashley van Gerven27-Mar-05 3:16 
Generaltypo in hyperlink URL Pin
Red Feet20-Feb-05 22:11
Red Feet20-Feb-05 22:11 
Generalxsd for DataGridColumns Pin
PiyushC20-Feb-05 20:15
PiyushC20-Feb-05 20:15 
GeneralRe: xsd for DataGridColumns Pin
Yitzhak Gootvilig9-Mar-05 4:58
Yitzhak Gootvilig9-Mar-05 4:58 
GeneralThanks for the info Pin
Todd Davis11-Feb-05 2:40
Todd Davis11-Feb-05 2:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.