Best practices for using relative paths in CSS, HTML and ASP .NET

In many cases, elements or controls on your page must refer to an external resource such as an image or another *.aspx file. To refer to these resources I prefer to use relative paths, because on my development machine the URL might be http//:localhost but on the production machine the URL might have a host header, like or an other port like http://localhost:81.

When using relative paths there are two kinds of elements

  • Not web server controls like (html tags, css statements etc.)
  • Web server controls (ASP .NET server controls)

Not web server controls (html tags, css statements etc.)

Always start an URL with a "/", no matter where you are in the tree structure, this relative URL will always work

  • "/Images/Logo.png"
  • <img alt="Logo" src="/Images/Logo.png" />

Don’t use:

  • "../" like "../Images/Logo.png"
  • "no slash at all" like "Images/Logo.png"


Web server controls

Always start an URL with a "~/"

  • "~/Images/Logo.png"
  • Response.Redirect("~/Pages/MyPage.aspx");
  • <asp:HyperLink ID="HyperLink1" runat="server" navigateUrl="~/Pages/Default.aspx">Test HyperLink</asp:HyperLink>

Don’t use:

  • "/" like "/Images/Logo.png"
  • "../" like "../Images/Logo.png"
  • "no slash at all" like "Images/Logo.png"



Best Practices – CSS

This post describes a code guidelines and a best practices for CSS, it will evolve over time and will be updated whenever I find new or better best practices.

CSS Files

  • Every web application should have a General.css, which contains only the classes that are used in more then 1 page or user control.
  • Every master page, page, user control, external library components should have there own css file.
  • Create a browser specific css file, if you have to use browser specific css hacks
  • All css files should be saved in a Css folder in the root of the web application.
  • The master page should first link to the General.css and then to the Master.css and then it should contain a contentplaceholder, so pages and user controls can add there css file to the head of the master page 
  • <asp:ContentPlaceHolder ID="head" runat="server"></asp:ContentPlaceHolder>

  • All pages and user controls should link to there own css file, by adding a link to there css file in the master page by implementing the contentplaceholder head from the master page:
  • <asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
        <link href="Css\Default.css" rel="Stylesheet" type="text/css" />



CSS Structure

  • Keep HTML free of presentational attributes, all styling should go in classes in css files.
  • Every class defined in CSS should be used, else it must be removed.
  • Every browser specific hack should be commented.
  • Don´t use a reset scripts except for padding and margin when working with a pixel perfect design, else external libraries might no work.
  • Don´t use css direct on html tags, only on html and body tag, all other tags should have classes, else external libraries might no work.
  • Don´t use important! only if you have to overwrite inline styling and then comment it.
  • Class name should start with lowercase and use camel casing.
  • Use only classes and avoid id´s, because asp .net will render a control in a usercontrol like ctrl001.
  • Order the classes within the css file based on the order of the markup. If the first element of the page is an <h1 class="titleHeader">, put it first.
  • Properties within a class should be in alphabetic order.
  • Set the font on the body tag, so other tags inherit from it.
  • Set classes on outer elements and use selectors to style the child elements.
  • Don’ t use * before property name, so don’ t use .exampleClass { *width: 20px; }

CSS Tips

  • If you want to add horizontal spacing between rows, use a border for the <tr> tag:
  • .rowWithBottomSpacing
        border-bottom: solid 5px #ffffff;

  • An example IE8 hack could be, a class that hides the dashed border arround a link when it’ s clicked:
  • .linkWithNoDashesOnActivation
        border-color: Red;
        border-style: solid;
        border-width: 1px;
        color: #000000;
        display: block;
        outline: none;
  • Note: a:hover MUST come after a:link and a:visited in the CSS definition in order to be effective.
  • Note: a:active MUST come after a:hover in the CSS definition in order to be effective.
  • Note: Pseudo-class names are not case-sensitive.
  • a:link {color:#FF0000}      /* unvisited link */
    a:visited {color:#00FF00}  /* visited link */
    a:hover {color:#FF00FF}  /* mouse over link */
    a:active {color:#0000FF}  /* selected link */
    a:focus {color:#00A0FF}  /* focused link */


Source and Related posts



Best Practices C#

This article will be updated every time I run into a best practice.

Checklist Microsoft Visual Studio 2008 Solution and Projects
– Solution name should be Customer.Product.sln, like MyCustomer.MyProduct.sln
– Project name should be Customer.Product.Subsystem.csproj, like MyCustomer.MyProduct.MySubsystem.csproj
– Setup project name should be Customer.Product.Subsystem.Setup.csproj, like MyCustomer.MyProduct.MySubsystem.Setup.csproj
– Test project name should be Customer.Product.Test.csproj, like MyCustomer.MyProduct.Test.csproj
– Create 1 C# project per subsystem and 1 folder per layer, like BC, BE, Dac, Common etc.
– Create 1 Setup project per subsystem
– Create 1 Test C# project per solution and 1 folder per subsystem in the test C# project
– For each project check the .NET version you want to build against (Target Platform)
– Enable “Check for arithmetic overflow/underflow” on debug build
– Disable “Check for arithmetic overflow/underflow” on release build

Checklist C# Projects
– Use String.Format to concatenate strings < 5
– Use StringBuilder to concatenate strings >=5
– Never use relative paths, because if an executable is called by an other executable or batch file located in an other folder the working directory is not the location of the executable.
  Always use

        public string AssemblyDirectory
                string codeBase = Assembly.GetExecutingAssembly().CodeBase;
                UriBuilder uri = new UriBuilder(codeBase);
                string path = Uri.UnescapeDataString(uri.Path);
                return Path.GetDirectoryName(path);

Or as property

        private static string _assemblyDirectory = null;
        public static string AssemblyDirectory
                if(_assemblyDirectory == null)
                    var codeBase = Assembly.GetExecutingAssembly().CodeBase;
                    var uri = new UriBuilder(codeBase);
                    var path = Uri.UnescapeDataString(uri.Path);
                    _assemblyDirectory = Path.GetDirectoryName(path);

                return _assemblyDirectory;

For difference Assembly.CodeBase en Assembly.Location, see msdn

Checklist Setup Projects

– Before editing anything on a setup project, check out the setup project by hand

– Change Author

– Change Description

– Change DetectNewerInstalledVersion to True

– Change InstallAllUsers to True

– Change Manufacturer

– Change ManufacturerUrl

– Change ProductName

– Change RemovePreviousVersions to True

– Change Subject

– Change SupportPhone

– Change SupportUrl

– Change Title

– Change Version

– Change Application Folder to “[ProgramFilesFolder][Manufacturer]\ProductName\[ProductName]”

– If you want to change the for “AddRemoveProgramsIcon” icon of the setup project:

           > Add a icon file to the primary output C# project

           > Open the C# project properties “Application” page

           > Select the icon file in the “Icon and manifest” dropdownbox

           > In the setup property grid select browse… > click on browse in icon dialog > select “All Files (*.*)” on the “Files of type” dropdownbox primairy > select primary output from “you’re C# project” as source for “AddRemoveProgramsIcon”

Don’t use a icon selected directly from filesystem, because you can get sourcecontrol binding problems for you’re setup project, unless it is added to the project it self in the same directory as de setup project file.


Every switch statement should have it’s own function.


In most cases a method should be on the object whose data it uses.


Every class should have it’s own unittest class.

Every method should have at least one unittest (happy path)


using System;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;

namespace Customer1.Product1.Test.Subsystem1.Bc
{ [TestFixture] public class DateTimeTester { [TestFixtureSetUp] public void FixtureSetUp() { Console.WriteLine("This code is executed before the first test in this class is executed"); } [TestFixtureTearDown] public void FixtureTeardown() { Console.WriteLine("This code is executed after the last test in this class is executed"); } [SetUp] public void Setup() { Console.WriteLine("This code is executed before the start of every test in this class"); } [TearDown] public void Teardown() { Console.WriteLine("This code is executed after the end of every test in this class"); } [Test] public void AddDays_HappyPath_AddedOneDayToDateTime() { // Arrange DateTime currentDateTime = new DateTime(2009,7,1); DateTime expectedDateTime = new DateTime(2009, 7, 2); // Act DateTime addedDateTime = currentDateTime.AddDays(1); // Assert Assert.That(addedDateTime, Is.EqualTo(expectedDateTime)); } } }

100% Coverage, even with read-only properties

If you have to write a unit test for properties that are read-only, like the  System.Configuration.ConfigurationManager.ConnectionStrings["Database1"
you can create a method with the read-only property as parameter. By doing that, you can have 100% coverage.

// Get connectionstring from App.config
string connectionString = GetConnectionString(ConfigurationManager.ConnectionStrings["Database1"])

public static string GetConnectionString(ConnectionStringSettings section)
           if (section == null)
               throw new NullReferenceException(@"No xmlnode <configuration>...<connectionStrings><add name=""Database1""... in App.config");
           if (string.IsNullOrEmpty(section.ConnectionString))
               throw new NullReferenceException(@"Xmlnode attribute <configuration>...<connectionStrings><add name=""Database1"" connectionString=""... was empty");
           return ConfigurationManager.ConnectionStrings["Database1"].ConnectionString;


If you want to log all the items in an string array, you can use the string.Join function:

string[] args = {"item1", "item2", "item3"};


Global Exception Handling in ASP .NET

As a good programmer, you normally want to catch all exceptions at the highest tier in code so that your program has the chance to display the error message to the user or log it to the appropriate location. Under ASP.NET, you can do so easily by overriding the Application_Error event in Global.asax file.

protected void Application_Error(object sender, EventArgs e)
    Exception ex = Server.GetLastError();

    // Perform error logging here...

    // Clear the error and maybe redirect to some other page...

Global Exception Handling in a .NET console application or Windows Service

Unfortunately, there is no concept of Global.asax in a winform or console application in .NET. In a windows form application, the equivalent global catch all is available by listening to the ThreadException and AppDomain UnhandledException events.

class Program
        static void Main()
            // Catch all unhandled exceptions in all threads.
            AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
        private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
                // Log error here or prompt user...
            catch { }

Global Exception Handling in a .NET windows application

class Program
        static void Main()
            // Catch all unhandled exceptions
            System.Windows.Forms.Application.ThreadException += ThreadExceptionHandler;

            // Catch all unhandled exceptions in all threads.
            AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;

            System.Windows.Forms.Application.Run(new MainForm());
        private static void ThreadExceptionHandler(object sender, System.Threading.ThreadExceptionEventArgs args)
                // Log error here or prompt user...
            catch { }
        private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
                // Log error here or prompt user...
            catch { }

Information found at


If you need to get the metadata of a type, use the typeof C# operator:


Chaining Constructors

Using chaining constructors if you define multiple constructors that use the (partial) same logic.

Constant and Read-Only fields

Use constant fields for fields that contain constant data that is know at compile time. Use read-only fields for fields that contain constant data that is not know at compile time and can be set in the constructor

The Is keyword

Use the Is keyword to check for correct type

Use thow; to rethrow an exception

This will preserve the original context of the exceptioin


Use the default keyword to get the default value for a type even generic types:

var result = default(T);

Garbage Collection

Don’t call the garbage collection from code, but if you have to (because you are managing a very large array or something), call garbage collection as follow:



Never use destructors or finalize methods, unless you have to, because you are using unmanaged code, because declaring destructors or finalize methods marks the object as finalizable and stores a pointer on the finalization que and then there is a whole story to tell,  but the point is, it is slower.


VMWare Workstation 6.5.2 (Fit all virtual machine memory into reserved host RAM)

Developmentimage MOSS installation guide
Microsoft Windows Server 2008 x64 sp1
  Use the defaults in the VMWare “Create Virtual Machine wizard”
    – 250GB harddisk
    – 2048MB ram
    – Virtualmachinename: DevMoss200904
    – Customize hardware: Removed floppy
– Windows customalization:
    – Disable the Windows Server 2008 Shutdown Event Tracker
    – Do not show Initial Configuration Tasks at logon
    – Set time zone (GMT+01:00 Amsterdam)
    – Provide computer name and domain (DevMoss200904)
    – Changed windows update to Microsoft update (Use recommended settings)
    – Disable screen saver
    – Change Time (HH:mm:ss) in Control Panel > Customize Regional Options > Time
    – Folder options and filesystem changes:
         – Don’t hide extensions for known file types
         – Don’t use Sharing Wizard
         – Added C:\Projects and C:\Temp
    – Add Web Server (IIS) role
         – All application development
         – All Health and Diagnostics
         – Security Windows Authentication
         – All Management Tools
– Run Microsoft Update
Microsoft SQL Server 2008 x64 Standard (Full installation, SSAS, SSIS, SSRS integrated with sharepoint)
    – Use DOMAIN\Administrator as service account for all services (because this is a development image)
    – Mixed Mode (SQL Server authentication and Windows authentication)
    – Add DOMAIN\Administrator and DOMAIN\Administrators to the SQL Server Administrators role
    – Add DOMAIN\Administrator and DOMAIN\Administrators to the Analysis Services Administrators role
    – Enable FILESTREAM for Transact-SQL access
    – Enable FILESTREAM for file I/O streaming access
    – Allow remote clients to have streaming access to FILESTREAM data
    – Install the Sharepoint integrated mode default configuration
– Run Microsoft Update
Microsoft Office 2007 Enterprise sp1 (Full installation, but skipped publisher,  some tools )
– Run Microsoft Update
Microsoft Visual Studio 2008 x64 with sp1 (Full installation, sp1 enables the dynamic data templates)
– Run Microsoft Update
Microsoft Office Sharepoint Server 2007 x64
     – Advanced installation
     – Complete – Install all componenets. Can add servers to form a sharepoint farm
     – Sharepoint product and technologies configuration wizard:
           – NO, I want to create a new server farm
           – Database server [.]
           – Enter username and password administrator account
           – Specify a portnumber [10000] for the Sharepoint central adminstration web application
           – NTLM (because this is a single server deployment, else we should use kerberos to avoid double hop problem)
Microsoft Office Sharepoint Designer x64 sp1
– Run Microsoft Update
Microsoft Internet Explorer 8
    – Turn off automatic phishing filter
    – Add http://<servername> to the local intranet sites
    – Disabling Internet Explorer Enhanced Security Configuration
    – Changed Google to default home page and default search provider
    – Don’t warn me when closing multiple tabs
    – Open only the first home page when Internet Explorer starts
– Run Microsoft Update
Microsoft Visual Studio extensions for WSS 3.0 v1.3 mar 2009 ctp
Microsoft Enterprise Library 4.1 – October 2008
Mozilla Firefox 3.0.8
LLBLGen Pro 2.60
WinRar 3.80
Redgate .NET Reflector
Jetbrains ReSharper 4.1
Notepad++ 5.3.1
– Run Microsoft Update