Wednesday, 29 August 2007

IE Renders Spurious '#text' Node as a Gap

I today had the misfortune of discovering IE6/7 doesn't like to display relatively "normal" HTML.

Take a look at this code and the bolded DIV in particular:

<html><body>
<style>
* { padding: 0px; margin: 0px; }
img { border-width: 0px; }
</style>
<div>
<img src="pic.gif" />
</div>
<div style="width: 285px; height: 100px; background-color: Green;">
<p>Other stuff</p>
</div>
</body></html>

This renders a lovely gap between the image in the top DIV and the bottom DIV:

Inspecting the DOM using the IE Developer Toolbar reveals IE is interpreting and rendering a spurious text node from the markup:

As you might expect, Firefox has no issues with this and renders the two DIVs one on top of the other with no gap between.

Despite average CSS skills, I was unable to style this into submission without completely mangling the existing code and CSS (simply adjusting the DIV's height had no effect); instead I managed to get around this by simply removing all whitespace between the opening and closing DIV tags:

<div><img src="pic.gif" /></div>

Since the image in this example is essentially functioning as background image for the DIV, I could have alternatively set its background-image to the URL of the image.

Tuesday, 28 August 2007

Generating Public, Strongly-Typed Resource Classes with Visual Studio

Visual Studio 2005 does a great job of managing your .resx files and automatically generates strongly-typed classes exposing the contents of those files. If you create a new resource file and add it to your project you'll notice the Custom Tool property has a value of ResXFileCodeGenerator to suppor this behaviour.

This is generally all well and good but there is a catch: ResXFileCodeGenerator generates classes with members marked internal; in other words, you won't be able to access your resources using the generated class if you're working in another project (i.e. another assembly).

The resgen.exe tool does all the hard work behind the scenes and does have a flag called PublicClass that will override this behaviour--set this flag and your classes will be generated with public visibility. Unfortunately you can't run this tool automatically until compile-time, which means your resources won't be as conveniently accessible as they are by default; you'll also have to write a post-build script or use another method to do all the hard work moving your generated files around.

Luckily Visual Studio 2008 solves this problem by allowing you to set the Custom Tool property to PublicResXFileCodeGenerator. As the name suggests, the generated methods come out the other end marked as public and this all happens from within Visual Studio.

If you really can't wait for Visual Studio 2008 (and it's not far away), you may want to look into a handy little extension called ResXFileCodeGeneratorEx. In addition to allowing you to create publically-accessible, strongly-typed classes for your resources, it also helps out if you're dealing with format strings in your resource files. The only downsides I can think of are the fact that Visual Studio 2008 will make this tool less necessary (apart from the format bit) and that every developer will need to install it on their machine. No biggie but the sort of thing that can cause headaches for new developers joining your team.

Wednesday, 15 August 2007

IE Dev Toolbar Stops Working (IE7)

The IEDevToolbar is a great help for web-based development with Internet Explorer. I've been using it since it was in beta and while it generally does the job, the bugs have been ever-present in different forms.

One of the latest things I've noticed is how the toolbar seems to stop working (generally after refreshing a page that's changed at the server). The menu options are greyed out and clicking with the pointer refuses to select any page elements. Closing the toolbar and re-opening it again fixes the problem but there is a better way.

For some reason, the toolbar doesn't always automatically refresh itself. As a result the DOM tree represented in the toolbar doesn't match the DOM tree in the browser. Closing and re-opening the toolbar synchronises the toolbar with the page but this can also be accomplished but hitting the IEDevToolbar's Refresh button (one of several icons that don't make a lot of sense at first glance). The menus function once again and page elements are clickable. Why this doesn't always happen automatically is beyond me.

ebay Retailers That Suck

My wife bought an MP3 player a few weeks ago and after deciding she wanted an arband for it we hunted around and finally found a workable version on ebay from Accstation (www.accstation.com). We won the auction at $0.99 and after adding a few dollars shipping and handling, it looked like yet another successfull online transaction. Then came the payment part.

Accstation uses a third-party company to process credit card payments (they also offer payment via PayPal). I opted to use the credit card payment method, completed the online forms, and clicked the submit button: transaction failed. Okay... I thought, probably just a temporary problem with their servers or a network issue, let's try again. Same error. Well, I thought, since I'm seeing this error, the transaction surely can't be reaching the payment gateway; let's start again from the beginning and double-check all my details. Transaction failed. Okay, I'm fed up now... one last time for the fun of it and I'll call my credit card company. Transaction failed.

At this point I give MasterCard a buzz to ensure my card hasn't been blocked and I have sufficient funds to pay the lousy $0.99 + S/H. The representative tells me everything is fine with the card and my account BUT four transactions just went through for the same amount. They haven't been approved yet but there are my four failed transactions. Blow gasket now.

I email the seller directly, I send the seller a note via ebay, I email the credit card processing company. Accstation's autoresponder autoresponds with a useless email message and the credit card processing company refuses to take any responsibility for this fiasco, despite their involvement in processing my payment four times over. A day passes while I wait for a response from Accstation and then another day and another day. I browse their web site and email their accounts department, their sales department, their customer service department, and their auctions department. MasterCard tells me they can't do anything until the transactions are approved.

Someone finally replies and asks me to email them back with my credit card number, expiry date, amount, etc and they'll get back to me within three to five days. There's no way I'm going to send a mysterious bot my credit card details via email and they shouldn't require that information anyway. I never hear back from "Tammy."

In the end, ebay notifies me I won the auction and must pay up before the week is out or I'll be stricken down by the Internet gods. The four payments finally disappear from my MasterCard account and I log in to Accstation's payment system to pay my $0.99 bill, this time via PayPal. The planets align and this time everything works... a week later and my wife has her arm band.

$0.99 plus shipping and handling works out to very little profit for Accstation but I did not hesitate to leave a negative feedback rating on ebay and there's no way I'll ever buy anything from this company again. For the minimal effort it takes to reply to an email from an upset customer, the end result could have been a win-win situation.

Friday, 10 August 2007

The Final Effect

Friday morning and the cummulative effects of yet another change request set in...

Thursday, 9 August 2007

Creating a Custom CultureInfo

The System.Globalization.CultureInfo class comes with a number of pre-defined cultures but thankfully Microsoft recognises it hasn't supplied all culture/language combinations (real or imagined) and will allow you to build your own. One way you can do this is using the System.Globalization.CultureAndRegionBuilder class.

We ran into trouble while attempting to localise the westernaustralia.com English-language sites targeted at specific regions (we've got a "global" EN site, a "domestic" AU site, and UK, NZ, and SG variants). .NET 2.0 (and Windows XP/Server 2003) define CultureInfos and locales for all of our language/region combinations except for Singapore; the closest in-built option we could find was zh-SG (which is Chinese/Singapore). Although we could have cheated and used zh-SG, we're also running a number of foreign-language variations of the site; to be explicit, avoid confusion, future-proof this aspect of the site, and--most importantly--to make use of .NET's resource fallback mechanism (from en-SG to en), we decided to define a custom CultureInfo.

While creating a new CultureInfo isn't a difficult task, it's not as easy as supplying an "en-SG" string to the CultureInfo constructor or deriving a new class from the CultureInfo class (you can derive a new CultureInfo from an existing CultureInfo, however).

CultureInfo ci = new CultureInfo ("en-SG"); // This will fail at runtime
internal class MyCultureInfo : CultureInfo // There's an easier way...

MSDN provides a succinct article on building a custom CultureInfo class and trust me, it's really quite easy. The article fails to mention that you need to add a reference to the sysglobl assembly to gain access to the CultureAndRegionBuilder class so as long as you remember that step you should be fine. The sample provided also prefixes the new CultureInfo with "x-" and I think this is a great idea: doing so should avoid any conflict when you move to the next version of the .NET Framework or a new platform. Vista and, presumably, Server 2008, include the en-SG locale so naming our new CultureInfo "x-en-SG" means we can anticipate a smooth transition if the existing wa.com code is ever moved to Windows Server 2008.

You don't need to create and register a new CultureInfo every time your application runs (and you probably don't want to since the CultureInfo is written to the filesystem when its registered and invoking Register () again will fail) so we've built the create/register code into our deployment script. We simply try to unregister the existing CultureInfo, create x-en-SG from scratch based on the en-US CultureInfo and the SG RegionInfo, and register the new CultureInfo. Our English-Singapore .resx files reflect the new CultureInfo and are named as though x-en-SG were an in-built CultureInfo: *.x-en-SG.resx.

Broken .resx files in Visual Studio 2005/.NET 2.0

Working with resources is so much easier in .NET 2.0 but things do occasionally go awry. Most of the time the problem is really easy to fix.

As discussed in another post, Visual Studio 2005 does a lot of work behind the scenes to surface your resources as strongly-typed objects; if you're not careful with your .resx files, however, you might end up in a situation where your .resx files aren't being compiled for you. As a result, you lose Intellisense for your resources, the ResourceManager may end falling back to your default resource file when it shouldn't, or your default resource file might not load at all. Copying and renaming Visual Source Safe-controlled .resx files is one little culprit that occasionally brings everything to a halt.

It's important to remember Visual Studio doesn't just "do" things for you--it must be told what to do and frequently relies on stand-alone tools included with .NET or sitting outside of the VS shell. A good example of this can be seen by inspecting the property sheet (in Visual Studio) for one of your .resx files.

When you create a new .resx file Visual Studio does all the right things by setting the Build Action to "Embedded Resource" and setting the Copy to Output Directory as "Do not copy". Just as importantly, Visual Studio also sets the Custom Tool property as "ResXFileCodeGenerator" and this particular setting can occasionally get stripped away when you're renaming or moving resource files. If in doubt and your resources are not being made available to your application, check this property; if it's not set, set it to "ResXFileCodeGenerator".

If the Custom Tool property on your .resx files is set correctly, Visual Studio will help you out by running the specified tool for you every so often to ensure your resources are available programmatically (this can actually be a pain in the neck sometimes so I recommend using a tool like Resourcer to edit your resource files...). If this isn't happening, you can simply right-click on your .resx file in Visual Studio and select Run Custom Tool.

Tuesday, 7 August 2007

Visual Studio 2005 ASP.NET Development Server (Cassini) and HttpCachePolicy

The ASP.NET Development Server (aka Cassini) included with Visual Studio 2005 (the default web server for new web projects) doesn't honour cache headers emitted using the HttpCachePolicy class.

Inspecting the response headers from a page running in Cassini reveals the Cache-Control header is set private despite instructions to make the response publically cacheable:

context.Response.Cache.SetExpires (DateTime.Now.AddSeconds (30));
context.Response.Cache.SetCacheability (HttpCacheability.Public);
context.Response.Cache.SetValidUntilExpires (true);
context.Response.Cache.VaryByHeaders ["Accept-Language"] = true;


Just as importantly, no Vary header is sent down the line.

By contrast, running the same page in IIS sets the Cache-Control header public and the Vary header is set to Accept-Language, as intended.

The ASP.NET Development Server is a great help--when it works; when it does stuff like this it really throws a spanner in the works.