Digital Colony!

Bind Generic List to ASP.NET DropDownList Example

Let us suppose you had a simple class of tags that you wanted to bind to an ASP.NET DropDownList control.
public class TagDetails
{
   private int tagID;
   public int TagID
   {
       get { return tagID; }
   }

   private string tagName;
   public string TagName
   {
       get { return tagName; }
       set { tagName = value; }
   }
   public TagDetails(int tagID, string tagName)
   {
       this.tagID = tagID;
       this.tagName = tagName;
   }
}
The DropDownList control on the .ASPX page looked like this.
<asp:DropDownList ID="ddlTagList" runat="server"/>
And for this class you had a method to return all tags called GetTags. How would you bind it to an ASP.NET DropDownList control? As such.
List<TagDetails> tags = new List<TagDetails>();
tags = tagDB.GetTags();
ddlTagList.DataSource = tags;
ddlTagList.DataTextField = "TagName";
ddlTagList.DataValueField = "TagID";
ddlTagList.DataBind();
At this point you may wish to add a blank item or select item row at the top of the DropDownList. That code can be found at Add Item to DropDownList After DataBind.

Labels: , ,

 

Setting Default Focus on ASP.NET Master Pages

In ASP.NET 2.0, the way to set focus on a particular form field is to add a defaultfocus declaration in the form tag.
<form id="Form1" defaultfocus="txtSearch" runat="server"> 
Since the form tag does not appear when using MasterPages, setting form focus is done in the code. This is the line that will accomplish the same focus in C#.
Page.Form.DefaultFocus = txtSearch.ClientID;

Labels: ,

 

Adding Javascript Select All to ASP.NET Control

A common user-interface technique to updating form fields is to have all the text selected when the user gives it focus. This allows the user the ability to clear out old text easily, without having to use the mouse and/or backspace button to clear out the selection themselves.
<asp:TextBox ID="txtSearch" runat="server" MaxLength="100" />
You can add the javascript code to select the text (this.select()) to the onclick or the onfocus event. Or you can add it to both.
txtSearch.Attributes.Add("onclick", "this.select();");
txtSearch.Attributes.Add("onfocus", "this.select();");

Labels: , ,

 

ASP:Label Control and ViewState

One of the tips that is thrown around for improving the speed and performance of an ASP.NET page is to minimize the use of server controls. This makes sense for data-bound controls, but what about the asp:Label control? I decided to create a test to answer this question.

Why Use the asp:Label Control?

I've been coding HTML since 1995, but I have to confess that until I read ASP.NET 2.0 Unleashed by Stephen Walther, I had never used the HTML label control. Here is what I learned.
Always use a Label control with an AssociatedControlID property when labeling form fields. This is important when you need to make your website accessible to persons with disabilities. If someone is using an assistive device, such as a screen reader, to interact with your website, the AssociatedControlID property enables the assistive device to associate the correct label with the correct form field.

Using the asp:Label control will render the HTML label control in the following manner.
<asp:Label ID="lblFirstName" runat="server" Text="First Name" AssociatedControlID="txtFirstName" />
<label for="txtFirstName" id="lblFirstName">First Name</label>

The Test

I constructed a basic Form with the fields of FirstName, LastName, Address1, Address2, City, State and Zip Code. Page A used the asp:Label control which renders the label control. Page B avoided using the server control and used just the HTML label for each form field. Page C decided not to be concerned with persons with screen readers and not use any labels.

The Results

Page A (Server Label): 7 KB, ViewState = 52 chars
Page B (Client Label): 7 KB, ViewState = 52 chars
Page C (No Label)    : 6 KB, ViewState = 52 chars

Conclusion

Use the asp:Label control and the AssociatedControlID to label each form field. You get no performance penalty. The upside is your form will be accessible by more readers and assuming you don't make other mistakes, your page will be Section 508 compliant.

Labels: , , ,

 

Sending Email in ASP.NET 2.0 (C#)

Here is the C# version of Sending Email in ASP.NET 2.0 (VB.NET).
using System.Net.Mail;
The code snippet demonstrates Larry King emailing Oprah.
MailMessage Message = new MailMessage();
SmtpClient Smtp = new SmtpClient();
// Build message
Message.From = new MailAddress("larryking@cnn.com", "Larry King");
Message.To.Add(new MailAddress("oprah@oprah.com", "Oprah"));
Message.IsBodyHtml = false;
Message.Subject = "Come on My Show Soon";
Message.Body = "Please be a guest on my show. - Larry";
// Send Message
// each web host is different 
// (adjust next 2 lines accordingly)
Smtp.Host = "localhost";
Smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
Smtp.Send(Message);

Labels: , ,

 

Consuming a Web Service in ASP.NET Tutorial

My fitness site DeepFitness.com has almost two thousand articles on fitness and nutrition topics. The other day I wrote an API that exposed a few WebMethods. Below I'm going to walk through setting up a page that consumes those Web Services in an ASP.NET page developed in Visual Studio.

Add Web Reference

The first step is to Add Web Reference. Either right-mouse click the web site name in the Solution Explorer or from the tool bar select Website. Add the URL http://deepfitness.com/i/api/DeepFitnessService.asmx and click the Go button. A Web reference name of com.deepfitness will be found. Click the Add Reference button.

Web Reference

Web Namespace

Create a new web page. Add the namespace com.deepfitness to the code-behind page.
using com.deepfitness;

ASP.NET Code

The lab demo displays three sections which demonstrate three web methods. The drop-down list displays a list of tags used to label the articles on the site. This drop-down is populated using the GetTags call. Once a tag is selected, a datalist is populated with articles for that given tag using the GetArticlesByTagName call. Beside each article title is an articleID. Clicking on that articleID, will call the GetArticle method and return the article title and text below.
<h4><em>GetTags</em></h4>
<asp:Label ID="lblTag" AssociatedControlID="ddlTags" runat="server" Text="Tag:" />
<asp:DropDownList ID="ddlTags" runat="server" AutoPostBack="true" />
  
<h4><em>GetArticlesByTagName</em></h4>
<p><asp:Label ID="lblSelectedTagName" runat="server" /></p>
<div style="height:150px; overflow:scroll; background-color:#ffffcc;">
<asp:DataList ID="dlArticles" runat="server">
<HeaderTemplate></HeaderTemplate>
    <ItemTemplate>
    <asp:LinkButton ID="lbtnArticleID" runat="server" 
        CommandArgument='<%# Eval("articleID") %>'
        CommandName="GetArticle"
        OnCommand="LinkButton_Command">
        <%# Eval("articleID") %>
        </asp:LinkButton>
        <%# Eval("title") %><br />
    </ItemTemplate>
    <FooterTemplate></FooterTemplate>
</asp:DataList>
</div>

<h4><em>GetArticle</em></h4>
<p><asp:Label ID="lblTitle" runat="server" /></p>
<div id="dvArticle" runat="server" style="height:150px; overflow:scroll; background-color:#ffffcc;"/>

The C Sharp Code

Both the calls to GetTags and GetArticlesByTagName return collections and can be data bound with a single line of code each. The GetArticle call returns the WebArticleDetails class which holds the article title (Title) and article text (Page).
public DeepFitnessService dfs = new DeepFitnessService();

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {            
        ddlTags.DataSource = dfs.GetTags();
        ddlTags.DataValueField = "tagName";
        ddlTags.DataBind();
    }
    GetArticlesByTagName();
}

protected void GetArticlesByTagName()
{
    string tagName = ddlTags.SelectedValue;
    dvArticle.InnerHtml = "";
    lblTitle.Text = "";
    lblSelectedTagName.Text = tagName;
    dlArticles.DataSource = dfs.GetArticlesByTagName(tagName);
    dlArticles.DataBind();
}

protected void LinkButton_Command(Object sender, CommandEventArgs e)
{
    int articleID;
    articleID = Convert.ToInt16(e.CommandArgument);
    // get article
    WebArticleDetails article = dfs.GetArticle(articleID);
    lblTitle.Text = article.Title;
    dvArticle.InnerHtml = article.Page;
}

Lab Demo

Consuming the DeepFitness Web Service API

Labels: , ,

 

Sending Email in ASP.NET 2.0 (VB.NET)

Way back in 2001, I wrote ASP.NET Email Using VB for ASP.NET 1.x. ASP.NET 2.0 now uses the System.Net.Mail instead of the System.Web.Mail Namespace.

Here is a quick snippet to get you started.
Dim Message As MailMessage = New MailMessage()
Dim Smtp As New SmtpClient()
'-- Build Message
Message.From = New MailAddress("larryking@cnn.com", "Larry King")
Message.To.Add(New MailAddress("oprah@oprah.com", "Oprah"))
Message.IsBodyHtml = False
Message.Subject = "Come on My Show Soon"
Message.Body = "Please be a guest on my show. - Larry"
'-- Send Message
'-- each web host is different 
'-- (adjust next 2 lines accordingly)
Smtp.Host = "localhost"
Smtp.DeliveryMethod = SmtpDeliveryMethod.Network
Smtp.Send(Message)
If your web host requires that you send the email using SMTP Authentication read Sending Email in ASP.NET Using SMTP Authentication (VB.NET).

Labels: , ,

 

Creating an ASP.NET Blog Using Blogger

When I relaunched Digital Colony in January 2007, I made the decision to do it using Blogger. Prior to that this site used a publisher I wrote myself in Classic ASP. It did a fine job for years, but it didn't have comments or tags. Also, I've been using Blogger since April 2000, so I have a high level of comfort with it's interface.

Blogger Accounts

Before proceeding, I believe it's important to explain there are 3 levels of accounts that can use Blogger.
  1. BlogSpot Hosted - xxxx.blogspot.com
  2. Blogger Hosting Domain - your xxxx.com site is hosted on their servers.
  3. FTP Account - you manage your server and Blogger FTPs built pages to your site.
If you want to use ASP.NET, you will have use option #3. This means you will need your own disk space and FTP access. From this point forward, everything discussed applies only to Blogger accounts accessed via FTP.

Why Blogger? The Good Stuff

What I like about Blogger is you can use any file extension you like. This means I can use ASP.NET 2.0 and use Master Pages. I can also drop server controls onto my template. And I like the fact the code base resides on Google's server, not mine. If they need to update the publisher, they can without me having to download and install anything. Most blog tools require the user to install patches and perform upgrades.

The 2006 release of Blogger gave us tags. Blogger calls them labels, but it's the same concept. You can create an unlimited number of tags to quickly locate posts. Here is a link to all my posts with the tag Hexadecimal.

Things I Dislike About Blogger

Before you jump headfirst into using Blogger, let me make you aware of some of the bad things. As of July 2007, many of the Blogger markup tags do not generate XHTML compliant code. Prior to 2006 upgrade, all Blogger tags generated valid XHTML. I've brought this error to their attention numerous times but they have never emailed me back. For the most part Blogger no longer cares about their customers that use the FTP option.

This brings me to the second thing I dislike about Blogger. Their support is somewhere between awful and non-existent. It's July 2007 and the site still has some bugs that should have been fixed by now. Other publishers have bugs as well. But other publishers aren't owned by Google, which has a market cap of $168 Billion at this moment.

WordPress, SubText, Community Server?

Before I get a flood of emails, let me explain why I picked Blogger over other blog publishing tools. I made my decision to go with Blogger in December 2006. Some of my reasons have been addressed since then or will be soon.
  • WordPress - WordPress is the best blog tool. However it was coded for Apache using PHP. And although you can run it on a .NET server using IIS, you won't be able to use Master Pages or ASP.NET server controls. If you need a blog and you don't need .NET, use WordPress.
  • SubText - Unlike Blogger it has trackbacks, but it doesn't do tags the way I like. When you click on a tag on this site it goes to more posts on that topic on THIS site. Subtext handles tags by sending your readers off your site and over to Technorati. That was unacceptable for my needs. This project continues to get better. At some point I predict it will become the best .NET publisher.
  • Community Server - I downloaded and tested Community Server. It was a beast. Huge install, slow and full of errors. As a community builder, it may be tweaked to run fine. As a simple blog publisher it was lousy.
  • Other .NET Blog tools - I tested a few other .NET blog tools. Anything that was written using ASP.NET 1.x or didn't have tags was eliminated from consideration. Master Pages were introduced in ASP.NET 2.0, so using 1.x was not an option for me.
  • weblogs.asp.net - Microsoft offers a way for developers to post their blogs on their server. How nice of them. No thanks. DigitalColony.com is my asset and I will spend my time creating value for my domain, not theirs. If design frightens you and you are too cheap to buy a domain and pay for hosting, consider using them. Some of the best ASP.NET bloggers use them.

Overview on Creating an ASP.NET Blog on Blogger

  1. Sign up for a Blogger account.
  2. From the Blogger Dashboard, click the link that says Create a New Blog
  3. Skip the top of this screen and follow the link for Advanced Setup
  4. In addition to completing all the required information, make sure your blog filename is default.aspx.
  5. The next screen forces you to pick an ugly template. Hold your nose and pick one. You can change it later.
  6. From the settings tab, select the Archive link. Change the Archive Filename to archive.aspx.
  7. Create and publish dummy blog post. The post should have one label.
  8. NOTE: Blogger is not expecting the .ASPX file extension and will often post the first blog as .HTML regardless of how you set it up. Delete the dummy post.
  9. Create and publish the second dummy blog post. If you have all your paths correct, it should now be working. Test the home page, the post page, the archive page and the label page. If all is working well, you can delete dummy post number 2. You now have a working .NET Blog running on Blogger.

Master Pages and Getting Rid of the Ugly Template

Some of you will be happy with the template you selected. I bid you farewell at this point in the article. For the rest of us, the goal is now to setup a Master Page and make a nicer template.

Use Visual Studio to create your Master Page and Theme. Then when you are happy, go back to Blogger. From the Template tag, you are now going to step into the Edit HTML screen. Mixing Blogger markup and ASP.NET markup without the benefit of Intellisense or a debugger is tough. The process could take hours. Always backup changes to Notepad as you go.

Cool Looking Code and Extending Blogger

To get cool looking sample code, I use the Manoli.net formatter. And because your blog is in ASP.NET, you can add namespaces or create custom controls. A while back I wrote Blogger Label List for FTP Accounts (ASP.NET), which creates the control in the right column that displays all the tags and number of posts for that tag.

Last Thought

Some of you will give up and decide to use another blog publishing tool or live with one of the ugly templates Blogger provides. It all depends on what is important to you. Digital Colony uses Master Pages, the Yahoo! UI library for positioning, ASP.NET Themes, Imports a custom Namespace and has server-side controls in the template. All that is possible using Blogger.

Labels: ,

 

Comment Out ASP.NET Control

Just like HTML has a way to comment out markup from being displayed by the browser, so does ASP.NET. Below is an example of a commented out ASP.NET tag.
<%-- <asp:Label ID="lblGreeting" runat="server" Text="Hello!"/>--%>

Labels:

 

Fix: RSS Feed is Encoded as ASCII not UTF-8

If you are using an ASP.NET page to generate your RSS feed, you may run into this validation error.

Your feed appears to be encoded as "utf-8", but your server is reporting "US-ASCII".

Set the ResponseEncoding property to utf-8. Here is an example of its use in the Page directive.
<%@ Page Language="C#" ResponseEncoding="utf-8" %>
Use FeedValidator.org to test your RSS and ATOM feeds.

Labels: ,

 

EnableTheming Propery Ignored - The Bug and the Fix

ASP.NET 2.0 gave developers a server-based styling technology called Themes. From the web.config file a single line can dictate how an entire web site will look. The theme of this site at this writing is called Joshua.
<system.web>
 <pages theme="Joshua"/>
</system.web>
Microsoft tells us that is we want to turn off theming for a given page and not the entire site, we can set the EnableTheming property to false. But this doesn't work. The web.config setting will override the Page directive.
<%@ Page Language="C#" EnableTheming="false" %>
To get around this bug for a single page, specify a Theme with no value in the Page directive.
<%@ Page Language="C#" Theme="" %>

Labels: , ,

 

Detect ASP.NET Version Running on Server

Below is a handy script that you can drop onto any server running any version of ASP.NET. When you load the page into the browser, it will report back to you which version of ASP.NET the current web site is configured to run.
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        lblVersion.Text = System.Environment.Version.ToString();
    }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>ASP.NET Version Checker</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>     
        <p>.NET CLR Version: 
        <strong><asp:Label ID="lblVersion" runat="server" /></strong></p>
    </div>
    </form>
</body>
</html>

Labels: ,

 

My First ADO.NET Table

This article was written in 2002 and is based upon ASP.NET 1.0.

The most common data centric task an ASP developer can do is create an HTML table from an SQL query. Below is an example of how to do this with ASP.NET. I'll be connecting to a SQL Server database, but with some slight modifications this code will work for an OLE-DB provider or OLE-ODBC driver. The 3 objects used in this example are SQLConnection, SQLCommand and the SQLDataReader.

Namespace

In order to access data objects a few namespaces are required. The first is System.Data. It contains the basic ADO.NET objects. The second namespace will depend upon your data source. If it is SQL Server then you will use the System.Data.SQLClient namespace. If it isn't then use System.Data.OleDb.
<%@Page Language="VB" %> 
<%@Import Namespace="System.Data" %> 
<%@Import Namespace="System.Data.SqlClient" %>

Connection

For SQL Server the object used to connect to the data source is SQLConnection. Ole-DB requires the OleDbConnection. Like the ADOB.Connection object in classic ADO, this also requires a connection string.
'-- SQL Server Example 
Dim strConnect As String 
strConnect = "server=localhost;database=elvis;uid=king;pwd=tcb;" 
Dim objConnection As New SQLConnection(strConnect) 
objConnection.Open 

Command

Now that I've connected to the data source, we will want to execute an SQL query against it. In this example I want a list of all Elvis movies. The SQLCommand object allows us to execute our SQL statement against the data source. Ole-DB users will use the OleDBCommand object.
Dim strSQL As String 
strSQL = "SELECT filmName, yearReleased FROM TCBFilms" 
Dim objCommand as New SQLCommand(strSQL,objConnection)

DataReader

The DataReader object is way to access the returned data quickly. It is similar to ADO's Recordset object. In the this example, I'll connect the DataReader to an asp:datagrid servers-side control. This will create an HTML table with column headers. No more iterating through the Recordset row-by-row writing HTML to the screen for each row. Ole-DB should use the OleDBDataReader object.
Dim objDataReader as SQLDataReader 
objDataReader = objCommand.ExecuteReader() 
myDataGrid.DataSource = objDataReader 
myDataGrid.DataBind() 
objConnection.Close()
<asp:datagrid id="myDataGrid" runat="server" />

Last Words

This is the just a basic example of using ADO.NET objects to create a database driven HTML table with ASP.NET. ADO.NET has many more powerful features including handling disconnected data and working with XML.

Labels: , , ,

 

Add META keywords and Description in ASP.NET

Here is the syntax for programmatically adding META tags to an ASP.NET 2.0 page.
HtmlMeta metaDesc = new HtmlMeta();
metaDesc.Name = "description";
metaDesc.Content = "Tips on roasting coffee at home";
Page.Header.Controls.Add(metaDesc);

HtmlMeta metaKey = new HtmlMeta();
metaKey.Name = "keywords";
metaKey.Content = "roast, coffee, home, tips";
Page.Header.Controls.Add(metaKey);
ASP.NET will render the above code to valid XHTML META tags.
<meta name="description" content="Tips on roasting coffee at home" />
<meta name="keywords" content="roast, coffee, home, tips" />

Labels: , ,

 

Search Engine Friendly Redirects in ASP.NET

Recently I learned a tough lesson. There are two ways to handle page redirects: the brute force way and the search engine friendly way. When redesigning this site and my fitness site, I used the brute force way. I didn't know any better. Only after seeing it take weeks (Google), months (Yahoo!) or I'm still waiting (Ask) before the site was reindexed did it dawn on me that there had to be a better way. Fortunately there is a better way.

Brute Force Redirect (Server Side)

Response.Redirect("/my-new-page.aspx");

Brute Force Redirect (Client Side)

Once I realized that Yahoo! wasn't comprehending my server-side redirects, I went client-side on them. Maybe this was the trick? It wasn't.
<meta http-equiv="refresh" content="2; URL=http://digitalcolony.com"/>

Search Engine Friendly Redirect

Inside the Page Load add the following code.
Response.Status = "301 Moved Permanently";
Response.AddHeader("Location","http://digitalcolony.com");
Learn from my mistake and perform your search engine redirects without jeopardizing your relationship with the search engines.

Labels: ,

 

Intellisense Not Displaying Controls Inside Wizard Control

I've noticed on many occasions that when adding a new control inside an asp:Wizard control using Visual Studio .NET 2005 that the Intellisense can't find the new control. There is nothing wrong with your code if this happens. It is a bug with Visual Studio. The solution I'm using to fix this is to add my new controls inside the asp:Wizard control first. Then I close and relaunch Visual Studio. At that point the code behind detects the new controls and Intellisense works again. If anyone is aware of a better solution, post your thoughts in the comments.

Labels: , , ,

 

Add Item to DropDownList After DataBind

We often want the first option in a drop-down list to not default to the first item in the list. Instead we would prefer the option of adding a --Select Item-- at the top of the asp:DropDownList. After the DataBind, execute an Item.Insert.
ddlAuthor.DataTextField = "FullName";
ddlAuthor.DataValueField = "AuthorID";
ddlAuthor.DataSource = authorDB.GetAuthors();
ddlAuthor.DataBind();
ddlAuthor.Items.Insert(0, "-- Select Author --");

Labels: ,

 

Roll Your own Drudge Report in ASP.NET

Matt Drudge is my hero. He started The Drudge Report while working the graveyard shift at 7-11. He was at the store when the newspapers first arrived and most of the world was asleep. He was able to parlay that first access to the news into creating one of the most popular sites on the internet. Read his book Drudge Manifesto if you wish to learn more about his rise to fame.

Unlike Matt Drudge, I have no desire to get up super early and go through endless newspapers to create a hand-edited HTML page of my favorite links. Fortunately for me, we now have RSS feeds. This article will mix the ASP.NET article of the day RSS feed with the Drudge look and feel to create the ASP.NET Report.

Reverse Engineer the Drudge Report

The Drudge Report is basically a 3 column layout with an image logo and a top story link. The layout will vary over time, but that is the usual template. For the 3 column news look, we will use the asp:DataList control.

Drudge Font and Image

The logo was created using the Impact font at 100 points and Italic. In PhotoShop, under Blending Options (right mouse click Text layer), add a Drop Shadow. Play with the color, width and direction of the shadow.

Drudge uses a large upper-case Arial font for the top story and Courier for the 3-column stories. All links are black.
body 
{
    font-family: "Courier New", Courier, monospaced;
    font-size: 10pt; 
    font-weight: bold;
    color: #000;
    background-color: #fff;        
}
#headerDIV
{
    text-align: center;
}
#topstoryDIV
{
    font-size:32pt;
    font-weight:bold;
    font-family: Arial, Verdana, Helvetica;
}    
a:link, a:active, a:visited {
    color:#000;
    text-decoration:underline;
    line-height:1.2;
}

The Code

The RSS feed is loaded into an XmlDocument. The first story in the RSS feed will be designated the top story. The rest of the stories will be loaded into a DataTable which is binded to the asp:DataList.

Since ASP.NET code tends not to be photogenic, I've hard-coded links to some of my vacation photos for the top image. To mix it up a little, it will change depending upon the day of the week.
DataTable dt = new DataTable();
dt.Columns.Add("title", Type.GetType("System.String"));
dt.Columns.Add("link", Type.GetType("System.String"));

// ASP.NET Article of the Day
string rssURL = "http://asp.net/community/articles/rss.ashx";

XmlDocument doc = new XmlDocument();
doc.XmlResolver = null;
doc.Load(rssURL);

// display a different image based upon day of week
int dayOfWeek = (int)DateTime.Now.DayOfWeek;

switch (dayOfWeek)
{
    case 0:
        imgHeader.ImageUrl = "http://criticalmas.smugmug.com/photos/117793421-S.jpg";
        break;
    case 1:
        imgHeader.ImageUrl = "http://criticalmas.smugmug.com/photos/118708221-S.jpg";
        break;
    case 2:
        imgHeader.ImageUrl = "http://criticalmas.smugmug.com/photos/95113678-S.jpg";
        break;
    case 3:
        imgHeader.ImageUrl = "http://criticalmas.smugmug.com/photos/91872258-S.jpg";
        break;
    case 4:
        imgHeader.ImageUrl = "http://criticalmas.smugmug.com/photos/91842649-S.jpg";
        break;
    case 5:
        imgHeader.ImageUrl = "http://criticalmas.smugmug.com/photos/79938653-S.jpg";
        break;
    default:
        imgHeader.ImageUrl = "http://criticalmas.smugmug.com/photos/92103181-S.jpg";
        break;
}        

XmlNode oNode = doc.DocumentElement;
XmlNodeList oNodeList = oNode.SelectNodes("channel/item");

for (int itemCount = 0; itemCount < oNodeList.Count; itemCount++)
{
    string title = oNodeList[itemCount].SelectSingleNode("title").InnerText;
    string link = oNodeList[itemCount].SelectSingleNode("link").InnerText;

    if (itemCount == 0)
    {
        // top story
        hypTopStory.Text = title.ToUpper();
        hypTopStory.NavigateUrl = link;
    }
    else
    {
        DataRow dr = dt.NewRow();
        dr["title"] = title;
        dr["link"] = link;
        dt.Rows.Add(dr);
    }
}
dlStories.DataSource = dt;
dlStories.DataBind();

The ASP.NET Page

<form id="form1" runat="server">
<div id="parentDIV">
<p><a href="/">Return to Digital Colony</a></p>
    <div id="headerDIV" >
        <asp:Image ID="imgHeader" runat="server" Width="400" Height="300" BorderWidth="1" />
        <div id="topstoryDIV"><asp:HyperLink ID="hypTopStory" runat="server" /></div>
        <img src="aspnet-report.gif" width="716" height="137" alt="ASP.NET Report" />
    </div>
    <div id="storiesDIV">
    <asp:DataList ID="dlStories" runat="server" RepeatColumns="3" Width="100%" CellPadding="5">
        <ItemTemplate>
          <a href="<%# Eval("link") %>"><%# Eval("title") %></a>
          <hr />
        </ItemTemplate>            
    </asp:DataList>
    </div>        
</div>
</form>

Caching

When pulling RSS feeds, it is wise to be considerate of the bandwidth of your host site. Don't pull a fresh feed with every Page Load. Add an OutputCache directive to your page. For the ASP.NET Report example, I know that this feed updates daily, so I can set the Duration to 86400 which is the number of seconds in one day.
<%@ OutputCache Duration="86400" VaryByParam="none" %>

Working Demo

Visit my ASP.NET Report web page.

Better Than Drudge

Unlike The Drudge Report which uses awful HTML, our ASP.NET version is both XHTML and CSS compliant. It also validates under Section 508.

Your Drudge Report

If you create your own version using a different RSS feed, share it by adding a comment with a link to your page.

UPDATE JULY 2007: Microsoft moved the URL to the ASP.NET RSS feed and now restricts it to 10 articles. This makes it look silly in the Drudge format, but at least it working again. An ideal number of posts in an RSS feed for the Drudge look would be around 40.

Labels: , , , ,

 

Update Fails on FormView

After a miserable afternoon chasing down this bug, I thought it may be helpful to step through why a database update could fail when using the asp:FormView control.

Symptoms

  1. The Database Connection String works, because the "Get" is displaying the record.
  2. The Stored Procedure works, because you ran it inside the database using dummy values.
  3. You stepped through the wizard to build both the asp:SqlDataSource and asp:FormView controls.
  4. When you modify a record and click the Update link, the record stays the same. No error is thrown and no changes are made to the database.

Try This

If this is your situation, then you may have the same problem I ran into. Only after I setup debugging in the stored procedure itself did I realize that the asp:FormView was returning a NULL as the primary key. And if a NULL is passed to an UPDATE stored procedure it will execute error free with an updated ROWCOUNT of zero.

My fix was to add the primary key to DataKeyNames field on the asp:FormView.
<asp:FormView ID="FormView1" runat="server" DataSourceID="SqlDataSource1" DataKeyNames="AuthorID">

Labels: ,

 

Displaying a SmugMug Gallery with ASP.NET

Back in the day I used to host all my own image galleries on my site. It's a tedious process and you can quickly use up your allocated disk space with today's multi-mega pixel cameras. Fortunately we have companies like SmugMug and Flickr that will host, manage and back-up all our images.

The problem with not hosting photo galleries is you send your audience away from your site over to their server. And your photo galleries develop their own audience which knows nothing about the parent site.

I discovered that using the XmlDataSource and DataList ASP.NET controls you can build a photo gallery on your site while the images stay over on SmugMug using a simple RSS feed.

The XmlDataSource

ASP.NET 2.0 introduced the asp:XmlDataSource control which we will use to connect to the SmugMug RSS feed. The DataFile parameter is the path to the RSS file for that photo gallery. In the snippet below, I hard-coded that value. In the example and lab that value is populated in the code behind. Also important is the XPath parameter. This is the address inside the XML Document that holds the information about each photo.
<asp:XmlDataSource ID="xmlDS" runat="server" XPath="rss/channel/item" 
    DataFile= "http://www.smugmug.com/hack/feed.mg?Type=gallery&Data=1838622&format=rss200" />

The RSS Feed (XML Document)

Here is a snippet of how a single photo is represented inside the XML Document. I've removed the portion which deals with the gallery name, as it is not used in this example. In this example the 2 values that are used when rendering the gallery inside the DataList will be link and guid.
<item>
    <title>Image Title</title>
    <link>http://criticalmas.smugmug.com/gallery/1838622/1/92083732</link>
    <description>Image description</description>
    <category>Vacation</category>
    <comments>http://criticalmas.smugmug.com/comment.mg...</comments>
    <exif:DateTimeOriginal>2006-08-24 19:07:19</exif:DateTimeOriginal>
    <pubDate>Thu, 31 Aug 2006 19:02:05 -0700</pubDate>
    <author>feeds-nobody@smugmug.com (criticalmas)</author>
    <guid isPermaLink="true">http://criticalmas.smugmug.com/photos/92083732-Th.jpg</guid>
    <enclosure url="http://criticalmas.smugmug.com/photos/92083732-Th.jpg" length="7741" type="image/jpeg"/>
</item>

The DataList Control

For this gallery, I set the RepeatColumns to 6 and just displayed the thumbnail image with a link to the full-sized image back on the SmugMug web site.
<asp:DataList ID="dlPhotos" runat="server" RepeatColumns="6">
<ItemTemplate>
  <a href="<%# XPath("link").ToString() %>">
     <asp:Image ID="img" ImageUrl='<%# XPath("guid") %>' runat="server"  /></a>
</ItemTemplate>
</asp:DataList>

Sample ASPX Using a Gallery Dropdown

The GalleryID is pulled from the URL of the photo gallery over on SmugMug. This is covered in detail in the sample lab.
<asp:XmlDataSource ID="xmlDS" runat="server" XPath="rss/channel/item" />
<h4>Select Gallery</h4>
<asp:DropDownList ID="ddlGallery" runat="server" AutoPostBack="true">
    <asp:ListItem Text="1838622: Uruguay - Colonia" Value="1838622" />
    <asp:ListItem Text="2473610: Lower Hellhole Canyon Desert Hike" Value="2473610" />
    <asp:ListItem Text="1887671: New Zealand - Whakarewarewa Thermal Village" Value="1887671" />
</asp:DropDownList>
<br /><br />
<asp:DataList ID="dlPhotos" runat="server" RepeatColumns="6">
<ItemTemplate>
    <a href="<%# XPath("link").ToString() %>">
        <asp:Image ID="img" ImageUrl='<%# XPath("guid") %>' runat="server" /></a>
</ItemTemplate>
</asp:DataList>
<asp:Label ID="lblError" Visible="false" runat="server" />

And the Code Behind (C#)

protected void Page_Load(object sender, EventArgs e)
{
   string rssURL;
   string galleryID;
   galleryID = ddlGallery.SelectedValue.ToString();
   rssURL = "http://www.smugmug.com/hack/feed.mg?Type=gallery&Data=" + galleryID + "&format=rss200";

   try
   {
       xmlDS.DataFile = rssURL;
       dlPhotos.DataSource = xmlDS;
       dlPhotos.DataBind();
   }
   catch (XmlException err)
   {
       lblError.Text = "Oops, looks like an error occured with this gallery: " + err.Message;
       lblError.Visible = true;            
   }   
}

Lab Demo

SmugMug Image Gallery in ASP.NET

The gallery uses a simple 6 column layout. Once you have the RSS feed, you can be as creative as you like in designing the layout for your photo gallery.

Other Gallery options

This demo is for the standard galleries. SmugMug has other Feed options described here.

My SmugMug referral code is: IzodUqeQndZYc
It will save you $5 on any new account.

Labels: , , , , , ,

 

DataFormatString Not Working? Here is the Fix

If you have a GridView control and are are trying to data format a BoundField, you may find your format instructions are being ignored. The fix is to add HtmlEncode="False" to the asp:BoundField. Then the DataFormatString should work.
<asp:BoundField DataField="LastModified" 
  HeaderText="Modified" 
  HtmlEncode="False" 
  DataFormatString="{0:d}" />

Labels:

 

Mask Email ASCII Control for ASP.NET

In the article Masking Your Email Address, we went over why you would want to hide your email address inside the source code of an HTML document, but still make it visible to the human readers of that page.

Encapsulating the code into a single .NET user control is ideal for protecting email addresses for ASP.NET sites.

Step 1 - Create a Web User Control

Add an asp:Literal control to that page.
<asp:Literal ID="ltEmail" runat="server" />

Step 2 - Jump to the Code Behind

The EmailMask code follows. Note the name of the class lab_maskemail_EmailMask was created by Visual Studio for me. Use whatever name you like or Visual Studio recommends here. The rest of the code should be the same. This code is for ASP.NET 2.0.
using System;
using System.Text;
using System.Web;

public partial class lab_maskemail_EmailMask : System.Web.UI.UserControl
{
    private string emailAddress;
    public string EmailAddress
    {
        get { return emailAddress; }
        set { emailAddress = value; } 
    }

    private string visibleAddress;
    public string VisibleAddress
    {
        get 
        {
            // if unassigned return emailAddress
            if (visibleAddress==null || visibleAddress.Length == 0)
            {
                return emailAddress;
            }
            else
            {
                return visibleAddress; 
            }
            
       }
        set { visibleAddress = value; }
    }

    private string mouseoverTag;
    public string MouseoverTag
    {
        get { return mouseoverTag; }
        set { mouseoverTag = value; }
    }

    private string subject;
    public string Subject
    {
        get { return subject; }
        set { subject = value; }
    }

    private string cssClass;
    public string CssClass
    {
        get { return cssClass;}
        set { cssClass = value; }
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        // The MIN required for control is an email address, confirm it exists
        if (emailAddress == null || emailAddress.Length == 0)
        {
            ltEmail.Text = "Assign EmailAddress to Control";
            return;
        }

        // Build ASCII encoded Link
        StringBuilder asciiLink = new StringBuilder();
        asciiLink.Append("<a href=\"m&#97;ilto:");
        asciiLink.Append(ASCIIEncode(EmailAddress));
        if(subject != null && subject.Length > 0 )
        {
            asciiLink.Append("?subject=" + subject);
        }
        asciiLink.Append("\"");
        if (mouseoverTag != null && mouseoverTag.Length > 0)
        {
            asciiLink.Append(" title=\"" + mouseoverTag + "\"");
        }
        if (cssClass !=null && cssClass.Length > 0)
        {
            asciiLink.Append(" class=\"" + cssClass + "\"");
        }
        asciiLink.Append(">");
        asciiLink.Append(ASCIIEncode(VisibleAddress));
        asciiLink.Append("</a>");

        ltEmail.Text = asciiLink.ToString();      
    }

    protected string ASCIIEncode(string regularText)
    {
        regularText = regularText.Trim();

        StringBuilder encodeSB = new StringBuilder();
        char regularLetter;

        for (int j = 0; j < regularText.Length; j++)
        {
            // peel off 1 character at a time
            regularLetter = regularText[j];
            encodeSB.Append("&#" + Convert.ToInt32(regularLetter).ToString() + ";");
        }

        return encodeSB.ToString();
    }

Step 3 - Create a Test Page

Register the control at the top using whatever path and naming convention you've chosen.
<%@ Register Src="~/lab/maskemail/EmailMask.ascx" TagName="EmailMask" TagPrefix="dc" %>
Inside the ASP.NET page and control is used like below.
<dc:EmailMask ID="eMask" 
 CssClass="Summer" 
 EmailAddress="larryKing@cnn.com" 
 VisibleAddress="Email Larry King"   
 MouseoverTag="Send feedback" 
 Subject="Tonight's Show" 
 runat="server" />

Labels: , ,

 

Delete Files using C#

When I first wrote the original version of the Mask Email Image Generator (Email Obfuscator), I didn't bother to add any code to periodically remove the images from the server. Yesterday I discovered it had almost 50,000 images in that folder. Not to repeat the same mistake, I wrote a function to delete image files that are older than 3 minutes. Sample code follows.
protected void CleanImageFolder()
{
    string imgFolder = Server.MapPath("~/lab/maskemail/img/");
    string[] imgList = Directory.GetFiles(imgFolder, "*.jpg");
    foreach (string img in imgList)
    {
        FileInfo imgInfo = new FileInfo(img);
        if (imgInfo.LastWriteTime < DateTime.Now.AddMinutes(-3))
        {
            imgInfo.Delete();
        }
    }
}

Labels: , , ,

 

ASP.NET Email Using VB

This article is for ASP.NET 1.x. For a 2.0 snippet read Sending Email in ASP.NET 2.0 (VB.NET).

By now we all know how to send server-side email using classic ASP. We either use Microsoft's CDONTS technology or a third-party component such as ServerObjects's ASPMail or Persist's ASPEmail. With ASP.NET server-side email is built-in and can be accessed using the System.Web.Mail namespace. In this article, I will demonstrate how to generate an email with form validation.

The Example

A lot of people post their resume on their web site. The advantage of having a resume available for recruiters and potential employers on a web site is valuable. The downside is you don't know who is reading your resume. A simple way to resolve that is to set up a form where the user requests that the resume be emailed to them. Then you can blind carbon copy yourself and you'll know exactly who showed interest. No more guessing if an employer pulled your resume, you'll have a receipt.

The Form

Elements we will want to capture are name, email, company, and the format of the resume they wish to receive. All fields will be required to successfully submit the form. Let's code the form using ASP.NET Web Form controls and attach validation controls.
<div id="requestResume" runat="server"> 
<!-- The values on this FORM will be posted back to the server. --> 
<!-- We will add the 'runat="server"' to the FORM and controls --> 
<form id="Form1" method="post" runat="server"> 
<table> 
<tr><td>Name</td>
<td><input type="text" id="txtName" 
value="" size="30" maxlength="50" 
runat="server" name="txtName"/> 
<!-- txtName is required. We will attach a 
RequiredFieldValidator by assigning 
txtName as the ControlToValidate --> 
<asp:RequiredFieldValidator id="valRequiredName" 
runat="server"     
ControlToValidate="txtName"     
ErrorMessage="* You must enter your Full Name."
     Display="dynamic".>
</td></tr> 
<tr><td>Email</td>
<td> <input type="text" id="txtEmail" 
value="" size="30" maxlength="50" 
runat="server" NAME="txtEmail"/> 
<!-- txtEmail is both required and must be a valid email address. -->
<asp:RequiredFieldValidator id="valRequiredEmail" 
runat="server"     
ControlToValidate="txtEmail"     
ErrorMessage="* You must enter your Email address."     
Display="dynamic"/>
<!-- txtEmail will be validated using Regular Expression. 
It is also attached to txtEmail via the ControlToValidate property. --> 
<asp:RegularExpressionValidator id="valValidEmail" 
runat="server"     
ControlToValidate="txtEmail"     
ValidationExpression="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*"     
ErrorMessage="* You must enter a valid Email address"     
Display="dynamic"/> 
</td></tr> 
<tr><td>Company</td>
<td> <!-- txtCompany works exactly like txtName. --> 
<input type="text" id="txtCompany" value="" size="30" 
maxlength="50" runat="server" 
NAME="txtCompany"/> 
<asp:RequiredFieldValidator id="valRequiredCompany" 
runat="server"     
ControlToValidate="txtCompany"     
ErrorMessage="* You must enter your Company name."     
Display="dynamic"/>
</td></tr> 
<tr><td>Resume Format</td>
<td> 
<!-- The dropdown list of resume formats 
will be assigned on the server --> 
<asp:DropDownList id="selResume" Runat="server"/>
</td></tr> 
<tr>
<td></td>
<td> 
<!-- When the FORM is submitted it will 
execute the "btnSubmit_OnClick" 
Subroutine on the server. --> 
<asp:button type="submit" name="btnSubmit" 
onclick="btnSubmit_OnClick" 
text="Request Resume" 
runat="server"/> </td></tr> 
</table> 
</form> 
</div>

Server-Side Code (VB.NET)

Regardless of what language you utilize, the above FORM will be coded the same way. To proceed on the server-side code, we need to pick a language. C# and Visual Basic are the two most popular at this time. Let's proceed in VB. The first line imports the ASP.NET code needed to perform sending email.
<%@ Import Namespace="System.Web.Mail" %> 
<script language="VB" runat="server">     
Sub Page_Load()         
'=== When the page first loads populate the Resume Format dropdown         
If NOT IsPostBack() Then            
   Call populateResumeFormats()            
   selResume.DataValueField = "Value"            
   selResume.DataTextField = "Key"            
   selResume.DataBind()         
End If     
End Sub     

Public Sub populateResumeFormats         
'=== create a HashTable to populate the dropdown.         
   Dim dropResume As New HashTable(4)         
   dropResume.Add("Word 97 (.RTF)", "resume.rtf")         
   dropResume.Add("HTML", "resume.htm")         
   dropResume.Add("Word 2002", "resume.doc")         
   dropResume.Add("Text", "resume.txt")         
   selResume.DataSource = dropResume     
End Sub     

Sub btnSubmit_OnClick(o as Object, e as EventArgs)         
'=== The Page.IsValid call checks all the validation controls. 
'=== If they all pass then the form is valid.         
If Page.IsValid Then             
'=== no longer display the FORM, display a status message. 
   requestResume.InnerHTML = "You will receive an email shortly with my resume.
 Thank you for your interest."             
   Call sendResume         
End If     
End Sub     

Sub sendResume        
'=== create a MailMessage         
  Dim resumeEmail as New MailMessage         
  Dim strResumeFilePath as String         
'=== To, CC, BCC, From, Subject are self-explainatory         
  resumeEmail.To = txtEmail.value
  resumeEmail.BCC = "larryking@cnn.com"         
  resumeEmail.From = "larryking@cnn.com"         
  resumeEmail.Subject = "Resume for Larry King"         
  resumeEmail.BodyFormat = MailFormat.text         
  resumeEmail.Body = txtName.value & ", attached is a copy of my resume. 
I look forward to working with you. - MAS "         
'=== for this example all versions of the resume reside in the 
/resume directory           
  strResumeFilePath = Server.MapPath("/") & "\resume\" & selResume.SelectedItem.value         
'=== create an attachment and add the file path to that attachment         
  Dim resumeAttachment as New MailAttachment(strResumeFilePath)            
  resumeEmail.Attachments.Add(resumeAttachment)         
'=== send the email         
  SmtpMail.Send(resumeEmail)     
End Sub </script>

Last Words

Pretty straightforward isn't it? Now you have a working resume mailer. The employer can specify the preferred format, and the potential employee gets to track who is viewing the resume.

This article was written in 2001 for ASP.NET 1.0. Forgive the non XHTML.

Labels: , ,

 

Using Yahoo! Maps GeoCoding API in C#

Building a map using Yahoo! Maps or Google Maps requires Latitude and Longitude points. Until Yahoo! released it's GeoCoding APIs getting address latitude and longitude was neither easy or free. At this time Yahoo! allows you to GeoCode 50,000 addresses a day. The code below will call the Yahoo! GeoCoding API using C# and ASP.NET 2.0. Yahoo!

Latitude and Longitude Precision

When supplying Yahoo! with an address, it will try to return the highest level of precision. The returned XML document will tell you how precise the latitude and longitude are. If it can't resolve an address, it will return a warning. Yahoo! Map

Files Included in Download

* GeoCode.aspx - Form to enter address. Also will display response from Yahoo!.
* GeoCode.aspx.cs - Calls class to make geocoding request.
* GeoAddress.cs - An address class.
* GeoAddressAPI.cs - Class which calls Yahoo! and parses response.
* web.config - Holds application ID required by Yahoo!

GeoCode.zip - Don't forget to enter your application ID at the end of the querystring in the web.config file. This format supports ASP.NET 2.0. If you are running 1.1, you will need to make some minor adjustments to how you store your API url in the web.config file.

Working Demo

GeoCoding C# Yahoo Demo

Labels: , , ,

 

Detecting HTTP vs HTTPS in ASP.NET

One simple command will let you know if the user is accessing your page via SSL.
Request.IsSecureConnection

Example

if (Request.IsSecureConnection)
{
    Response.Write("HTTPS");
}
else
{
    Response.Write("HTTP");
}

Redirecting to HTTPS

if (!Request.IsSecureConnection)
{
    // send user to SSL 
    string serverName =HttpUtility.UrlEncode(Request.ServerVariables["SERVER_NAME"]);        
    string filePath = Request.FilePath;
    Response.Redirect("https://" + serverName + filePath);
}
HttpRequest.IsSecureConnection Property (MSDN)

Labels:

 

Blogger Label List for FTP Accounts (ASP.NET)

The new version of Blogger includes tags. They call them Labels. Hosted accounts (BlogSpot) get templates which display a list of tags for quick navigation. This feature is not available to FTP accounts. This post is a hack to create a user control in ASP.NET that displays a Label List. Before I do, lets breakdown how labels look on the file system.
  1. Labels reside in a folder. By default that folder is called labels.
  2. Every label gets one file.
  3. The label file name is the same as the label name plus extension.
  4. Inside each file is a copy of each blog with that label.
  5. Blogger places the class name of blogger-labels before the Labels text.
For example, on this site I have a label called Javascript.
http://digitalcolony.com/labels/Javascript.aspx
It resides in the labels folder just outside the blog root. The name of the label is Javascript. The name of the file is Javascript.aspx. As of this writing there is 1 post and therefore 1 instance of blogger-labels in the View Source. With the above facts laid out, this is an overview of how to create your own Label List control.
  1. Using server-side code to handle Files and Directories, open the labels folder.
  2. Collect each file name (minus the file extension).
  3. Open each file and count how many times blogger -labels appears
  4. Write label and count to screen.
Here is my .NET code for the Blogger Label List control. On the .ASPX page that the control will be on add this line to the top to register the control.
<%@ Register Src="~/LabelList.ascx" TagName="LabelList" TagPrefix="dc" %>
And the control on the page gets placed with this line.
<dc:LabelList ID="LabelList1" runat="server" Folder="labels" DisplayCount="true" />
And here is the code for the LabelList.ascx user control.
using System;
using System.Web;
using System.IO;
using System.Text;

public partial class LabelList : System.Web.UI.UserControl
{
    protected void Page_Load(object sender, EventArgs e)
    {       
        string rootDir = Server.MapPath("~");
        string labelDirectory = rootDir + folder;
        StringBuilder sb = new StringBuilder();
        if(Directory.Exists(labelDirectory)){
            
            string[] fileList = Directory.GetFiles(labelDirectory,"*.aspx");
            string labelName;
            string labelCount;

            foreach (string file in fileList)
            {
                FileInfo fi = new FileInfo(file);
                labelName = fi.Name.Replace(fi.Extension, "");

                if (displayCount)
                {
                    StreamReader sr = new StreamReader(fi.FullName);
                    string webPage = sr.ReadToEnd();
                    sr.Close();
                    int webPageLen = webPage.Length;
                    webPage = webPage.Replace("bl0gger-labels", "bl0gger-labelsX");
                    int iLabelCount = webPage.Length - webPageLen;
                    labelCount = "(" + iLabelCount.ToString() + ")";
                }
                else
                {
                    labelCount = "";
                }
                folder = folder.Replace("\\","/");
                if (folder.Substring(folder.Length - 1, 1) == "/")
                    folder = folder.Substring(0, folder.Length - 1);
                sb.Append("<a href=\"" + folder + "/" + fi.Name + "\">" + 
labelName + labelCount + "</a><br/>");               
            }            
        } 
        else {
            sb.Append("No labels in folder [" + labelDirectory + "] defined.");
        }
        Response.Write(sb.ToString());
    }

    private string folder;
    public string Folder
    {
        get { return folder; }
        set {
            if (value.Substring(0, 1) == "/")
                folder = value;
            else
                folder = "/" + value;
            folder = folder.Replace("/","\\");
        }
    }
    private bool displayCount;
    public bool DisplayCount
    {
        get { return displayCount; }
        set { displayCount = value; }
    }
}
Note that the code above misspells blogger-labels as to not confuse the count of the Label control that I'm using for this site. If you cut and paste the code, fix that spelling.

PHP and ASP coders should be able to hack up something similar using the above code as a framework.

UPDATE (Feb 2007): I created a version of Blogger Label Lists using Classic ASP.

Labels: ,

 

Tips to Create a Javascript Directory or Newsfeed

One of the developer newsletters I received a while back directed me to the DevASP site. On that site I spied something wonderful. They had created a directory of their site with 1 line of Javascript code, available to any web site. Although I had no need to use their directory, I had to create one of these for INeedCoffee and DeepFitness. It was essential that I figured out how this magic took place. Getting ASP to write valid Javascript was easy enough, figuring out which folder the user clicked on wasn't. After playing with code for a few hours, I decided to contact DevASP. After all, they are supposedly a site dedicated to helping developers. Did they help me? No. They didn't even return my email. So much for helping the developer. After getting shunned by DevASP, my goal was to figure it out, perfect it, and then make the code available here on DigitalColony.

It took quite a bit of research as well as trial and error, but I figured it out. My hope is after you read this you won't spend as much time as I did creating your Javascript directory.

The DeepFitness Newsfeed

Before we proceed, here is the Javascript newsfeed that was created for DeepFitness.
<script language="JavaScript" 
src="http://www.deepfitness.com/togo.aspx" 
type="text/javascript"></script>

Design Backwards

My biggest mistake completing this project was jumping straight into the server-side code. In order for this project to work, you need to have valid HTML generated by valid Javascript, produced by bug-free server-side code. If you jump into the server-side code and get a bug, you won't see it on the screen. The View Source will only yield you that single line of javascript code, which doesn't make debugging an easy task. You can minimize these problems by mocking up the HTML first. Once you've completed the mockup, test your HTML with different DOCTYPE values. Since your javascript directory or newsfeed will reside on many different pages with potentially different DOCTYPE values, it's important that your HTML looks good in each scenerio. Note: unless you do a pure CSS output, your code probably won't validate under each DOCTYPE case. That's OK. If the host of your directory wanted W3C approval, they would be importing your RSS feed and styling it themselves. Breaking Down the Steps

Here is a quick overview of what takes place after an HTML page is loaded that points to the DeepFitness directory generator (www.deepfitness.com/togoDir.aspx).

  1. The .ASPX page will use Response.Write to output Javascript's document.write. Inside the document.write will be valid HTML.
  2. When the DeepFitness directory loads, the code will check the HTTP Referral. In classic ASP this is done with a Request.ServerVariable("http_referer"). In .NET, it is retrieved with a Request.UrlReferrer.
  3. Inside the UrlReferrer there may be a querystring telling the code which folder has been requested. If there is no querystring, that tells the code to load the root. If there is a querystring, our next step is to parse it and retrieve the folderID being requested.
  4. Once you have the folderID, then comes the easy stuff. Using that you can get a list of folders and links that match that folder and provide a link back to the home page. This part of the task will depend upon your database structure and what data your wish to represent.

Potential Problems

Above I described how the DOCTYPE on the host page can potentially change the look of your outputted javascript. That can easily be avoided by testing mockups prior to starting your server-side coding. A more serious problem is the prevelance of software tools that block the HTTP referral. Programs like Norton Internet Security and the browser Opera have options to disguise what page the user is coming from. In order for a nested directory application to work, we need to know this information. But if we can't have access to it, then they can't have access to our directory. The DeepFitness directory will hide the folders and just display the Latest Updates if the user blocks their HTTP Referral. If we didn't hide the folders, those users would click onto folders only to be returned back to the root everytime. Not exactly a friendly user interface. Prior to coding, define the behavior your directory should take if it you don't have access to the user's HTTP Referral.

Formatting For Javascript

Certain characters in your data will cause Javascript to crash. Special characters need to be handled with your server-side code. Below is a C# function that I create to do just such task.

EX: FormatForJS(myDataReader["currentBranch"].ToString());

Converting this to Classic ASP should be easy enough

protected string FormatForJS(object input) { 
  string data = input.ToString(); 
  // cast the input to a string 
  data = data.Trim(); 
  // replace those characters that will crash JAVASCRIPT 
  data = data.Replace("'", "\\'"); 
  data = data.Replace("\n", ""); 
  data = data.Replace("\r", ""); 
  return data; 
}

Prevent Caching

Users will often use the back button on their browsers to navigate back up your directory. Unless we do something in our code, the directory will not refresh and the user will remain on the same folder. In .NET the way we force a refresh is with this command: Response.Cache.SetNoStore(); By doing this, the code is never cached and the back button will behave the same as our back link.

Speed Improvements

You can create a Javascript directory using any server-side language. They should all do a fine job, however if you expect a lot of web sites to host it, you'll want it to run as fast as possible. One option is to use compiled code (.NET) instead of scripting code (Classic ASP). And to get the database portion smoking fast, compile your SQL into views and/or stored procedures.

This article was originally written in 2004.

Labels: ,

 

SortedList Example for ASP.NET in C#

The SortedList object uses a key/value combination to sort a list. The SortedList can be databound to the following ASP.NET controls:
  • asp:RadioButtonList
  • asp:CheckBoxList
  • asp:DropDownList
  • asp:ListBox
Our examples will bind a SortedList to an asp:ListBox.
<asp:ListBox ID="lsbPresidents" runat="server" />

Numeric Sort

SortedList s = new SortedList();
s.Add(16, "Lincoln");
s.Add(42, "Clinton");
s.Add(40, "Reagan");
s.Add(1, "Washington");

lsbPresidents.DataSource = s;
lsbPresidents.DataValueField = "key";
lsbPresidents.DataTextField = "value";
lsbPresidents.DataBind();
Results:
Washington
Lincoln
Reagan
Clinton      

Alphabetic

SortedList s = new SortedList();
s.Add("L", "Lincoln");
s.Add("C", "Clinton");
s.Add("R", "Reagan");
s.Add("W", "Washington");

lsbPresidents.DataSource = s;
lsbPresidents.DataValueField = "key";
lsbPresidents.DataTextField = "value";
lsbPresidents.DataBind();
Results:
Clinton   
Lincoln  
Reagan 
Washington

Date Sort

SortedList s = new SortedList();
s.Add(Convert.ToDateTime("2/12/1809"), "Lincoln");
s.Add(Convert.ToDateTime("8/19/1946"), "Clinton");
s.Add(Convert.ToDateTime("2/6/1911"), "Reagan");
s.Add(Convert.ToDateTime("2/22/1732"), "Washington");

lsbPresidents.DataSource = s;
lsbPresidents.DataValueField = "key";
lsbPresidents.DataTextField = "value";
lsbPresidents.DataBind();
Results:
Washington
Lincoln
Reagan
Clinton      

Labels:

 

Digital Colony Copyright © 1999-2009 XHTML   508
This site uses Blogger, which is not 100% XHTML compliant.
Try...Catch Disclaimer: For brevity many examples do not include error handling. That is your responsibility.