Putting your ASP.NET Web Application in Maintenance Mode (using ISAPI_Rewrite)

Prompted by @slace’s tweet:

I replied with a suggestion that we’ve used in the past. Aaron said I should blog about it… so here I am (again)!

A while ago we needed to do an Umbraco upgrade (from v3 to v4) on a production server – in my opinion it was a pretty major upgrade on a live site, we had done a couple of test upgrades on dev and staging, all was successful.  But since there was various parts of the site that we need to regression test, I felt it best to take the entire site offline whilst we upgraded.

Usually creating an “App_Offline.htm” page in the root of your web app is enough to take it offline.  However that was no good for testing… so what to do?

This is where ISAPI_Rewrite is your best friend, (or .htaccess to be precise).  We needed to configure the site to allow access for us and redirect everyone else to a “Site under maintenance” page.  I found a few examples across the web, but to save you all that hassle, here are the .htaccess rules that we use:

# BEGIN Maintanence Mode
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} !/offline.html$
RewriteCond %{REMOTE_ADDR} !^82.13.23.230$
RewriteRule ^(.*)$ /offline.html [R=302,L]
</IfModule>
# END Maintanence Mode

What does it do? The first “RewriteCond” rule checks that you are not requesting the “offline.html” page (otherwise you would end up in a constant loop!) The second “RewriteCond” checks the IP address of the visitor – in my case it was “82.13.23.230″ (remember to escape the dots). If those two rules aren’t satisfied, then the “RewriteRule” is used, redirecting the visitor to the “offline.html” page.

As always, I am open to any suggestions or improvements!

How to convert NameValueCollection to a (Query) String [Revised]

Following on from a comment on my previous post about converting a NameValueCollection to a (query) string – I have finally got around to revising my code snippet.  Now the method will handle same key multiple values, (it no longer comma-separates them).

I have also added extra parameters so that you can define your own delimiter (since the HTTP specification says that you can use both ampersands & and semicolons ;) and there is an option for omitting keys with empty values.

/// <summary>
/// Constructs a NameValueCollection into a query string.
/// </summary>
/// <remarks>Consider this method to be the opposite of "System.Web.HttpUtility.ParseQueryString"</remarks>
/// <param name="parameters">The NameValueCollection</param>
/// <param name="delimiter">The String to delimit the key/value pairs</param>
/// <returns>A key/value structured query string, delimited by the specified String</returns>
public static String ConstructQueryString(NameValueCollection parameters, String delimiter, Boolean omitEmpty)
{
	if (String.IsNullOrEmpty(delimiter))
		delimiter = "&";

	Char equals = '=';
	List<String> items = new List<String>();

	for (int i = 0; i < parameters.Count; i++)
	{
		foreach (String value in parameters.GetValues(i))
		{
			Boolean addValue = (omitEmpty) ? !String.IsNullOrEmpty(value) : true;
			if (addValue)
				items.Add(String.Concat(parameters.GetKey(i), equals, HttpUtility.UrlEncode(value)));
		}
	}

	return String.Join(delimiter, items.ToArray());
}

Umbraco: Ultimate Picker XSLT Example

Chatting with Dan (my partner-in-code at Bodenko) about the Ultimate Picker data-type in Umbraco, we realised that we couldn’t find any examples of how to use the data in XSLT. So obviously needing an excuse to write-up a new blog post, here we go.

If you need a quick overview about the Ultimate Picker data-type, see Tim Geyssens’ blog post.

For my example, using a default Umbraco install (with Runway), we will create a new data-type using the Ultimate Picker, (let’s call it “Runway Textpage Picker“), we select the ‘Database datatype‘ to be “Nvarchar”; the ‘Type‘ as a “List Box”; The ‘Parent nodeid‘ is “1048″ and the ‘Document Alias‘ filter is “RunwayTextpage” – we also tick the ‘Show grandchildren‘ checkbox.

Ultimate Picker Data Type settings

Ultimate Picker Data Type settings

Next, we will assign the new “Runway Textpage Picker“ data-type to a property of the Runway Textpage, we will call it “Related Content“. For more information about working with document types, please refer to the our.umbraco.org wiki page, (Working with document types).

Adding Ultimate Picker to a Document Type

Adding Ultimate Picker to a Document Type

Once the document-type is saved, we move on to the ‘Content‘ section. Select any of the Runway Textpages.  You should now see the ‘Related Content‘ property panel, containing a list of all the other Runway Textpages.  To select multiple items, hold-down the CTRL (or Command on the Mac) button.  When you have finished, click the ’Save and publish‘ button.

Selecting related content

Selecting related content

For the next part we get to the real meat of this blog post… the XSLT!

Create a new XSLT

Create a new XSLT

Create a new XSLT called “RelatedContent” (without the .xslt extension), keep it ‘Clean’ and tick the ‘Create Macro‘ checkbox.  Next a quick short-cut for you; copy-n-paste the following XSLT into the main editor window.

Copy-n-paste the XSLT

Copy-n-paste the XSLT


 ]>

	

	

	
		
					
					
					
						
					
		
		
		



	

Here’s a quick explanation of what is happening in the XSLT.  We are provided with a list of comma-separated nodeIds from the Ultimate Picker, which we need to parse/split – then pull back the XML node data, then we can transform it however we like!

The way we do this is 2-tiered, first we must loop through the comma-separated list, pulling back the XML node for each nodeId, adding it to an XSLT variable.  Doing this will cause the XSLT variable to be a fragmented node-tree, which means that we need to convert the node-tree fragment into a node-set.  (*Note: there are a gazillion ways to skin a cat – suggestions are welcome – this method works for me).

Once we have the complete XML node-set, we can transform into whatever HTML we  like.

Now that we are done with the XSLT, we can edit our template in the ‘Settings‘ section. Select the “Runway Textpage” template. Somewhere after the ‘bodyText‘ property item, click on the ‘Insert Macro’ button.  From the ‘Choose a macro‘ drop-down, select the ‘Related Content‘ option – this will add the macro code to the template.

Insert the macro into the template

Insert the macro into the template

Save the template and view the front-end page. We can now see the Related Content pages. Tada!

The end result! Related Content

The end result! Related Content

Robots.txt Editor for Umbraco

Following up on my recent post of using Robots.txt with Umbraco, I decided that it would be nice to be able to edit the robots.txt directly from the Umbraco back-end.  (Also I wanted to play a bit more with the BaseTree/ITree classes).

This afternoon I had a few hours to spare – actually I was procrastinating on another job, (don’t tell my client – I’ll finish it off later tonight) – so I got down to some coding.

The source-code is available on the Umbraco Extensions project (on CodePlex) and created a project page on the new Our Umbraco community website. (Don’t forget to give it some karma points! ;-p)

A direct download to the package installer (zip) is available here.

Very special thanks to Dave Kinsella for providing the Robot icon robot icon – although I know Dave, it was great proof that Twitter really does work! (Thanks to Dan too for his Johnny 5 attempt! 18480255 – Here was my attempt too!  18484040)

If you have any bugs, comments, feedback or suggestions – please feel free to get in touch with me via the Our Umbraco forums.

Add YouTube Plug-in to Umbraco/TinyMCE

Update: Following on from Dirk and Ismail’s comments, I found out that this YouTube plug-in does not work with TinyMCE v3 (which is the default richtext editor in Umbraco v4). This guide is written to works  for Umbraco v3 only, (using TinyMCE v2).

If you are looking for similar functionality in Umbraco v4, (TinyMCE v3), then all you need to do is enable the ‘Flash/media’ button in your Richtext editor data-type and embed the YouTube video like any other Flash movie (swf) – more details in my comment below.

/End of update.


Recently one of my clients wanted the ability to insert YouTube video clips directly into the TinyMCE editor within Umbraco.  My initial thought was to create a macro that would take a YouTube video URL, parse it and display it on the rendered (front-end) page.  Tim G has a blog post on how to do this on his Nibble blog, (love the Surfin’ Bird reference).

This approached worked fine, but we ran into problems trying to edit the YouTube video URL, along with that, my client had an additional step of selecting a macro, then entering the YouTube URL.

After a little researching, I eventually found a native YouTube plug-in for TinyMCE, (direct link to ZIP download).

Here is how I integrated in Umbraco:

  1. Extract the contents of the youtube.zip archive to your /umbraco_client/tinymce/plugins/youtube/
  2. In your /config/ folder, open the tinyMceConfig.config file.
  3. Insert the following lines:
    After the last <command /> entry, add…
    
    	mceYoutube
    	../umbraco_client/tinymce/plugins/youtube/images/youtube.gif
    	youtube
    75
    

    Then after the last <plugin /> entry, add…

    youtube
  4. Once the XML config entries are in place, you will need to restart the  Umbraco application – the quickest way of doing this is by modifying your Web.config, (literally open it, add a space, remove it, hit save).
  5. The YouTube button is now available in Umbraco. However, it’s not quite ready yet, there is still one more step!
  6. In your Umbraco back-end, go to the “Developer” section, expand the “DataTypes” folder and then select “Richtext editor”. In the “Buttons” section you should see a YouTube icon. Check the box next to the icon, and you’re done! If you don’t see the YouTube icon, then check that the did the config steps above, and/or check that the read permissions are set correctly on your /umbraco_client/ folder, (re-apply them if needs be).

Robots.txt for use with Umbraco

I originally posted this over at the Our Umbraco community wiki. [Robots.txt for use with Umbraco] I am only posting it on my blog as a cross-reference. The Our Umbraco wiki version will evolve with the community’s experience and knowledge.

The Robots Exclusion Protocol has been around for many years, yet there are a lot of web-developers who are unaware of the reasons for having a robots.txt file in the root of their websites.

There have been many rumours around whether the bigger search engine crwalers (i.e. Googlebot) consider your website amateurish if you didn’t have a robots.txt – and if handled badly, could lead to your site being invisible on SERPs.

If you are happy for a crawler to crawl/index all of your website’s content, then you can use the following:

User-agent: *
Disallow:

However, when using Umbraco to power my websites, it is preferable to define which folders are accessible by the crawler. Personally, I would not like to see the contents of my /umbraco/ folder to be returned in Google’s SERPs.

Here is an example of the robots.txt that I have used on several Umbraco-powered websites.

# robots.txt for Umbraco
User-agent: *
Disallow: /aspnet_client/
Disallow: /bin/
Disallow: /config/
Disallow: /css/
Disallow: /data/
Disallow: /scripts/
Disallow: /umbraco/
Disallow: /umbraco_client/
Disallow: /usercontrols/
Disallow: /xslt/

From my perspective, there is no reason for a search engine crawler to be crawling/indexing files from any of the above folders – you may have a different perspective, to which you can amend your robots.txt accordingly.

For more information about the robots.txt standard, please refer to the official website: http://www.robotstxt.org/robotstxt.html

Source Code Released for User Control File Tree Umbraco Package

A few months ago I released the User Control File Tree package for Umbraco, this allowed developers to edit the front-end code/mark-up in their ASCX user-controls from the Umbraco back-end, (remotely), rather than editing them directly on the server via a text-editor.

Today I have released the source-code on the Umbraco Extensions project (on CodePlex) and created a project page on the new Our Umbraco community website.

If you have any comments, feedback or suggestions – please feel free to get in touch with me via the Our Umbraco forums.

MySql data-source support for ELMAH

Following on from my last post (a couple of months ago) about Integrating ELMAH with Umbraco, I received a comment if it was possible for ELMAH to use MySQL as a back-end data-source.

After a few emails back and forth between myself and Rajiv, (as well as Rajiv’s requests over at the ELMAH support group), the advice was to simpily develop some code that implemented the ErrorLog class, (making use of the 3 core methods: Log, GetError and GetErrors). Rajiv make a start with this code, but ran into a few problems, (mostly because he was trying to reference methods/properties that were internal to the core Elmah.dll).

Given that I’d said it was quick and easy to develop this code, I best put my money (or time in this case) where my mouth was. Fifteen minutes later the code was written… and then another hour later, the code was tested and bugs fixed.

For the MySQL connectivity, I used the MySql.Data.MySqlClient connector. For the MySqlErrorLog, I followed the code/design patterns that Atif had used in both the core SqlErrorLog and SQLiteErrorLog classes.

I have uploaded the Visual Studio (2008) solution to the ELMAH support group file repo (here is a direct link to the ZIP) – you will need to compile the DLL from the solution. If you need a pre-compiled DLL, then give me a shout, I’ll sort something out.

Once you have the compiled Elmah.MySql.dll, you will need to add following to your Web.config file:

In your <elmah> section, change the <errorLog> to: (if you haven’t installed ELMAH before, please see the WebBase)


In the VS2008 solution, there is a script called MySql.sql – run this against your database to create the new table (called “elmah”) needed to log the errors/exceptions.

Then add your MySql connection string in the <connectionStrings> section:


It is very important that you call the connection string “ELMAH_MySql” – as this is hard-coded in the backend. (Let me know if this is a problem, I think it could be moved to the <errorLog> section?)

Once you have saved the changes to your Web.config, you are all set to use MySql as your ELMAH back-end data-source!

Known issues:

  • The “Sequence” column in the “elmah” table should be auto-incremental, but it isn’t! (I don’t claim to know enough about MySql to have multiple auto-incremental columns) – any suggestions?
  • The MySql connection string is hard-coded as “ELMAH_MySql
  • The code will only compile with .NET 2.0 and above (no support for .NET 1.0 or 1.1 – sorry)

I have mentioned to Atif about the possibility of including MySql support to the ELMAH core – to which he is willing to do, only if I support it. Which I will be happy to do – but only if there is a need for it.  So my suggestion would be, if you would like to see MySql support in the ELMAH core – then raise a feature request on the ELMAH Google Code site. Once it gains momentum, we’ll take it from there.

Integrating ELMAH with Umbraco

Update: For the latest details on how to integrate ELMAH with Umbraco, please read the article over on the Our Umbraco wiki.

I have a few Umbraco projects that have a lot of custom .NET code, mostly in they are in the form of user-controls and XSLT extensions.  As far as I’m aware Umbraco doesn’t have an extendable mechanism for exception handling and sending out notification emails, (there is the umbraco.BusinessLogic.Log, which writes to the umbracoLog table in the database, but that’s all).

Initially I looked at Tim Gaunt’s Advanced Error Reporting – a great drop-in solution that does exactly what it says on the tin!  Whilst reading the comments on Tim’s blog, Simon Dingley reminded me of the ELMAH project – which has been one of those web-applications that you keep meaning to try out, but never get around to.

What is ELMAH?

ELMAH (Error Logging Modules and Handlers) is an application-wide error logging facility that is completely pluggable. It can be dynamically added to a running ASP.NET web application, or even all ASP.NET web applications on a machine, without any need for re-compilation or re-deployment.

So I decided to see how nicely it plays with Umbraco… the result, it plays very nicely indeed.

If you are interested, here’s how…

  1. Download the latest ELMAH binary release (1.0 Beta 3 at the time of writing [direct link]) from the Google Code project page. (http://code.google.com/p/elmah/)
  2. Extract the files from the ZIP.
  3. Select the DLLs from the “/bin/” folder, for Umbraco you’ll be using the DLL from “/bin/net-2.0/Release/“.  For the benefit of this post, I decided to use the SQLite option to store the error logs in a database. I could easily have used the SQL Server or VistaDB options.
  4. Drop the DLLs into the “/bin/” folder of your Umbraco installation.
  5. Open the web.config of your Umbracoo installation and add the following lines:

Add the following to your <configSections> section:

<sectionGroup name="elmah">
	<section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah"/>
	<section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah"/>
	<section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah"/>
	<section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah"/>
</sectionGroup>

Add the following just after the </configSections> section:

<elmah>
	<security allowRemoteAccess="yes" />
	<errorLog type="Elmah.SQLiteErrorLog, Elmah" connectionStringName="ELMAH.SQLite" />
	<errorMail from="no-reply@domain.com" to="webmaster@domain.com" />
</elmah>

Add the following to your <connectionStrings> section, (if you have one, otherwise create one):

<add name="ELMAH.SQLite" connectionString="Data Source=~/data/errors.s3db"/>

In the <httpModules> section, add this:

<add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah"/>
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah"/>
<add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah"/>

… and finally, in the <httpHandlers> section, add this:

<add verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah"/>

If you run into any trouble, there is a more detailed guide on Setting Up ELMAH from DotNetSlackers.

By now you should have ELMAH up and running.  Open up your web-browser and go to http://localhost/elmah.axd, (obviously replace “localhost” with whatever your hostname is). You should see the ELMAH Error Log page.  Since this is open to the public, you may want to secure it, see the Securing Error Log Pages article for further details.

The last part is to integrate the ELMAH Error Log page into the Umbraco back-end.  I created a new user-control in the “/usercontrols/” folder called “ELMAH.ascx”, using the following HTML:

<%@ Control Language="C#" %>
<iframe height="98%" width="100%" scrolling="auto" src="elmah.axd" style="margin-top:5px;"></iframe>

Then in the “/config/Dashboard.config” configuration file, I added a new section for the developer area.

<section>
	<areas>
		<area>developer</area>
	</areas>
	<tab caption="Error Logging Modules and Handlers for ASP.NET">
		<control>/usercontrols/ELMAH.ascx</control>
	</tab>
</section>

Now in the Umbraco back-end the developer area looks like this.

ELMAH in Umbraco

I have been very impressed with how well ELMAH functions.  Aside from the essential email notifications, the RSS feeds are a great bonus!

kick it on DotNetKicks.com

Umbraco Package – User Control File Tree

A few months ago, Tim Geyssens (aka Umbraco rockstar) released a package that gave you access to the *.config files in the /config/ folder. This has been a lifesaver in those few times where I have only had web-access to an Umbraco install and needed to tweak some config settings.

Recently I found myself in the same situation, but this time I needed to quickly update a few text changes to an ASCX user-control. Due to the nature of the .NET user-controls in Umbraco, there is no native way of accessing/editing those files via the admin back-end.

… well, not until now …

Following on from Tim’s lead with the Config Tree package, I have developed a “User Control File Tree” for the developer section.

This package displays all the user-control files (from the /usercontrols/ folder), allowing you to edit the front-end ASCX code/mark-up.  (There is no way to edit the code-behind using this package).  If the user-control contains any inline code (C#/VB.NET), then you will be able to edit it.  Keep in mind that there is no validation when editing the user-controls – so please be careful!

You can download the User Control File Tree package from here.

I created the package using Umbraco 4′s built-in Create Package Wizard. Please note that this package has been designed to work with v4 (and above), apologies to v3 users.

To install the package, go to the Developer section of your Umbraco back-end, expand the “Packages” folder, click on “Install local package”, select the “User_Control_Files_Tree_1.0.zip” (from wherever you saved it) and press the “Load Package” button.  Follow a few more on-screen steps and you’ll be done in no time!

If you have any problems with it, please do let me know – either by leaving a comment here or posting a thread on the Umbraco forum.

Convert XmlReader to String

I was in the middle of developing a member look-up AJAX function for an Umbraco project, when I ran into a slight problem, (confusion rather), about how to pull the XML back from SQL Server and return it to the browser (AJAX).

The SQL statement was straight-forward, very simple, does a LIKE query against the members table, no problem there. Added “FOR XML AUTO” to return the result-set back as an XML data-type … all going well so far.

Umbraco makes use of Microsoft Data Access Application Block‘s SqlHelper class, so I followed the same pattern.

XmlReader reader = SqlHelper.ExecuteXmlReader(connection, CommandType.Text, "SELECT n.id, n.text, m.Email, m.LoginName FROM cmsMember AS m INNER JOIN umbracoNode AS n ON m.nodeId = n.id WHERE n.text LIKE '%' + @query + '%' FOR XML AUTO", new SqlParameter[] { new SqlParameter("@query", query) })

At first I tried to return the XML as a String by calling XmlReader‘s GetOuterXml() method. But it returned nothing. After a lot of googling, (of converting an XmlReader to a String), I found a suggestion of iterating through the XmlReader, appending the current node to a StringBuilder.

Here’s what I ended up with…

using (SqlConnection connection = new SqlConnection(umbraco.GlobalSettings.DbDSN))
{
	using (XmlReader reader = SqlHelper.ExecuteXmlReader(connection, CommandType.Text, "SELECT n.id, n.text, m.Email, m.LoginName FROM cmsMember AS m INNER JOIN umbracoNode AS n ON m.nodeId = n.id WHERE n.text LIKE '%' + @query + '%' FOR XML AUTO", new SqlParameter[] { new SqlParameter("@query", query) }))
	{
		if (reader != null)
		{
			StringBuilder sb = new StringBuilder();

			while (reader.Read())
				sb.AppendLine(reader.ReadOuterXml());

			return sb.ToString();
		}
	}
}

return String.Empty;

I hope it helps… any improvements and suggestions are welcome!

Tagged by Techn0tic

After years of dodging this tag-the-blogger (“you’re it!“) memes, it was Dave Kinsella who finally got me. So with pure fear of a little dead girl climbing out of my TV in 7 days time, I’ll continue the chain.

If you are tagged you should:

(a) republish these rules

  • Link to your original tagger(s) and list these rules in your post.
  • Share seven facts about yourself in the post.
  • Tag seven people at the end of your post by leaving their names and the links to their blogs.
  • Let them know they’ve been tagged.

(b) share seven facts about myself

  1. People believe that “North by Northwest” is my favourite film, but it’s actually “The Big Lebowski
  2. Known as a Scouser, apart from being born in Liverpool, I have only lived there for 10 years, (I’m 30-years old).
  3. The remaining 20 years (so far) I have lived in the following places; Northop/North Wales, Hindley/Wigan, Westhoughton/Bolton, Columbo/Sri Lanka, Alicante/Spain and Backwell/Yatton/Bristol.
  4. Although I did a Computer Science degree, I consider myself to be a self-taught developer.
  5. Embarrassingly, I have calculated the VAT wrong 3 times on issued invoices! – how difficult can it be?
  6. I was in a band called Tremor… we played at The Cavern, Matthew Street!
  7. Made a few short films, which have been shown at the FACT and regularly on Canvas @ 3345 Parr St.

(c) Tag seven people. This is difficult, I don’t even know 7 bloggers, but here goes:

Seven days…

Using jQuery to swap form fields

Due to an technical decision early on in my project, the date-of-birth field on a profile edit page in a single text-input element.  My client would now like the date-of-birth to be 3 dropdown lists, (day, month and year).  The amount of work involved making changes to both the back and front ends would take at least a day. (It sounds a lot, but you know it would).

Here’s where a front-end developer’s best friend comes in… use jQuery to:

  1. create the 3 dropdown lists
  2. hide the original text-input field
  3. update any changes [to the dropdown lists] back to the original text-input.

After 15 mins of building up the HTML output string in JavaScript, here’s the code:

$(document).ready(function()
{
	var $dob = $('input#ProfileEdit_DateOfBirth');
	if ($dob.length > 0) {
		var dob = $dob.val().split('-', 3);
		var html = '


 ';

		var months = new Array('January','February','March','April','May','June','July','August','September','October','November','December');
		html += '


 ';

		var thisYear = new Date().getFullYear();
		html += '


 ';

		$dob.after(html).css('display','none');

		$('select.dob-date').change(function(){
			$dob.val($('select#dob-year').val() + '-' + $('select#dob-month').val() + '-' + $('select#dob-day').val())
		});
	}
});

Any changes to the <select> elements trigger the jQuery .change() event, which are then updated back in the original text-input field.  The server-side code (in this case ASP.NET) is non the wiser.

So there you go, that’s my quick-n-dirty approach to using jQuery to swap form fields.

Populating multiple DropDownList controls with generic ListItem array

I’ve just had some fun spending the last half-an-hour trying to figure out why when I used the SelectedValue property of a DropDownList, it also set the value of another DropDownList control.

Here’s some background to the problem.  On my web-form, I have 2 fieldsets, one for a “Start Date”, the other for an “End Date”.  For each fieldset there are 3 DropDownList; Day, Month and Year.

Now rather than populating the values declaratively, using <asp:ListItem>; since the year values will need to be incremented annually. I opted to do this programmatically in the code-behind.

Here was my code (for the Day DropDownList):

List days = new List(32);
days.Add(new ListItem("Day", "-1"));
for (int i = 1; i <= 31; i++)
	days.Add(new ListItem(i.ToString(), i.ToString()));

// start date
ddlStartDateDay.Items.Clear();
ddlStartDateDay.Items.AddRange(days.ToArray());

// end date
ddlEndDateDay.Items.Clear();
ddlEndDateDay.Items.AddRange(days.ToArray());

So, whenever I tried to set the value of ddlStartDateDay.SelectedValue, the value of ddlEndDateDay would also change. So frustrated!

What I soon realised that when I was adding new ListItem objects to the List<ListItem>, it was creating a unique (internal) ID for each ListItem. Therefore when I was selecting the value for one DropDownList, it was selecting it across all DropDownList controls that contained that ListItem!

I've refactored my code to the following:

ddlStartDateDay.Items.Clear();
ddlEndDateDay.Items.Clear();

ddlStartDateDay.Items.Add(new ListItem("Day", "-1"));
ddlEndDateDay.Items.Add(new ListItem("Day", "-1"));

for (int i = 1; i <= 31; i++)
{
	ddlStartDateDay.Items.Add(new ListItem(i.ToString(), i.ToString()));
	ddlEndDateDay.Items.Add(new ListItem(i.ToString(), i.ToString()));
}

I'm not sure if there is any performance difference with this approach, I was just trying to use a single generic array (of ListItem) to populate multiple DropDownList controls. Obviously, this has it's own drawbacks.

Lil’ Bookninja

After helping Bookninja with their blog, George kindly sent me some swag, (or “thanking him by advertising on his chest“, as George put it), including a baby-grow (we were expecting Katelyn at the time).

Well, 9 months later, Katelyn fits into the baby-grow (she’s always been quite a small baby).

lil' bookninja

I promised George that I’d sent him a photo when we got chance.  It was by pure coincidence that I was wearing a WordPress hoodie! (I’m such a geek!)