Sunday, September 7, 2008

Making Tomcat Support JSF

1. Download the latest version of the JSF Reference Implementation from http://java.sun.com/j2ee/javaserverfaces/download.html

2. Unzip the downloaded file. Copy all JAR files files from the lib subdirectory of JSF unzipped folder to the lib subdirectory of Tomcat. (At the time of writing, they are jsf-api.jar jsf-impl.jar)

3. Copy the following two JAR files from the webapps/examples/WEB-INF/lib subdirectory of Tomcat installation to the lib subdirectory of Tomcat
  • jstl.jar
  • standard.jar

Wednesday, August 20, 2008

Show Ellipsis Text in GridView

protected void Page_Load(object sender, EventArgs e)
{
// ... ...
gv1.Style["TABLE-LAYOUT"] = "fixed";
}

protected void gv1_RowCreated(object sender, GridViewRowEventArgs e)
{
foreach (TableCell cell in e.Row.Cells)
{
cell.Wrap = false;
cell.Style["overflow"] = "hidden";
cell.Style["text-overflow"] = "ellipsis";
}
}

// Showing full text tooltip in each cell
protected void gv1_RowDataBound(object sender, GridViewRowEventArgs e)
{
foreach (TableCell cell in e.Row.Cells)
{
cell.ToolTip = cell.Text;
}
}

The above code just shows what style settings need to be set for the ellipsis effect. It is not recommended to write product code like this. Keep in mind that
gv1.Style["TABLE-LAYOUT"] = "fixed";
actually sets "TABLE-LAYOUT" style property of HTML Table. Therefore, this should be done in a CSS file: not only to separate UI part from logic part, but to make the data travel between server and client much light-weighted. (To demonstrate the last part, look at the source of the rendered HTML to see the table cell styles are duplicated across all the tags.)

Monday, August 18, 2008

Register Custom Control in Web Page

When we want to use a User Control (which is defined in *.ascx) in a web page, we can register the control like this:
<%@ Register Src="~/MyObjectDisplayer.ascx" TagName="MOD" TagPrefix="CustomControl" %>

If we use a Custom Control (which is defined in *.cs), however, the register tag should be a little different:
<%@ Register Namespace="MyControls" TagPrefix="CustomControl" %>

Whenever we want to place this control in the webpage, we write:
<CustomControl:PromptButton id="pb1" runat="server"/>

It assumes that our control classes is PromptButton and it is in MyControls namespace. And note that it has been tested in ASP.NET Web Site project, but not in ASP.NET Web Application project.

Tuesday, August 5, 2008

The Statelessness of Web Page

In ASP.NET, it is common to have some data members of our Page class (one who derived from System.Web.UI.Page). It is important to keep in mind that those members are re-defined or re-declared every time a new instance of our Page class is created. This happens each time there is a round-trip between the server and the browser (i.e. but may not limited to: Page_Load event). Therefore, we need to be aware of the state of those data members.

One more thing: even if an UpdatePanel is used in a page, Page_Load event still gets fired every time.

Tuesday, July 29, 2008

Building TreeView Programmably in a Recursively Fashion

Below is an example of building a TreeView from a registry hierarchy. Note that RegistryKey and Registry objects are in Microsoft.Win32 namespace.

private int maxDepth = 0, depth = 0;

protected void Page_Load(object sender, EventArgs e)
{
RegistryKey schangeRoot = Registry.LocalMachine.OpenSubKey("Software\\SeaChange", false);

TreeNode rootNode = new TreeNode(schangeRoot.Name);

Traverse(schangeRoot, rootNode);

Response.Write(maxDepth.ToString() + "
" + depth.ToString());

TreeView1.Nodes.Add(rootNode);
}

private void Traverse(RegistryKey current, TreeNode currentNode)
{
Response.Write(depth.ToString() + current.Name + "
");

if (current.SubKeyCount <= 0)
{
if (depth > maxDepth)
maxDepth = depth;

return;
}

foreach (string subkeyName in current.GetSubKeyNames())
{
// Before traversing the subkey, create a new node for it
TreeNode childNode = new TreeNode(subkeyName);

// Traverse subkey
depth++;
Traverse(current.OpenSubKey(subkeyName), childNode);
depth--;

// Finish traversing the subkey, add the subkey to this node
currentNode.ChildNodes.Add(childNode);
}
}

Thursday, July 24, 2008

Cannot Access Profile Property in Webpage

In web application projects, we cannot access Profile property as what we do in web site projects. (See the differences between the two projects here: Introduction to Web Application Projects)

To work around this, use ASP.NET WebProfileGenerator, which is an add-in for Visual Studio. If we don't want to use an add-in, we could implement a custom profile provider. Use this article for a reference.

Wednesday, July 23, 2008

The Importance of Page.IsPostBack

For most of the time, we should think about adding if(!Page.IsPostBack) block in Page_Load event of our webpage. Since ViewState of all the webcontrols are enabled by default, most of the time the logic in Page_Load event is supposed to be executed only during the first time webpage is loaded. If we forget to wrap the logic inside the if block when we are supposed to do so, not only can it causes inefficiency, but leads to bugs.

Here is an example. (Refer to ASP.NET security tutorial #10 http://www.asp.net/learn/security/tutorial-10-cs.aspx, step 4)

If we don't wrap the following logic inside if(!Page.IsPostBack) block:

// Reference the SpecifyRolesStep WizardStep
WizardStep SpecifyRolesStep = RegisterUserWithRoles.FindControl("SpecifyRolesStep") as WizardStep;
// Reference the RoleList CheckBoxList
CheckBoxList RoleList = SpecifyRolesStep.FindControl("RoleList") as CheckBoxList;
// Bind the set of roles to RoleList
RoleList.DataSource = Roles.GetAllRoles();
RoleList.DataBind();

something unexpected will happen.

During the second step of the wizard, if we check any role we'd like to assign to the newly created user, it will fail to assign. After we check any CheckBox on the CheckBoxList and press the next button, the logic in Page_Load event is executed again. This will cause the RoleList to be bound again. It seems to be harmless at first, but the truth is, RoleList.DataBind() will clear any change made to the CheckBoxList. Hence, even if we've checked some CheckBox in the RoleList the second step, they all become unchecked now.

So, in this case, there is no reason not to use if(!Page.IsPostBack) block.

Perhaps saying "most of the time if(!Page.IsPostBack) block should be added in Page_Load event" is a bit absolute; but for me, I'll wrap the logic in the block every time; unless there is a reason convinces me not to do so.

Friday, July 18, 2008

Configuring passwordFormat="Encrypted" in ASP.NET

When setting passwordFortmat="Encrypted" (compared to "Hashed" by default) in web.config, I got an exception after trying to create a user.

Solution:
A tag is necessary in this case. I think it is used to encrypt password. MSDN says about machineKey:
Configures keys to use for encryption and decryption of forms authentication cookie data and view-state data, and for verification of out-of-process session state identification.
A typical tag looks like this:


The following is a generator:
http://www.aspnetresources.com/tools/keycreator.aspx

Monday, July 7, 2008

Passing Reference in C#

In the article Passing Reference-Type Parameters, we've seen that we need to use the "ref" keyword in order to swap string values. This is also true for any other reference-types such as an ArrayList.

Remember:
When you pass a reference-type parameter by value, it is possible to change the data pointed to by the reference, such as the value of a class member. However, you cannot change the value of the reference itself; ...

This reminds me the differences between a reference and a pointer: In the swap function, if we are passing parameters by pointer(like in C or C++), then any local change is also global; if we are passing parameters by reference (like in C#), then any local change is not global.

Monday, March 31, 2008

Every keyCode is 229?!

I need to blog this. When I was debugging javascript, I found that no matter I press "A" or "X", the keyCode of it is 229. It turned out that I was typing using Microsoft PinYin. Although the mode is English instead of Chinese, it is Chinese input keyboard anyway. That's why the keyCode behaves strangly.

Sunday, March 2, 2008

Constant Pointers and Pointers to Constants

1. Pointer to Constant
char char_A = 'A';
const char * myPtr = &char_A;
*myPtr = 'J'; // error - can't change value of *myPtr

2. Constant Pointer
char char_A = 'A';
char char_B = 'B';

char * const myPtr = &char_A;
myPtr = &char_B; // error - can't change address of myPtr

Difference between Reference and Pointer

Difference between Reference and Pointer in C++

  1. Reference should be initialized and pointer need not be.

  2. Reference can’t point to NULL and pointer can.

  3. Reference can’t be changed to some other objects it is once initialized but pointer can be.

  4. Pointer can be incremented or decremented and subjected to any pointer arithmetic operations, but reference can’t.

Sunday, February 10, 2008

Quotes

Half the world is composed of people who have something to say and can't; the other half have nothing to say and keep saying it.

If anything can go wrong, it will.

Chance favors the prepared mind.

Wednesday, January 30, 2008

Add() method in .NET (passing parameter)

Collection objects have Add() method in .NET. It is very important to understand how it works.

Code Block 1:
TableRow row = new TableRow();
for (int i = 0; i < resultFields.Count; i++)
{

foreach(string str in resultFields[i])
{
TableCell cell = new TableCell();
cell.Text = str;
this.Response.Write(str);
row.Cells.Add(cell);
}
tbl_rsltFields.Rows.Add(row);
}
In this example, if resultFields.Count is 2, how many rows do you think are in the tbl_rsltFields?
The answer is 1. It is worth noticing this case.

Code Block 2:
for (int i = 0; i < resultFields.Count; i++)
{
TableRow row = new TableRow();

foreach(string str in resultFields[i])
{
TableCell cell = new TableCell();
cell.Text = str;
this.Response.Write(str);
row.Cells.Add(cell);
}
tbl_rsltFields.Rows.Add(row);
}
In this case, the answer is 2.

Actually, if we try to add "row.Cells.Clear();" right after "tbl_rsltFields.Rows.Add(row)" in code block 1, there will be nothing contained in tbl_rsltFields at last. Based on this fact, we know that by calling Add() method, it only gets a copy of the reference, instead of making a copy of the whole row. So it acts like it is passing reference by value.

Wednesday, January 23, 2008

Application["UserCount"] += 1 doesn't work

Why does Application["UserCount"] += 1; incur compiler error? It's the same reason why Application["UserCount"] = Application["UserCount"] + 1; doesn't work either.

And once you know the correct way should be: Application["UserCount"] = (int)Application["UserCount"] + 1; you'll also know (int)Application["UserCount"] += 1; is wrong too.