Wednesday, November 02, 2005

CAST or AS

CAST or AS


If compiler knows the type of the object it is casting, it checks to see if it is a valid cast. If not, throws a compile time error.
If the compiler doesn't know the actual type of the object, it passes compilation.
Now the runtime checks if the object is of right type (the same type or a
derived type) or not.
If you use as operator, and the object is not of right type, then the reference is set to null.
If you use cast, and the object is not of right type, then the reference is not changed (i.e., NOT set to null)





















  DataTable o; ArrayList arr = new
ArrayList();

arr.Add(null);


object o = arr[0];


ArrayList arr = new
ArrayList();

arr.Add(new DataTable());


object o = arr[0];


MyType m = (MyType)o; Compiler: Throws an error [Cannot convert from DataTable to
MyType]
Compiler : GOOD

Run-time : GOOD


m is now set to null [Run time thinks that null can be converted to
MyType and so does the conversion and assigns the result of conversion
(null) to m]


Compiler : GOOD

Run-time : Throws an exception [Specified cast is not valid]


m still points to the object it was originally pointing to. [It is not
assigned null]


MyType m = o as MyType;

 


Compiler: Throws an error [Cannot convert from DataTable to
MyType via a built-in conversion]
Compiler : GOOD

Run-time : GOOD


m is now null [As o is not of the right type, the "as"
operator returns null which is assigned to m]


Compiler : GOOD

Run-time : GOOD


m is now null [As o is not of the right type, the "as"
operator returns null which is assigned to m]


Monday, October 10, 2005

IPostBackDataHandler - to track client-side changes

How to track the client-side changes in your custom control's Childen?

ASP.NET server control
If you have asp:textbox, it tracks itself to see if it has been changed on the client side and if so, it raises the OnDataChanged event.

HTML form-element
If you have a normal HTML input field, then it is your control's responsibility to see if it has been changed on the client-side and raise any event, like DataChanged, if needed.
To do so, your control must implement the IPostBackDataHandler interface by defining LoadPostData and RaisePostDataChangedEvent methods.
In LoadPostData, your control must check to see if your HTMl input field's value is updated on the client side and if so, return true.
Note that the LoadPostData method is passed a collection of form elements and their values as one of its inputs and that your HTML input field will be one of them.
If LoadPostData returns true, the RaisePostDataChangedEvent method is called where you can raise any appropriate events, if needed, or handle it accordingly.

HTML non-form element (table, div, span etc.)
If you have an HTML non-form element, then again it is your control's responsibility to see if it has been changed on the client-side and raise any event, like DataChanged, if needed.
To do so, your control must again implement the IPostBackDataHandler interface by defining LoadPostData and RaisePostDataChangedEvent methods. In LoadPostData, your control must check to see if your HTMl input field's value is updated on the client side and if so, return true. But note that your non-form element will NOT be in the collection of form elements and their values passed to the LoadPostData method as one of its inputs.
So, we have to use hidden input fields that carry the data for non-form elements. These hidden fields need to be updated as the non-form element is changed on the client side so that when the page is posted back, the hidden fields store required information about the updates made to the non-form element and hence can be used by teh server to track the updates.
Like in the above case, if LoadPostData returns true, the RaisePostDataChangedEvent method is called where you can raise any appropriate events, if needed, or handle it accordingly.

When will the page framework call the LoadPostData method of a control that implements IPostBackDataHandler interface?
Sln: When one of the folowing is true...

1. When a postback occurs, the page framework searches the posted data for values matching the server controls' unique IDs that implement the IPostBackDataHandler interface. [The page framework uses the UniqueID property to route postback data back to the control]
Then, LoadPostData method is invoked on each control that implements the interface.

If you use this method to track changes on an HTML form-element, then you have to name that element with the control's UniqueID, for obvious reasons(so that there will be an element with the control's UniqueID in the posted data which tells the page framework to call that control's LoadPostData method).

If you use this method to track changes on an HTML non-form element, then you have to name the HTML hidden field, created only to save the updates made on the non-form element, with the control's UniqueID.

2. When your control registers itself to require postback as follows.
Page.RegisterRequiresPostBack(this);
In this case, even though the posted data has no element matching your controls' unique ID, it still calls your controls' LoadPostData method.

If you use this method to track changes on an HTML form-element, then you need not name that element with the control's UniqueID.

If you use this method to track changes on an HTML non-form element, then you again need not name the HTML hidden field, created only to save the updates made on the non-form element, with the control's UniqueID.

The page framework then tracks tracks all the controls that return true from the LoadPostData method and invokes RaisePostDataChangedEvent on them.

Wednesday, September 28, 2005

Custom HTTP Handlers

Your application can be configured to use any class that implements IHttpHandler interface to process the incoming request.

For this

1. you define a class that implements IHttpHandler interface

2. Define the ProcessRequest and IsReusable methods of the IHttpHandler interface

3. Tell the worker process (through web.config file) to use your class, instead of a default one, to process the request and form the Response.



Step 3 can be eliminated If you name the file with .ashx extension instead of .cs.

That is, if you write an ashx file that defines a class which implements IHttpHandler, you need not update either web.config or IIS settings.

Following is a very simple example. [This doesn't hold true for handlers in custom controls or other applications. See example B]

Example A:

Single-file model:

Test.ashx

<%@ webhandler language="C#" class="Hyland.Applications.Web.Client.Test"%>



using System;

using System.Web;



namespace Hyland.Applications.Web.Client

{

     public class Test : System.Web.IHttpHandler

     {

          public bool IsReusable

          {

              
get { return true; }

          }



          public void ProcessRequest(HttpContext hc)

          {

              
hc.Response.ContentType = "text/html";

              
hc.Response.Write("<B>SOME TEXT</B>");

          }

     }

}




Code-behind model:


Test.ashx

<%@ webhandler language="C#" class="Hyland.Applications.Web.Client.Test" CodeBehind="Test.ashx.cs"%>



Test.ashx.cs

using System;

using System.Web;



namespace Hyland.Applications.Web.Client

{

     public class Test : System.Web.IHttpHandler

     {

          public bool IsReusable

          {

              
get { return true; }

          }



          public void ProcessRequest(HttpContext hc)

          {

                 
hc.Response.ContentType = "text/html";

                 
hc.Response.Write("<B>SOME TEXT</B>");

          }

     }

}



In either case:

Browser

http://localhost/AppNet/Test.ashx



[No web.config settings need to be updated, no IIS setttings need to be updated]



[Note: Class attribute is REQUIRED which specifies the class that implements IHttpHandler interface and can handle the current request. If Class attribute is not specified, "Directive is missing a class attribute" exception occurs.]



Example B:

Suppose you have a Custom Control that has embedded images in its assembly.

Suppose the Control outputs some HTML as follows...

<img src="MyControlNamespace.Test.ashx"/>

Suppose the above code(Test.ashx, in case of the single file model or Test.ashx.cs in case of the Code-behind model) is in your Custom Control. [The code for the handler grabs the embedded image information as IOStream and writes it to the Response object]

When the Custom control's project is built, the .ashx file will NOT be included in the resulting assembly.

So, when you ship your control's dll, it has no Test.ashx file.

So, when you request for http://localhost/AppNet/Test.ashx
or http://localhost/AppNet/MyControlNamespace.Test.ashx, the browser displays a HTTP: 404 (resource not found) error.



Solution


You have to update the web.config settings of the application to tell it to use the class, Test, as a handler for any requests to the Test.ashx file.

<httpHandlers>

<add verb="*" path="Test.ashx" type="MyControlNamespace.Test, MyControlAssembly" />

</httpHandlers>


Here, for obvious reasons, Test.ashx is not required at all (because it anyway is not shipped with your control's assembly).

[Note: In this case, you don't have to name the file, that defines the class which implements IHttpHandler, with .ashx.cs extension.

It can just be a .cs file.]

.ashx is used to avoid manipulating web.config file (because the ashx handler knows its purpose and correctly invoked the right handler). In case where you HAVE to manipulate web.config file, using .ashx extension would not be of any help.



By default the files with .aspx, .ashx extensions are compiled when first requested.

So, the Classes you define in these files cannot be accessed by other classes or files.

But if you set the Build Action Property of these files to "Compile" instead of the default "None", then these files are also compiled and the resulting .dll file (of the project) contains this compiled code.

[Note: In this case, you cannot have <%@ Page or <%@ WebHandler directives. It should be written like a regular .cs file with "using" and "namespace" elements.]

Monday, September 19, 2005

Repeater - ITemplate

Simple code to demonstrate how a custom control that has repeater as its child control defines its template.
Three classes, MyHeaderTemplate, MyItemTemplate and MyFooterTemplate derive from Itemplate class and hence implement the InstantiateIn method.

The Repeater’s DataBind() method calls the InstantiateIn methods of the HeaderTemplate, ItemTemplate(once for each row of data in the DataTable) and FoorterTemplate respectively.

The implementation of the ItemTemplate’s InstantiateIn method creates a TableRow and a TableCell for each Column of the DataTable and adds it to the repeater control’s RepeaterItem (passed in to the InstantiateIn method).
The BindRow method handles the DataBinding event of each of the rows. It is called for each row after the InstantiateIn method returns.
The implementation of this method adds some client side attributes to the current row and updates the format of the DateTime columns.


public class MyItemTemplate : ITemplate
{
//DataTable is supplied by the custom control and has a set of rows.
private DataTable dt = null;
private WebControl webControl;

public MyItemTemplate(DataTable dt, WebControl webControl)
{
this.dt = dt;
this.webControl = webControl;
}

public void InstantiateIn(Control container)
{
TableRow _row = new TableRow();

for(int i=0;i < dt.Columns.Count;++i)
{
TableCell _column = new TableCell();
_row.Cells.Add(_column);
}
_row.DataBinding+=new EventHandler(BindRow);
container.Controls.Add(_row);
}

public void BindRow(object sender, EventArgs e)
{
TableRow row = (TableRow)sender;
RepeaterItem container = (RepeaterItem)
row.NamingContainer;
DataRowView _dataRow = (DataRowView) container.DataItem;

StringBuilder sbKeyValuePairs = new StringBuilder();
for(int i=0;i < _dataRow.DataView.Table.Columns.Count;++i)
{
string columnName = _dataRow.DataView.Table.Columns[i].Caption;
if(i>0)
{
sbKeyValuePairs.Append(",");
}
sbKeyValuePairs.Append("'");
sbKeyValuePairsString.Append(columnName);
sbKeyValuePairs.Append("','");
sbKeyValuePairs.Append(Convert.ToString(_dataRow[i]));
sbKeyValuePairs.Append("'");
}
row.Attributes.Add("KeyValuePairs",sbKeyValuePairs.ToString());
row.Attributes.Add("ondblclick","javascript:webControl.Page.GetPostBackEventReference(webControl,"DblClick-" + _dataRow[0].ToString()) );

for(int i=0;i < row.Cells.Count;++i)
{
TableCell cell = (TableCell) row.Cells[i];
cell.Wrap = false;
string cellText = _dataRow[i].ToString();
try
{
DateTime dColumnValue = Convert.ToDateTime(cellText);
cellText = dColumnValue.ToString("yyyy-MM-dd");
}
catch{}
cell.Text = cellText;
}
}
}

//Our repeater is essentially a table (You can have very complex HTML here. We used table for simplicity)
public class MyHeaderTemplate : ITemplate
{
public void InstantiateIn(Control container)
{
LiteralControl lcTABLE = new LiteralControl("");
container.Controls.Add(lcTABLE);
}
}

//As our repeater is a table we close it in the footer
public class MyFooterTemplate : ITemplate
{
public void InstantiateIn(Control container)
{
LiteralControl lcTABLE = new LiteralControl("
");
container.Controls.Add(lcTABLE);
}
}

The following is the code in the custom control that assigns the above Templates to the repeater control.

protected override void CreateChildControls()
{
Controls.Clear();

Controls.Add(repeater);

repeater.HeaderTemplate = new MyHeaderTemplate();
repeater.ItemTemplate = new MyItemTemplate(dt,this);
repeater.FooterTemplate = new MyFooterTemplate();

// Prevent child controls from being created again.
ChildControlsCreated = true;

//call the parent method
base.CreateChildControls();
}

protected override void OnPreRender(EventArgs e)
{
// Populate the repeater control with the Items DataSet
repeater.DataSource=dt;
// Bind the data to the repeater
repeater.DataBind();

base.OnPreRender (e);
}

Paging using PagedDataSource class

PagedDataSource objPagedDataSource = new PagedDataSource();
objPagedDataSource.DataSource = //All rows - some datasource (like DataTable, DataView, DataSet's Dtatable, DataReader, Array etc.)

objPagedDataSource.AllowPaging = true;
objPagedDataSource.CurrentPageIndex = 0;
objPagedDataSource.PageSize = 5;

repeater.DataSource = objPagedDataSource;
repeater.DataBind();


Now you create your own navigation buttons First, Prev, Next, Last.
In their server side event handlers, you update bind the repeater with other set of rows as follows


PagedDataSource objPagedDataSource = new PagedDataSource();
objPagedDataSource.DataSource = //ALL rows - some datasource (like DataTable, DataView, DataSet's Dtatable, DataReader, Array etc.)

objPagedDataSource.AllowPaging = true;
objPagedDataSource.CurrentPageIndex = //new value depending on which navigational button is clicked
objPagedDataSource.PageSize = 5;

repeater.DataSource = objPagedDataSource;
repeater.DataBind();


When AllowCustomPaging is set to true, PagedDataSource looks at the total record count of the DataSource, PageSize and CurrentPageIndex properties to set its IsFirstPage, IsLastPage and other properties.

When the repeater's DataBind method is called, it accesses the objPagedDataSource's DataSource, CurrentPageIndex and PageSize properties and loads itself with the appropriate set of rows.
Here All of the rows are being accessed from the database.
So, if this data is stored in the viewstate, then the size of the file sent back and forth the browser becomes very large and is unnecessary.

You can hide/show the navigational buttons depending on the PagedDataSource class's IsLastPage, IsFirstPage properties.

CUSTOM PAGING



PagedDataSource objPagedDataSource = new PagedDataSource();
objPagedDataSource.DataSource = //Only first set of rows - some datasource (like DataTable, DataView, DataSet's Dtatable, DataReader, Array etc.)

objPagedDataSource.AllowPaging = true;
objPagedDataSource.AllowCustomPaging = true;
objPagedDataSource.VirtualCount = //TOTAL number of rows
objPagedDataSource.CurrentPageIndex = 0;
objPagedDataSource.PageSize = 5;

repeater.DataSource = objPagedDataSource;
repeater.DataBind();


Now you create your own navigation buttons First, Prev, Next, Last.
In their server side event handlers, you update bing the repeater with other set of rows as follows

PagedDataSource objPagedDataSource = new PagedDataSource();
objPagedDataSource.DataSource = //only the required set of rows - some datasource (like DataTable, DataView, DataSet's Datatable, DataReader, Array etc.)

objPagedDataSource.AllowPaging = true;
objPagedDataSource.AllowCustomPaging = true;
objPagedDataSource.VirtualCount = //TOTAL number of rows
objPagedDataSource.CurrentPageIndex = //new value depending on which navigational button is clicked
objPagedDataSource.PageSize = 5;

repeater.DataSource = objPagedDataSource;
repeater.DataBind();

When AllowCustomPaging is set to true, PagedDataSource looks at the VirtualCount, PageSize and CurrentPageIndex properties to set its IsFirstPage, IsLastPage and other properties.

When the repeater's DataBind method is called, it accesses the objPagedDataSource's DataSource, CurrentPageIndex and PageSize properties and loads itself with the appropriate set of rows.
Here only the required set of the rows are accessed from the database for each page.
So, if this data is stored in the viewstate, then the size of the file sent back and forth the browser is minimized.

You can hide/show the navigational buttons depending on the PagedDataSource class's IsLastPage, IsFirstPage properties.

Friday, September 16, 2005

ViewState

StateBag contains key-value pairs.

When you set a value for any key, it first checks to see if IsTrackingViewState is true or not.
[IsTrackingViewState is false by default. It is set to true when the TrackViewState() method is called]
If IsTrackingViewState returns true, the item that is being set is marked as dirty.

SaveViewState() method, when called, takes all dirty items and saves their values and returns that saved object of type "object array".

So, even though you add items to the StateBag, only those items that were set AFTER TrackViewState() has been called will be saved across postbacks.

In Page class, ViewState object if of type StateBag.
Right after the Init stage, the TrackViewState() method is called on all the Page's child controls and on the ViewState StateBag also.

In Custom controls, when ever a child control is added to the Custom Control's ChildControl collection, the Controls.Add method itself calles the TrackViewState() method on the newly added child control.
So, if you want to assign some values to the child control only once and let the control load these values from teh ViewState bag, every time the page is posted back, you MUST assign those values AFTER you have added the control to the Custom control's child collection.

Ex:
Controls.Add(btnNext); // --- (A)
if(!Page.IsPostBack)
{
btnNext.CommandName = "Some_Command_Name"; // Do this AFTER adding btnNext to the Child Controls collection (A), if you want that CommandName value to be loaded in the ViewState bag.
}

INamingContainer - Custom Controls

Your custom control MUST implement this Marker interface if it needs to handle any of its child control's events.
INamingContainer interface identifies your control as a container control that creates a new ID namespace within a Page object's control hierarchy.

INamingContainer is a marker interface that, when implemented by a control, creates unique IDs for its child controls. This naming scheme guards against potentially dangerous naming collisions. the ASP.NET runtime will throw an exception if a server control inside the page template has the same ID as a control on the calling page.

Ex: Suppose your custom control is given and "custDG" as its ID in the aspx page and that your custom control has a Button control as one of its children.
















 Custom control implements INamingContainer interfaceCustom control does not implement INamingContainer interface
You do not assign any ID to the Custom Control's Child controlinput type="button" name="custDG:_ctl0"

Programmatically, on the server side, you can access the button's ClientID as custDG__ctl0. But it doesn't make any sense on the client side as the Control doesn't output "ID" attribute of the button.
input type="button" name="_ctl0"

Does not fire the server side events of these Child Controls.
You do assign "MyBtn" as the ID to the Custom Control's Child controlinput type="button" name="custDG:MyBtn" ID="custDG_MyBtn"input type="button" name="MyBtn" id="MyBtn".

Does not fire the server side events of these Child Controls.


For a Child Control to save its state in ViewState and load it on postback, it has nothing to do with whether the Custom Control implemented the INamingContainer interface or not.

Friday, September 09, 2005

Embedding static files inside a .NET assembly

Any web application contains static data like images, scripts, text etc.These can be embedded in the assembly itself instead of distributing them as separate files.Advantages are obvious.
1. You do not need to worry about their exact location.
2. You only have one .dll file to be shipped (so you can use XCOPY deployment)

I was creating a Custom server control that uses images and script files.Instead of shipping them separately with the control assembly, I was looking for ways to embed it into the assembly itself. I found many ways of doing it. I'm going to discuss two of the ways (to store script files) in this post.

One way:

Step 1. Embedding:

1. Add a .resx file to the projectA .resx file stores information in the XML format.Specifically, the section stores the key-value pairs.[Note: You could also create a .txt file to store your static data. But it can only hold text data. Inorder to store images or objects, you have to use .resx files]

MyProject

------ MyStaticFiles.resx

------ MyCode.cs

2. Open the .resx file in the Visual Studio editor.Give some name, say, "ClientScript", to your script that you can use in your code to access it.In the value field, copy all the client script (static data) you want to be rendered.

3. Set the build type of the .resx file to be Embedded Resource, and not Content.This informs the compiler that the file's contents should be embedded in the assembly.[In visual studio you can change the Build Type when you right click on the file and go to Properties.]

4. Build the project as you normally would.Once you build the project, the file structure is as follows...

MyProject

------ MyStaticFiles.resx

------ MyCode.cs

------ bin

------------ Debug

------------------ MyProject.dll

------ obj

------------ Debug

------------------ MyProject.MyStaticFiles.resources

5. Now a .resources file is automatically created for you that has the same name as the .resx file. The name of the output resources file will then be MyStaticFiles.resources.
The physical location of this file is \obj\Debug

[Note: You can also manually create a .resources file from a .resx file using the following commandresgen MyStaticFiles.resx MyStaticFiles.resourcesresgen can only convert .txt or .resx files in to .resources files and vice versaIt can also convert .txt and .resx in to each other.]
This .resources file has been embedded in the resulting project's assembly.You can confirm this fact in one of the 2 ways.

1. By looking at the assembly manifest using Ildasm.exe, shipped with .NET SDK.
[Ildasm.exe is a takes a portable executable (PE) file that contains Microsoft intermediate language (MSIL) code and creates a text file and can be found on your machine somewhere at \Program Files\Microsoft Visual Studio .NET 2003\SDK\v1.1\Bin]

The assembly manifest should contain an entry as follows...
.mresource public namespaceName.MyStaticFiles.resources
{
}

where namespaceName is your project's namespace.

2. Programmatically
The following statement can be used to get an array of all resource names, embedded in the current assembly.

string[] mRNames = this.GetType().Assembly.GetManifestResourceNames();

In our case, it should just be one and that is, namespaceName.MyStaticFiles.resources.


Step 2: Shipping

All you have to ship is the assembly, ie., MyProject.dll. It has the executable code as well as the static data (client script)


Step 3: Accessing

Now that you have data embedded in the assembly, how do you go about retrieving it?

Create a ResourceManager object for this assembly, get the resource set and access the required object using the Key.

ResourceManager manager = new ResourceManager( this.GetType() );
string script = manager.GetResourceSet(System.Globalization.CultureInfo.CurrentCulture, true, true).GetString("ClientScript");

If you have stored an object in the .resx file, you would use GetObject() method of the ResourceSet class, instead of the GetString() method. And then you have to cast it back to the appropriate type.


Another way:

Step 1. Embedding:

1. Add your static data file to the project (you can create a folder to store all your static data files)

MyProject

------ MyStaticFiles

------------ MyClientScript.js

------ MyCode.cs

2. Set the build type of all of the static files to be Embedded Resource, and not Content.This informs the compiler that the file's contents should be embedded in the assembly.[In visual studio you can change the Build Type when you right click on the file and go to Properties.]


3. Build the project as you normally would.
Once you build the project, the file structure is as follows

MyProject

------ MyStaticFiles

------------ MyClientScript.js

------ MyCode.cs

------ bin

------------ Debug

------------------ MyProject.dll

4. Now the static file MyClientScript.js has been embedded in the resulting project's assembly.
You can confirm this fact in 2 ways.

1. By looking at the assembly manifest using Ildasm.exe.
The assembly manifest should contain an entry as follows...

.mresource public namespaceName.MyStaticFiles.MyClientScript.js
{
}
where namespaceName is your project's namespace.

2. Programmatically
The following statement can be used to get an array of all resource names, embedded in the current assembly.

string[] mRNames = this.GetType().Assembly.GetManifestResourceNames();

In our case, it should just be one and that is, namespaceName.MyStaticFiles.MyClientScript.js


Step 2: Shipping

All you have to ship is the assembly, ie., MyProject.dll. It has the executable code as well as the static data (client script)

Step 3: Accessing

Now that you have data embedded in the assembly, how do you go about retrieving it?
Load the specified Manifest resource from this assembly and convert the resulting stream into a string.

System.IO.Stream scriptStream = this.GetType().Assembly.GetManifestResourceStream(".MyStaticFiles.MyClientScript.js");
byte[] buf = new byte[scriptStream.Length];

scriptStream.Read(buf,0,Convert.ToInt32(scriptStream.Length));

The following line converts the byte array to a string.
string script = System.Text.Encoding.Default.GetString(buf));