-6-

 

User Controls

 

In this chapter, you will learn a little more about C#, so that you can incorporate it while writing your own controls.

 

We will create our own controls in a slightly different manner than what was done earlier. To do so, we create:

 

     a.aspx in c:\inetpub\wwwroot subdirectory.

     b.cs in c:\inetpub\wwwroot\bin subdirectory.

 

a.aspx

<%@ Register TagPrefix="ttt" Namespace="nnn" Assembly="c"%>

<html>

<body>

<form method="POST" action="s.aspx" runat=server>

<ttt:zzz runat=server/>

</form>

</body>

</html>

 

b.cs

using System.Web.UI;

namespace nnn

{

public class zzz : Control

{

}   

}

 

You can compile the above file named b.cs, by giving the following command:

 

>csc /target:library  /out:c.dll b.cs /r:System.Web.dll

 

The compiler creates a file called c.dll in the inetpub\wwwroot\bin subdirectory.

 

In the above C# program, we have simply created a class zzz that is derived from the class Control. The class Control belongs to the System.Web.UI namespace. We do not have any other code in our program. Thus, we see a blank screen.

 

The file a.aspx will remain the same for the initial programs.

 

b.cs

using System.Web.UI;

namespace nnn

{

public class zzz : Control

{

protected override void Render(HtmlTextWriter o)

{

o.Write("<b>hell</b>");

}

}   

}

 

We now have added a function called Render that accepts a parameter 'o', which is an instance of the class HtmlTextWriter, and its return type is void. The modifiers 'override' and 'protected', shall be explained to you, in a while. Also, we now call the Write function in the class HtmlTextWriter, and give it a string, <b>hell</b>.

 

When you load the aspx file, you will see 'hell' displayed in bold on your screen.

 

o is an instance of HtmlTextWriter. Now that we have seen some output, let us understand the working of the program.

 

In the file a.aspx, we have used a directive called Register (which has been explained in one of the earlier chapters), and created our own tag prefix called ttt. Along with these details, we have also declared a namespace called nnn. Our custom tag starts with the prefix ttt, followed by a colon and then the word zzz. By merely furnishing this information, we see the word 'hell' displayed in the browser window.

 

While executing the aspx file, when the web server comes across a custom tag, it first looks for the directory that is running the aspx file. On receiving the directory, it then looks into a subdirectory to locate another subdirectory called bin. In our case, bin is located in the c:\inetpub\wwwroot subdirectory. Thereafter, all the dlls in the bin subdirectory, are loaded into memory.

 

Every dll that is loaded into memory, contains information or metadata that consists of the classes and namespaces existing in it. The web server will check every file for the presence of a class named zzz (ttt: is followed by zzz) within the nnn namespace (This namespace is given with the Register directive). The file c.dll meets the match.

 

The Web server is programmed to call a function by the name of Render, which takes an HtmlTextWriter object as a parameter in the acquired dll. Using this object, text is rendered to the browser.

 

Thus, a user-defined tag is nothing but a class with the same name, but which derives from the class Control. Also, every user-defined tag must contain a function called Render.

 

Now, let us get back to C# and understand what the word 'protected' signifies.

 

z.cs

public class zzz {

public static void Main()

{

yyy a = new yyy();

a.abc();

}

}

public class yyy {

private void abc()

{

}

}

 

Output

z.cs(6,1): error CS0122: 'yyy.abc()' is inaccessible due to its protection level

 

If you compile the above code as

 

>csc z.cs

 

an error will be generated.

 

This is because the function abc is tagged with the modifier private. The private modifier allows only the members of the same class to access the function. As a result, only members of the class yyy are allowed to access the function abc. Therefore, the  object 'a' of type yyy in the class zzz, is restricted any access to private members of the class yyy.

 

Thus, private is called an access modifier, since it prohibits access to members of a class. Private is the most restrictive access modifier, while public is the most tolerant. These access modifiers do not apply to members of the same class.

 

z.cs

public class zzz

{

public static void Main()

{

xxx a = new xxx();

a.abc();

}

}

public class yyy

{

}

public class xxx : yyy

{

protected void abc()

{

}

}

 

Output

z.cs(6,1): error CS0122: 'xxx.abc()' is inaccessible due to its protection level

 

The same error gets repeated again with the protected modifier as a protected member is accessible only to a derived class object.

 

Had any member of class yyy tried to access the functions in class xxx, which has been derived from the class yyy, no errors would have been generated. A protected modifier signifies that only derived classes are allowed access its members. Thus, a protected modifier lies in the midst of the two extremes, private and public.

 

z.cs

public class zzz

{

public static void Main()

{

xxx a = new xxx();

yyy b = new yyy();

xxx c = new yyy();

yyy d = new xxx();

}

}

public class yyy

{

}

 

public class xxx

{

}

 

 

Output

z.cs(7,9): error CS0029: Cannot implicitly convert type 'yyy' to 'xxx'

z.cs(8,9): error CS0029: Cannot implicitly convert type 'xxx' to 'yyy'

 

C# is extremely stern about equating different data types to each other. Thus, we cannot equate a yyy object with an xxx object. The entities on both sides of an 'equal to' sign must have the same data type, or else an error stating 'cannot covert to', is generated.

 

z.cs

public class zzz

{

public static void Main()

{

xxx a = new xxx();

yyy b = new yyy();

xxx c = new yyy();

yyy d = new xxx();

}

}

public class yyy

{

}

public class xxx : yyy

{

}

 

Output

z.cs(7,9): error CS0029: Cannot implicitly convert type 'yyy' to 'xxx'

 

Now, the error vanishes because, class xxx is derived from class yyy. As a result, class xxx now comprises of two classes, a yyy class and an xxx class. Therefore, the statement xxx c = new yyy() does not raise any error.

 

As class xxx derives from class yyy, we call xxx a higher class, since it contains the lower class yyy, and more. We can thus, have a higher class on the right hand side of the 'equal to' sign and a lower class on the left hand side. Technically speaking, we can use a derived class on the right hand side, and a base class on the left hand side of an 'equal to' sign.

 

If they are placed the other way around, that is, if the class yyy is on the right hand side and the xxx class is on the left hand side, an error is generated. A derived class cannot be made equal to a base class.

 

z.cs

public class zzz {

public static void Main()

{

yyy a = new xxx();

a.abc();

a.pqr();

}

}

public class yyy {

public virtual void abc()

{

System.Console.WriteLine("yyy abc");

}

public virtual void pqr()

{

System.Console.WriteLine("yyy pqr");

}

}

public class xxx : yyy

{

public override void abc()

{

System.Console.WriteLine("xxx abc");

}

public new void pqr()

{

System.Console.WriteLine("xxx pqr");

}

}

 

Output

xxx abc

yyy pqr

 

We have two functions in class yyy namely, abc and pqr. We have added a modifier named 'virtual' to these functions. This modifier permits the derived classes to override the functions abc and pqr. Without the word virtual, the derived classes cannot override functions in the base class.

 

The derived class has the option of overriding the functions of the base class, i.e. creating a new function with the same names which may or may not bear any relation to the functions of the base class.

 

Adding a modifier 'new' to the virtual function in the derived class, breaks all links with the function having the same name in the base class. Thus, the base class pointer has no access to the derived class functions.

 

The override modifier overrides the function of the base class. 'a'  is a pointer to a yyy class and it is initialized to the derived class xxx. Hence, when we call a.abc(), the function abc of the derived class xxx will be called.

 

Thus, in the case of a virtual function, C# goes a step further and checks the run time data type of the object, and not the compile time data type. If the function in the derived class has an override modifier, as in the case of the function abc, the function gets called from the derived class. In case of new, as in the case of the function pqr, it becomes a new function. And thus, it ignores the existence of such a function in the class xxx. The only option available is to call the function from the class yyy itself.

 

There is a function called Render in the class Control. If we want our function Render to be called in class zzz, we have to use the override modifier. Otherwise, the function will be called from the class Control instead.

 

z.cs

public class zzz

{

public static void Main()

{

yyy a = new xxx();

a.abc();

}

}

public class yyy

{

public virtual void abc()

{

System.Console.WriteLine("yyy abc");

}

}

public class xxx : yyy

{

protected override void abc()

{

System.Console.WriteLine("xxx abc");

}

}

 

Output

z.cs(18,25): error CS0507: 'xxx.abc()': cannot change access modifiers when overriding 'public' inherited member 'yyy.abc()'

 

We get the above error because, when we override a function of the base class, we cannot change it's access modifiers. Everything, including the modifiers, must be identical to what is present in the base class.  Since the function Render is marked as protected in the base class, we have to use the same access modifier in the class zzz also.

 

Let us get back to Controls again.

 

a.aspx

<%@ Register TagPrefix="ttt" Namespace="nnn" Assembly="c"%>

<html>

<body>

<form method="POST" action="S.aspx" runat=server>

<ttt:zzz aa="hi" bb=100 runat=server/>

</form>

</body>

</html>

 

b.cs

using System.Web.UI;

namespace nnn

{

public class zzz : Control

{

public string s;

public string aa

{

get

{

return s;

}

set

{

s = value;

}

}

public int bb = 12;

protected override void Render(HtmlTextWriter o)

{

o.Write("hell " + bb + " " + aa);

}

}   

}

 

Output

hell 100 hi

 

A property can either consist of a getset accessor or it can contain a simple instance variable. Also, it can be used in place of a variable. In the case of a property, source code can be executed. However, the user of the tag will never be exposed to the internal workings of a property and its implementation in a tag.

 

The set accessor is called before the function Render gets executed. This enforces the situation where all the properties have to be initialized first, and only then the Render function can be executed.

 

a.aspx

<%@ Register TagPrefix="ttt" Namespace="nnn" Assembly="c"%>

<html>

<body>

<form method="POST" action="S.aspx" runat=server>

<ttt:zzz aa-bb="hi" aa-cc=100 runat=server/>

</form>

</body>

</html>

 

b.cs

using System;

using System.Web;

using System.Web.UI;

namespace nnn

{

public class yyy

{

public string bb = "no";

public int cc = 200;

}

public class zzz : Control

{

yyy a = new yyy();

public yyy aa

{

get

{

return a;

}

}

protected override void Render(HtmlTextWriter o)

{

o.Write(aa.bb + " " + aa.cc);

}

}   

}

 

Output

hi 100

 

We look at the .cs file first. In the class zzz, we have created an object a, that is an instance of the class yyy. This class is created in the same namespace nnn as given along with the aspx Register directive. aa is a property that returns a yyy object in its get accessor.

Class yyy also has two members named bb and cc, which are initialized to 'no' and 200, respectively. Once a yyy object is created, using the dot syntax, we can access these members individually. In an aspx file, a minus sign is used instead of a dot, to access the members. Hence, to access the bb member in yyy, we use the statement 'aa-bb'.

 

The only difference here is in the choice of the separator, between the name of the property and the name of the member, belonging to the object that the property returns.

 

Now, we return back to C#.

 

z.cs

public class zzz

{

public static void Main()

{

int [] a;

int i;

a = new int[3];

a[0] = 1;

a[2] = 8;

a[0]++;

System.Console.WriteLine(a[0]);

i = 0;

System.Console.WriteLine(a[i]);

i = 2;

System.Console.WriteLine(a[i]);

}

}

 

Output

2

2

8

 

Whenever we want to store multiple items of the same type, we use a data type called an array. An array stores similar items together. Here, 'a' is an array that is declared, using a pair of square brackets [].

We now proceed to create an array of ints. To do so, the keyword 'new' is used along with the size of the array, which in our case is 3. The array size cannot be specified at the time of creation of the array. It must be specified along with 'new'.

 

To access the members of the array, we use the name of the array, followed by a pair of square brackets which enclose the index of the specific array variable. Thus, the first variable is a[0], the second is a[1], and so on. The counting for the array index begins with zero and not from 1.

 

An array variable can be easily used in place of a normal variable. The advantage in using arrays is that it makes the code concise. For example, when we use the form a[i], i is an integer variable holding values ranging from 0 to 2. When the value of i is 0, the name of the variable becomes a[0]. When we change the value of i to 1, the name of the variable now becomes a[1]. Thus, by changing the value of one variable, we are able to change the name of another variable, and thus, access a different value.

 

It is this feature of arrays, which makes them very useful. We are guilty of the crime of having used arrays earlier in this book, without explaining them in detail.

 

z.cs

public class zzz

{

public static void Main()

{

int [] a;

int i;

a = new int[3];

for ( i = 0; i<=2; i++)

a[i] = i*2;

System.Console.WriteLine(a.Length);

for ( i = 0; i< a.Length; i++)

System.Console.Write(a[i]);

}

}

 

 

Output

3

024

 

Arrays are ideal in a looping construct. In the first 'for' loop, we initialize the variable a[0] to 0, a[1] to 2 and a[2] to 4. Every array type has a member called Length, that returns the size of the array. In our case, the array size is 3. We can use this fact to iterate through all the members of the array.

 

z.cs

public class zzz

{

public static void Main()

{

string  [] a;

a = new string[]{"hi","bye"};

foreach ( string s  in a)

System.Console.WriteLine(s);

}

}

 

Output

hi

bye

 

We can create an array of any data type. Earlier, we created an array of type int. In this program, we have created an array of type string. We are also allowed to initialize the array members at the time of creation, by using the square brackets.

 

We can use the 'foreach' statement to iterate through the array. During each iteration, the string 's' will sequentially hold the string values in the array. Thus, in the first iteration, 's' will contain the string 'hi', and in the next iteration, it will contain the string 'bye'.

 

The 'foreach' construct is a more convenient way of iterating through an array, though we could even have used a 'for' construct. You are free to choose from any of these constructs, as per your requirement.

 

 

z.cs

public class zzz

{

public static void Main()

{

yyy a = new yyy();

a[2] = "hi";

System.Console.WriteLine(a[2]);

}

}

public class yyy

{

public string a;

public string this[int i]

{

get

{

System.Console.WriteLine("get " + i);

return a;

}

set

{

System.Console.WriteLine("set " + value + " " + i);

a = value;

}

}

}

 

Output

set hi 2

get 2

hi

 

Although, we have not created any array in the above program, we are still using the array syntax. We first create an object 'a' of type yyy. Then we use the syntax a[2] = "hi". When the program reaches this statement, C# stops for a while and looks for an indexer in our program.

 

An indexer is a property with a special name called 'this'. Since we have created such a property, C# calls the set accessor of this property and assigns a value of 2 to the parameter i.

As the return type of the property is a string, the hidden variable named 'value' contains the string 'hi'. We are supposed to simulate an array within the property. Since this has not been done, the indexer simulates an array that does not exist.

 

Let us get back to ASP+.

 

a.aspx

<%@ Register TagPrefix="ttt" Namespace="nnn" assembly="c"%>

<html>

<body>

<form method="POST" action="a.aspx" runat=server>

<ttt:zzz runat=server>

vijay mukhi

</ttt:zzz>

</form>

</body>

</html>

 

b.cs

using System;

using System.Web;

using System.Web.UI;

namespace nnn

{

public class zzz : Control

{

protected override void Render(HtmlTextWriter o)

{

if ( HasControls() )

o.Write("True <br>");

LiteralControl l = (LiteralControl) Controls[0];

o.Write(l.Text);

}

}

}

 

Output

True

vijay mukhi

 

 

In the aspx file, we have inserted the text 'vijay mukhi' within ttt, which is a newly created tag. We would like to display this text in the browser.

 

To do so, we call a function called HasControls in the Render function, which does not take any parameter.

 

Since this function is not present in our class zzz, it is taken from the Control class. The return value of true or false depends upon the presence of controls in our web page. If the page has controls, HasControls shows true, or else, it shows false.

 

The class Control has a property called Controls that returns a ControlCollection object. This class has an indexer that returns a Control object. Thus, the use of Controls[0] will return the first control on our page.

 

In our program, Controls[0] returns a LiteralControl, which is a class derived from Control. Hence, the cast operator is required.

 

The Text property in this control, displays the text given within the tag.

 

a.aspx

<%@ Register TagPrefix="ttt" Namespace="nnn" assembly="c"%>

<html>

<body>

<form method="POST" action="a.aspx" runat=server>

<ttt:zzz id="hi" runat=server>

vijay mukhi

</ttt:zzz>

<asp:Button id="bye" runat=server>

</asp:Button>

</form>

</body>

</html>

 

b.cs

using System;

using System.Web;

using System.Web.UI;

namespace nnn

{

public class zzz : Control

{

protected override void Render(HtmlTextWriter o)

{

int j = Controls.Count;

o.Write(j + "<br>");

for( int i = 0; i< j; i++)

{

if ( Controls[i] is LiteralControl )

{

LiteralControl l = (LiteralControl) Controls[i];

o.Write(l.Text + " " + l.ClientID);

}

}

}

}   

}

 

Output

1

vijay mukhi ctrl1 

 

The class ControlCollection has a member called Count, which contains a count of the number of Controls that are present on our page. The value held in this member is 1, which indicates that the Button control used outside the ttt tag, has not got added to our collection.

 

A 'for' loop is used to iterate through the collection. A check is performed for a LiteralControl; thereafter, two members of this control, Text and ClientID, are displayed. The member ClientID shows crtl1, even though it has been assigned an id of 'hi'. These controls are called child controls.

 

Composite Controls

 

a.aspx

<%@ Register TagPrefix="ttt" Namespace="nnn" assembly="c"%>

<html>

<script language="C#" runat=server>

void abc(Object sender, EventArgs e)

{

hi.aa++;

}

</script>

<body>

<form method="POST" action="a.aspx" runat=server>

<ttt:zzz id="hi" runat=server/>

<asp:button text="Add" OnClick="abc" runat=server/> 

</form>

</body>

</html>

 

b.cs

using System;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

namespace nnn

{

public class zzz: Control, INamingContainer

{

public int aa

{

get

{

return Int32.Parse(((TextBox)Controls[1]).Text);

}

set

{

((TextBox)Controls[1]).Text = value.ToString();

}

}

protected override void CreateChildControls()

{

this.Controls.Add(new LiteralControl("Vijay Mukhi: "));

TextBox b = new TextBox();

this.Controls.Add(b);

b.Text = Controls.Count.ToString();

}

}   

}

 

Output

Vijay Mukhi:  

 

On loading the aspx file, we see ' 2' in the textbox and then, a button labeled Add is displayed.

 

In the second program, we have explained the function called Render, which is called when you have a User Defined Control. In the same vein, if a function by the name of CreateChildControl is present in the file, it too gets called. In this function, we can add as many controls as we desire.

 

Every function in a class is given an extra parameter called 'this'. This parameter refers to an instance of the class that the function resides in. Thus, truly speaking, the function Render is called with two parameters and not one. The code for it is as follows:

 

Render ( zzz this, HtmlTextWriter o).

 

Therefore, this.Controls.Add or Controls.Add means the same thing. Whenever you see this syntax, you can safely ignore it.

 

In the function CreateChildControls, we have added a Literal Control object, and created a Literal Control object on the same line. The syntax Add introduces one more control to the Controls collection, and hence, we can proudly see 'Vijay Mukhi' displayed in the browser window.

 

To add a TextBox to the controls collection, we simply create a TextBox object and use the same Add function from the Controls class. Hence, the count 'property' shows the number 2 in the editbox, because we have initialized the Text member to this value.

 

In the class zzz, we have a property called aa which is given the id of 'hi' in the aspx file. The property can now be accessed by writing hi.aa. The syntax consists of the id name, followed by name of the property.

Within this property, the value contained in the TextBox is incremented by one, every time. The set accessor uses value, which is the hidden variable, to change the Text member. In the get accessor, we simply return the value stored in the Text member. The cast operators are mandatory.

 

We also assume that the TextBox is the second member of the Controls Collection and hence, we use Controls[1] to access it. The user cannot be made aware of the fact that the controls were created in the function CreateChildControls.

 

The HTML file simply shows it as HTML tags. We are thus creating new controls by combining existing controls. Such controls are called Composite controls, and the technique used is called class composition. The User Controls created earlier, were present in a file with the .ascx extension which is a text file, whereas now, we have placed the control in an assembly or a dll file.

 

In every other sense, composite controls are similar to User Controls and use the same ASP.NET syntax.

 

Zzz is derived from the interface INamingContainer that does not contain any methods at all. We use it merely as a tagging interface. When the class implements the above interface, every new child control that is created, is given a unique id in ASP+. This concept is important because we may have many instances of our control on the same page; therefore, their ids need to be different. Zzz does not need to override the Render method, because the child controls contain their own rendering logic or code.

 

You can also expose a single property that internally uses multiple properties from multiple controls.

 

But before that, let us first learn all about events and delegates.

 

z.cs

using System;

delegate void ddd();

class zzz

{

public void abc()

{

Console.WriteLine("abc " );

}

public static void Main()

{

zzz z = new zzz();

ddd a;

a = new ddd(z.abc);

a();

}

}

 

Output

abc

 

The delegate ddd is created by specifying the return type, the parameter types, and the reserved word delegate. Thereafter, we are allowed to declare an object 'a', which is of the delegate type ddd.

 

To instantiate a delegate object, 'new' is used along with the name of the function, z.abc, which has to be called through a delegate object named 'a'. Thus, a() will call the function abc. This is an indirect way of calling the function abc through the delegate object.

 

z.cs

using System;

delegate void ddd(string s);

class zzz {

public static void abc(string s)

{

Console.WriteLine("abc " + s);

}

public static void pqr(string s)

{

Console.WriteLine("pqr " + s);

}

public static void Main()

{

ddd a, b, c, d;

a = new ddd(abc);

b = new ddd(pqr);

c = a + b;

d = c - a;

a("one");

b("two");

c("three");

d("four");

}

}

 

Output

abc one

pqr two

abc three

pqr three

pqr four

 

This example shows us the capabilities of a delegate. We have created two delegate objects, a and b, and associated them with the functions abc and pqr, respectively. These functions are called indirectly through the delegate objects.

 

The power of a delegate gets displayed when we combine them, as in a + b . The result of this operation is stored in delegate c. Thus, the syntax c() calls both the functions abc and pqr. This is the first time that the plus sign is being used to call two functions through a delegate. The same concept could be applied to call numerous functions by using a single delegate. The delegate 'c' represents the functions abc and pqr. Subtracting 'a' from 'c', i.e. subtracting the function abc from the functions abc and pqr, results in the delegate 'd' calling only the function pqr.

 

Events

 

z.cs

public delegate void ddd();

class zzz

{

public static void Main()

{

yyy l = new yyy();

l.c += new ddd(l.abc);

xxx x = new xxx();

l.c += new ddd(x.xyz);

l.pqr();

}

}

public class yyy

{

public event ddd c;

public void pqr()

{

c();

}

public void abc()

{

System.Console.WriteLine("abc");

}

}

public class xxx

{

public void xyz()

{

System.Console.WriteLine("xyz");

}

}

 

Output

abc

xyz

 

The delegate ddd is created to call the function abc. Next, we create two objects named 'l' and 'x', that look like classes yyy and xxx, respectively. In the class yyy, we create 'c', which is an instance of a delegate ddd. Note that the return type is replaced by the word event. Hence, 'c' is now an event.

 

The event 'c' is initialized to a delegate instance, which is associated with the function abc from the same class. Thereafter, we again initialize the event c to a delegate instance, but this time, we use a different function name i.e. xyz, from the class xxx. Thus, the event 'c' is now associated with two functions. The command l.pqr() executes the event object as c(). Thus, two functions belonging to two different classes get executed together. This is the power of an event, and it is used extensively for notification purposes.

 

a.aspx

<%@ Register TagPrefix="ttt" Namespace="nnn"  assembly="c"%>

<html>

<body>

<form method="POST" action="a.aspx" runat=server>

<ttt:zzz id="hi" runat=server/>

</form>

</body>

</html>

 

b.cs

using System;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

namespace nnn

{

public class zzz: Control,INamingContainer

{

public int aa

{

get

{

return Int32.Parse(((TextBox)Controls[1]).Text);

}

set

{

((TextBox)Controls[1]).Text = value.ToString();

}

}

protected override void CreateChildControls()

{

this.Controls.Add(new LiteralControl("Vijay Mukhi: "));

TextBox b = new TextBox();

b.Text = "0";

this.Controls.Add(b);

Button a = new Button();

a.Text = "Add";

a.Click += new EventHandler(abc);

Controls.Add(a);

}

void abc(Object s, EventArgs e)

{

aa++;

}

}   

}

 

Output

Vijay Mukhi:  

 

The above example demonstrates as to how a delegate can be used in a control. In addition to the two controls that existed previously, we have a third control, which is a button named 'b'. The text or the label of the button is Add.

 

The syntax a.Click += new EventHandler(abc) can only be used with a delegate. Click is a delegate type property in the Button class, and is similar to our delegate objects a, b and c. The EventHandler class is like our delegate ddd, which represents the function abc. Thus, every time we click on the button, the function Click() is called.  As a result, all the functions associated with the delegate, get called. Thus, function abc is called, which increments the value of aa by 1.

 

b.cs

using System;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

namespace nnn

{

public class zzz: Control,INamingContainer

{

public int aa

{

get

{

return Int32.Parse(((TextBox)Controls[1]).Text);

}

set

{

((TextBox)Controls[1]).Text = value.ToString();

}

}

protected override void CreateChildControls()

{

this.Controls.Add(new LiteralControl("Vijay Mukhi: "));

TextBox b = new TextBox();

b.Text = "0";

this.Controls.Add(b);

Button a = new Button();

a.Text = "Add";

a.Click += new EventHandler(abc);

a.Click += new EventHandler(pqr);

Controls.Add(a);

}

void abc(Object s, EventArgs e)

{

aa++;

}

void pqr(Object s, EventArgs e)

{

aa++;

}

}   

}

 

Each time we click on the button, the number increases by 2. This is because we have associated the two functions abc and pqr with the delegate object name Click. We could have called numerous other functions in a similar manner.

 

The use of delegates offers a cleaner method of calling our code whenever an event takes place. A composite control can add itself to an event raised by a child control. We guarantee that our code will get called with a click on a child control, i.e. a button.

 

a.aspx

<%@ Register TagPrefix="ttt" Namespace="nnn" assembly="c"%>

<html>

<script language="C#" runat=server>

void abc(Object sender, EventArgs e)

{

hi.aa++;

Response.Write(hi.aa.ToString());

}

</script>

<body>

<form method="POST" action="a.aspx" runat=server>

<ttt:zzz id="hi" Onchange="abc" runat=server/>

</form>

</body>

</html>

 

b.cs

using System;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

namespace nnn

{

public class zzz: Control, INamingContainer

{

public event EventHandler Change;

public int aa

{

get

{

return Int32.Parse(((TextBox)Controls[0]).Text);

}

set

{

((TextBox)Controls[0]).Text = value.ToString();

}

}

protected override void CreateChildControls()

{

TextBox b = new TextBox();

b.Text = "0";

Controls.Add(b);

Button a = new Button();

a.Text = "Add";

a.Click += new EventHandler(add1);

Controls.Add(a);

}

void add1(Object sender, EventArgs e)

{

aa++;

Change(this,EventArgs.Empty);

}

}   

}

 

Output

4

 

This example once again demonstrates an event. Here, we have a tag with an id of 'hi'. Along with it, a new property called onChange is introduced, which calls the function abc when a change occurs in our user control. Thus, when anything is entered in the textbox, it  will result in a call to the function abc. This function merely increments the value of the property aa by 1, and then displays it.

 

The user control notifies ASP+ about the change, as a result of which, the code that has been written outside the control, gets called. The onChange property must be initialized to a function, if we want it to discern any change in the control. The control will simply trigger off an OnChange event, which results in a call to the function abc. An event object named Change, which is of delegate type EventHandler, is created. This happens because our property in the tag is named as OnChange.

 

A property called aa is added, which gets and sets the value in the textbox, i.e. Child control [0]. The function CreateChildControls creates a textbox, just as it did before, and a button with the label Add is also added.

 

The event object Click in the button class is associated with the EventHandler delegate, which represents the function named add1. The function add1 is executed when the button is clicked.

 

In this function, we merely add 1 to the property aa and then trigger off the Change event. This in turn, calls the function abc, since it is associated with the OnChange property in the aspx file. The program is not aware of this association, because these linkages are given in the aspx file.

 

The function abc requires two parameters, namely, a sender and an EventArgs data type. The sender in this case is the program itself. Hence, the sender and the EventArgs are empty. The statement aa++ increments the value by 1, and the Write function finally displays this value. Note that aa has been incremented twice overall.

 

Thus, it is evident that code can be called in a dll, as well as, in the aspx file. This occurs with each having no knowledge about the other's existence.

 

a.aspx

<%@ Register TagPrefix="ttt" Namespace="nnn" assembly="c"%>

<html>

<script language="C#" runat=server>

void abc(Object sender, EventArgs e)

{

if ( hi.aa < 0)

hi.aa=0;

}

</script>

<body>

<form method="POST" action="a.aspx" runat=server>

<ttt:zzz id="hi" Ond="abc" runat=server/>

</form>

</body>

</html>

 

b.cs

using System;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

namespace nnn

{

public class zzz: Control, INamingContainer

{

public event EventHandler d;

public int aa

{

get

{

return Int32.Parse(((TextBox)Controls[0]).Text);

}

set

{

((TextBox)Controls[0]).Text = value.ToString();

}

}

protected void ddd()

{

d(this,EventArgs.Empty);

}

protected override void CreateChildControls()

{

TextBox b = new TextBox();

b.Text = "0";

b.TextChanged += new EventHandler(ccc);

Controls.Add(b);

Button a = new Button();

a.Text = "Add";

a.Click += new EventHandler(add1);

Controls.Add(a);

Button s = new Button();

s.Text = "Minus";

s.Click += new EventHandler(sub1);

Controls.Add(s);

}

void ccc(Object sender, EventArgs e)

{

ddd();

}

void add1(Object sender, EventArgs e)

{

aa++;

ddd();

}

void sub1(Object sender, EventArgs e)

{

aa--;

ddd();

}       

}   

}

 

Output

 

In the aspx file, we have now named the property as Ond, instead of OnChange, and it is assigned to the function abc. This function simply checks whether the property aa holds a negative number or not. If it does, then aa is set to zero. So, no matter how many times we click on the button labeled Minus, the textbox value will remain frozen at the value of 0.

 

The name of the event type has been changed from Change to 'd'. Hence, the name of the property has also been changed from OnChange to Ond. The textbox has an event called TextChanged, that calls the function ccc whenever the contents of the textbox are changed. We have also added an extra button labeled as Minus, which shall call the function sub1.

 

The three event handler functions finally call the function ddd. We also add or subtract the property aa by 1. Thus, whenever we click on the button, the function ddd finally gets called. Here, we carry out a single task i.e. call the event d(). This will result in a call to the event function, which is associated with the event d or Ond, in the aspx file. Thus, we can call the property by any name we want, provided, there is an event of the same name. But, we should not start the name of the aspx tag with the word 'On'.

 

We have encapsulated all the event handling code in the function ddd, even though it is a single function call. This is done to enable us to add a large amount of code associated with the events, in future. The textbox event handling function is not required. It comes into picture only when the user enters -100, directly into the textbox. Since a change occurs on doing so, the function abc gets called, which reverts the value back to zero.

 

State Management

 

a.aspx

<%@ Register TagPrefix="ttt" Namespace="nnn" assembly="c" %>

<html>

<script language="C#" runat=server>

void abc(Object sender, EventArgs e)

{

hi.FSize++;

}

</script>

<body>

<form method="POST" action="a.aspx" runat=server>

<ttt:zzz id="hi" FSize=1 runat=server/>

<br>

<asp:button Text="click" OnClick="abc" runat=server/>

</form>

</body>

</html>

 

b.cs

using System;

using System.Web;

using System.Web.UI;

namespace nnn

{

public class zzz: Control

{

public int FSize

{

get

{

return (int) ViewState["FSize"];

}

set

{

ViewState["FSize"] = value;

}

}

protected override void Render(HtmlTextWriter o)

{

o.Write("<font size=" + FSize + ">" + "Vijay Mukhi"+ "</font>");

}

}   

}

 

Output

 

Vijay Mukhi

 

Vijay Mukhi

 

View Source

<html>

<body>

<form name="ctrl2" method="POST" action="a.aspx" id="ctrl2">

<input type="hidden" name="__VIEWSTATE" value="YTB6LTIwMTg1MDIyMjVfYTB6X2h6NXoyeF9hMHpfaHo1ejF4X2Ew

emh6RlNpXHplXzV6NXh4X194eF94eF94X194e447caf7" />

<font size=5>Vijay Mukhi</font>

<br>

<input type="submit" name="ctrl6" value="click" />

</form>

</body>

</html>

 

The control zzz displays the text 'Vijay Mukhi'. It has a property called Fsize that controls the size of the font that is displayed. A button labeled 'click' is displayed, which calls the function abc, whenever it is clicked. The function abc increments the property Fsiz,e by one. The final effect that is perceived is an increase in the font size of the text 'Vijay Mukhi' displayed on the screen.

 

The Web Server keeps a record of our actions, because the code of the function resides on the server and not on the client. If you open a new browser window, you will observe that the two Fsize properties are maintained independent of each other. This concept is similar to the one we learnt earlier, when we were dealing with the shopping cart.

The protocol used while transferring the files is called HTTP or the Hyper Text Transfer Protocol. This protocol is stateless, implying that, it does not maintain the history of the interactions between the client and the server. Each interaction is an independent action, with no information being stored about the earlier proceedings. Since the protocol does not maintain any history, the web server is provided with the facility for storing state information.

 

In the file b.cs, there is a user control with the property Fsize and the overridden Render function. This function calls the Write function, which simply outputs 'Vijay Mukhi' after substituting the font size with the property value obtained from Fsize.

 

Normally, in a property, we store the value that is contained in the hidden parameter called value in a variable. But, in this case, we have used a different syntax, i.e. State["FSize"] = value;. This indicates that State is an indexer in the Control class, which is passed a property called FSize as a string parameter. It is now the Web Server's responsibility to store this value for every instance of the browser. To retrieve the value, the same indexer called State, is used with the property of FSize.

 

The View-Source menuoption reveals a hidden field called __VIEWSTATE that has a different value for each new copy of the browser. As this field is of type hidden, it does not get displayed on the screen. However, its value is sent across to the web server. The web server stores the value of the property Fsize, for every copy of the browser. Thus, if there are 100 connections that are active, there will be 100 copies of Fsize, each storing different numbers in a double dimensional array. This is how the information about the state is maintained between a server and a browser, with the server doing all the grunt work.

 

The indexer called State is of type System.Web.UI.StateBag, which is a data structure similar to a hash table. This structure stores values and gives each of them a number or a hash value. The hash value is used to access these values in a quick and efficient manner. Thus, data in the State indexer is stored as 'FSize=10', or better still, in the form of name-value pairs. The value of Fsize is not sent over. Instead, a unique number representing the browser server connection is sent. The HTML file created at the server, fills up the FSize property using this hash value.

 

However, the use of Indexers, such as State, entail a performance overhead. Hence, they should be used with care, or else, the performance is bound to suffer.

 

a.aspx

<%@ Register TagPrefix="ttt" Namespace="nnn"  assembly="c"%>

<html>

<script language="C#" runat=server>

void abc(Object sender, EventArgs e)

{

hi.aa++;

}

</script>

<body>

<form method="POST" action="a.aspx" runat=server>

<ttt:zzz id="hi" runat=server/>

<br>

<asp:button text="Vijay" OnClick="abc" runat=server/> 

</form>

</body>

</html>

 

b.cs

using System;

using System.Web;

using System.Web.UI;

using System.Collections.Specialized;

using System.Web.UI.WebControls;

namespace nnn

{

public class zzz: Control, IPostBackDataHandler

{

int bb = 0;

String p1;

public int aa

{

get

{

return bb;

}

set

{

bb = value;

}

}

public bool LoadPostData(String p, NameValueCollection v)

{

p1 = p;

bb = Int32.Parse(v[UniqueID]);

return false;

}

public void RaisePostDataChangedEvent ()

{

}

protected override void Render(HtmlTextWriter o)

{

o.Write("<input name=" + UniqueID + " type=text value=" + aa + ">");

o.Write("<br>" + p1);

}

}   

}

 

We first add the option /R:System.dll  to the compiler command and run it as follows:

 

>csc /target:library  /out:c.dll b.cs /r:System.Web.dll /r:System.dll

 

Output

 

In the aspx file, we see a textbox containing the value of 0 and a button labeled 'Vijay'. When we click on the button, the value increases by 1. We also see the word 'hi' displayed between the textbox and the button. Have we not performed something similar earlier?

We certainly have, but this time, the control code is very different.

 

The control zzz is now derived from an interface called IPostBackDataHandler, that has two functions named LoadPostData and RaisePostDataChangedEvent. Since it is the interface that we are deriving from, we have to include the code for these functions in our class.

 

Zzz also contains a property called aa that uses an int variable named bb, to store the value. In this program, the State indexer is avoided completely. Instead, every time the data is transferred to the server, or whenever we send the data on a round trip, the function LoadPostData gets called. It has two parameters:

 

     a string p stating the id of the contro, i.e 'hi'

     a NameValueCollection object v, which is an indexer that is used to access the property values.

 

Thus, the variable p1 in this function will contain the word 'hi'. UniqueId is a reserved word. It is the name given to the control, which has an id of 'hi'. A variable named 'bb' is used to hold values for the property. It will now contain this new value returned by the indexer. This is how the stateful values of the properties are maintained.

 

Since the value of false is returned, the next function named RaisePostDataChangedEvent does not get called. Hence, there is no code present in it. If a value of true is returned, it conveys a message to the system that we want to raise a changed notification. This is known as implementing post back data.

 

By deriving from the above interface, we are informing ASP+ about our participation in post back data handling.

 

a.aspx

<%@ Register TagPrefix="ttt" Namespace="nnn" assembly="c" %>

<html>

<body>

<form method="POST" action="a.aspx" runat=server>

<ttt:zzz id="hi" runat=server/>

</form>

</body>

</html>

 

b.cs

using System;

using System.Web;

using System.Web.UI;

using System.Collections.Specialized;

using System.Web.UI.WebControls;

namespace nnn

{

public class zzz : Control, IPostBackDataHandler, IPostBackEventHandler

{

int bb = 0;

public int aa

{

get

{

return bb;

}

set

{

bb = value;

}

}

public bool LoadPostData(String p, NameValueCollection v)

{

bb = Int32.Parse(v[this.UniqueID]);

return false;

}

public void RaisePostDataChangedEvent()

{

}

public void RaisePostBackEvent (String e)

{

if (e == "Add")

{

aa++;

}

else

{

aa--;

}

}

 

protected override void Render(HtmlTextWriter o)

{

o.Write("<input name=" + UniqueID + " type=text value=" + aa + ">");

o.Write("<input type=button value=Add OnClick=\"jscript:"+ Page.GetPostBackEventReference(this, "Add")+ "\">");

o.Write("<input type=button value=Subtract OnClick=\"jscript:"+ Page.GetPostBackEventReference(this, "Subtract")+ "\">");

}   

}   

}

 

View Source

 

<html>

<body>

<form name="ctrl0" method="POST" action="a.aspx" id="ctrl0">

<input type="hidden" name="__VIEWSTATE" value="dDwtMTIyNDI1MzU1Njs7Pg==" />

 

<input name=hi type=text value=2><input type=button value=Add OnClick="jscript:__doPostBack('hi','Add')"><input type=button value=Subtract OnClick="jscript:__doPostBack('hi','Subtract')">

 

<input type="hidden" name="__EVENTTARGET" value="" />

<input type="hidden" name="__EVENTARGUMENT" value="" />

<script language="javascript">

<!--

            function __doPostBack(eventTarget, eventArgument) {

                        var theform = document.ctrl0;

                        theform.__EVENTTARGET.value = eventTarget;

                        theform.__EVENTARGUMENT.value = eventArgument;

                        theform.submit();

            }

// -->

</script>

</form>

</body>

</html>

 

Output

 

 

We have a very simple aspx file which only displays a user control named ttt:zzz. In the control code, we are deriving from two interfaces this time, as against one, which was derived from, in the previous program. The new interface called IPostBackEventHandler has only one function named RaisePostBackEvent that enables a control to take charge of an event fired by the control, in the aspx file.

 

The property aa and the post back data handling code remain the same.

 

Each time we click on the buttons, the code in the control gets called. The function RaisePostBackEvent is called with a parameter, which signifies the value of the control. If we click on the button labeled Add, the string parameter 'e' stores Add. As a result, the property aa is increased by 1. The Subtract button does the opposite, i.e. it decreases the value by 1. Earlier, on an event, code was called in the aspx file. However in this case, code is called in our control.

 

In the Render function, we have created a textbox followed by two buttons. Each of these buttons has a label and an attribute called OnClick, which is initialized to a JavaScript function named GetPostBackEventReference. This function accepts two parameters, namely, this and some text viz. Add or Subtract. The text that is supplied as a parameter to this function, is further passed on as a parameter to the function RaisePostBackEvent.

 

You will not see any code for the Javascript function in our control. Since it is prefaced with Page, the code is generated in the HTML file created by the server.

 

 

The Render function in turn creates the Javascript function called  __doPostBack in our HTML file. (You could verify it through View-Source). Further, the function name given to the OnClick property in the Render method, is changed.

 

Thus, each time we click on the button, the function __doPostBack is called with two parameters. The first parameter is 'hi', while the second parameter is the string assigned in the Render function. This function initializes the hidden form variables with the values passed as parameters. And then, it calls the submit function, thereby, transferring parameters out of the HTML file to the web server.

 

To summarise, in order to capture post back events, such as, form submits from a client, the interface called IPostBackEventHandler is implemented. Each time an event occurs on the client, the RaisePostBackEvent method in the control, is called. Also, the ASP+ system generates the client side Javascript, which incorporates customized event handling, such that, any HTML element can initiate post back.

 

Attributes

 

Try out the following C# program to understand, what attributes are all about.

 

z.cs

class zzz

{

public static void Main()

{

yyy y = new yyy();

}

}

[vijay("hi")]

public class yyy

{

public yyy()

{

System.Console.WriteLine("yyy const");

}

}

public class vijay : System.Attribute

{

public vijay(string s)

{

System.Console.WriteLine("vijay const " + s);

}

}

 

Output

yyy const

 

An attribute is a class derived from System.Attribute. It can be assigned any name that we like. Hence, we chose the name 'vijay'. The parameters passed to the attribute 'vijay' are simply passed on to the constructor.

 

We could discuss a large number of concepts regarding attributes. Instead, we will make a simple statement here, and that is "C# likes attributes and it has a large number of attributes".

 

a.aspx

<%@ Register TagPrefix="ttt" Namespace="nnn" Assembly="c" %>

<script runat=server language=c#>

void Page_Load()

{ DataBind();

}

</script>

<html>

<body>

      <form method="POST" runat="server">

            <ttt:zzz aa="Hello World!" runat=server/>

          <br>

          <ttt:zzz aa="Hello World!" runat=server>

              <m>

                <u> <%# Container.aa %><br> </u>

              </m>

          </ttt:zzz>

      </form>

   </body>

</html>

 

b.cs

using System;

using System.Web;

using System.Web.UI;

namespace nnn

{

public class yyy: Control, INamingContainer

{        private String     msg = null;

        public yyy(String g) {

            msg = g;

        }

        public String aa {

           get {

                 return msg;

            }

           set {

                 msg = value;

            }

        }

    }

[ParseChildren(true)]

public class zzz : Control, INamingContainer

{

ITemplate mm = null;     

String s = null;  

public String aa

{

get {

return s;

}

set {

s = value;

}

}

[TemplateContainer(typeof(yyy))]

public ITemplate m

{

get {

return mm;

}

set {

mm = value;

}

}

public override void DataBind()

{

            EnsureChildControls();

            base.DataBind();

}

protected override void CreateChildControls()

{

 if (m != null) {

             Controls.Clear();

             yyy i = new yyy(aa);

             m.InstantiateIn(i);

             Controls.Add(i);

           }

           else {

              Controls.Add(new LiteralControl(aa));

           }

}

}   

}

 

Output

Hello World!

Hello World!

 

Templates in controls help in customizing the display of the output, as per our liking. First, we display our user control without a template. The control has a property named aa, whose value 'Hello World' is displayed by the control. Then, we use the same syntax, but add the tags with the template name i.e. <m> and </m>, to customize this display. The template is given the name 'm', thereby, indicating that we can have many more templates in the file. In this tag, we use the html tags as they are, at the same time, place C# code, but within the <%# symbol.

 

A container is an object available in ASP.Net that enables access to the properties of a control. Thus, Container.aa will result in display of the text "Hello World". As the code is placed within the HTML underline tag u, the property value is displayed as underlined.

 

In the file b.cs, zzz has a property named aa. The variable 's' stores the current value of this property. It also contains an instance variable mm of type ITemplate.

 

The template 'm' in the a.aspx file, is translated into a property 'm' of type ITemplate. The mm object stores the value of this property. Notice that an attribute Template has been added to this property.

 

An attribute is placed within square brackets[]. Thus, Template is an attribute that accepts one parameter, which is the name of the class that the code resides in. The attribute too is stored in the dll as part of the metadata.

 

Thus, to create a template, we simply need a property of type ITemplate along with the Template attribute.