Thursday, June 29, 2006

 

Say it in Chinese

Expressing binary information by using text characters has always been wasteful. The simplest solutions use octal or hexadecimal representations, and they waste half or more of storage space or transmission bandwidth. In a Unicode environment like that provided by ASP.NET 2.0, the bloat is much worse.

A Unicode environment, however, permits a safe and relatively efficient alternative, using the segment of the 16-bit code points occupied by the "Unihan" characters. Set the high-order two bits of each character to 01, and use the low-order fourteen bits for binary data. Data expressed in this way will populate 16K of the 27K code points assigned by 16-bit Unicode to Chinese characters. Data bit efficiency is 87.5 percent.

Thursday, June 22, 2006

 

Lightly crossing pages

The Server.Transfer method is an efficient way to transition between pages under server control, but it transmits no parameters to specify a context. There are indirect ways to do this with added costs in performance and data management. However, the C# language provides data transfer with no added programming or overhead, provided the destination page knows the type of the origin page. Simply declare and assign public members in the origin page and use their values in the destination page. This is made possible by the PreviousPage property of the Page class, the strong typing of C# and the run-time type identification system.

In a Login page, for example, declare a member such as m_ilUserTag to identify a validated user:

public partial class Login : System.Web.UI.Page
{
    public long m_ilUserTag = 0;

Once a user is validated, assign an identifier value to m_ilUserTag and transfer to the Home page:

        Server.Transfer("Home.aspx");

The Home page can pick up the value of m_ilUserTag directly. For example:

    protected void Page_Load(object sender, EventArgs e)
    {
        object oObject = (object)PreviousPage;
        Login oLoginPage = (Login)oObject;
        long ilUserTag = oLoginPage.m_ilUserTag;

Although you might think that the reference to the Page base class furnished by PreviousPage could be cast immediately to the Login derived class, Visual Studio 2005 will bark at that with a diagnostic such as "Error...Cannot convert type 'System.Web.UI.Page' to 'System.Web.UI.WebControls.Login'." If you pass the reference through the type, object, it's happy. RTTI will check validity of the cast and throw an exception if PreviousPage did not reference a Login object.

Sunday, June 18, 2006

 

Events in the life of a page

In its cute fashion, Microsoft discourages application programmers from combining JavaScript with most ASP.NET programming, while making frequent use of the very same JavaScript in the HTML markups that it renders for ASP.NET pages, as a look at the source for many such pages will show. Microsoft does not worry itself with telling you about attributes available for controls in the Source (or markup) view of .aspx files. What you get from them is just a description of server-side classes. Of course Microsoft has all this knowledge well organized and documented. They just don't let you know. If you use the < asp: > elements on a page, as you nearly must to take best advantage of the tools, how do you respond to events on the client side?

The quick answer is that you put them in, just as though you were writing HTML as you otherwise might. For example:

    < asp:DropDownList ID="ofTextHour" runat="server" OnChange="TimeExtender()" >

The tools will bark at you, "Warning...Validation (ASP.Net): Attribute 'OnChange' is not a valid attribute of element 'DropDownList'." However, they will copy what you gave them to the HTML markup they produce, resulting in:

    < select name="ofTextHour" id="ofTextHour" OnChange="TimeExtender()" >

When you add a corresponding JavaScript function to the .aspx markup, it will be called as you would expect on a change to the selection for the ofTextHour element.

Microsoft's official approach to supporting client-side events, as one might expect, requires server-side code. Each control has a server-side Attributes property that is an AttributeCollection object with an Add function to add pairs of strings. For the example shown, the code usually placed in Page_Load() would be:

    ofTextHour.Attributes.Add("OnChange", "TimeExtender()");

Of course this breaks Microsoft's widely touted "declarative" paradigm. If you take the official approach, you can't see all the attributes of a control in one place. Some server properties are supported in markup, however. This one might have been:

    < Attributes >
        < Add OnChange="TimeExtender()" / >
    < /Attributes >

Not so, unfortunately, and the above markup will stop the build.


[Note: because of display limitations, characters "< " and " >" here are shown with a space after or before them.]

Thursday, June 15, 2006

 

Comments in a skin file

Skin files, introduced in ASP.NET 2.0, make it easier to specify an application's look and feel. Commercial developers will want to add a comment with their copyright notice, but Microsoft does not explain how to put a comment in a skin file. In his book "Programming ASP.NET 2.0 Core Reference," Dino Esposito shows skin file content with an HTML comment, but that will cause a build failure in the release version of Visual Studio 2005.

Although a skin file looks like .aspx markup, it is highly restricted. However, it does recognize ASP code blocks and will accept code block comments, such as this example:

< %-- Copyright 2006 XYZ Corp. All rights reserved. --% >

[Note: because of display limitations, characters "< " and " >" here are shown with a space after or before them.]

Wednesday, June 14, 2006

 

Setting a default button with default input focus

Microsoft has not documented how to set a default button through ASP.NET 2.0 when a page also has data entry elements that need a default focus. It's easy, though. Before rendering the page, set focus on the default button, then on the default data entry element. For example, in the Page_Load function:

    ofButtonLogin.Focus();
    ofTextUserName.Focus();

To change the default button, depending on which data entry element has focus, the easiest way is JavaScript. See Darrell Norton's approach at http://codebetter.com/blogs/darrell.norton/archive/2004/03/03/8374.aspx.

Thursday, June 01, 2006

 

Mastering and paging JavaScript: Problems

ASP.NET 2.0 introduced the potentially useful master and content pages, but with them it also introduced new problems for client-side scripts. The ID of each content page element is extended and qualified in one of Microsoft's obscurely documented conventions. Client-side scripts that try to use the ID values that a programmer specified will fail.

Microsoft usually gives client-side scripts a pass; ASP.NET 2.0 was designed to promote and sell server software. Client-side scripts have the added demerits, in Microsoft's eyes, of using JavaScript, a language designed by a competitor, or ECMAscript, an international standard. Their document purgers, currently in ascendance with VS (Visual Studio), have removed from distributed "Help" documents several articles and sections of articles that might reveal the client-side problems and assist in solving them. However, many articles remain available in libraries distributed by MSDN (Microsoft Developer Network) and displayed at Microsoft's web sites. Key sections of documentation are the topics "Web Server Controls," "ASP.NET Programming" and "ASP.NET Controls" under "Visual Web Developer" in the VS library and the topic "ASP.NET Server Controls" under "Creating Web Applications and Services" in the MSDN library.

The root of the client-side problems with master pages is name qualification, which occurs when a control is located within the scope of another control that acts as a "naming container." Naming containers are largely of interest with controls that generate collections of other controls--any templated control and all standard controls that use templates: Repeater, DataGrid, DataList, CheckBoxList, ChangePassword, LoginView, Menu, SiteMapNodeItem and RadioButtonList. See the articles "Web Forms Control Identification," available in both VS and MSDN, and "Referencing Controls in Web Forms Pages," available only in MSDN. The article "Client Script in ASP.NET Web Pages" says that the ID values of controls in the scope of a naming container will be extended and qualified so that they are unique on a page, but it does not say how this is done. The article warns against using the extended and qualified names in client-side script: "The formula used to generate unique IDs for child controls can change."

A master page is associated with the MasterPage class, which is derived from the UserControl class and therefore implements the INamingContainer Interface. ID values of all controls in the scope of a master page will be extended and qualified--including everything on associated content pages. The fact that a master page acts as a naming container is referred to obliquely in just a single "Visual Web Developer" article, "How to: Reference ASP.NET Master Page Content." This article says that to identify a control located on a master page, one needs "the FindControl method, using the value returned by the Master property as the naming container." An October, 2003, article from MSDN, "Web Site Consistency Made Easy with ASP.NET 2.0 Master Pages," says, "Each instance of System.Web.UI.ContentPlaceHolder within a Master Page acts as a naming container."

Most client-side scripts used with master and content pages must somehow deal with controls on their pages, but Microsoft provides no guidance for how to do this. Its documentation addresses only issues of how to identify controls in server-side code. There is no equivalent to the FindControl method available to a client-side script, yet Microsoft has been aware since at least 2003 that its master-content architecture would be incompatible with client-side scripts.

 

Mastering and paging JavaScript: Solutions

After uncovering problems with JavaScript in master and content pages, some software developers found solutions using ASP code blocks, a technology that ASP.NET 2.0 carried from the original 1996 ASP (Active Server Pages) but now documents only sparsely in the VS article "Embedded Code Blocks in ASP.NET Web Pages." A more thorough discussion can be found in the January, 2002, article "How to Upgrade ASP Pages to ASP.NET Pages that Use JScript .NET" supplied by MSDN. These articles describe a "display block" with the < % = ... % > format. The content of such a block is compiled and executed by the page server at runtime; the entire block is replaced by the result when a page is rendered. If it references a page element's ID value, then the rendered page will contain a runtime control's ID value that may have been extended and qualified.

ASP display blocks obtain correct control ID values for JavaScript with statements such as:

var oControl1 = document.getElementById('< % = ofControl1.ClientID % >');

where ofControl1 is the ID value given by a programmer to a control on the page. The server-side run-time evaluation of ofControl1.ClientID returns an extended and qualified name replacing the ASP code block, and the JavaScript variable oControl1 can then be used for access to properties of that control.

A limitation to this approach was soon discovered. Once a master page header has taken code blocks, it refuses to accept dynamic scripts. However, dynamic scripts will be accepted for content pages. Another limitation is that the run-time processing provided for scripts imbedded in .aspx page files is not provided for dynamic scripts. Dynamic scripts must be created using the FindControl method in server-side code to generate correct ID values for controls.

The run-time processing to obtain correct control ID values will be provided for scripts included from files. The .aspx page file that is to contain the JavaScript needs a < script > element such as:


    < script type="text/javascript" >
    < !--#include file='~\JavaScript\ControlValidator.js'-- >
    < /script >


The included file, called JavaScript\ControlValidator.js here, contains a JavaScript program such as:


function ValidateControl(source, arguments)
{
    var oRadioControl1 = document.getElementById('< % = ofRadioControl1.ClientID % >');
    var oRadioControl2 = document.getElementById('< % = ofRadioControl2.ClientID % >');
    arguments.IsValid = (oRadioControl1.checked | | oRadioControl2.checked);
}


A minor problem with this approach to using JavaScript is its incompatibility with Microsoft's clumsy Design mode editor. If you imbed Javascript as shown and you try to switch to Design mode, Visual Studio 2005 will bark at you with a diagnostic such as "Error Creating Control...Object reference not set to an instance of an object." On those rare occasions when you need to use Design mode, the cure is to cut the Javascript out of the markup and paste it into a plaintext editor such Notepad, then when done paste it back into the markup file.

[Note: because of display limitations, characters "< " and " |" and " >" here are shown with a space after or before them.]

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]