December 2007 - Posts

I have finally finished reading Jacob J. Sanford's book Professional ASP.NET 2.0 Design: CSS, Themes, and Master Pages. This is one of several books I've bought on ASP.NET 2.0 to deepen my expertise. Now we've moved on to ASP.NET 3.5 before I've mastered ASP.NET 2.0. I expected this book to be full of design ideas for web controls, themes, and skins but instead it leaves much of the creativity up to you and only covers the aspects of ASP.NET 2.0 that apply to design. However I did learn a lot by reading this book.

The first few chapters cover the CSS vs tables for layout debate, basic Photoshop techniques, and accessibility concerns which is familiar material for me. I still picked up a few tidbits of info like the Check Accessibility tool in Visual Studio 2005. There was also an extensive overview of the CSS for expandable columns which is a layout problem that I had not come across before.

The book has a chapter on the CSS Friendly Control Adapters. I found this useful because I wanted to use a control adapter in my current project and needed some guidance on how to import them. I actually only needed to use one of the control adapters and I don't remember if the book tells you how to import just one. Anyway, you should delete tags from the CSSFriendlyAdapters.browser file if there are just some particular web control styles you need more control over. In my case it was the label tag in the Login control that I could not reference through an id or a class name to apply the text color.

The chapter on navigation was helpful because there is one aspect of the navigation controls that other books I've read did not cover, the security trimming. I am currently converting an ASP web application to ASP.NET 2.0 and I do need to show different navigation links to administrators and users based on roles. Chapter 6 tells you how to apply security to your navigation controls based on the Membership API.

Even the chapter on themes for mobile browsers was interesting because I've recently bought a Pocket PC as a mobile video device and didn't even think about using it to check out my site layouts on that platform. I kept notes while reading the book and some other noteworthy material included conditional comments in Internet Explorer (I finally did some research on that), and setting the ASPX page content type to "text/css" (an interesting hack to dynamically generate CSS).

However, there are many purely aesthetic aspects of design that this book did not get into. For example, rounded corners is a popular design element. There are many ways to create rounded corners but I've never tried to apply them to web controls like a DataGrid or the GridView. I did find a good way to apply rounded corners to a DIV element using nothing but CSS and PNG files. Adding drop shadows to borders is another popular style. I've found some JavaScript that can dynamically add a drop shadow to the page border without requiring you to create any images. I have not tried that with any web control borders.

Then there are stripped backgrounds which are used to crosshatch the background. I think these backgrounds are popular because they give the background a different texture than the page content. I used to steal background images but then I found a web site that can generate different patterns for you. However, you should examine the pixel patterns for popular stripe patterns and learn how to change their colors by adjusting the hue. You may find you'll have to extend the images to avoid tiling.

There are various gradient effects that I've noticed but I have not researched gradients yet. The Yahoo! home page is a fine example of a very tasteful use of gradients. But you can really do some ugly things with gradients if you don't know all the tricks.

My New Years resolution for a career goal is to spend more time picking up graphic design tips and tricks. Conversely, I want to spend less time on researching programming and web application development. This may seem like an unwise goal for a web developer but I feel that I need more improvement in this area.

Programming and designing are often considered to require two fundamentally different mentalities. Web developers usually create functional web pages with a blank design. I've seen designers that can't even apply their design to an ASPX page without messing up the page declarations and user controls. I have to admit that while developers can apply a design and make minor changes to graphics, and designers can code CSS and learn some basic JavaScript, you usually don't want to combine both skill sets in one position. While I believe one person can do a little bit of everything, you really have to respect the considerable expertise required by each profession.

I've known programmers to underestimate the skills needed to be a web designer. It requires more than a knowledge of CSS. Only a code centric programmer would make the mistake of considering just the CSS. There are also an enormous number of Photoshop tricks and trips to learn which means working through a lot of tutorials. A designer also needs to know the history of graphic design in order to combine design elements or recreate a retro look. I think it is a full time job to keep your focus on design elements and work through all the tutorials to create various effects.

Unlike a "software engineer" I am not into advanced math and logic. I'm not totally ego invested in being a programmer. I'm also quite confident that I have an artistic side. I'm strongly attracted to creative work and find it easy to identify with anything strange and fantastic. As a matter of fact, I'm surprised that many artists lack confidence in their talent or question themselves. You can be dissatisfied with your work which may not equal your inspiration, but you should find confidence in your imagination's affinity for the sublime.

Recently I came up with a simple way to add some flare to my web applications. Displaying some numbers as a digital readout can really enhance the look of a page. You can get the digital numbers from clock widgets and use them to display
record counts, IP addresses, or temperatures. Replacing numbers with graphics requires some programming which may be why you don't see a lot of designers using digital readouts for real data. I would expect many programmers to balk at using graphics for actual numbers because it adds nothing to the functionality.

Weather with digital readout

Here you can see a digital readout of the temperature which draws the eye to the most important bit of information. It could use a digital "F" to indicate Fahrenheit but this will require editing a digit in Photoshop. A web designer would probably be inspired to make the entire interface resemble a photo-realistic device with a reflective glass cover, plastic molding, etc.

After finding a way to read RSS feeds in a custom help collection web page without cross domain restrictions, see previous blog post, I naturally wanted to do the same thing with XML feeds. You could probably figure that out for yourself given the RSS example but I hate it when I find sample code that does not quite do what I need. There were also some annoyances with my previous solution and I have some improvements.

The first step is to create a new generic handler to convert XML data to the JSON format:

<%@ WebHandler Language="C#" Class="xml2json" %>

using System;
using System.Web;
using System.Xml;
using System.Text;

using System.Collections;

public class xml2json : IHttpHandler {

    
public void ProcessRequest (HttpContext context) {
        context.Response.ContentType =
"text/javascript";
        
string strFeed;
        
string strJSON;
        strFeed =
HttpContext.Current.Request.QueryString["feed"];
        
XmlDocument xdoc = new XmlDocument();
        xdoc.Load(strFeed);
        
// Convert XML to a JSON string
        string JSON = XmlToJSON(xdoc);
        
// return an object
        strJSON = "var obj = eval (" + JSON + ");";
        context.Response.Write(strJSON);
    }

    
public bool IsReusable {
        
get {
            
return false;
        }
    }
}

As for the code to actually do the data format conversion, XmlToJSON, I just used the first sample code I found on the Interet which was How to convert XML to JSON in ASP.NET C# but there may be better methods available. The generic handler can be passed any XML feed web address through the query string value. I used the National Weather Service for my tests because I'm familiar with their XML format. I created a simple form for inputing the weather station ID:

<form id="frm" action="">
Enter Weather Station ID: <input type="text" id="station" size="20" />
<br /><br />	
<input type="button" value="Submit" id="btnSubmit" onclick="LoadJSON();" />
</form>

You also need a div where the response is going to appear. This div should appear above the JavaScript and the form should appear below the JavaScript:

<div id="response"></div>

In the JavaScript, I needed to make an improvement to deal with some latency in the response. I found I would need to click the submit button a few times before the JavaScript object would be available. So I added some code to test for the object and repeat the request if the object was undefined. This proved to be surprisingly difficult because it is tricky to test for an undefined JavaScript object without causing an error. That is something to add to my notes. I also needed to dynamically create a table so I learned a few more JavaScript DOM methods like insertRow and insertCell. I like it when these little research projects turn up some additional tips and tricks.

<script type="text/javascript">
// repeatedly calls AddScript until there is an object	
function LoadJSON() {
	if (document.getElementById("station").value != "") {
		AddScript();
		while (typeof(obj) == "undefined") {
			AddScript();
		}
		ShowWeather();
	}
	else {
		alert("Enter the Weather Station ID first!");
		document.getElementById("station").focus();
	}		
}
function AddScript() {
	jscript = document.createElement("script");
	jscript.setAttribute("type", "text/javascript");
	jscript.setAttribute("src", "http://localhost/rss2json/xml2json.ashx?feed=http://www.weather.gov/data/current_obs/" + document.getElementById("station").value + ".xml");
	document.getElementsByTagName('head')[0].appendChild(jscript);
}
function ShowWeather() {
    //for (prop in obj.current_observation) {
    //    alert(prop);
    //}
    // create table
    var table = document.createElement("table");
    table.setAttribute("bgcolor","#eeeeee");
    table.setAttribute("cellspacing","0");
    table.setAttribute("cellpadding","1");
    table.style.backgroundColor = "#eeeeee";

    // create 1st row
    var tr = table.insertRow(-1)
    tr.setAttribute("bgcolor","black");
    tr.style.backgroundColor = "black";
    tr.style.color = "white";
    var td = tr.insertCell(-1)
    td.setAttribute("colspan","2");
    td.colSpan = 2;
    var loc = document.createTextNode(obj.current_observation.location); 
    td.appendChild(loc);
    var br = document.createElement("br");
    td.appendChild(br);
    var time = document.createTextNode(obj.current_observation.observation_time); 
    td.appendChild(time);

    // create 2nd row
    var trWeather = table.insertRow(-1)
    var tdWeather = trWeather.insertCell(-1)
    var cellText = document.createTextNode("Weather:");
    tdWeather.appendChild(cellText);
    var tdWeatherValue = trWeather.insertCell(-1)
    var cellText = document.createTextNode(obj.current_observation.weather);
    tdWeatherValue.appendChild(cellText);

    // create 3rd row
    var trTemperature = table.insertRow(-1)
    var tdTemperature = trTemperature.insertCell(-1)
    var cellText = document.createTextNode("Temperature:");
    tdTemperature.appendChild(cellText);
    var tdTemperatureValue = trTemperature.insertCell(-1)
    var cellText = document.createTextNode(obj.current_observation.temperature_string);
    tdTemperatureValue.appendChild(cellText);

    // create 4th row
    var trHumidity = table.insertRow(-1)
    var tdHumidity = trHumidity.insertCell(-1)
    var cellText = document.createTextNode("Humidity:");
    tdHumidity.appendChild(cellText);
    var tdHumidityValue = trHumidity.insertCell(-1)
    var cellText = document.createTextNode(obj.current_observation.relative_humidity + " %");
    tdHumidityValue.appendChild(cellText);

    //create 5th row
    var trWind = table.insertRow(-1)
    var tdWind = trWind.insertCell(-1);
    var cellText = document.createTextNode("Wind:");
    tdWind.appendChild(cellText);
    var tdWindValue = trWind.insertCell(-1);
    var cellText = document.createTextNode(obj.current_observation.wind_string);
    tdWindValue.appendChild(cellText);

    //create 6th row based on a condition
    if (obj.current_observation.windchill_string != null) {
        var trWindChill = table.insertRow(-1)
        var tdWindChill = trWindChill.insertCell(-1);
        var cellText = document.createTextNode("WindChill:");
        tdWindChill.appendChild(cellText);
        var tdWindChillValue = trWindChill.insertCell(-1);
        var cellText = document.createTextNode(obj.current_observation.windchill_string);
        tdWindChillValue.appendChild(cellText);
    }
    else {
        var trHeatIndex = table.insertRow(-1)
        var tdHeatIndex = trHeatIndex.insertCell(-1);
        var cellText = document.createTextNode("HeatIndex:");
        tdHeatIndex.appendChild(cellText);
        var tdHeatIndexValue = trHeatIndex.insertCell(-1);
        var cellText = document.createTextNode(obj.current_observation.heat_index_string);
        tdHeatIndexValue.appendChild(cellText);
    }
    // create table body
    var tbody = document.createElement("tbody");
    table.appendChild(tbody);
    var div = document.getElementById("response");
    // add table to div
    div.appendChild(table);
}
</script>

In an initial effort to solve the latency problem, I did some research into precompiling the generic handler. I'd never tried to precompile an ASP.NET 2.0 web site before and getting the correct command line tool syntax required some experimentation. This is another instance of the documentation failing to provide enough examples! When I copied the staging files into the web site I had to delete the App_Code directory and lost the source code for my generic handler although I'd already added that to my notes:

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727>aspnet_compiler -v /rss2json -p 
"C:\Inetpub\wwwroot\rss2json" "C:\staging"
Utility to precompile an ASP.NET application
Copyright (C) Microsoft Corporation. All rights reserved.

This may actually be useful for me because I have several shipping rate web services that I need to document. Now I can build actual test scripts into my notes on these shipping rate services and eliminate the need to track down my test scripts. This project also taught me how to test for a JavaScript object, how to dynamically create a table, and how to precompile an ASP.NET 2.0 web application so it was a worthwhile exercise.

I don't see the independent web developer's viewpoint expressed in the ASP.NET community. Most of the blog posts concern highly technical aspects of architecture, testing, advanced language features, etc. I would guess that the community is primarily made of up Microsoft employees involved in advancing the ASP.NET platform, technology writers specializing in ASP.NET, and consultants like MVPs. This makes http://weblogs.asp.net a good place to keep up with the state of the technology but every community can benefit from a variety of viewpoints.

I used to work for a very small company as a web developer and then I was let go and had to work for myself. Currently I am working full-time for a non-profit and should be concerning myself more with Windows Application development for the desktop. However, there is a surprisingly high demand for web developers and I've seen that you can easily remain employed as long as you keep up your web development skills. Going into business for yourself as a web developer is definitely practical due to the large number of Internet entrepreneurs out there. I didn't like being swamped with projects when I worked for that small company but I soon found myself swamped with work when I was working for myself because there is just too much work to be had. You don't even need to advertise or promote yourself. The cost of client acquisition is zero making it a sweet business to start. You get to work with a lot of entrepreneurs and act as an entrepreneur yourself making it somewhat exciting.

Nevertheless, there are some important considerations to take into account if you want to remain marketable as a web developer. First there is what technology and skills to invest in. I would argue that the platform is less important than the choice of business application. You can pick ASP.NET, LAMP, or Java but you should stick to one framework because it is too time consuming to master more than one. But after you've invested in a language and a framework you still need to make a wise decision in picking an application. Half of my work was customizing e-commerce shopping carts and half was taking up an abandoned custom web application. I think e-commerce shopping carts are a safe bet because you build expertise in a web application that is used by many companies.

Currently I am still doing a lot of work customizing LaGarde's Storefront shopping cart which was built for ASP.NET 1.1. So I still do most of my work in Visual Studio 2003.  I've built up a lot of expertise in that specific application which is a mess of VB classes in five projects. However, I now regret my investment in Storefront because LaGarde has abandoned their developer community and only services their enterprise clients. On the other hand this has left many small businesses in the lurch. They have also invested heavily in Storefront to run their online stores and now they must rely on a dwindling supply of developers to maintain it. I probably don't have a lot of competition anymore.

I should consider my options for a new web application to develop expertise in. There are many choices available to a web developer familiar with the ASP.NET platform. DotNetNuke seems to be a very popular web application framework with a good developer community. I know of a lot of independent ASP.NET developers involved in the DotNetNuke marketplace. I get the impression that there are many incomplete DotNetNuke deployments out there. It is easy to install DotNetNuke and appear to be making a lot of progress because you have the application infrastructure established. But then there is the difficult task of customizing the application to meet the business needs. This is where a lot of projects are abandoned leaving businesses with a need for a DotNetNuke developer.

Considering my knowledge of e-commerce I should favor another shopping cart application. There are many more shopping carts built with ASP.NET in competition with LaGarde's Storefront. I am somewhat familiar with BV Commerce which is very similar to Storefront. I see that BV Commerce 5 is now running on ASP.NET 2.0 but it costs $1500.00 for the developer version with source code. Maybe I could get the source code version by convincing a Storefront client to convert to BV Commerce?

Sharepoint is also a good web application option. I've even seen a few local jobs requiring Sharepoint expertise and Microsoft promotes it at local TechEvents. However, Sharepoint has a steep learning curve. I get the impression that you can make a lot of money as an expert Sharepoint consultant but I'm not so sure there is a demand for custom development. There does not seem to be a market for web parts. Maybe there is a lot of opportunity here because web parts are a cool technology which is being neglected.

There are other mysterious Microsoft enterprise technologies which I could specialize in but which seem to have no presence in the marketplace whatsoever. For example, Commerce Server has only a stub article on Wikipedia and I've never casually come across any articles on developing for Commerce Server. BizTalk is another server technology with a steep learning curve, a specialized market, and very little developer mind share.

I'm thinking about getting into more creative work doing web design and digital video editing but I don't think these careers offer as much steady work.

In attempting to use the Microsoft AJAX Library 1.0 in a MSHelp2 collection (to be viewed in the Microsoft Document Explorer) I ran into the same origin policy (SOP) restrictions on the XMLHttpRequest object. I cannot make web service data requests to another server from a page in compiled help without getting a JavaScript error message: "Access is denied".

 Access Is Denied

Even requests to my localhost web server fail. I believe this is due to the strange protocol MSHelp2 uses (i.e. ms-help://) which cannot match the web service protocol, therefore violating same origin equality. This eliminates the use of locally running proxies to retrieve data from remote web services. It is essentially impossible to make any use of the XMLHttpRequest object from a help collection.

The one solution I have found to this problem is to use JSON and dynamic script tags as described on the Yahoo! Developer Network. This is a pure client-side JavaScript solution which does not make use of the XMLHttpRequest object or any proxy servers. I have found two major sources of web services that can return JSON data with a callback function; Yahoo! and Google. For information on the Google Data APIs visit http://code.google.com/apis/gdata/json.html. Since YouTube is now owned by Google, there happens to be a Google data feed for getting information about YouTube videos. I was able to use the JSON version of this data feed to display my video favorites within my custom help collection:

YouTube Video Favorites

This works because the JSON data is being pulled in by a script block with its source set to the web service supplying the data:

<script type="text/javascript" src="http://gdata.youtube.com/feeds/users/robrobbins/favorites?start-index=1&max-results=25&alt=json-in-script&callback=ws_results"></script>

Note that the data format is specified as JSON rather than the usual XML by the query string parameter json-in-script and the callback function will be ws_results. Then there is another script block for the callback function which can reference the web service data as a JavaScript object:

function ws_results(obj) {
//alert(obj.feed.entry.length);
// create paragraph
var paragraph = document.createElement("p");
var textnode = document.createTextNode("Total Results: " + obj.feed.entry.length);
paragraph.appendChild(textnode);
// create unordered list
var unorderedList= document.createElement("ul");
// create list items
for (var i = 0; i < obj.feed.entry.length; i++) {
var header = document.createElement("h2");
var itext = document.createTextNode(obj.feed.entry[i].title.$t);
header.appendChild(itext);
unorderedList.appendChild(header);
var div = document.createElement("div");
div.innerHTML = obj.feed.entry[i].content.$t;
unorderedList.appendChild(div);
}
var div = document.getElementById("response");
// add paragraph to div
div.appendChild(paragraph);
// add unordered list to div
div.appendChild(unorderedList);
}

When working with these JavaScript objects from JSON web services you may have trouble figuring out how to reference the data. I just use a simple for loop to pop up an alert for each object property:

 for (prop in obj) {
    alert(prop);
}

After figuring out that JSON and callback functions were the solution to creating MSHelp2 mash ups, I was eager to expand my options by creating my own web service to supply data in the JSON format. Unfortunately, while it is easy enough to create a web service that returns JSON data feeds there is also the requirement to support callback functions and that has me stymied right now. One of the things I tried was Jayrock, JSON and JSON-RPC for the Microsoft .NET Framework. Jayrock makes it easy to create web services that return JSON data feeds using a generic web handler. Unfortunately, it appears to use the XMLHttpRequest object which gives me the familiar "Access is denied" JavaScript error. You can clearly see the XMLHttpRequest object being created in the JavaScript for the web service test page. It may be possible to use a callback function with Jayrock but the documentation does not provide any examples on how to do that.

I also tried to create a generic web handler that will use context.Response.ContentType = "application/json"; to return JSON data but I was still unable to implement the callback function. You may be able to do this quite simply by surrounding the JSON string with a function by appending the strings "wp_result("  and ")" but I cannot find any sample code to work out the exact syntax. If anyone can figure out how to implement the callback function, let me know.

You may be wondering why anyone should bother to call web services from within a help collection. I think it would be a very clever way to create mash ups in compiled help and project documentation. Many windows applications provide help in a compiled help files but this is static information. It is not kept up to date with new data unless you directly link to a live web page. Application help files could become applications in their own right using this technology. In any event, being able to create JSON web services that support callback functions is a useful trick so I intend to pursue this further. Yahoo! and Google supply such web services but it does not seem possible to do the same thing using ASP.NET.

UPDATE:
A tip from Hernan Garcia allowed me to complete my generic web handler. The trick was to change the ContentType to JavaScript so the object can be returned without requiring a callback function. To convert RSS feeds to JSON data, I used the code example that can be found on Piyush Shah's blog.

<%@ WebHandler Language="C#" Class="rss2json" %>

using System;
using System.Web;

public class rss2json : IHttpHandler {
    
    public void ProcessRequest (HttpContext context) {
        context.Response.ContentType = "application/javascript";
        string strFeed;
        strFeed = HttpContext.Current.Request.QueryString["feed"];
        // return an object
        strFeed = "var obj = eval (" + RssToJson.App_Code.RssDocument.Load(strFeed).ToJson() + ");";
        context.Response.Write(strFeed);
    }

    public bool IsReusable {
        get {
            return false;
        }
    }

} 

The web address for the feed is obtained from a query string value. A form in my custom help collection now allows me to paste in the web address for any RSS feed and display the post titles in an unordered list with a tooltip of the content. I can paste in additional feed addresses to continue to create lists just like on a mash up web site.

RSS to JSON

The JavaScript for the form could use a little improvement because you often need to click the button twice before the list will appear because I'm dynamically adding script blocks.

function ShowPosts() {
	//for (prop in obj.Channel) {
	//	alert(prop);
	//}
	// create paragraph 
	var header = document.createElement("h1");
	var textnode = document.createTextNode(obj.Channel.Description);
	header.appendChild(textnode);
	// create unordered list
	var unorderedList = document.createElement("ul");
	// create list items
	for (var i = 0; i < obj.Channel.Items.length; i++) {
	    var listitem = document.createElement("li");
	    var alink = document.createElement("a");
	    var itext = document.createTextNode(obj.Channel.Items[i].Title + "  (" + obj.Channel.Items[i].PubDate + ") ");
	    alink.appendChild(itext);
	    alink.setAttribute("href", obj.Channel.Items[i].Link);
		alink.setAttribute("title", stripTags(obj.Channel.Items[i].Description));
		listitem.appendChild(alink);
	    unorderedList.appendChild(listitem);
	}
	var div = document.getElementById("response");
	// add header to div
	div.appendChild(header);
	// add unordered list to div
	div.appendChild(unorderedList);
}
function stripTags(s) {
    return s.replace(/<\/?[^>]+>/gi, '');
}
function AddScript() {
	//alert(document.getElementById("feed").value);
	if (document.getElementById("feed").value != "") {
		jscript = document.createElement("script");
		jscript.setAttribute("type", "text/javascript");
		jscript.setAttribute("src", "http://localhost/rss2json/rss2json.ashx?feed="+document.getElementById("feed").value);
		document.getElementsByTagName('head')[0].appendChild(jscript);
	}
	else {
		alert("Enter the RSS feed web address first!");
		document.getElementById("feed").focus();
	}
}

Microsoft AJAX Library Essentials: Client-side ASP.NET AJAX 1.0 Explained

by Cristian Darie, Bogdan Brinzarea

This book provides in-depth coverage of the Microsoft client library for AJAX. I didn't even know Microsoft had a JavaScript library but you can download it at: http://asp.net/ajax/downloads/library/ You can use the Microsoft AJAX Library with whatever server technology you wish including PHP or Java. There are many AJAX JavaScript libraries available but this one is designed to make the most sense to ASP.NET developers because the API will be similar to C#.

The first few chapters of the book provide an extensive introduction to the object-oriented programming capabilities of JavaScript. Most web developers just hack JavaScript without really learning the language so there is a lot to be gained from these chapters. For instance, I learned that the proper way to create HTML code is through the Document Object Model using functions like createElement, createTextNode, and appendChild instead of just doing it the lazy way with innerHTML. It irked me that I did not know that but I've since found innerHTML still being used in Microsoft source code so you see a lot of bad usage.

JavaScript isn't really a proper OOP language so there are many work arounds to get it to emulate classes. Some of these language features seem to have been added to C# 3.0 like anonymous functions, inner functions, closures, functions as variables, and prototypes. Don't ask me to explain any of that because it makes my head spin. However you don't really need to know all the insidious details to use the library, though I would recommend this book as an excellent resource if you wanted to understand JavaScript OOP.  There is also some information on JSON (JavaScript Object Notation) which the Microsoft AJAX Library uses to exchange data. JSON has been on my programmer radar because I found it used in some MediaWiki code when I was investigating wiki security. Basically it is a way to serialize JavaScript object arrays as plain text.

The final chapter of the book covers all the debugging tools available for JavaScript which most web developers already know about and recommend. I did find a few I did not have like the Venkman JavaScript Debugger and Nikhil Kothari's Web Development Helper. There is a debug version of the Microsoft.Ajax.js library which contains code comments, readable code, and summary data for Visual Studio 2008 code completion. The library itself contains a debug class with methods for debugging and tracing. I consider this an excellent reason to use this library instead of other AJAX libraries because I rely heavily on debug statements and tracing.

I have not been using AJAX much in my custom web application development. However, I would like to see if the Microsoft AJAX Library is suitable for developing widgets and gadgets which usually require creating the XMLHttpRequest object. I'm also going to try using it in my custom help collection. I already have several JavaScript libraries baked into my notes including; base64.js, swfobject.js,  jsTrace.js,  json.js, styleswitcher.js, and shadedborder.js.

You can find more information on this book at: http://www.packtpub.com/ajax-csharp-essentials/book

 

I have a project where a shopping cart is using file upload web controls to allow customers to upload image files. This is not working out so well because the image files can be huge. I think the session is timing out before the file uploads and the web application may be recycling because the ASP.NET worker process exceeds its maximum memory allowance with those huge image files. Well that is my theory. I've recommended using FTP to make the file uploads less taxing on the web application. If an image file is uploaded via FTP then I will need to get some information from the FTP logs. I was unable to find any sample code for parsing FTP log files using ASP.NET.

To easiest way to parse the FTP logs would be to use the Microsoft Log Parser 2.2 which you can download at: http://www.iis.net/downloads/default.aspx?tabid=34&i=1287&g=6 When you install it there will be a compiled help file included. I converted this into a Help 2.0 Collection to integrate it with my other MSDN documentation in the Microsoft Document Explorer. Anyway, look for the C# sample code under Log Parser COM API Overview. This help topic includes information on how to use the COM interop feature of the .NET framework to instantiate and use the COM object version of LogParser. The sample code is for the System Event Log so the only value I'm adding with this blog post is to show you how to use this with the FTP logs (plus I converted the code to VB).

Note that you must change the input format to IIS W3C for the FTP logs.  My FTP logs are actually using the W3C Extended Log File Format. You don't need to know the file path for the log files but it was slightly tricky to figure out the correct syntax for the FROM part of the query. I think sample code and documentation should provide you with examples for all the options and not just a few representative cases. Don't force me to experiment with syntax! I also changed the parameter to the file last modified date. I found that my results included the logs from two log files so be aware that the query will apply to more than one log file. You should probably add the Date to your FTP log's Extended Properties so you can query by a precise date.

Imports LogQuery = MSUtil.LogQueryClassClass
Imports IISW3CLogInputFormat = MSUtil.COMIISW3CInputContextClassClass

Public Class ReadFTPLogs
    
Inherits System.Web.UI.Page

#
Region " Web Form Designer Generated Code "

    
'This call is required by the Web Form Designer.
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()

    
End Sub
    Protected WithEvents lblFTPLog As System.Web.UI.WebControls.Label
    
Protected WithEvents lblErrorMessage As System.Web.UI.WebControls.Label

    
'NOTE: The following placeholder declaration is required by the Web Form Designer.
    'Do not delete or move it.
    Private designerPlaceholderDeclaration As System.Object

    
Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Init
        
'CODEGEN: This method call is required by the Web Form Designer
        'Do not modify it using the code editor.
        InitializeComponent()
    
End Sub

#End Region

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        
Try
            ' Instantiate the LogQuery object
            Dim oLogQuery As LogQuery = New LogQuery

            
' Instantiate the W3C Log Input Format object
            Dim oIISW3CLogInputFormat As IISW3CLogInputFormat = New IISW3CLogInputFormat

            
' Set its "minDateMod" parameter
            oIISW3CLogInputFormat.minDateMod = "2007-01-01 00:00:00"

            
' Create the query
            Dim query As String = "SELECT TOP 250 time, c-ip, cs-method, cs-uri-stem, sc-status FROM <//SERVER/MSFTPSVC/1>"

            
' Execute the query
            Dim oRecordSet As MSUtil.ILogRecordset = oLogQuery.Execute(query, oIISW3CLogInputFormat)

            
' Browse the recordset
            While Not oRecordSet.atEnd()
                lblFTPLog.Text &= oRecordSet.getRecord().toNativeString(",") & "<br />"
                oRecordSet.moveNext()
            
End While

            ' Close the recordset
            oRecordSet.close()
        
Catch exc As System.Runtime.InteropServices.COMException
            lblErrorMessage.Text = "Unexpected error: " & exc.Message & "<br /><br />" & exc.StackTrace
        
End Try
    End Sub

End
Class

 

I've been busy all day long and it is sometimes interesting to read about a day in the life of a professional. I learned a lot of new things today in the course of my work. Since it was the end of the month, I totaled up my hours and sent a client an invoice. I should probably do something to make this less of a chore. Preparing an invoice means copying all my activities from a list I keep in a notebook into a table in a web page. Then I have to add up the hours which aren't totaled automatically in a web page. We used a Quickbooks Timer in my old job but it was a funky DOS application that kept crashing. 

  The first thing I wanted to do today was to make some progress on a project migrating an ASP web application to ASP.NET 2.0. There are some things about this ASP web application that warrant a migration. It is extremely difficult to troubleshoot because there are some occasional SQL syntax errors. Unfortunately ASP has very limited error handling capabilities so I would like to move to ASP.NET 2.0 which will make it easier to instrument the code. Also the client side code attempts to load too much data into JavaScript arrays and this is bogging down the browser. I think some modern AJAX code would help with that problem. There have also been many session expiration and performance problems which I hope to solve.

When I attempted to load the web application in my browser I got a web.config error I've never seen before: Unrecognized attribute 'type'. I wonder if this was caused by my recent install of ASP.NET 3.5? In any event, it turned out that the web application mysteriously converted to ASP.NET 1.1 in Internet Information Server so I had to change it back to ASP.NET 2.0. I added this error and its solution to my notes on ASP.NET 2.0 problems. I also noticed that my web.config file was still using 3.5.0.0 assemblies as generated by Visual Studio 2008 Beta 2 even though I've been working on this project in Visual Studio 2005. I have not used any ASP.NET 3.5 features yet so I can still use Visual Studio 2005 for the development work. It does not appear to cause any problems if you are not doing anything that Visual Studio 2005 would not know about. NOTE: The client actually wants to migrate to ASP.NET 3.5 so this web application won't be using old technology anymore.

My goal was to complete just one page in an hour but it took longer than that. First I copied a SQL query from the old ASP page and ran it in SQL Query Analyzer. Then I added some columns using a join in preparation for a GridView control. I used this query in the DataSet Designer to create a new TableAdapter. Next I added the GridView control to my ASP.NET page. I struggled to configure the columns to match the original table design and then I decided it would be easier to adjust my query. So I rewrote my SQL statement using some string concatenation and column name aliases to make my job easier. I was unable to concatenate an integer to a string so I had to look up the CONVERT function syntax. Then I had to regenerate my TableAdapter and refresh the GridView columns.

The first column needed to contain a radio button. This page is intended to allow you to select a column and then click a button to create an Excel spreadsheet for that record. Fortunately, I found a tutorial on Adding a GridView Column of Radio Buttons. I also had another column that required an ItemTemplate because it uses two different ID numbers. I used cross-page posting for the button because I already had the Excel code written. In fact, the ASP site is now using an ASP.NET 1.1 page to generate the Excel spreadsheet because it needed to be rewritten and the client does not want any more ASP code maintenance done. The original ASP page just posts to the ASP.NET page which has all its code in a script block, i.e. no code behind file. A postback would have been out of the question anyway because the content type needs to be changed for Excel.

All that work ate up 5 hours of my day because the radio buttons were a bit tricky. I added a new topic to my notes on using radio buttons in a GridView control because this project will need that on many pages. I noticed that I kept getting an annoying JavaScript error on many web sites so I decided to investigate that. The error was "A Runtime Error has occured. Line: 1. Error: Invalid character".  I even get that pesky JavaScript error on http://weblogs.asp.net/ and was considering jeering about the bad coding but I saw the exact same error on many other sites so I suspected it was not a problem with the web site. I found a lengthy newsgroup posting about this problem and eventually managed to solve it by disabling third party browser extensions. I may have picked up some Hijackware somehow.

I came across a web page about some obscure HTML table tag attributes, frames and rules. I thought I knew everything about tables so it annoyed me to discover something new about tables. I think I've seen lhs (left hand side) somewhere and was mystified about it so I added this information to my notes on HTML tables. This is a web page in my custom help collection with some useful information on using THEAD and TBODY to get table headers printed on every page of a web page print out, and the CSS to style a table in the format I prefer.

While I was adding stuff to my notes I decided to document how to set the text for a RadioButtonList control's ListItems. A few days ago I had to add some names from a database to the radio button text and it took me quite awhile to figure out how to do that. You just set the asp:RadioButtonList's Items collection text properties like so:

rblFruit.Items(0).Text = objReader("Qty") & " apples"
rblFruit.Items(1).Text = objReader("Qty") & " oranges"
rblFruit.Items(2).Text = objReader("Qty") & " pears"

After adding that much information to my notes I decided to recompile my custom help collection. It now contains 1076 topics which means I have 1076 web pages compiled into it! I've been keeping notes for 9 years. I timed the recompile and it took 11 minutes for this project to compile. I don't even try to load it in Visual Studio 2003 anymore because it chokes on it. I just edit my project files in a text editor and do command line builds. After the recompile it took 9.5 minutes for the help collection to update itself for new content. That is 20 minutes just to update my notes!

Well that is all I managed to do today. I watched a few YouTube videos inbetween tasks so I would not get too bored. I found a few YouTube videos on Visual Studio 2008.

More Posts