Setting Up Development Environments Based on Life Cycle
When working in an enterprise environment, invariably, you’re going to need to have a process for developing and eventually deploying applications to production. This is an important and potentially stressful process in development because it affects live data, business up-time, and highlights any failures in the development of the project.
There are several ways to structure the process of eventually deploying software applications and much of this depends on the business commitment to the software being developed. I will attempt to discuss what stages in the development life cycle to base environments on regardless of the software development life cycle (SDLC) methodology being employed.
Three-Stage Environments
We’ll start with what I consider to be the most basic, yet still complete, environment setup. This will consist of three physical (or virtual) boxes labeled: Development, Staging, and Production.

Development – This box will contain the latest and greatest (and most unstable) code which developers are currently deploying to or working against. This is not the local/client-development environment which developers write code against (Visual Studio for example). This is a stand-alone box which has the development-grade code deployed to it. This box may have a database associated with it which either resides on the same box or on another physical (or virtual) box within the development realm. Often times, developers using their local development environment (their client computer) may talk directly to this Development database instead of their own local versions.
Staging – This box contains the expected production-level code which may need to be tested or accepted prior to being deployed to production. The staging environment should be an exact replica of the Production environment in regards to physical attributes of the server and software installed on the server. Using three-stage environments, the staging server also acts as the quality assurance (QA) and user acceptance testing (UAT) environments. Once all tests are completed and the code is officially accepted, the code may be deployed again to staging to test the deployment procedure in a mock production deployment test. This procedure will be repeated when it is time to officially deploy the application to production.
Production – This box is the live system which end-users perform their work in. The production environment is always the oldest, and hopefully, most stable environment. Code changes, including bug fixes or patches, should only be deployed to production after they’ve been tested by the development team in the Development environment, and test/approved via the Staging environment before being deployed to production. In most cases, including Agile development, the production environment should not be in constant change because of the increased instability this often causes.
Pro’s
There are several pro’s to setting up three-stage environments; primarily, they’re fairly easy and more inexpensive than other models while still giving you much of the benefit of life cycle environments. Having a limited number of environments means lower maintenance of the machines, fewer licensing costs, and less backup responsibilities to worry about.
Con’s
Of course, this comes with a few trade-offs. Primarily, in larger environments where there is a dedicated QA team that can perform automated tests on the code which may be processor or bandwidth intensive can significantly impact the performance of an environment. This can therefore impact UAT testing or approval demonstration. Additionally, many QA software packages require additional software to be installed on the box which then somewhat invalidates the cardinal rule of Staging and Production environments being the same.
An additional note on Staging and Production being the same:
Even though we attempt to make Staging and Production exactly the same, there will be some differences; such as where the box sits relative to the network (firewalls, etc.), user permissions, production-specific data, etc. It is at your discretion which of these things need to be the same and which can be different; just keep in mind, the more differences, the more likely a failure during production deployment will occur.
Distributed Systems
Setting up environments based on the development life cycle for distributed systems is similar just on a larger scale. Instead of three boxes, you may have specific boxes for each layer in your architecture (such as Data Access, Business Logic, Presentation, etc.). In this scenario your environments would simply contain more physical (or virtual) boxes.

Implementing Data Access Interfaces and Stubbing
This article describes one approach on how to interface a data access layer to allow for multiple data access platforms including a stubbed data access layer. I’ll show how to link up a stubbed layer and an MS SQL layer which both utilize the same interface contract.
Sounds Great! What Are We Doing Again?
It sounds a bit more complicated than it really is. Sometimes, especially when you are working with a large project, or one that is very critical (core to the business), you may want to take some additional considerations in your data access architecture. In enterprise applications, or in applications where you may foresee wholesale changes in the data access in the future, you may want to create an interface for any data access to follow in case you decide to switch from SQL to Oracle for example.
Another good use is when you want to “stub” the data access layer. Stubbing is simply when you return fake hardcoded data that emulates what you would expect the data access layer to really return. At first, it sounds like stubbing doesn’t really do anything. But there are many purposes, stubbing allows you to split work between developers (or teams) between layers; allowing one developer to work on the business logic or presentation layer, while another team works on the data access layer. Stubbing is also useful for unit tests when you do not want to actually build up/tear down the database and simply emulate what you would expect to return from the database.
Not 100% convinced? Well, don’t feel alone, it took me a little while to accept there was any real return on developing stubbed layers; but trust me, there is. Hopefully, this will become more clear further down the artice.
The Solution
First, we need to set up the solution. Our solution is going to use several projects to logically (and potentially physically) seperate code into layers/tiers. The projects we will be working with our as follows:
- P2.Business
- P2.DataAccess
- P2.DataAccess.MSSQL
- P2.DataAccess.Stub
- P2.Common.Entities
There are other projects within this solution, but for the purpose of this article they’re not referenced.
A Note About Business Entities
For the sake of brevity, I’m not going to go into the details of the business entities, simply note that when the proceeding code references a data type from Common.Entities this simply means there is a business entity created to frame the data into an object oriented view.
For more information on business entities you can view the following posts: Common Reusable Business Entities Part 1, Common Business Entities Part 2, and Common Business Entities Part 3.
Creating the Data Access Interface
First, we need to create an interface (contract) to tell our real data access layers how we want them to interact. In this demonstration, I will have a solution called “P2″. P2 will have the following projects:
The project P2.DataAccess is going to house all of our interface knowledge; P2.DataAccess.MSSQL and P2.DataAccess.Stub will implement the interfaces within P2.DataAccess. We’ll create an interface for working with our “products” first called IProductData.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using P2.Common.Entities.Ecommerce;
namespace P2.DataAccess
{
public interface IProductData
{
bool SaveProduct(ref Product product);
bool DeleteProduct(Product product);
Product GetProductByID(int id);
Product[] GetProducts();
Product[] SearchProducts(string search);
Product[] SearchProducts(bool posSearch, string search, bool posCategory, Category[] categories);
}
}
This interface describes what the product data access code will be able to do, in this case, it will be able to save a product, delete a product, and retrieve products in a variety of ways. Each data access, regardless of platform, will need to implement this interface and address these concerns.
Creating the Stubbed Data Access Layer
Now, let’s create a “real” data access layer which implements this interface. Because the interface resides in a different project, make sure that the P2.DataAccess.Stub project references P2.DataAccess. Next, create a file called ProductData.cs as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using P2.Common.Entities.Ecommerce;
using P2.Common.Entities.General;
namespace P2.DataAccess.Stub
{
public class ProductData : IProductData
{
#region IProductData Members
public bool SaveProduct(ref Product product)
{
return true;
}
public bool DeleteProduct(Product product)
{
return true;
}
public Product GetProductByID(int id)
{
var product = new Product(id)
{
Name = "Cosmic Uamou",
Summary = "A handpainted original from Tokyocube.",
Description = "Tokyocube are very pleased to present a beautiful new custom series from Ayako Takagi called Cosmic Uamou. The series is made up of 7 hand-painted originals, each with its own unique colours and design. The series is based on the Buuts Buuts Uamou, and each figure is signed and exclusively available through Tokyocube.",
Price = 12.50,
Weight = .25,
Width = 2,
Height = 6,
Length = 2,
SKU = "CA001",
LargeImage = new Image("1_l.jpg", "Cosmic Uamou", "Cosmic Uamou - Front"),
MediumImage = new Image("1_m.jpg", "Cosmic Uamou", "Cosmic Uamou - Front"),
Thumbnail = new Image("1_t.jpg", "Cosmic Uamou", "Cosmic Uamou - Front"),
};
return product;
}
public Product[] GetProducts()
{
Product[] products = new Product[3];
products[0] = new Product(1)
{
Name = "Cosmic Uamou",
Summary = "A handpainted original from Tokyocube.",
Description = "Tokyocube are very pleased to present a beautiful new custom series from Ayako Takagi called Cosmic Uamou. The series is made up of 7 hand-painted originals, each with its own unique colours and design. The series is based on the Buuts Buuts Uamou, and each figure is signed and exclusively available through Tokyocube.",
Price = 12.50,
Weight = .25,
Width = 2,
Height = 6,
Length = 2,
SKU = "CA001",
LargeImage = new Image("1_l.jpg", "Cosmic Uamou", "Cosmic Uamou - Front"),
MediumImage = new Image("1_m.jpg", "Cosmic Uamou", "Cosmic Uamou - Front"),
Thumbnail = new Image("1_t.jpg", "Cosmic Uamou", "Cosmic Uamou - Front"),
};
products[1] = new Product(2)
{
Name = "Mermaid",
Summary = "Designed by Keiichi Tanaami.",
Description = "Designed by Keiichi Tanaami, and uniquely produced in polystone, the Mermaid is a very special piece showing the potential of current production techniques with its superb detailing and immense quality. Produced to a run of just 200 worldwide, Keiichi Tanaami's Mermaid stands 19 cm tall.",
Price = 10.50,
Weight = .25,
Width = 2,
Height = 6,
Length = 2,
SKU = "CA002",
LargeImage = new Image("2_l.jpg", "Mermaid", "Mermaid - Front"),
MediumImage = new Image("2_m.jpg", "Mermaid", "Mermaid - Front"),
Thumbnail = new Image("2_t.jpg", "Mermaid", "Mermaid - Front"),
};
products[2] = new Product(3)
{
Name = "Kaiju Blue",
Summary = "Designed by Sun Guts.",
Description = "Kaiju Blue release a new collection of art toys from Japanese designers Sun Guts, including variations of the Uraname and Suiko figures. All produced in Japan, and available as a limited run.",
Price = 12.50,
Weight = .25,
Width = 2,
Height = 6,
Length = 2,
SKU = "CA003",
LargeImage = new Image("3_l.jpg", "Kaiju Blue", "Kaiju Blue - Front"),
MediumImage = new Image("3_m.jpg", "Kaiju Blue", "Kaiju Blue - Front"),
Thumbnail = new Image("3_t.jpg", "Kaiju Blue", "Kaiju Blue - Front"),
};
return products;
}
public Product[] SearchProducts(string search)
{
Product[] products = new Product[0];
products[0] = new Product(1);
return products;
}
public Product[] SearchProducts(bool posSearch, string search, bool posCategory, Category[] categories)
{
Product[] products = new Product[0];
products[0] = new Product(1);
return products;
}
#endregion
}
}
There are a couple of things to notice in this code example. First, I am including the mapping of data to an entity in my data access layer; some people may do this in the business logic layer and return datatables/datasets or some other generic DTO (data transfer object) from the data layer. Because my objects have very little if any business logic, they are essentially DTO’s. Second, everything is hardcoded with actual values and there is no actual database interaction – this is what makes this a “stub”. The results are always the same, and can be unit tested against with certainty of the expected result. Finally, on a non-technical note, my products all have pretty unique names and descriptions. I found these while looking at e-commerce websites and got a kick out of their products. Here are a few of their product images:
Article Intermission
Creating the MSSQL Data Access Layer
Let’s now create the MSSQL layer. This project will also reference the main P2.DataAccess project and implement the same interface as before:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using P2.Common.Entities.Ecommerce;
using P2.Common.Utilities;
using System.Data;
namespace P2.DataAccess.MSSQL
{
public class ProductData : IProductData
{
#region Mappers
//TODO: Removed for brevity.
#endregion
#region IInventoryData Members
public bool SaveProduct(ref Product product)
{
const string sql = "SaveProduct";
var parameters = new SqlHelperParameter[]
{
new SqlHelperParameter("Product", product.ID),
new SqlHelperParameter("Name", product.Name)
};
//Create product
object id = null;
var idColumn = "ProductID";
var result = SqlHelper.ExecuteNonQuery(ConnectionManager.Databases.P2_Ecommerce, sql, parameters, ref id, idColumn);
product.ID = Convert.ToInt32(id);
return result;
}
public bool DeleteProduct(Product product)
{
const string sql = "DeleteProduct";
var parameters = new SqlHelperParameter[]
{
new SqlHelperParameter("Product", product.ID),
};
var result = SqlHelper.ExecuteNonQuery(ConnectionManager.Databases.P2_Ecommerce, sql, parameters);
return result;
}
public Product GetProductByID(int id)
{
const string sql = "GetProductByID";
var parameters = new SqlHelperParameter[]
{
new SqlHelperParameter("ProductID", id)
};
DataRow row = null;
var data = SqlHelper.RetrieveDataTable(ConnectionManager.Databases.P2_Ecommerce, sql, parameters);
if (data.Rows.Count > 0)
{
row = data.Rows[0];
}
return MapDataToProduct(row);
}
public Product[] GetProducts()
{
const string sql = "GetProducts";
var data = SqlHelper.RetrieveDataTable(ConnectionManager.Databases.P2_Ecommerce, sql);
return MapDataToProducts();
}
public Product[] SearchProducts(string search)
{
const string sql = "SearchProducts";
var parameters = new SqlHelperParameter[]
{
new SqlHelperParameter("Search", search)
};
var data = SqlHelper.RetrieveDataTable(ConnectionManager.Databases.P2_Ecommerce, sql, parameters);
return MapDataToProducts(data);
}
public Product[] SearchProducts(bool posSearch, string search, bool posCategory, Category[] categories)
{
//TODO: Leaving out for brevity.
throw new NotImplementedException();
}
#endregion
}
}
Note: I like to use a SQL Helper class that I’ve written, therefore the code above does not look like normal ADO.NET. The actual code for the data access is not as important as the concept that we’ve seperated two versions of the data access layers which are tied together by a common interface.
Now, Let’s Tie It All Together in the Business Layer
The final piece to the puzzle is the business layer, where we want to give it the power to decide which data layer to execute. We can do this several ways but one that I have found really useful is by specifying the desired data access in the business logic constructor. We can do this in the following way:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using P2.DataAccess;
using P2.Common.Entities.Ecommerce;
namespace P2.Business.Ecommerce
{
public class ProductLogic
{
private IProductData _data;
public ProductLogic() : this(new P2.DataAccess.Stub.ProductData())
{
}
public ProductLogic(IProductData data)
{
_data = data;
}
public Product[] GetProducts()
{
return _data.GetProducts();
}
}
}
Notice that the constructor allows the caller to specify what type of Data Access to use. An example of this call could be:
var logic = new ProductLogic(new P2.DataAccess.Stub.ProductData()); var products = logic.GetProducts();
This call would get products from the data access stub. In a later post, I will demonstrate how to default a data access layer with a blank constructor using reflection. Normal usage of code could use the parameterless constructor and specify the default, while unit testing can specifically call the stub to avoid build up/tear down of the database.
And there you have it, the business logic now has total control of calling down to the desired data access and the data access layer becomes platform independent by implementing a common interface. Hope this was helpful, happy coding!
A Tale of Two Architectures
At times, software development can be a complicated task, especially when developing enterprise-level systems with heavy load or critical functionality. It is because of this, that software architecture has become increasingly more important and refined. There are now so many types of software architectural models, that choosing the most appropriate solution is a skill in itself.
In this article, I would like to demonstrate two fairly generic architectures that I prefer to use for enterprise applications which require a decent amount of complexity and the flexibility to be easily maintained and updated in the future. There are of course pro’s and con’s to each of these architecture models, which we will discuss later in this article.
Both architectures take into account the following requirements:
- Logical seperation between functional purpose.
- Reuseable code.
- Flexible for maintenance/upgrades.
- Tiers are able to be distributed across several servers.
Objects as Actors

First, to clarify what I mean by “actors”: an actor as I am using it refers to the code that executes behavior. In the case of objects as actors, the behavior is derived from the objects (business entities). This architecture is a hybrid and borrows concepts from several official architectural patterns such as Layered Architecture and OOAD.
As you can see, the layers are split into four major areas; data access, business logic, presentation, and common/crosscut. In this case, the business logic layer contains both business logic (such as validation, computations, etc.) and business entities. Business entities become the main section of code that all interaction with the presentation layer derives from.
Logic and entities are tightly coupled because they are packaged together in the same layer. This is more closely related to object oriented design, where everything is object-centric. Now, many of you may have read the words “tightly coupled” and were turned off; but, choosing this architecture means you are deciding that if a business entity structure changes, the business logic will need to change as well. In most real-world cases, this is true, so tightly coupling is not as negative as you may expect.
Additionally in the architecture, there is are common assemblies that cross across all layers such as Utilities (ex: extension methods), Communication (such as between layers on multiple servers), and security.
The presentation layer could include any form of presentation platform such as Web, Windows, Mobile, Silverlight, etc. The primary point is that as much as possible, whichever presentation is in use, or if multiple presentations exist, all code interacts with a common point, the business entities.
Optionally, you can implement a services layer between the business and presentation layers to lean closer to a service-oriented architecture.
Presentation-Level Code Interaction Example
Let’s look at a short example of how you would interact with the entities from any layer above business logic. This code creates a “person” entity, assigns a first and last name in the constructor, and then saves the person to the database.
var person = new Person("John", "Smith");
person.Save();
Business Logic as Actors

The main differece between this architecture and “Objects as Actors” is that business entities are moved into the common crosscut section and treated more as data structures (thin vs. thick entities).
The interaction with these business entities occur through calls to the business logic layer directly instead of the entity. The advantages of this are that entities are lighter, changes in the structure do not have to be changed in the business tier, and the objects tend to conform better to WCF.
The drawbacks are that entities no longer have structured behavior of what they can and cannot do. You use another class (in the business logic layer) to define and execute the actiosn of business entities – the business logic actor.
Presentation-Level Code Interaction Example
Our same “person” example using the Business Logic Actor. The MembershipActor contains a list of possible functions to do with various membership-related logic (such as saving Person).
var person = new Person("John", "Smith");
var membershipActor = new MembershipActor();
membershipActor.SavePerson(person);
Additional Resources
While in the middle of writing this article, I came across the Application Architecture Guide 2.0 by Microsoft Patterns and Practices. You can download the complete guide from Codeplex here. Kudos to Microsoft for putting this out for free!
Closing Notes
Various points on architecture are debateable as to what is good or bad; mostly because it depends so heavily on the individual situation. However, I hope after you’ve read this article you will leave your feedback as to what you agree, and more importantly, disagree about the above architectural models.
Happy coding!
Subversion Tips
Source control is an important concept in modern day programming, especially when working in a team environment. Most Microsoft-based shops use either Sourcesafe or Team Foundation Server (TFS). TFS is a great product, it does everything under the sun, but at a pretty hefty cost. In steps Subversion, lightweight, capable, and (drum roll)… free! I tend to promote things that are both competent and well priced. I certainly encourage you to look into Subversion as an alternative to TFS if you lack the size or resources at your current stage of development.
Below, I’ve outlined a few pieces of information that I found useful while setting up Subversion and integrating it into my Visual Studio instance. This is by no means comprehensive, as you can find much more information and specific instructions for installation and use from Subversion directly. Consider this the “cliff-notes” version.
The Tools You Need
CollabNet Subversion
CollabNet provides an “enterprise-ready distribution of Subversion”. It is simply another flavor of Subversion which is purported to be one of the most stable versions. This provides the server and command-line client.
Tortoise SVN
Tortoise SVN provides a graphical interface for using Subversion as a shell extension. You simply install Tortoise SVN, right click on a folder and are provided with additional Subversion actions within the file context menu. Additionally, you can use the Repo-Browser to open a File Explorer-like view of the source controlled files.
Ankh SVN
Ankh SVN is a Visual Studio plugin which can be added to integrate Subversion into Visual Studio (similar to TFS/Sourcesafe).
There are several tools available that you can use in place of these, but I’ve found these three work pretty smoothly for real-world use.
Setting up a Repository
Creating a repository once you have Tortoise SVN installed is very easy. Create a folder in Windows, for example, c:\svn; right click on the folder, in the context menu, inside TortoiseSVN, select “Create repository here”. See screenshot below.
This will automatically setup all of the directories and files necessary for a repository where you can begin adding source controlled files and projects to.

Making the Repository Available Online
To allow your Subversion repository to be available remotely, you’ll need to add it as a service on a hosting server. You can do this by running the following command (change directory paths to match the location of subversion as well as the location of the repository) on your server.
sc create svn binpath= "\"c:\program files\collabnet subversion server\bin\svnserve.exe\" --service -rc:\svn" displayname= "Subversion Server" depend= Tcpip start= auto
Kick off the service if it is not already running, by entering the following command line:
sc start svn
Setting up Access/Authorization
Once you create a repository, open the folder (c:\svn), and open the “conf” folder to see the following files:
- authz
- passwd
- svnserve.conf
authz File
The authz file contains a sample “database” of authorization rules which can be used by Subversion to limit access to users, groups, or aliases. Let’s take an example of two users, Bob and Susan. First we need to add the users to the [users] section and assign them read/write (rw) access, as follows:
[users] bob = rw susan = rw
Next, we’ll need to modify the authz file to specify that all authenticated users have access to the repository; we do this with the following:
[users] bob = rw susan = rw [/] $authenticated = rw
There are several additional examples and notes autogenerated within the authz file; you can learn quite a bit by playing around with these entries.
Limiting Access to Specific Directories
Let’s now further restrict the repository so specific users will have access to certain areas, while others will not. Bob will have full access to the entire repository, while Susan will have access to everything except a directory called “private”.
Our repository directory will look like this:
c:\svn c:\svn\private c:\svn\public
In the authz file, we need to make an additional change to designate that only Bob has access to the private directory. Here is our previous authz file with the new access rule; we’ve added the directory to which only Bob has access to. The two criteria beneath the rule indicate to set access of ” (none) to everyone (*) which essentially blocks read/write access from all users. We then add to that entry by saying Bob = rw (read/write access for Bob only) in the next line to allow Bob access.
[users] bob = rw susan = rw [/] $authenticated = rw [/private] * = bob = rw
passwd
The passwd file contains a “database” of users and corresponding passwords. Simply add your authenticated users and their passwords (one per line):
[users] bob = password1 susan = password2
svnserve.conf
The svnserve.conf file contains several configuration settings for Subversion which you will need to modify to enable access control.
Here are the key entries that you should adjust:
anon-access = none auth-access = write password-db = passwd authz-db = authz realm = My Repository
There are directions and examples in the svnserve.conf file that are generated when you create a repository. The above example is pretty standard if you are going to allow multiple authenticated users to access your repository while keeping anonymous users out.
Further Information
You can find an abundance of documentation in the above links as well as at http://subversion.tigris.org/. If you have any quick tips that you would like to share, please add them below in the comments.
Developing ASP.NET Custom Controls
You can do a lot of home improvement with a hammer, screwdriver, and tape measurer; however, certain jobs require more specialized tools. Programming is no different. You can do almost anythign with the base toolset provided by Microsoft, but sometimes, to make life easier and code cleaner, developing your own Custom Controls are the way to go.
I’ll discuss how to develop ASP.NET custom controls by inheriting from an existing control and providing a bit of additional functionality. This is probably the easiest way to begin writing custom controls and can be very useful in your projects.
MyButton: A Better .NET Button
Let’s take a fairly simple example that can be used in nearly every web application you develop, the noble button. The button already comes in several flavors through .NET, such as a LinkButton, ImageButton, and the standard Button. However, it does have a few drawbacks that we’re going to aim to correct with our own custom button called MyButton.
The first drawback is that, by default, a button can be clicked several times causing a postback each time you click the button. For most pages, this is a non-issue because they’re fast enough the user does not have time to “double-post”. This does occur, however, when a long process runs in codebehind wired against the button being clicked. We’ll attempt to provide functionality so once the button is clicked, it will be disabled from the user to prevent multiple triggers of the button.
Next, we’ll want to fix a common problem when you request client-side confirmation of the button (javascript confirm() function), and even upon cancelling (negating the confirmation), the button posts regardless.
Finally, we’ll provide the ability to use a button as a simple redirecting url without causing a postback.
View the demo and see what we’re getting into ahead of time.
The MyButton Class
We’ll want to begin by create a new C# Class Library Project called CustomControls. Once you’ve created this, you’ll want to add a reference to System.Web, since, by default, class libraries do not reference web-specific controls. We can later add this class library reference to any of our projects which would like to consume the MyButton (and all other custom controls you may create).
Below is the MyButton Class in it’s entirety. I’ve added a number of properties to enhance the standard button (which as you can see, it is inherited from), such as:
- CSSClass
Specifies the CSS class of the button in a normal/default state. Overridden to provide a default CSSClass to match built in CSS. By default, this is MyButton_button.
- DisabledCSSClass
Specifies the CSS class of the button when it is in a disabled state. This is only visible if DisableButtonOnClick is true (default). By default, this is MyButton_buttonDisabled.
- DisableButtonOnClick
Disables the button after it is clicked. By default, this is true.
- DisableBuiltInCSS
Disables the built in CSS reference to be included (both in the control and on the page). By default, this is false.
- DisableBuiltInClientSideCode
Disables the built in clientside (javascript) code to be included (both in the control and on the page). By default, this is false.
- ControlPath
Specifies the path to the control external files from the current file location. By default, this is ~/Controls/MyButton/.
- IsSimpleRedirect
Specifies whether this button acts like a simple redirect (by calling window.location).
- AutoPostBack
Specifies whether the control will cause a postback. By default, this is true. If IsSimpleRedirect is true, AutoPostBack will be overridden as false.
- RequiresConfirmation
Specifies whether the button will show a confirmation dialog box and require the user to confirm before submitting. By default, this is false.
- ConfirmationText
If RequiresConfirmation is true, this specifies the text that will be shown to confirm the button click action. By default, this reads “Are you sure you want to proceed?”
The button will implement some javascript and have default CSS packaged with it to assist in the increased functionality and look a bit nicer. These files could have been made as embedded resources, but to make them easy to customize for each project, and to utilize resource caching, they’ve been maintained as external files and placed in a ControlPath directory, which is configurable.
using System;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.Web.UI.HtmlControls;
namespace CustomControls
{
[AspNetHostingPermission(System.Security.Permissions.SecurityAction.Demand,
Level=AspNetHostingPermissionLevel.Minimal),
AspNetHostingPermission(System.Security.Permissions.SecurityAction.InheritanceDemand,
Level=AspNetHostingPermissionLevel.Minimal),
DefaultProperty("Text"),
ToolboxData("<{0}:MyButton runat=\"server\"></{0}:MyButton>")]
public class MyButton : Button
{
/// <summary>
/// The default css filename for the control.
/// </summary>
const string baseCssFileName = "MyButton.css";
/// <summary>
/// The default javascript filename for the control.
/// </summary>
const string baseJSFileName = "MyButton.js";
/// <summary>
/// Flag to determine if the css tag has already been added or not.
/// (so the css reference isn't added every time a control exists on the page.)
/// </summary>
private bool cssTagIsAdded = false;
/// <summary>
/// Flag to determine if the js script tag has already been added or not.
/// (so the script tag isn't added every time a control exists on the page.)
/// </summary>
private bool jsTagIsAdded = false;
/// <summary>
/// Assign a CSS class to the control.
/// </summary>
[Category("Custom"), Description("The css class to apply to this button.")]
public override string CssClass
{
get
{
if(string.IsNullOrEmpty(_cssClass))
{
_cssClass = "MyButton_button";
}
return _cssClass;
}
set
{
_cssClass = value;
}
}
private string _cssClass;
/// <summary>
/// Assign a CSS class to the control when the control is in a disabled state.
/// </summary>
[Category("Custom"), Description("The css class to apply after the button is clicked.")]
public string DisabledCssClass
{
get
{
if(string.IsNullOrEmpty(_disabledCssClass))
{
_disabledCssClass = "MyButton_buttonDisabled";
}
return _disabledCssClass;
}
set
{
_disabledCssClass = value;
}
}
private string _disabledCssClass;
/// <summary>
/// Confirmation text to be displayed if the button requires confirmation.
/// </summary>
[Category("Custom"), Description("The confirmation question to be asked when the button is clicked and requires confirmation.")]
public string ConfirmationText
{
get
{
if(string.IsNullOrEmpty(_confirmationText))
{
_confirmationText = "Are you sure you want to proceed?";
}
return _confirmationText;
}
set
{
_confirmationText = value;
}
}
private string _confirmationText;
/// <summary>
/// The path to the external javascript, css, and image resources the defaults for this control uses.
/// </summary>
[Category("Custom"), Description("The path to the external javascript, css, and image resources the defaults for this control uses.")]
public string ControlPath
{
get
{
if(string.IsNullOrEmpty(_controlPath))
{
_controlPath = "~/Controls/MyButton/";
}
if (!_controlPath.EndsWith("/"))
{
_controlPath += "/";
}
return _controlPath;
}
set
{
_controlPath = value;
}
}
private string _controlPath;
/// <summary>
/// Determines whether to use the built in css file and page reference for the control.
/// By default, this is false.
/// </summary>
[Category("Custom"), Description("Determines whether to register a css class at the top of the html.")]
public bool DisableBuiltInCSS
{
get
{
if (_disableBuiltInCSS == null)
{
_disableBuiltInCSS = false;
}
return (bool)_disableBuiltInCSS;
}
set
{
_disableBuiltInCSS = value;
}
}
private bool? _disableBuiltInCSS;
/// <summary>
/// Determines whether to use the built in javascript file and page reference for the control.
/// By default, this is false.
/// </summary>
[Category("Custom"), Description("Determines whether to register an external clientside file to the html.")]
public bool DisableBuiltInClientSideCode
{
get
{
if (_disableBuiltInClientsideCode == null)
{
_disableBuiltInClientsideCode = false;
}
return (bool)_disableBuiltInClientsideCode;
}
set
{
_disableBuiltInClientsideCode = value;
}
}
private bool? _disableBuiltInClientsideCode;
/// <summary>
/// Determines whether to run built in javascript code to disable the button after being clicked to
/// prevent multiple postbacks by users.
/// By default, this is true.
/// </summary>
[Category("Custom"), Description("Determines whether to disable the button after click or not.")]
public bool DisableButtonOnClick
{
get
{
if (_disableButtonOnClick == null)
{
_disableButtonOnClick = true;
}
return (bool)_disableButtonOnClick;
}
set
{
_disableButtonOnClick = value;
}
}
private bool? _disableButtonOnClick;
/// <summary>
/// Determines whether this button simply acts as a clientside redirect.
/// </summary>
[Category("Custom"), Description("Determines whether this button simply acts as a clientside redirect.")]
public bool IsSimpleRedirect
{
get
{
if (_isSimpleRedirect == null)
{
_isSimpleRedirect = false;
}
return (bool)_isSimpleRedirect;
}
set
{
_isSimpleRedirect = value;
}
}
private bool? _isSimpleRedirect;
/// <summary>
/// Redirect URL for simple clientside redirect.
/// </summary>
[Category("Custom"), Description("Redirect URL for simple clientside redirect.")]
public string RedirectURL { get; set; }
/// <summary>
/// Determines whether clicking the button requires a confirmation.
/// </summary>
[Category("Custom"), Description("Determines whether clicking the button requires a confirmation.")]
public bool RequiresConfirmation { get; set; }
/// <summary>
/// Determines whether this control should cause a postback.
/// </summary>
[Category("Custom"), Description("Should this control cause a postback?")]
public bool AutoPostBack
{
get
{
if (_autoPostBack == null)
{
_autoPostBack = true;
}
return (bool)_autoPostBack;
}
set
{
_autoPostBack = value;
}
}
private bool? _autoPostBack;
protected override void OnPreRender(EventArgs e)
{
//Embed javascript tag
if (this.DisableBuiltInClientSideCode == false && jsTagIsAdded == false)
{
string jsPath = this.ControlPath.Replace("~", Context.Request.ApplicationPath.TrimEnd('/')) + baseJSFileName;
HtmlGenericControl js = new HtmlGenericControl("script");
js.Attributes["type"] = "text/javascript";
js.Attributes["src"] = jsPath;
this.Page.Header.Controls.Add(js);
jsTagIsAdded = true;
}
//Embed css tag
if (this.DisableBuiltInCSS == false || (string.IsNullOrEmpty(this.CssClass) && this.CssClass != baseCssFileName) && cssTagIsAdded == false)
{
string cssPath = this.ControlPath.Replace("~", Context.Request.ApplicationPath.TrimEnd('/')) + baseCssFileName;
HtmlLink cssLink = new HtmlLink();
cssLink.Href = cssPath;
cssLink.Attributes.Add("rel", "stylesheet");
cssLink.Attributes.Add("type", "text/css");
this.Page.Header.Controls.Add(cssLink);
cssTagIsAdded = true;
}
base.OnPreRender(e);
}
protected override void Render(HtmlTextWriter writer)
{
StringBuilder sb = new StringBuilder();
//If simple redirect and redirect url is provided add clientside redirect without postback
if (this.IsSimpleRedirect && !string.IsNullOrEmpty(this.RedirectURL))
{
//Add javascript to redirect page
sb.Append("window.location='");
sb.Append(this.RedirectURL);
sb.Append("';");
this.UseSubmitBehavior = false;
this.AutoPostBack = false;
}
//If not a confirmation button, disables button to prevent double postbacks
if (this.DisableButtonOnClick && this.RequiresConfirmation == false)
{
sb.Append("doDisable('");
sb.Append(base.ClientID);
sb.Append("', '");
sb.Append(this.DisabledCssClass);
sb.Append("', ");
sb.Append(this.AutoPostBack.ToString().ToLower());
sb.Append(");");
}
if (!string.IsNullOrEmpty(this.OnClientClick))
{
sb.Append(this.OnClientClick);
}
//If requires confirmation, else if clientside button only and no postback should occur
if (this.RequiresConfirmation)
{
this.UseSubmitBehavior = false;
this.AutoPostBack = false;
sb.Append(string.Format("var val = confirm('{0}'); if(val) __doPostBack('{1}','');", this.ConfirmationText, this.ClientID));
}
else if ((this.IsSimpleRedirect && !string.IsNullOrEmpty(this.RedirectURL)) || this.AutoPostBack == false)
{
//Add javascript to disable the postback that occurs on ASP.NET buttons
sb.Append("return false;");
}
writer.AddAttribute("class", this.CssClass);
writer.AddAttribute("onclick", sb.ToString());
base.Render(writer);
}
}
}
The Render method is where most of the logic occurs based on the properties provided to the control. The Render method is where I’m injecting the Javascript to help with much of the new functionality.
Miscellaneous MyButton Files
As mentioned, I’m using external files for the javascript and CSS. Here are the files as I currently have them. They are completely open to be configured to add additional features or customize based on project.
Client-side code (javascript) – MyButton.js
/*
Set's timer to call doDisableButton() after it is clicked, the reason this fires instead of directly
calling doDisableButton() is because a disabled control will not postback, so there needs to be a
slight delay.
param id = the id of the button being disabled.
param css = the css class being used for the disabled state (visual appearance).
param submit = bool flag whether to submit form or not
*/
function doDisable(id, css, submit)
{
if(submit)
{
//document.forms[0].submit(); //removed to allow AJAX calls
document.getElementById(id).click();
}
window.setTimeout("doDisableButton('" + id + "', '" + css + "')", 0);
}
/*
Disables the button and sets the button's css class to a disabled state.
param id = the id of the button being disabled.
param css = the css class being used for the disabled state (visual appearance).
*/
function doDisableButton(id, css)
{
var obj = document.getElementById(id);
obj.disabled = true;
obj.className = css;
}
CSS – MyButton.css
/*The button control - normal state*/
.MyButton_button
{
background-color: #000033;
background-image: url("bg_nav.gif");
background-repeat: repeat;
font-size: 10px;
font-weight: bold;
color: #FFFFFF;
border: 1px solid;
padding-top: 2px;
padding-bottom: 2px;
cursor: pointer;
}
/*The button control - hover state*/
.MyButton_button:hover
{
/*background-image: url("bg_nav_red.gif"); -- Uncomment this when we have a hover state to add by default*/
color: #FFFFCC;
}
/*The button control - after clicked if disable button is turned on*/
.MyButton_buttonDisabled
{
background-color: #CCC;
background-image: none;
font-size: 10px;
font-weight: bold;
color: #FFFFFF;
border: 1px solid;
padding-top: 2px;
padding-bottom: 2px;
cursor: pointer;
}
You’ll notice in the CSS there is also a hover state. I added this to demonstrate that even though there is not an explicit CSS class name property in the control for hoverstate, additional flexibility can be included through the CSS.
These files will need to reside in the application which is consuming your custom control, so you will need to create a Controls\MyButton directory (or similar if pointing to another directory through the ControlPath property). I find it also useful to create this in the C# class library project to maintain the original files even thought they are not technically used from there.
You will want to also include any graphics that you are calling from your CSS file in this directory as well. In this case, we have two, one for each state of the button.
Let’s Take it for a Spin
Let’s begin by setting up a quick demo page using our new custom button control. The following is a couple of scenarios that highlight the use of the custom button as opposed to the regular button you would normally use.
Feel free to modify the code as much as you like to provide a working template of various button uses in your own applications.
<asp:Label ID="TimeLabel" runat="server"></asp:Label><br />
<p>The above label helps to demonstrate whether a postback is ocurring or not in the following examples.</p>
<hr />
<cc1:MyButton ID="MyButton1" runat="server" Text="Plain Button" /><br />
<p>This is the default button which causes a server-side post back.</p>
<hr />
<cc1:MyButton ID="MyButton2" runat="server" Text="Confirmation Button" RequiresConfirmation="true" ConfirmationText="Are you sure you like waffles?" /><br />
<p>This button causes a server-side post back if the confirmation question is answered positively. If not confirmed, no postback will occur fixing the ASP.NET button issue for posting upon not confirmed response.</p>
<hr />
<cc1:MyButton ID="MyButton3" runat="server" Text="Redirect to Google" IsSimpleRedirect="true" RedirectURL="http://www.google.com" /><br />
<p>This button acts to redirect to a url. It does not cause a postback to occur.</p>
<hr />
<cc1:MyButton ID="MyButton4" runat="server" Text="Clientside Hello World - No Postback" AutoPostBack="false" OnClientClick="alert('Hello World');" DisableButtonOnClick="false" /><br />
<p>This button does not cause a postback and instead fires any clientside code. In this example, a hello world alertbox.</p>
<hr />
<cc1:MyButton ID="MyButton5" runat="server" Text="No Double Postback" /><br />
<p>This button demonstrates the default behavior of disabling the button after clicking it, to eliminate a double-post back scenario on long server-side processing. A 2 second sleep is called when clicking this button.</p>
Don’t forget to register your new control on your page like the following: (Note: This may be in a different namespace depending on where you’ve created the custom controls project.)
<%@ Register Assembly="CustomControls" Namespace="CustomControls" TagPrefix="cc1" %>
We’ll also need just a slight bit of code behind (written in VB) to help highlight the features we’re demonstrating. The code here is not necessary for using the buttons.
First, we are going to assign the current timestamp to the TimeLabel label everytime the page loads so we can easily track when a postback is ocurring and when it is not. Second, for our example #5, “No Double Postback”, we’re going to add a time delay so we can show the button being disabled and disallowing users to click causing multiple postbacks during lenthy server-side processes.
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load TimeLabel.Text = Now() End Sub Protected Sub MyButton5_Click(ByVal sender As Object, ByVal e As EventArgs) Handles MyButton5.Click Threading.Thread.Sleep(2000) End Sub
At this point, you should have a completely functioning collection of demo’s for using the MyButton custom control and a pretty good idea on how to begin developing your own custom controls.
Keep in mind, in this scenario, we’ve built custom code onto a pre-existing control (the ASP.NET button control); however, if we need even greater customization, we can create custom controls from scratch, a topic I’ll attempt to cover down the road.
Good luck in your next custom coding control project, and please leave any suggestions or questions in the comments below!
Southwest Florida Code Camp 2009

Development in my neck of the woods seems to be growing and it is evidenced by the 2nd annual Southwest Florida .NET Code Camp on October 3rd, 2009. Code Camp will be held at Florida Gulf Coast University in Fort Myers, FL and is sure to turn out a substantial crowd of developers as well as a great collection of distinguished speakers.
If you’re interested in attending the Southwest Florida Code Camp, head over to to find out more and sign up for free.
Code Camp should cover several different topics in various tracks including the following:
- .NET 4.0 Entity Framework
- Be Productive with MVC: Telerik Open Source Extensions for ASP.NET MVC
- Bing Map Web Control API
- Building Windows Mobile Widgets
- Cloud Computing: What you can do now to move your software infrastructure online
- Code Style & Standards
- Enterprise Library and Unity Tips & Techniques
- Game Programming on Windows Mobile
- Introduction to DotNetNuke 5
- Introduction to DotNetNuke 5 Widgets
Preparing for Code Camp
Before attending an educational seminar such as Code Camp, I prefer to do a bit of research on the topics presented. Most lectures provide a good introduction to the topic, so it’s not critical to “study-up”, but I’ve found that you get the most “bank for your buck” when you come in knowing a little already.
Additionally, a great way to understand and retain the information from Code Camp is to come with a specific project in mind and ask yourself “How can this subject make my project better?”. At least in my case, I tend to retain much more by applying the topic to a real life application.
A Few Links to Help You Prep
- bing Develop Center – Read Virtual Earth and the Windows Live CTP
- Telerik (third party control suites) – Read Battle of the Third Party Controls
- Windows Mobile Developer Center
- Windows Azure Platform
- Read Homegrown CSS Standards
- Enterprise Library – (See David Hayden)
- DotNetNuke
Job Hunting
For those of you developers that have been hit hard by the current economic market, Code Camp acts as a double wammy to keep you sharp and build on your current skillset as well as being a forum to meet recruiters, fellow developers, and potentially, your next job opening. Now’s the time to network and Code Camp is the perfect place to do so. Hope to see you there!
OpenCourseWare from MIT-Harvard-Yale

Education provides one of the greatest advantages in life. It is knowledge, that indiscriminantly improves a persons’ position in life. In my experience, it doesn’t matter age, ethnicity, gender or origin, you’re valued by what you know and what you can accomplish.
Yet education isn’t necessarily cheap, not only in terms of money but time as well. In cases where,
for whatever reason, going to (or back to) school is not an option, or you’re simply a self-learner, there is another option.
MIT, in conjunction with several other renowned educational institutes have spear-headed the Open Courseware Initiative, an “open-source” view of education. Many courses spanning across all subjects are provided without charge over the internet. Courses vary in what material is provided, but many include audio/video sessions of actual course classes.
For those interested in Information Technology, Stanford has one of the most comprehensive collections of computer engineering courses called Stanford Engineering Everywhere. Video is provided via YouTube, iTunes U, Vyev, WMU Torrent, and MP4 Torrent and cover the following course schedule:
Introduction to Computer Science
- Programming Methodology
- Programming Abstractions
- Programming Paradigms
Artificial Intelligence
- Introduction to Robots
- Natural Language Processing
- Machine Learning
Linear Systems and Optimization
- The Fourier Transform and it’s Applications
- Introduction to Linear Dynamical Systems
- Convex Optimization I
- Convex Optimization II
In addition to Stanford, MIT, Yale, UC Berkely, and many other schools offer free courseware. Now you can actually download many of the courses audio/video through iTunes U, a section within the iTunes Store which contains free open courseware content. All of your college classes right on your iPod, can’t beat that!
If you would like to learn more about open courseware, please check out some of the following links and begin reaping the benefits of a free education.
Code Disassembly and Reflection
One of the first tasks I was assigned when I started my new job was to recover some old source code that a contractor had developed for the company several years back. My company had working dll’s but no source code to make changes from. This was my first real exposure to Reflector and disassembling binary code. I’ll be honest, I got a real kick out of, the hacker mentality of my youth came flooding back but without any guilt because this was a legitimate (and legal) reason to disassemble code.
Since then, I’ve found Reflector to be an invaluable tool in a developer’s arsenal for the following scenarios:
- Retrieving lost source code.
- As a learning tool to peak into other, possibly more sophisticated code and code architecture.
- To assist in troubleshooting third-party vendor software (which is certainly not immune to bugs).
These are just a few reasons, but all contribute to advancing your skill as a developer and worth to your client(s)/company.
Where can I get it and how much does it cost?

There probably are a couple out there, but I use Red Gate’s .NET Reflector which you can download a FREE copy.
Red Gate produces various other products that are useful, though many of them are not free.
How do I use it?
Glad you asked; Reflector is pretty easy to use. First, begin by opening Reflector, selecting File > Open and selecting a .NET based DLL that you would like to analyze. For demonstration, I will be using one of the nopCommerce DLL’s, Nop.Common.dll. (nopCommerce is a .NET open-source ecommerce project).
If the Disassemble panel is not already open, go ahead and open it now by right clicking on the assembly you just loaded, and selecting Disassemble.

By drilling down the tree-style panel on the left side, you can see all of the individual namespaces that comprise the DLL.

This takes a bit of digging, but with enough patience you can find what you are looking for. We will now select NopSolutions.NopCommerce.Common.Audit > LogManager to view the code in this class.

Next let’s move to the right side panel and scroll to the very bottom and click “Expand Methods”. This will allow us to see all of the code in the class. A nice touch is that you can view code in your language of preference, such as C# or VB.

You are now able to disassemble .NET code in a breeze, remember, “with great power, comes great responsibility”! Please make sure that whatever you are disassembling you have the right to disassemble it. Good luck on your next project.
Technorati code: zneh8mkst6
Staying in Touch – How to Build a Contact Form
Whether you are a Fortune 500 company or a mom-and-pop shop online, you need some way for visitors to contact you. Most contact forms are pretty much the same, you choose what information you want your visitors to provide to you as well as an open area for any comments/questions they would like to leave. The information is then sent to an administrator email account for correspondence. You may also want to display a confirmation to the user that their email has been sent.
To demonstrate how to do this, I will use my own contact form in ASP.NET (VB.NET) and an old PHP version I used to run a long time ago.
ASP.NET
For those of you Microsoft-based developers, here is some sample code that can be used as a base for your contact forms.
ASPX (frontend)
<h1>Contact Nicholas Barger</h1> <p>Please feel free to contact me with questions, comments, or anything else that you would like to discuss. I appreciate your interest/involvement so I try and respond as quickly as possible.</p> <p>You can also find me on <i>LinkedIn</i> at: <a href="http://www.linkedin.com/in/nicholasbarger" target="_blank">http://www.linkedin.com/in/nicholasbarger</a></p> <div class="formdisplay"> <span>Please enter your email address:</span><br /> <asp:TextBox ID="tbEmail" runat="server" Width="700px"></asp:TextBox><br /> <span>Please enter a subject for this message:</span><br /> <asp:TextBox ID="tbSubject" runat="server" Width="700px"></asp:TextBox><br /> <span>Please enter your message below:</span><br /> <asp:TextBox ID="tbMessage" runat="server" TextMode="MultiLine" Width="700px" Height="250px"></asp:TextBox><br /> </div> <asp:Button ID="btnSubmit" runat="server" Text="Send Message" />
VB.NET (backend/codebehind)
Protected Sub btnSubmit_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnSubmit.Click
'Prep basic values
Dim strFromEmail As String = tbEmail.Text
Dim strToEmail As String = ConfigurationManager.AppSettings("AdminEmail")
Dim strSubject As String = tbSubject.Text
'Prep custom values
Dim strBody As String = ""
Dim strFromName As String = ""
strBody = tbMessage.Text
Dim oCommonFunc As New Common.Functions
Try
oCommonFunc.sendEmail(strFromEmail, strFromName, strToEmail, strSubject, strBody, Nothing, Nothing, Nothing, True)
Response.Redirect("contactcomplete.aspx")
Catch ex As Exception
'TODO - Display Error Message
Finally
oCommonFunc = Nothing
End Try
End Sub
VB.NET (common class/codebehind)
Public Function sendEmail(ByVal strFromEmail As String, _
ByVal strFromName As String, _
ByVal strToEmail As String, _
ByVal strSubject As String, _
ByVal strBody As String, _
Optional ByVal strCCEmail As String = Nothing, _
Optional ByVal strBCCEmail As String = Nothing, _
Optional ByVal strAttachmentPath As String = Nothing, _
Optional ByVal bIsHTML As Boolean = False) As Boolean
'Check for proper from email
If String.IsNullOrEmpty(strFromEmail) = True Then
Throw New Exception("From email is not specified.")
End If
'Check for proper to email
If String.IsNullOrEmpty(strToEmail) = True Then
Throw New Exception("To email is not specified.")
End If
'Check for a non-empty message subject
If String.IsNullOrEmpty(strSubject) = True Then
Throw New Exception("Subject was not specified.")
End If
'Check for a non-empty message body
If String.IsNullOrEmpty(strBody) = True Then
Throw New Exception("Body of email was not specified.")
End If
'Create mail object
Dim oMail As New MailMessage()
'oMail.Host = ConfigurationManager.AppSettings("MailHost")
'Convert all "," into ";"
strToEmail = strToEmail.Replace(",", ";")
'Check if "To" email contains multiple emails
If strToEmail.IndexOf(";") >= 0 Then 'add multiple emails
Dim strToArray As String() = Split(strToEmail, ";")
For i As Integer = 0 To strToArray.Length - 1
oMail.To.Add(strToArray(i))
Next
Else 'add single email
oMail.To.Add(strToEmail)
End If
'If CC exists
If String.IsNullOrEmpty(strCCEmail) = False Then
'Convert all "," into ";"
strCCEmail = strCCEmail.Replace(",", ";")
'Check if "CC" email contains multiple emails
If strCCEmail.IndexOf(";") >= 0 Then 'add multiple emails
Dim strCCArray As String() = Split(strCCEmail, ";")
For i As Integer = 0 To strCCArray.Length - 1
oMail.CC.Add(strCCArray(i))
Next
Else 'add single email
oMail.CC.Add(strCCEmail)
End If
End If
'If BCC Exists
If String.IsNullOrEmpty(strBCCEmail) = False Then
'Convert all "," into ";"
strBCCEmail = strBCCEmail.Replace(",", ";")
'Check if "BCC" email contains multiple emails
If strBCCEmail.IndexOf(";") >= 0 Then 'add multiple emails
Dim strBCCArray As String() = Split(strBCCEmail, ";")
For i As Integer = 0 To strBCCArray.Length - 1
oMail.Bcc.Add(strBCCArray(i))
Next
Else 'add single email
oMail.Bcc.Add(strBCCEmail)
End If
End If
'Add additional information to mail object
oMail.From = New MailAddress(strFromEmail, strFromName)
oMail.Subject = strSubject
oMail.Body = strBody
If bIsHTML = True Then
'oMail.IsHTML = True
oMail.IsBodyHtml = True
End If
Dim strSMTPUserName As String = ConfigurationManager.AppSettings("SMTPEmail")
Dim strSMTPPassword As String = ConfigurationManager.AppSettings("SMTPPassword")
Dim smtpClient As New SmtpClient(ConfigurationManager.AppSettings("MailHost"))
smtpClient.Credentials = New Net.NetworkCredential(strSMTPUserName, strSMTPPassword)
oMail.DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure
If String.IsNullOrEmpty(strFromEmail) = False Then
oMail.ReplyTo = New MailAddress(strFromEmail)
End If
If String.IsNullOrEmpty(strFromEmail) = False Then
oMail.Headers.Add("Return-Path", strFromEmail)
oMail.Headers.Add("Errors-To", strFromEmail)
oMail.Headers.Add("Bounces_To", strFromEmail)
End If
'Add attachment
If String.IsNullOrEmpty(strAttachmentPath) = False Then
oMail.Attachments.Add(New Attachment(strAttachmentPath))
End If
'Send email
Try
smtpClient.Send(oMail)
Catch ex As Exception
Return getErrorMessage(ErrorMessage.ErrorSendingEmail)
Finally
oMail = Nothing
End Try
Return True
End Function
PHP
And now for all things open-source, a PHP version.
Disclaimer: I haven’t professionally worked with PHP since 2004, so if you have any suggestions to improve the code please add them to the comments.
All-In-One (spaghetti-code)
<?php
if(isset($_POST['btnSubmit'])) {
$mailto = "nicholas@nicholasbarger.com";
$email = $_POST['tbEmail'];
$subject = $_POST['tbSubject'];
$message = $_POST['tbMessage'];
$headers = 'From: $mailto'."\r\n"
.'Reply-To: $mailto'."\r\n";
#Mail to Administrator
if(mail($mailto, $subject, $message, $headers))
{
header('Location:contact_complete.php');
}
else
{
//Display an error
}
}
?>
<html>
<head>
<title>PHP Contact Page Demo</title>
</head>
<body>
<h1>Contact Nicholas Barger</h1>
<p>Please feel free to contact me with questions, comments, or anything else that you would like to discuss. I appreciate your interest/involvement so I try and respond as quickly as possible.</p>
<p>You can also find me on <i>LinkedIn</i> at: <a href="http://www.linkedin.com/in/nicholasbarger" target="_blank">http://www.linkedin.com/in/nicholasbarger</a></p>
<div class="formdisplay">
<span>Please enter your email address:</span><br />
<input type="text" id="tbEmail" name="tbEmail" style="width:700px;"></input><br />
<span>Please enter a subject for this message:</span><br />
<input type="text" id="tbSubject" name="tbSubject" style="width:700px;"></input><br />
<span>Please enter your message below:</span><br />
<textarea id="tbMessage" name="tbMessage" style="width:700px; height:250px;"></textarea><br />
</div>
<input type="submit" id="btnSubmit" name="btnSubmit" value="Send Message" />
</body>
</html>
Make It Your Own
What I’ve outlined here is just a beginning demonstration. There is still work to be done, such as creating the contact complete page and notifying users that they successfully (or unsucessfully) contacted an administrator. Additionally, you may want to add additional fields to be captured, add field validation to ensure proper data entry, or perhaps add a confirmation email response to the user as well.
Have fun with it, and take a boring contact page and make it something really powerful. Hope this was helpful in getting you started, or refreshed on how to stay in touch with your visitors through a simple contact form!
CSS Zen Garden
For years when trying to convey the power and pupose of CSS I’ve deferred to a single example; CSS Zen Garden. I’ve always been so impressed by the demonstation of this project and contributions from designers all over the world. The basis of CSS Zen Garden is to take the exact same HTML content, and without making ANY changes to the HTML, create a completely unique visual design and portrayal of the content.
For those of you who think CSS is merely changing fonts and colors, try out a few examples found on CSS Zen Garden and you will be amazed at the huge range in presentations.
Here are a few of my favorites:
Care to take a look at the base HTML? Click here to view the HTML without any CSS styling.
CSS Zen Garden: The Book
If you are interested in learning more about the history behind this project, the original creators have released a book which I highly recommend.













