Looking for a good ASP.Net front-end developer in New York City

A client I’ve been working with is looking for a front-end ASP.Net developer to work on-site for them in their New York City (midtown) office.

The right candidate should be able to:

  • develop a complex ASP.Net solution using WebForms;
  • create web markup (HTML/CSS) from Photoshop mockups;
  • use ASP.Net Ajax, jQuery, or other client-side solutions, and know when to use what;
  • write efficient front-end code that is tested and works well across a wide range of browsers;
  • come up with user interface solutions (i.e. help define the client side of the business requirements);
  • be able to write code-behind and be able to read, understand, and enhance existing business logic code.

If you or someone you know has the right stuff, send me an email with your resume and I’ll pass it along.

Cleaning up the GridView’s EmptyDataTemplate (damn those tables!)

A project I’ve been working on makes frequent use of the ASP.Net GridView. However, as many people know, the GridView has one glaring issue: It wraps your <EmptyDataTemplate> in an HTML table — something that I don’t want, for two reasons:

  1. My empty data template does not consist of tabular data.
  2. My stylesheet has a default style for tables — a style that I don’t want applied to my empty data template.

One solution is to use the CSS Friendly Adapters for ASP.Net, which potentially solves both prolems, but that will bring in potential breaking changes to my GridView controls. I don’t have time for that (yet).

The solution I came up with is to do two things:

  1. Create a new CSS class that removes table formatting (borders, backgrounds, padding, margins, etc.). This class will be applied to the GridView only when it has no data.
  2. Add a line of code in the OnPreRender() method for each web page or control that has a GridView, conditionally setting the CSS class of the GridView to the aforementioned class.

My CSS class looks like the following.

.empty, .empty td { border:0;background:none;margin:0;padding:0; }

The line of code in your OnPreRender() event follows.

protected override void OnPreRender(EventArgs e)
{
    base.OnPreRender(e);

    if (myGridView.Rows.Count == 0)
        myGridView.CssClass = "empty";
}

Why the OnPreRender stage? By placing your code here, you ensure that all data loaded into your GridView is already bound, whether it be from code (myGridView.DataBind()), from data sources in markup (such as the ObjectDataSource), or from viewstate.

It isn’t pretty, and it doesn’t give you semantic markup, but it’s easy enough to implement and can solve the visual problem.

Rewriting the ASP.Net CSS Friendly Adapters – does anyone care?

A few days ago, I wrote a lengthy post on the official ASP.Net forums where I discussed some thoughts on a new version of the CSS-friendly ASP.Net control adapters. In the post, I outlined some new approaches to improve testability and configuration.

Since that post (written on the evening of Sept 16), there have been no replies, which leads me to consider a few possible realities…

  1. No one reads the ASP.Net forums. (Not true; the post was read at least 79 times, as of this writing.)
  2. No one posts on the ASP.Net forums.  (Not true; other posts were written since my post.)
  3. No one uses the CSS Friendly adapters. (Not true; they have been downloaded over 1,800 times in the last week, according to CodePlex — making it the 14th most popular CodePlex project in that time.)
  4. No one cares about rewriting them.
  5. No one who cares about rewriting them reads the ASP.Net forums.

OK, so either no one cares about improving on what we currently have, or no one who cares saw my post. Which is it? Do you use the CSS adapters? Would you use them if they were improved?

Remove anonymous users from ASP.Net Membership tables

If you use the ASP.Net membership tools, have <anonymousIdentification enabled="true" /> specified in your Web.config, and get lots of anonymous visitors, it’s only a matter of time before your database grows. What’s filling it up is the countless user records for your anonymous users.

If you don’t need to track user and profile information for an anonymous user once they leave the site, you can delete the unneeded data by running a SQL script. The following script will delete from your membership tables all anonymous users whose last activity was more than 7 days ago.

delete from aspnet_profile
where userid in ( select userid from aspnet_users
where isanonymous = 1
and datediff(dd, lastactivitydate, getdate()) &gt; 7
)

delete from aspnet_usersinroles
where userid in ( select userid from aspnet_users
where isanonymous = 1
and datediff(dd, lastactivitydate, getdate()) &gt; 7
)

delete from aspnet_membership
where userid in ( select userid from aspnet_users
where isanonymous = 1
and datediff(dd, lastactivitydate, getdate()) &gt; 7
)

delete from aspnet_personalizationperuser
where userid in ( select userid from aspnet_users
where isanonymous = 1
and datediff(dd, lastactivitydate, getdate()) &gt; 7
)

delete from aspnet_users
where userid in ( select userid from aspnet_users
where isanonymous = 1
and datediff(dd, lastactivitydate, getdate()) &gt; 7
)

Doing this may be very important to those using a shared hosting plan that has limited SQL disk space, as those anonymous users can quickly eat up disk space. Since each row in aspnet_users takes up just over 1kb, having 1,000 anonymous users will eat up 1MB — something that adds up quick if you only have 250MB of SQL disk storage.

Firefox, LinkButtons, and the Panel.DefaultButton: a (Prototype) fix

Recently I’ve stepped away from the MonoRail world to work on a project that uses ASP.Net WebForms. It didn’t take long before I found an annoying problem. (Actually I found many annoying problems, but I’ll focus on one here.)

The <ASP:Panel> control has a DefaultButton property which, according to the documentation, "Gets or sets the identifier for the default button that is contained in the Panel control." In other words:

Use the DefaultButton property to indicate which button gets clicked when the Panel control has focus and the user presses the ENTER key.

It works perfectly, if you’re not using a LinkButton control. Actually, that’s not true; if you use a LinkButton control and Internet Explorer, it works fine. It just doesn’t work in Firefox. Why?

Dmytro Shteflyuk outlines why in his blog post, Using Panel.DefaultButton property with LinkButton control in ASP.Net. Apparently, it’s an issue with the JavaScript code that Microsoft generates, which expects a click() method on the anchor (i.e. LinkButton). Firefox doesn’t have such an event for anchors — at least, not by default.

Dmytro outlines a fix which requires you to injext some JavaScript for each button. I prefer a simpler approach using CSS selectors, so I wrote the following script (Prototype required) to do it. Simply add this script to your web page, add the CSS class "button" to each LinkButton control, and press ENTER on your Firefox forms.

function prepareLinkButtonClicks()
{
	$$('a.button').each(function(tag) {
		if (tag && typeof(tag.click == 'undefined')) {
			tag.click = function() { 
				var result = true;
				if (tag.onclick) result = tag.onclick();
				if (typeof(result) == 'undefined' || result) {
					eval(tag.href);
				}
			}
		}
	});
}
Event.observe(window, 'load', prepareLinkButtonClicks);

A simple asp:Repeater replacement for simple needs

How many times have you had to parse through a collection of objects, outputting nothing more than a comma-delimited list of items in the result set. Typically, you’ll do this:

<asp :repeater runat="server">
	<itemtemplate>Eval("Name")</itemtemplate>
	<separatortemplate>, </separatortemplate>
</asp>

If you were parsing a list of states, it might look like this:

Alabama, Alaska, Arizona, Arkansas

There’s an easier way to handle these simple needs: create a CollectionToString() method. This method would accept an IEnumerable and, using reflection, read a property and return a delimited string. Of course, you choose the property and delimiter.

Note that we have two versions of this method: one which accepts an IEnumerable, the other which accepts an object. This is done to avoid the necessary typecasting in your code, since Eval() returns everything as an object.

public static string CollectionToString(IEnumerable collection, string property, string delimiter)
{
	IEnumerator enumerator = collection.GetEnumerator();
	if (enumerator == null || enumerator.MoveNext() == false)
		return String.Empty;

	Type type = enumerator.Current.GetType();
	PropertyInfo propInfo = type.GetProperty(property);
	if (propInfo == null)
		throw new Exception(String.Format("Property '{0}' not found in collection", property));

	StringBuilder output = new StringBuilder();
	output.Append(propInfo.GetValue(enumerator.Current, null).ToString());

	while (enumerator.MoveNext())
	{
		output.Append(delimiter);
		output.Append(propInfo.GetValue(enumerator.Current, null).ToString());
	}

	return output.ToString();
}

public static string CollectionToString(object collection, string property, string delimiter)
{
	return CollectionToString(collection as IEnumerable, property, delimiter);
}

This method — which I typically apply to a StringHelper class whose namespace is added to my web.config (more on that another day) — can replace the Repeater code above as follows.

<!-- you must first expose the collection to the ASPX page as a public property -->
< %= StringHelper.CollectionToString("ListOfStates", "Name", ", ") %>

A similar method could be added to a helper class in Castle MonoRail to do the same without using foreach loops in your view.